@anuma/sdk 6.25.1 → 6.25.2
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 +343 -146
- package/dist/expo/index.d.mts +34 -9
- package/dist/expo/index.d.ts +34 -9
- package/dist/expo/index.mjs +343 -146
- package/dist/react/index.cjs +343 -146
- package/dist/react/index.d.mts +34 -9
- package/dist/react/index.d.ts +34 -9
- package/dist/react/index.mjs +343 -146
- package/dist/server/index.cjs +343 -146
- package/dist/server/index.d.mts +34 -9
- package/dist/server/index.d.ts +34 -9
- package/dist/server/index.mjs +343 -146
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -15457,7 +15457,7 @@ __decorateClass([
|
|
|
15457
15457
|
], VaultFolder.prototype, "context", 2);
|
|
15458
15458
|
|
|
15459
15459
|
// src/lib/db/schema.ts
|
|
15460
|
-
var SDK_SCHEMA_VERSION =
|
|
15460
|
+
var SDK_SCHEMA_VERSION = 47;
|
|
15461
15461
|
var sdkSchema = (0, import_watermelondb13.appSchema)({
|
|
15462
15462
|
version: SDK_SCHEMA_VERSION,
|
|
15463
15463
|
tables: [
|
|
@@ -15473,7 +15473,11 @@ var sdkSchema = (0, import_watermelondb13.appSchema)({
|
|
|
15473
15473
|
// source. `watermark` alone is an id, and deleting that message erased
|
|
15474
15474
|
// the anchor — see the column note in extractionJobs/models.ts.
|
|
15475
15475
|
{ name: "watermark_seq", type: "number", isOptional: true },
|
|
15476
|
-
{ name: "folder_id", type: "string", isOptional: true }
|
|
15476
|
+
{ name: "folder_id", type: "string", isOptional: true },
|
|
15477
|
+
// Poison accounting for the head batch; see extractionJobs/models.ts.
|
|
15478
|
+
{ name: "failed_sessions", type: "number", isOptional: true },
|
|
15479
|
+
{ name: "failed_head", type: "string", isOptional: true },
|
|
15480
|
+
{ name: "failed_at", type: "number", isOptional: true }
|
|
15477
15481
|
]
|
|
15478
15482
|
}),
|
|
15479
15483
|
// Chat storage tables
|
|
@@ -16497,6 +16501,22 @@ var sdkMigrations = (0, import_migrations.schemaMigrations)({
|
|
|
16497
16501
|
]
|
|
16498
16502
|
})
|
|
16499
16503
|
]
|
|
16504
|
+
},
|
|
16505
|
+
// v46 -> v47: persisted poison count (and when it was last counted) for a
|
|
16506
|
+
// durable-extraction job's head batch. NULL = never failed, which is right
|
|
16507
|
+
// for every existing row.
|
|
16508
|
+
{
|
|
16509
|
+
toVersion: 47,
|
|
16510
|
+
steps: [
|
|
16511
|
+
(0, import_migrations.addColumns)({
|
|
16512
|
+
table: "memory_extraction_jobs",
|
|
16513
|
+
columns: [
|
|
16514
|
+
{ name: "failed_sessions", type: "number", isOptional: true },
|
|
16515
|
+
{ name: "failed_head", type: "string", isOptional: true },
|
|
16516
|
+
{ name: "failed_at", type: "number", isOptional: true }
|
|
16517
|
+
]
|
|
16518
|
+
})
|
|
16519
|
+
]
|
|
16500
16520
|
}
|
|
16501
16521
|
]
|
|
16502
16522
|
});
|
|
@@ -20382,8 +20402,42 @@ async function vaultMemoryToStored(memory, walletAddress, signMessage, embeddedW
|
|
|
20382
20402
|
}
|
|
20383
20403
|
return raw;
|
|
20384
20404
|
}
|
|
20405
|
+
function populateNewVaultMemory(record, ctx, opts, encryptedContent) {
|
|
20406
|
+
record._setRaw("content", encryptedContent);
|
|
20407
|
+
record._setRaw("scope", opts.scope ?? "private");
|
|
20408
|
+
record._setRaw("folder_id", opts.folderId ?? null);
|
|
20409
|
+
record._setRaw("user_id", ctx.userId ?? null);
|
|
20410
|
+
record._setRaw("is_deleted", false);
|
|
20411
|
+
if (opts.embedding !== void 0) {
|
|
20412
|
+
record._setRaw("embedding", opts.embedding);
|
|
20413
|
+
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
20414
|
+
}
|
|
20415
|
+
if (opts.sourceChunkIds !== void 0) {
|
|
20416
|
+
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
20417
|
+
}
|
|
20418
|
+
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
20419
|
+
record._setRaw("source", opts.source ?? "manual");
|
|
20420
|
+
if (opts.eventTime) {
|
|
20421
|
+
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
20422
|
+
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
20423
|
+
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
20424
|
+
}
|
|
20425
|
+
if (opts.factType !== void 0) {
|
|
20426
|
+
record._setRaw("fact_type", opts.factType);
|
|
20427
|
+
}
|
|
20428
|
+
if (opts.trustTier !== void 0) {
|
|
20429
|
+
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
20430
|
+
}
|
|
20431
|
+
record._setRaw("visibility", opts.visibility ?? "private");
|
|
20432
|
+
record._setRaw(
|
|
20433
|
+
"published_at",
|
|
20434
|
+
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
20435
|
+
);
|
|
20436
|
+
if (opts.geohash !== void 0) {
|
|
20437
|
+
record._setRaw("geohash", opts.geohash);
|
|
20438
|
+
}
|
|
20439
|
+
}
|
|
20385
20440
|
async function createVaultMemoryOp(ctx, opts) {
|
|
20386
|
-
const scope = opts.scope ?? "private";
|
|
20387
20441
|
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
20388
20442
|
opts.content,
|
|
20389
20443
|
ctx.walletAddress,
|
|
@@ -20393,47 +20447,14 @@ async function createVaultMemoryOp(ctx, opts) {
|
|
|
20393
20447
|
const created = await ctx.database.write(async () => {
|
|
20394
20448
|
if (ctx.canWrite && !await ctx.canWrite())
|
|
20395
20449
|
throw new Error("Memory source is no longer eligible");
|
|
20396
|
-
return ctx.vaultMemoryCollection.create(
|
|
20397
|
-
record
|
|
20398
|
-
|
|
20399
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
20400
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
20401
|
-
record._setRaw("is_deleted", false);
|
|
20402
|
-
if (opts.embedding !== void 0) {
|
|
20403
|
-
record._setRaw("embedding", opts.embedding);
|
|
20404
|
-
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
20405
|
-
}
|
|
20406
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
20407
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
20408
|
-
}
|
|
20409
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
20410
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
20411
|
-
if (opts.eventTime) {
|
|
20412
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
20413
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
20414
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
20415
|
-
}
|
|
20416
|
-
if (opts.factType !== void 0) {
|
|
20417
|
-
record._setRaw("fact_type", opts.factType);
|
|
20418
|
-
}
|
|
20419
|
-
if (opts.trustTier !== void 0) {
|
|
20420
|
-
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
20421
|
-
}
|
|
20422
|
-
record._setRaw("visibility", opts.visibility ?? "private");
|
|
20423
|
-
record._setRaw(
|
|
20424
|
-
"published_at",
|
|
20425
|
-
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
20426
|
-
);
|
|
20427
|
-
if (opts.geohash !== void 0) {
|
|
20428
|
-
record._setRaw("geohash", opts.geohash);
|
|
20429
|
-
}
|
|
20430
|
-
});
|
|
20450
|
+
return ctx.vaultMemoryCollection.create(
|
|
20451
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContent)
|
|
20452
|
+
);
|
|
20431
20453
|
});
|
|
20432
20454
|
return vaultMemoryToStored(created, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
20433
20455
|
}
|
|
20434
20456
|
async function createSupersedingMemoryOp(ctx, opts, targetId) {
|
|
20435
20457
|
if (!targetId) return { created: null, retired: false };
|
|
20436
|
-
const scope = opts.scope ?? "private";
|
|
20437
20458
|
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
20438
20459
|
opts.content,
|
|
20439
20460
|
ctx.walletAddress,
|
|
@@ -20451,27 +20472,9 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
|
|
|
20451
20472
|
return;
|
|
20452
20473
|
}
|
|
20453
20474
|
if (target.isDeleted || target.supersededBy || !isOwnedByCtxUser(ctx, target)) return;
|
|
20454
|
-
createdRecord = await ctx.vaultMemoryCollection.create(
|
|
20455
|
-
record
|
|
20456
|
-
|
|
20457
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
20458
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
20459
|
-
record._setRaw("is_deleted", false);
|
|
20460
|
-
if (opts.embedding !== void 0) {
|
|
20461
|
-
record._setRaw("embedding", opts.embedding);
|
|
20462
|
-
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
20463
|
-
}
|
|
20464
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
20465
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
20466
|
-
}
|
|
20467
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
20468
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
20469
|
-
if (opts.eventTime) {
|
|
20470
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
20471
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
20472
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
20473
|
-
}
|
|
20474
|
-
});
|
|
20475
|
+
createdRecord = await ctx.vaultMemoryCollection.create(
|
|
20476
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContent)
|
|
20477
|
+
);
|
|
20475
20478
|
await target.update((r) => {
|
|
20476
20479
|
r._setRaw("superseded_by", createdRecord.id);
|
|
20477
20480
|
r._setRaw("superseded_at", Date.now());
|
|
@@ -20551,41 +20554,9 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
|
|
|
20551
20554
|
if (ctx.canWrite && !await ctx.canWrite())
|
|
20552
20555
|
throw new Error("Memory source is no longer eligible");
|
|
20553
20556
|
const prepared = optionsArray.map(
|
|
20554
|
-
(opts, i) => ctx.vaultMemoryCollection.prepareCreate(
|
|
20555
|
-
record
|
|
20556
|
-
|
|
20557
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
20558
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
20559
|
-
record._setRaw("is_deleted", false);
|
|
20560
|
-
if (optionsArray[i].embedding !== void 0) {
|
|
20561
|
-
record._setRaw("embedding", optionsArray[i].embedding);
|
|
20562
|
-
record._setRaw("embedding_model", optionsArray[i].embeddingModel ?? null);
|
|
20563
|
-
}
|
|
20564
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
20565
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
20566
|
-
}
|
|
20567
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
20568
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
20569
|
-
if (opts.eventTime) {
|
|
20570
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
20571
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
20572
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
20573
|
-
}
|
|
20574
|
-
if (opts.factType !== void 0) {
|
|
20575
|
-
record._setRaw("fact_type", opts.factType);
|
|
20576
|
-
}
|
|
20577
|
-
if (opts.trustTier !== void 0) {
|
|
20578
|
-
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
20579
|
-
}
|
|
20580
|
-
record._setRaw("visibility", opts.visibility ?? "private");
|
|
20581
|
-
record._setRaw(
|
|
20582
|
-
"published_at",
|
|
20583
|
-
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
20584
|
-
);
|
|
20585
|
-
if (opts.geohash !== void 0) {
|
|
20586
|
-
record._setRaw("geohash", opts.geohash);
|
|
20587
|
-
}
|
|
20588
|
-
})
|
|
20557
|
+
(opts, i) => ctx.vaultMemoryCollection.prepareCreate(
|
|
20558
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContents[i])
|
|
20559
|
+
)
|
|
20589
20560
|
);
|
|
20590
20561
|
await ctx.database.batch(...prepared);
|
|
20591
20562
|
return prepared;
|
|
@@ -21379,21 +21350,73 @@ async function hardDeleteDecayedOp(ctx, id, opts) {
|
|
|
21379
21350
|
return false;
|
|
21380
21351
|
}
|
|
21381
21352
|
}
|
|
21382
|
-
async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
|
|
21353
|
+
async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel, expected) {
|
|
21383
21354
|
try {
|
|
21384
21355
|
const record = await ctx.vaultMemoryCollection.find(id);
|
|
21385
21356
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
21357
|
+
const readUpdatedAt = record.updatedAt.getTime();
|
|
21358
|
+
const readStoredContent = record._getRaw("content");
|
|
21359
|
+
if (expected?.updatedAt !== void 0 && expected.updatedAt !== readUpdatedAt) return false;
|
|
21360
|
+
if (expected?.content !== void 0) {
|
|
21361
|
+
const current = await vaultMemoryToStored(
|
|
21362
|
+
record,
|
|
21363
|
+
ctx.walletAddress,
|
|
21364
|
+
ctx.signMessage,
|
|
21365
|
+
ctx.embeddedWalletSigner
|
|
21366
|
+
);
|
|
21367
|
+
if (current.content !== expected.content) return false;
|
|
21368
|
+
}
|
|
21369
|
+
let written = false;
|
|
21386
21370
|
await ctx.database.write(async () => {
|
|
21371
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return;
|
|
21372
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
21373
|
+
if (originalUpdatedAt !== readUpdatedAt || record._getRaw("content") !== readStoredContent)
|
|
21374
|
+
return;
|
|
21387
21375
|
await record.update((r) => {
|
|
21388
21376
|
r._setRaw("embedding", embedding);
|
|
21389
21377
|
r._setRaw("embedding_model", embeddingModel);
|
|
21378
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
21390
21379
|
});
|
|
21380
|
+
written = true;
|
|
21391
21381
|
});
|
|
21392
|
-
return
|
|
21382
|
+
return written;
|
|
21393
21383
|
} catch {
|
|
21394
21384
|
return false;
|
|
21395
21385
|
}
|
|
21396
21386
|
}
|
|
21387
|
+
function normalizeForDedupe(content) {
|
|
21388
|
+
return content.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
21389
|
+
}
|
|
21390
|
+
async function findQuarantinedDuplicateOp(ctx, content, sourceIds, where) {
|
|
21391
|
+
const wanted = [...new Set(sourceIds)].sort().join("\0");
|
|
21392
|
+
const target = normalizeForDedupe(content);
|
|
21393
|
+
const rows = await ctx.vaultMemoryCollection.query(
|
|
21394
|
+
import_watermelondb18.Q.where("trust_tier", "quarantined"),
|
|
21395
|
+
import_watermelondb18.Q.where("is_deleted", false),
|
|
21396
|
+
import_watermelondb18.Q.where("scope", where.scope),
|
|
21397
|
+
import_watermelondb18.Q.where("folder_id", where.folderId),
|
|
21398
|
+
...ctx.userId !== void 0 ? [import_watermelondb18.Q.where("user_id", ctx.userId)] : []
|
|
21399
|
+
).unsafeFetchRaw();
|
|
21400
|
+
for (const raw of rows) {
|
|
21401
|
+
let ids;
|
|
21402
|
+
try {
|
|
21403
|
+
ids = JSON.parse(raw.source_chunk_ids ?? "[]");
|
|
21404
|
+
} catch {
|
|
21405
|
+
continue;
|
|
21406
|
+
}
|
|
21407
|
+
if (!Array.isArray(ids)) continue;
|
|
21408
|
+
const key = [...new Set(ids.filter((id) => typeof id === "string"))].sort().join("\0");
|
|
21409
|
+
if (key !== wanted) continue;
|
|
21410
|
+
const stored = await vaultMemoryRawToStored(
|
|
21411
|
+
raw,
|
|
21412
|
+
ctx.walletAddress,
|
|
21413
|
+
ctx.signMessage,
|
|
21414
|
+
ctx.embeddedWalletSigner
|
|
21415
|
+
);
|
|
21416
|
+
if (normalizeForDedupe(stored.content) === target) return stored.uniqueId;
|
|
21417
|
+
}
|
|
21418
|
+
return null;
|
|
21419
|
+
}
|
|
21397
21420
|
|
|
21398
21421
|
// src/lib/memory/decay.ts
|
|
21399
21422
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -21417,6 +21440,10 @@ var DEFAULT_DECAY_POLICY = {
|
|
|
21417
21440
|
hardDeleteWindowMs: HARD_DELETE_WINDOW_MS,
|
|
21418
21441
|
pastEventGraceMs: PAST_EVENT_GRACE_MS
|
|
21419
21442
|
};
|
|
21443
|
+
function lastActivityAt(m) {
|
|
21444
|
+
const observedAt = Number.isFinite(m.lastObservedAt) ? m.lastObservedAt : m.updatedAt;
|
|
21445
|
+
return Math.max(m.updatedAt, observedAt);
|
|
21446
|
+
}
|
|
21420
21447
|
function resolvePolicy(policy) {
|
|
21421
21448
|
if (!policy) return DEFAULT_DECAY_POLICY;
|
|
21422
21449
|
return {
|
|
@@ -21456,8 +21483,7 @@ function classifyDecay(m, now, policy) {
|
|
|
21456
21483
|
if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
|
|
21457
21484
|
if (m.eventTimeEnd >= now) return "keep";
|
|
21458
21485
|
}
|
|
21459
|
-
|
|
21460
|
-
if (now - Math.max(m.updatedAt, observedAt) > ttlForType(m.factType, resolved)) return "archive";
|
|
21486
|
+
if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
|
|
21461
21487
|
return "keep";
|
|
21462
21488
|
}
|
|
21463
21489
|
|
|
@@ -26996,6 +27022,9 @@ var MANUAL_FACT_TYPES = [
|
|
|
26996
27022
|
function normalizeManualFactType(value) {
|
|
26997
27023
|
return typeof value === "string" && MANUAL_FACT_TYPES.includes(value) ? value : void 0;
|
|
26998
27024
|
}
|
|
27025
|
+
function isSharedScope(scope) {
|
|
27026
|
+
return scope === "shared";
|
|
27027
|
+
}
|
|
26999
27028
|
async function notifyWritten(options, input, outcome) {
|
|
27000
27029
|
try {
|
|
27001
27030
|
await options?.onWritten?.({ input, outcome });
|
|
@@ -27064,14 +27093,17 @@ function createMemoryVaultTool(vaultCtx, options, embeddingOptions, cache) {
|
|
|
27064
27093
|
try {
|
|
27065
27094
|
const isUpdate = !!id;
|
|
27066
27095
|
let previousContent;
|
|
27096
|
+
const scope = options?.scope ?? "private";
|
|
27067
27097
|
if (isUpdate) {
|
|
27068
27098
|
const existing = await getVaultMemoryOp(vaultCtx, id);
|
|
27069
27099
|
if (!existing) {
|
|
27070
27100
|
return `Error: Memory with ID "${id}" not found. Creating a new memory instead would require a separate call without an ID.`;
|
|
27071
27101
|
}
|
|
27102
|
+
if (isSharedScope(existing.scope) !== isSharedScope(scope)) {
|
|
27103
|
+
return `Error: Memory "${id}" belongs to a different scope than this conversation and cannot be updated from here. It was not modified. To record this fact, save it as a new memory without an ID.`;
|
|
27104
|
+
}
|
|
27072
27105
|
previousContent = existing.content;
|
|
27073
27106
|
}
|
|
27074
|
-
const scope = options?.scope ?? "private";
|
|
27075
27107
|
const operation = {
|
|
27076
27108
|
action: isUpdate ? "update" : "add",
|
|
27077
27109
|
content,
|
|
@@ -28271,6 +28303,24 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
28271
28303
|
const tRetain = Date.now();
|
|
28272
28304
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
28273
28305
|
try {
|
|
28306
|
+
if (isQuarantined) {
|
|
28307
|
+
const existingId = await findQuarantinedDuplicateOp(
|
|
28308
|
+
retainCtx.vaultCtx,
|
|
28309
|
+
candidate.content,
|
|
28310
|
+
candidate.sourceMessageIds,
|
|
28311
|
+
// The same defaults retain() writes the row with.
|
|
28312
|
+
{ scope: options.scope ?? "private", folderId: options.folderId ?? null }
|
|
28313
|
+
).catch(() => null);
|
|
28314
|
+
if (existingId) {
|
|
28315
|
+
quarantinedInfo.push({
|
|
28316
|
+
candidate,
|
|
28317
|
+
memoryId: existingId,
|
|
28318
|
+
reason,
|
|
28319
|
+
signature
|
|
28320
|
+
});
|
|
28321
|
+
continue;
|
|
28322
|
+
}
|
|
28323
|
+
}
|
|
28274
28324
|
const result = await retain(candidate.content, retainCtx, {
|
|
28275
28325
|
source: "auto-extracted",
|
|
28276
28326
|
sourceChunkIds: candidate.sourceMessageIds,
|
|
@@ -28716,7 +28766,7 @@ function createLlmDecayClassifier(options) {
|
|
|
28716
28766
|
if (!content || content.trim().length === 0) return ruleVerdict;
|
|
28717
28767
|
const trimmed = content.trim().slice(0, MAX_CONTENT_CHARS);
|
|
28718
28768
|
const safe = redactor ? (await redactor.redactTextAsync(trimmed)).text : trimmed;
|
|
28719
|
-
const meta = `factType: ${input.factType ?? "none"}; ageDays: ${ageDays(input
|
|
28769
|
+
const meta = `factType: ${input.factType ?? "none"}; ageDays: ${ageDays(lastActivityAt(input), now)}`;
|
|
28720
28770
|
let parsed;
|
|
28721
28771
|
try {
|
|
28722
28772
|
parsed = await callPortalJsonCompletion({
|
|
@@ -28749,9 +28799,9 @@ Should this be kept or archived?`,
|
|
|
28749
28799
|
}
|
|
28750
28800
|
};
|
|
28751
28801
|
}
|
|
28752
|
-
function ageDays(
|
|
28753
|
-
if (!Number.isFinite(
|
|
28754
|
-
return Math.max(0, Math.floor((now -
|
|
28802
|
+
function ageDays(activityAt, now) {
|
|
28803
|
+
if (!Number.isFinite(activityAt) || !Number.isFinite(now)) return 0;
|
|
28804
|
+
return Math.max(0, Math.floor((now - activityAt) / (24 * 60 * 60 * 1e3)));
|
|
28755
28805
|
}
|
|
28756
28806
|
function parseVerdict(parsed) {
|
|
28757
28807
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
@@ -28804,7 +28854,7 @@ function createDecaySweeper(options) {
|
|
|
28804
28854
|
if (!classifier || !isBorderline(input)) return ruleVerdict;
|
|
28805
28855
|
if (input.id) {
|
|
28806
28856
|
const cached = classifierCache.get(input.id);
|
|
28807
|
-
if (cached && cached.
|
|
28857
|
+
if (cached && cached.activityAt === lastActivityAt(input)) return cached.verdict;
|
|
28808
28858
|
}
|
|
28809
28859
|
if (sweep.classifierCalls >= maxClassifierCalls) {
|
|
28810
28860
|
if (!sweep.ceilingLogged) {
|
|
@@ -28818,7 +28868,7 @@ function createDecaySweeper(options) {
|
|
|
28818
28868
|
sweep.classifierCalls++;
|
|
28819
28869
|
try {
|
|
28820
28870
|
const verdict = await classifier.classify(input, ruleVerdict, now);
|
|
28821
|
-
if (input.id) classifierCache.set(input.id, {
|
|
28871
|
+
if (input.id) classifierCache.set(input.id, { activityAt: lastActivityAt(input), verdict });
|
|
28822
28872
|
return verdict;
|
|
28823
28873
|
} catch (err) {
|
|
28824
28874
|
getLogger().warn(
|
|
@@ -30451,6 +30501,37 @@ async function assembleMemoryContext(options) {
|
|
|
30451
30501
|
var import_watermelondb24 = __toESM(require_watermelondb());
|
|
30452
30502
|
var MAX_JOBS_PER_PASS = 3;
|
|
30453
30503
|
var MAX_ATTEMPTS = 3;
|
|
30504
|
+
var MAX_FAILED_SESSIONS = 3;
|
|
30505
|
+
var MIN_FAILED_SESSION_GAP_MS = 60 * 60 * 1e3;
|
|
30506
|
+
var REQUEST_REJECTED_STATUSES = /* @__PURE__ */ new Set([400, 404, 413, 422]);
|
|
30507
|
+
var CONTENT_FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
30508
|
+
"empty-content",
|
|
30509
|
+
"invalid-json",
|
|
30510
|
+
"null-completion",
|
|
30511
|
+
"body-parse-failed"
|
|
30512
|
+
]);
|
|
30513
|
+
var BACKOFF_ALLOWANCE_MS = 2100;
|
|
30514
|
+
var RETAIN_BUDGET_MS = 12e4;
|
|
30515
|
+
function defaultBatchTimeoutMs(extract) {
|
|
30516
|
+
const attempts = Math.max(1, extract.maxAttempts ?? 3);
|
|
30517
|
+
const calls = (extract.timeoutMs ?? 6e4) * attempts + BACKOFF_ALLOWANCE_MS * (attempts - 1);
|
|
30518
|
+
const extraction = extract.totalTimeoutMs !== void 0 ? Math.min(extract.totalTimeoutMs, calls) : calls;
|
|
30519
|
+
return extraction + RETAIN_BUDGET_MS;
|
|
30520
|
+
}
|
|
30521
|
+
var BatchFailureError = class extends Error {
|
|
30522
|
+
constructor(message, terminal) {
|
|
30523
|
+
super(message);
|
|
30524
|
+
this.terminal = terminal;
|
|
30525
|
+
}
|
|
30526
|
+
};
|
|
30527
|
+
var AccountFailureError = class extends Error {
|
|
30528
|
+
};
|
|
30529
|
+
var LockedSourcesError = class extends Error {
|
|
30530
|
+
constructor(persistentIds) {
|
|
30531
|
+
super("Source messages are locked; retained for retry");
|
|
30532
|
+
this.persistentIds = persistentIds;
|
|
30533
|
+
}
|
|
30534
|
+
};
|
|
30454
30535
|
function idsOf(job) {
|
|
30455
30536
|
return JSON.parse(String(job._getRaw("message_ids")));
|
|
30456
30537
|
}
|
|
@@ -30458,6 +30539,16 @@ function seqOf(job) {
|
|
|
30458
30539
|
const raw = job._getRaw("watermark_seq");
|
|
30459
30540
|
return typeof raw === "number" && raw > 0 ? raw : void 0;
|
|
30460
30541
|
}
|
|
30542
|
+
function failedSessionsOf(job, head) {
|
|
30543
|
+
if (job._getRaw("failed_head") !== head) return 0;
|
|
30544
|
+
const raw = job._getRaw("failed_sessions");
|
|
30545
|
+
return typeof raw === "number" && raw > 0 ? raw : 0;
|
|
30546
|
+
}
|
|
30547
|
+
function failedAtOf(job, head) {
|
|
30548
|
+
if (job._getRaw("failed_head") !== head) return void 0;
|
|
30549
|
+
const raw = job._getRaw("failed_at");
|
|
30550
|
+
return typeof raw === "number" ? raw : void 0;
|
|
30551
|
+
}
|
|
30461
30552
|
function createDurableAutoExtractor(options) {
|
|
30462
30553
|
const vault = options.retainCtx.vaultCtx;
|
|
30463
30554
|
const database = vault.database;
|
|
@@ -30480,6 +30571,9 @@ function createDurableAutoExtractor(options) {
|
|
|
30480
30571
|
let writes = Promise.resolve();
|
|
30481
30572
|
let storeFailures = 0;
|
|
30482
30573
|
const attempts = /* @__PURE__ */ new Map();
|
|
30574
|
+
const sessionFailures = /* @__PURE__ */ new Map();
|
|
30575
|
+
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
30576
|
+
const clock = options.now ?? Date.now;
|
|
30483
30577
|
const reportError = (error, conversationId) => {
|
|
30484
30578
|
try {
|
|
30485
30579
|
options.onError?.(error instanceof Error ? error : new Error(String(error)), conversationId);
|
|
@@ -30508,6 +30602,7 @@ function createDurableAutoExtractor(options) {
|
|
|
30508
30602
|
}
|
|
30509
30603
|
async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
|
|
30510
30604
|
let watchdog;
|
|
30605
|
+
let cancelled = false;
|
|
30511
30606
|
const model = options.modelForScope?.(jobScope) ?? options.extract.model;
|
|
30512
30607
|
try {
|
|
30513
30608
|
return await Promise.race([
|
|
@@ -30518,7 +30613,7 @@ function createDurableAutoExtractor(options) {
|
|
|
30518
30613
|
...options.retainCtx,
|
|
30519
30614
|
vaultCtx: {
|
|
30520
30615
|
...vault,
|
|
30521
|
-
canWrite: async () => !disposed && await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("id", import_watermelondb24.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed
|
|
30616
|
+
canWrite: async () => !disposed && !cancelled && await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("id", import_watermelondb24.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed && !cancelled
|
|
30522
30617
|
}
|
|
30523
30618
|
},
|
|
30524
30619
|
// The model follows the job's scope, not the mode alive at drain time.
|
|
@@ -30538,14 +30633,116 @@ function createDurableAutoExtractor(options) {
|
|
|
30538
30633
|
new Promise((_resolve, reject) => {
|
|
30539
30634
|
watchdog = setTimeout(
|
|
30540
30635
|
() => reject(new Error("Extraction did not settle in time; retained for retry")),
|
|
30541
|
-
options.batchTimeoutMs ??
|
|
30636
|
+
options.batchTimeoutMs ?? defaultBatchTimeoutMs(options.extract)
|
|
30542
30637
|
);
|
|
30543
30638
|
})
|
|
30544
30639
|
]);
|
|
30545
30640
|
} finally {
|
|
30641
|
+
cancelled = true;
|
|
30546
30642
|
if (watchdog !== void 0) clearTimeout(watchdog);
|
|
30547
30643
|
}
|
|
30548
30644
|
}
|
|
30645
|
+
async function acknowledge(job, ids, loaded, abandoned) {
|
|
30646
|
+
attempts.delete(job.id);
|
|
30647
|
+
sessionFailures.delete(job.id);
|
|
30648
|
+
skippedThisSession.delete(job.id);
|
|
30649
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
30650
|
+
for (const message of loaded)
|
|
30651
|
+
if (message && message.messageId > 0) sequences.set(message.uniqueId, message.messageId);
|
|
30652
|
+
await database.write(async () => {
|
|
30653
|
+
const current = idsOf(job);
|
|
30654
|
+
const acknowledged = ids.filter((id) => current.includes(id));
|
|
30655
|
+
if (!acknowledged.length) return;
|
|
30656
|
+
const done = new Set(acknowledged);
|
|
30657
|
+
const remaining = current.filter((id) => !done.has(id));
|
|
30658
|
+
await job.update((r) => {
|
|
30659
|
+
r._setRaw(
|
|
30660
|
+
"message_ids",
|
|
30661
|
+
JSON.stringify(
|
|
30662
|
+
remaining.length ? [...abandoned ? [] : acknowledged.slice(-2), ...remaining] : []
|
|
30663
|
+
)
|
|
30664
|
+
);
|
|
30665
|
+
const newest = acknowledged[acknowledged.length - 1];
|
|
30666
|
+
r._setRaw("watermark", newest);
|
|
30667
|
+
const sequence = sequences.get(newest);
|
|
30668
|
+
if (sequence !== void 0) {
|
|
30669
|
+
const prior = seqOf(job);
|
|
30670
|
+
r._setRaw("watermark_seq", prior === void 0 ? sequence : Math.max(prior, sequence));
|
|
30671
|
+
}
|
|
30672
|
+
r._setRaw("failed_sessions", abandoned ? MAX_FAILED_SESSIONS : null);
|
|
30673
|
+
r._setRaw("failed_head", abandoned ? newest : null);
|
|
30674
|
+
r._setRaw("failed_at", null);
|
|
30675
|
+
});
|
|
30676
|
+
});
|
|
30677
|
+
}
|
|
30678
|
+
async function recordFailure(job, ids, loaded, error, conversationId) {
|
|
30679
|
+
const head = ids[0];
|
|
30680
|
+
if (!head || !loaded.some(Boolean)) return false;
|
|
30681
|
+
if (error instanceof AccountFailureError) {
|
|
30682
|
+
skippedThisSession.add(job.id);
|
|
30683
|
+
return false;
|
|
30684
|
+
}
|
|
30685
|
+
const counted = error instanceof BatchFailureError || error instanceof LockedSourcesError && error.persistentIds.length > 0;
|
|
30686
|
+
if (!counted) return false;
|
|
30687
|
+
const prior = sessionFailures.get(job.id);
|
|
30688
|
+
const count = prior?.head === head ? prior.count + 1 : 1;
|
|
30689
|
+
sessionFailures.set(job.id, { head, count });
|
|
30690
|
+
const terminal = error instanceof BatchFailureError && error.terminal;
|
|
30691
|
+
if (!terminal && count < MAX_ATTEMPTS) return false;
|
|
30692
|
+
skippedThisSession.add(job.id);
|
|
30693
|
+
const now = clock();
|
|
30694
|
+
const lastCounted = failedAtOf(job, head);
|
|
30695
|
+
if (lastCounted !== void 0 && now - lastCounted < MIN_FAILED_SESSION_GAP_MS) return false;
|
|
30696
|
+
const failures = failedSessionsOf(job, head) + 1;
|
|
30697
|
+
if (failures < MAX_FAILED_SESSIONS) {
|
|
30698
|
+
await database.write(
|
|
30699
|
+
() => job.update((r) => {
|
|
30700
|
+
r._setRaw("failed_sessions", failures);
|
|
30701
|
+
r._setRaw("failed_head", head);
|
|
30702
|
+
r._setRaw("failed_at", now);
|
|
30703
|
+
})
|
|
30704
|
+
);
|
|
30705
|
+
return false;
|
|
30706
|
+
}
|
|
30707
|
+
if (error instanceof LockedSourcesError) {
|
|
30708
|
+
const dropped = new Set(error.persistentIds);
|
|
30709
|
+
let newest;
|
|
30710
|
+
for (const message of loaded)
|
|
30711
|
+
if (message && dropped.has(message.uniqueId) && message.messageId > (newest?.seq ?? 0))
|
|
30712
|
+
newest = { id: message.uniqueId, seq: message.messageId };
|
|
30713
|
+
const advance = newest !== void 0 && newest.seq > (seqOf(job) ?? 0) ? newest : void 0;
|
|
30714
|
+
await database.write(
|
|
30715
|
+
() => job.update((r) => {
|
|
30716
|
+
r._setRaw("message_ids", JSON.stringify(idsOf(job).filter((id) => !dropped.has(id))));
|
|
30717
|
+
if (advance) {
|
|
30718
|
+
r._setRaw("watermark", advance.id);
|
|
30719
|
+
r._setRaw("watermark_seq", advance.seq);
|
|
30720
|
+
}
|
|
30721
|
+
r._setRaw("failed_sessions", advance ? MAX_FAILED_SESSIONS : null);
|
|
30722
|
+
r._setRaw("failed_head", advance ? advance.id : null);
|
|
30723
|
+
r._setRaw("failed_at", null);
|
|
30724
|
+
})
|
|
30725
|
+
);
|
|
30726
|
+
attempts.delete(job.id);
|
|
30727
|
+
sessionFailures.delete(job.id);
|
|
30728
|
+
skippedThisSession.delete(job.id);
|
|
30729
|
+
reportError(
|
|
30730
|
+
new Error(
|
|
30731
|
+
`Dropped ${dropped.size} source id(s) that stayed locked for ${failures} sessions to unblock extraction`
|
|
30732
|
+
),
|
|
30733
|
+
conversationId
|
|
30734
|
+
);
|
|
30735
|
+
return true;
|
|
30736
|
+
}
|
|
30737
|
+
await acknowledge(job, ids, loaded, true);
|
|
30738
|
+
reportError(
|
|
30739
|
+
new Error(
|
|
30740
|
+
`Abandoned an extraction batch of ${ids.length} source id(s) after it failed in ${failures} sessions`
|
|
30741
|
+
),
|
|
30742
|
+
conversationId
|
|
30743
|
+
);
|
|
30744
|
+
return true;
|
|
30745
|
+
}
|
|
30549
30746
|
async function drain() {
|
|
30550
30747
|
if (disposed || running) return;
|
|
30551
30748
|
running = true;
|
|
@@ -30560,21 +30757,23 @@ function createDurableAutoExtractor(options) {
|
|
|
30560
30757
|
for (const job of pending) {
|
|
30561
30758
|
if (disposed) break;
|
|
30562
30759
|
const attempted = attempts.get(job.id) ?? 0;
|
|
30563
|
-
if (attempted >= MAX_ATTEMPTS) continue;
|
|
30760
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id)) continue;
|
|
30564
30761
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
30565
30762
|
scheduleAfterSuccess = true;
|
|
30566
30763
|
break;
|
|
30567
30764
|
}
|
|
30568
30765
|
processed++;
|
|
30569
30766
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
30767
|
+
let ids = [];
|
|
30768
|
+
let loaded = [];
|
|
30570
30769
|
try {
|
|
30571
30770
|
const conversations = await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount();
|
|
30572
30771
|
if (!conversations) {
|
|
30573
30772
|
await database.write(() => job.destroyPermanently());
|
|
30574
30773
|
continue;
|
|
30575
30774
|
}
|
|
30576
|
-
|
|
30577
|
-
|
|
30775
|
+
ids = idsOf(job).slice(0, 20);
|
|
30776
|
+
loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
|
|
30578
30777
|
const missing = ids.filter((_id, index) => loaded[index] === null);
|
|
30579
30778
|
if (missing.length) {
|
|
30580
30779
|
if (attempted < MAX_ATTEMPTS - 1)
|
|
@@ -30598,17 +30797,21 @@ function createDurableAutoExtractor(options) {
|
|
|
30598
30797
|
);
|
|
30599
30798
|
continue;
|
|
30600
30799
|
}
|
|
30601
|
-
|
|
30602
|
-
|
|
30603
|
-
|
|
30604
|
-
|
|
30800
|
+
const locked = loaded.filter((message) => message && isEncrypted(message.content));
|
|
30801
|
+
if (locked.length) {
|
|
30802
|
+
throw new LockedSourcesError(
|
|
30803
|
+
locked.flatMap(
|
|
30804
|
+
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
30805
|
+
)
|
|
30806
|
+
);
|
|
30605
30807
|
}
|
|
30606
30808
|
const messages = loaded.flatMap(
|
|
30607
30809
|
(m) => m && m.conversationId === conversationId && (m.role === "user" || m.role === "assistant") ? [{ id: m.uniqueId, role: m.role, content: m.content }] : []
|
|
30608
30810
|
);
|
|
30609
30811
|
let success = messages.length === 0;
|
|
30812
|
+
let result;
|
|
30610
30813
|
if (messages.length) {
|
|
30611
|
-
|
|
30814
|
+
result = await extractBatch(
|
|
30612
30815
|
messages,
|
|
30613
30816
|
conversationId,
|
|
30614
30817
|
ids,
|
|
@@ -30618,41 +30821,35 @@ function createDurableAutoExtractor(options) {
|
|
|
30618
30821
|
success = result.failedCount === 0 && result.outcome !== "empty-after-retry";
|
|
30619
30822
|
options.onTurnComplete?.(result);
|
|
30620
30823
|
}
|
|
30621
|
-
if (!success)
|
|
30622
|
-
|
|
30623
|
-
|
|
30624
|
-
|
|
30625
|
-
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
const acknowledged = ids.filter((id) => current.includes(id));
|
|
30630
|
-
if (!acknowledged.length) return;
|
|
30631
|
-
const done = new Set(acknowledged);
|
|
30632
|
-
const remaining = current.filter((id) => !done.has(id));
|
|
30633
|
-
if (remaining.length) {
|
|
30634
|
-
await job.update(
|
|
30635
|
-
(r) => r._setRaw("message_ids", JSON.stringify([...acknowledged.slice(-2), ...remaining]))
|
|
30636
|
-
);
|
|
30637
|
-
} else await job.update((r) => r._setRaw("message_ids", "[]"));
|
|
30638
|
-
const newest = acknowledged[acknowledged.length - 1];
|
|
30639
|
-
await job.update((r) => {
|
|
30640
|
-
r._setRaw("watermark", newest);
|
|
30641
|
-
const sequence = sequences.get(newest);
|
|
30642
|
-
if (sequence !== void 0) {
|
|
30643
|
-
const prior = seqOf(job);
|
|
30644
|
-
r._setRaw(
|
|
30645
|
-
"watermark_seq",
|
|
30646
|
-
prior === void 0 ? sequence : Math.max(prior, sequence)
|
|
30824
|
+
if (!success) {
|
|
30825
|
+
const failure = result?.failure;
|
|
30826
|
+
if (failure?.reason === "http-terminal") {
|
|
30827
|
+
const status = failure.httpStatus;
|
|
30828
|
+
if (status !== void 0 && REQUEST_REJECTED_STATUSES.has(status))
|
|
30829
|
+
throw new BatchFailureError(
|
|
30830
|
+
`Extraction rejected the batch (HTTP ${status}); not retried this session`,
|
|
30831
|
+
true
|
|
30647
30832
|
);
|
|
30648
|
-
|
|
30649
|
-
|
|
30650
|
-
|
|
30833
|
+
throw new AccountFailureError(
|
|
30834
|
+
`Extraction refused for the account (HTTP ${status ?? "unknown"}); retried next session`
|
|
30835
|
+
);
|
|
30836
|
+
}
|
|
30837
|
+
if ((result?.failedCount ?? 0) > 0 || failure !== void 0 && CONTENT_FAILURE_REASONS.has(failure.reason))
|
|
30838
|
+
throw new BatchFailureError("Extraction batch incomplete; retained for retry", false);
|
|
30839
|
+
throw new Error("Extraction batch incomplete; retained for retry");
|
|
30840
|
+
}
|
|
30841
|
+
await acknowledge(job, ids, loaded, false);
|
|
30651
30842
|
scheduleAfterSuccess = true;
|
|
30652
30843
|
} catch (error) {
|
|
30653
30844
|
attempts.set(job.id, attempted + 1);
|
|
30654
30845
|
retry = true;
|
|
30655
30846
|
reportError(error, conversationId);
|
|
30847
|
+
try {
|
|
30848
|
+
if (await recordFailure(job, ids, loaded, error, conversationId))
|
|
30849
|
+
scheduleAfterSuccess = true;
|
|
30850
|
+
} catch (recordError) {
|
|
30851
|
+
reportError(recordError, conversationId);
|
|
30852
|
+
}
|
|
30656
30853
|
}
|
|
30657
30854
|
}
|
|
30658
30855
|
} catch (error) {
|
|
@@ -30722,7 +30919,7 @@ function createDurableAutoExtractor(options) {
|
|
|
30722
30919
|
const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
|
|
30723
30920
|
if (start >= ids.length) return;
|
|
30724
30921
|
const ownWatermark = job?._getRaw("watermark");
|
|
30725
|
-
const atOwnWatermark = !!job && boundary >= 0 && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
|
|
30922
|
+
const atOwnWatermark = !!job && boundary >= 0 && failedSessionsOf(job, String(ownWatermark)) < MAX_FAILED_SESSIONS && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
|
|
30726
30923
|
const overlapStart = atOwnWatermark ? Math.max(0, boundary - 1) : start;
|
|
30727
30924
|
const fresh = ids.slice(overlapStart).filter((id) => !foreignSources.has(id));
|
|
30728
30925
|
if (!fresh.length) return;
|