@anuma/sdk 6.25.1 → 6.25.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 +348 -148
- package/dist/expo/index.d.mts +34 -9
- package/dist/expo/index.d.ts +34 -9
- package/dist/expo/index.mjs +348 -148
- package/dist/react/index.cjs +348 -148
- package/dist/react/index.d.mts +34 -9
- package/dist/react/index.d.ts +34 -9
- package/dist/react/index.mjs +348 -148
- package/dist/server/index.cjs +348 -148
- package/dist/server/index.d.mts +34 -9
- package/dist/server/index.d.ts +34 -9
- package/dist/server/index.mjs +348 -148
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -17129,7 +17129,7 @@ __decorateClass([
|
|
|
17129
17129
|
], VaultFolder.prototype, "context", 2);
|
|
17130
17130
|
|
|
17131
17131
|
// src/lib/db/schema.ts
|
|
17132
|
-
var SDK_SCHEMA_VERSION =
|
|
17132
|
+
var SDK_SCHEMA_VERSION = 47;
|
|
17133
17133
|
var sdkSchema = (0, import_watermelondb13.appSchema)({
|
|
17134
17134
|
version: SDK_SCHEMA_VERSION,
|
|
17135
17135
|
tables: [
|
|
@@ -17145,7 +17145,11 @@ var sdkSchema = (0, import_watermelondb13.appSchema)({
|
|
|
17145
17145
|
// source. `watermark` alone is an id, and deleting that message erased
|
|
17146
17146
|
// the anchor — see the column note in extractionJobs/models.ts.
|
|
17147
17147
|
{ name: "watermark_seq", type: "number", isOptional: true },
|
|
17148
|
-
{ name: "folder_id", type: "string", isOptional: true }
|
|
17148
|
+
{ name: "folder_id", type: "string", isOptional: true },
|
|
17149
|
+
// Poison accounting for the head batch; see extractionJobs/models.ts.
|
|
17150
|
+
{ name: "failed_sessions", type: "number", isOptional: true },
|
|
17151
|
+
{ name: "failed_head", type: "string", isOptional: true },
|
|
17152
|
+
{ name: "failed_at", type: "number", isOptional: true }
|
|
17149
17153
|
]
|
|
17150
17154
|
}),
|
|
17151
17155
|
// Chat storage tables
|
|
@@ -18169,6 +18173,22 @@ var sdkMigrations = (0, import_migrations.schemaMigrations)({
|
|
|
18169
18173
|
]
|
|
18170
18174
|
})
|
|
18171
18175
|
]
|
|
18176
|
+
},
|
|
18177
|
+
// v46 -> v47: persisted poison count (and when it was last counted) for a
|
|
18178
|
+
// durable-extraction job's head batch. NULL = never failed, which is right
|
|
18179
|
+
// for every existing row.
|
|
18180
|
+
{
|
|
18181
|
+
toVersion: 47,
|
|
18182
|
+
steps: [
|
|
18183
|
+
(0, import_migrations.addColumns)({
|
|
18184
|
+
table: "memory_extraction_jobs",
|
|
18185
|
+
columns: [
|
|
18186
|
+
{ name: "failed_sessions", type: "number", isOptional: true },
|
|
18187
|
+
{ name: "failed_head", type: "string", isOptional: true },
|
|
18188
|
+
{ name: "failed_at", type: "number", isOptional: true }
|
|
18189
|
+
]
|
|
18190
|
+
})
|
|
18191
|
+
]
|
|
18172
18192
|
}
|
|
18173
18193
|
]
|
|
18174
18194
|
});
|
|
@@ -22065,8 +22085,42 @@ async function vaultMemoryToStored(memory, walletAddress, signMessage, embeddedW
|
|
|
22065
22085
|
}
|
|
22066
22086
|
return raw;
|
|
22067
22087
|
}
|
|
22088
|
+
function populateNewVaultMemory(record, ctx, opts, encryptedContent) {
|
|
22089
|
+
record._setRaw("content", encryptedContent);
|
|
22090
|
+
record._setRaw("scope", opts.scope ?? "private");
|
|
22091
|
+
record._setRaw("folder_id", opts.folderId ?? null);
|
|
22092
|
+
record._setRaw("user_id", ctx.userId ?? null);
|
|
22093
|
+
record._setRaw("is_deleted", false);
|
|
22094
|
+
if (opts.embedding !== void 0) {
|
|
22095
|
+
record._setRaw("embedding", opts.embedding);
|
|
22096
|
+
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
22097
|
+
}
|
|
22098
|
+
if (opts.sourceChunkIds !== void 0) {
|
|
22099
|
+
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
22100
|
+
}
|
|
22101
|
+
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
22102
|
+
record._setRaw("source", opts.source ?? "manual");
|
|
22103
|
+
if (opts.eventTime) {
|
|
22104
|
+
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
22105
|
+
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
22106
|
+
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
22107
|
+
}
|
|
22108
|
+
if (opts.factType !== void 0) {
|
|
22109
|
+
record._setRaw("fact_type", opts.factType);
|
|
22110
|
+
}
|
|
22111
|
+
if (opts.trustTier !== void 0) {
|
|
22112
|
+
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
22113
|
+
}
|
|
22114
|
+
record._setRaw("visibility", opts.visibility ?? "private");
|
|
22115
|
+
record._setRaw(
|
|
22116
|
+
"published_at",
|
|
22117
|
+
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
22118
|
+
);
|
|
22119
|
+
if (opts.geohash !== void 0) {
|
|
22120
|
+
record._setRaw("geohash", opts.geohash);
|
|
22121
|
+
}
|
|
22122
|
+
}
|
|
22068
22123
|
async function createVaultMemoryOp(ctx, opts) {
|
|
22069
|
-
const scope = opts.scope ?? "private";
|
|
22070
22124
|
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
22071
22125
|
opts.content,
|
|
22072
22126
|
ctx.walletAddress,
|
|
@@ -22076,47 +22130,14 @@ async function createVaultMemoryOp(ctx, opts) {
|
|
|
22076
22130
|
const created = await ctx.database.write(async () => {
|
|
22077
22131
|
if (ctx.canWrite && !await ctx.canWrite())
|
|
22078
22132
|
throw new Error("Memory source is no longer eligible");
|
|
22079
|
-
return ctx.vaultMemoryCollection.create(
|
|
22080
|
-
record
|
|
22081
|
-
|
|
22082
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
22083
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
22084
|
-
record._setRaw("is_deleted", false);
|
|
22085
|
-
if (opts.embedding !== void 0) {
|
|
22086
|
-
record._setRaw("embedding", opts.embedding);
|
|
22087
|
-
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
22088
|
-
}
|
|
22089
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
22090
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
22091
|
-
}
|
|
22092
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
22093
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
22094
|
-
if (opts.eventTime) {
|
|
22095
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
22096
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
22097
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
22098
|
-
}
|
|
22099
|
-
if (opts.factType !== void 0) {
|
|
22100
|
-
record._setRaw("fact_type", opts.factType);
|
|
22101
|
-
}
|
|
22102
|
-
if (opts.trustTier !== void 0) {
|
|
22103
|
-
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
22104
|
-
}
|
|
22105
|
-
record._setRaw("visibility", opts.visibility ?? "private");
|
|
22106
|
-
record._setRaw(
|
|
22107
|
-
"published_at",
|
|
22108
|
-
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
22109
|
-
);
|
|
22110
|
-
if (opts.geohash !== void 0) {
|
|
22111
|
-
record._setRaw("geohash", opts.geohash);
|
|
22112
|
-
}
|
|
22113
|
-
});
|
|
22133
|
+
return ctx.vaultMemoryCollection.create(
|
|
22134
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContent)
|
|
22135
|
+
);
|
|
22114
22136
|
});
|
|
22115
22137
|
return vaultMemoryToStored(created, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
22116
22138
|
}
|
|
22117
22139
|
async function createSupersedingMemoryOp(ctx, opts, targetId) {
|
|
22118
22140
|
if (!targetId) return { created: null, retired: false };
|
|
22119
|
-
const scope = opts.scope ?? "private";
|
|
22120
22141
|
const encryptedContent = ctx.walletAddress && ctx.signMessage ? await encryptVaultMemoryContent(
|
|
22121
22142
|
opts.content,
|
|
22122
22143
|
ctx.walletAddress,
|
|
@@ -22134,27 +22155,9 @@ async function createSupersedingMemoryOp(ctx, opts, targetId) {
|
|
|
22134
22155
|
return;
|
|
22135
22156
|
}
|
|
22136
22157
|
if (target.isDeleted || target.supersededBy || !isOwnedByCtxUser(ctx, target)) return;
|
|
22137
|
-
createdRecord = await ctx.vaultMemoryCollection.create(
|
|
22138
|
-
record
|
|
22139
|
-
|
|
22140
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
22141
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
22142
|
-
record._setRaw("is_deleted", false);
|
|
22143
|
-
if (opts.embedding !== void 0) {
|
|
22144
|
-
record._setRaw("embedding", opts.embedding);
|
|
22145
|
-
record._setRaw("embedding_model", opts.embeddingModel ?? null);
|
|
22146
|
-
}
|
|
22147
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
22148
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
22149
|
-
}
|
|
22150
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
22151
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
22152
|
-
if (opts.eventTime) {
|
|
22153
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
22154
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
22155
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
22156
|
-
}
|
|
22157
|
-
});
|
|
22158
|
+
createdRecord = await ctx.vaultMemoryCollection.create(
|
|
22159
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContent)
|
|
22160
|
+
);
|
|
22158
22161
|
await target.update((r) => {
|
|
22159
22162
|
r._setRaw("superseded_by", createdRecord.id);
|
|
22160
22163
|
r._setRaw("superseded_at", Date.now());
|
|
@@ -22234,41 +22237,9 @@ async function createVaultMemoriesBatchOp(ctx, optionsArray) {
|
|
|
22234
22237
|
if (ctx.canWrite && !await ctx.canWrite())
|
|
22235
22238
|
throw new Error("Memory source is no longer eligible");
|
|
22236
22239
|
const prepared = optionsArray.map(
|
|
22237
|
-
(opts, i) => ctx.vaultMemoryCollection.prepareCreate(
|
|
22238
|
-
record
|
|
22239
|
-
|
|
22240
|
-
record._setRaw("folder_id", opts.folderId ?? null);
|
|
22241
|
-
record._setRaw("user_id", ctx.userId ?? null);
|
|
22242
|
-
record._setRaw("is_deleted", false);
|
|
22243
|
-
if (optionsArray[i].embedding !== void 0) {
|
|
22244
|
-
record._setRaw("embedding", optionsArray[i].embedding);
|
|
22245
|
-
record._setRaw("embedding_model", optionsArray[i].embeddingModel ?? null);
|
|
22246
|
-
}
|
|
22247
|
-
if (opts.sourceChunkIds !== void 0) {
|
|
22248
|
-
record._setRaw("source_chunk_ids", JSON.stringify(opts.sourceChunkIds));
|
|
22249
|
-
}
|
|
22250
|
-
record._setRaw("proof_count", opts.proofCount ?? 1);
|
|
22251
|
-
record._setRaw("source", opts.source ?? "manual");
|
|
22252
|
-
if (opts.eventTime) {
|
|
22253
|
-
record._setRaw("event_time_start", opts.eventTime.start ?? null);
|
|
22254
|
-
record._setRaw("event_time_end", opts.eventTime.end ?? null);
|
|
22255
|
-
record._setRaw("event_time_kind", opts.eventTime.kind ?? null);
|
|
22256
|
-
}
|
|
22257
|
-
if (opts.factType !== void 0) {
|
|
22258
|
-
record._setRaw("fact_type", opts.factType);
|
|
22259
|
-
}
|
|
22260
|
-
if (opts.trustTier !== void 0) {
|
|
22261
|
-
record._setRaw("trust_tier", normalizeTrustTier(opts.trustTier));
|
|
22262
|
-
}
|
|
22263
|
-
record._setRaw("visibility", opts.visibility ?? "private");
|
|
22264
|
-
record._setRaw(
|
|
22265
|
-
"published_at",
|
|
22266
|
-
opts.visibility && opts.visibility !== "private" ? opts.publishedAt ?? Date.now() : null
|
|
22267
|
-
);
|
|
22268
|
-
if (opts.geohash !== void 0) {
|
|
22269
|
-
record._setRaw("geohash", opts.geohash);
|
|
22270
|
-
}
|
|
22271
|
-
})
|
|
22240
|
+
(opts, i) => ctx.vaultMemoryCollection.prepareCreate(
|
|
22241
|
+
(record) => populateNewVaultMemory(record, ctx, opts, encryptedContents[i])
|
|
22242
|
+
)
|
|
22272
22243
|
);
|
|
22273
22244
|
await ctx.database.batch(...prepared);
|
|
22274
22245
|
return prepared;
|
|
@@ -23062,21 +23033,73 @@ async function hardDeleteDecayedOp(ctx, id, opts) {
|
|
|
23062
23033
|
return false;
|
|
23063
23034
|
}
|
|
23064
23035
|
}
|
|
23065
|
-
async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
|
|
23036
|
+
async function updateVaultMemoryEmbeddingOp(ctx, id, embedding, embeddingModel, expected) {
|
|
23066
23037
|
try {
|
|
23067
23038
|
const record = await ctx.vaultMemoryCollection.find(id);
|
|
23068
23039
|
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return false;
|
|
23040
|
+
const readUpdatedAt = record.updatedAt.getTime();
|
|
23041
|
+
const readStoredContent = record._getRaw("content");
|
|
23042
|
+
if (expected?.updatedAt !== void 0 && expected.updatedAt !== readUpdatedAt) return false;
|
|
23043
|
+
if (expected?.content !== void 0) {
|
|
23044
|
+
const current = await vaultMemoryToStored(
|
|
23045
|
+
record,
|
|
23046
|
+
ctx.walletAddress,
|
|
23047
|
+
ctx.signMessage,
|
|
23048
|
+
ctx.embeddedWalletSigner
|
|
23049
|
+
);
|
|
23050
|
+
if (current.content !== expected.content) return false;
|
|
23051
|
+
}
|
|
23052
|
+
let written = false;
|
|
23069
23053
|
await ctx.database.write(async () => {
|
|
23054
|
+
if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return;
|
|
23055
|
+
const originalUpdatedAt = record.updatedAt.getTime();
|
|
23056
|
+
if (originalUpdatedAt !== readUpdatedAt || record._getRaw("content") !== readStoredContent)
|
|
23057
|
+
return;
|
|
23070
23058
|
await record.update((r) => {
|
|
23071
23059
|
r._setRaw("embedding", embedding);
|
|
23072
23060
|
r._setRaw("embedding_model", embeddingModel);
|
|
23061
|
+
r._setRaw("updated_at", originalUpdatedAt);
|
|
23073
23062
|
});
|
|
23063
|
+
written = true;
|
|
23074
23064
|
});
|
|
23075
|
-
return
|
|
23065
|
+
return written;
|
|
23076
23066
|
} catch {
|
|
23077
23067
|
return false;
|
|
23078
23068
|
}
|
|
23079
23069
|
}
|
|
23070
|
+
function normalizeForDedupe(content) {
|
|
23071
|
+
return content.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
23072
|
+
}
|
|
23073
|
+
async function findQuarantinedDuplicateOp(ctx, content, sourceIds, where) {
|
|
23074
|
+
const wanted = [...new Set(sourceIds)].sort().join("\0");
|
|
23075
|
+
const target = normalizeForDedupe(content);
|
|
23076
|
+
const rows = await ctx.vaultMemoryCollection.query(
|
|
23077
|
+
import_watermelondb18.Q.where("trust_tier", "quarantined"),
|
|
23078
|
+
import_watermelondb18.Q.where("is_deleted", false),
|
|
23079
|
+
import_watermelondb18.Q.where("scope", where.scope),
|
|
23080
|
+
import_watermelondb18.Q.where("folder_id", where.folderId),
|
|
23081
|
+
...ctx.userId !== void 0 ? [import_watermelondb18.Q.where("user_id", ctx.userId)] : []
|
|
23082
|
+
).unsafeFetchRaw();
|
|
23083
|
+
for (const raw of rows) {
|
|
23084
|
+
let ids;
|
|
23085
|
+
try {
|
|
23086
|
+
ids = JSON.parse(raw.source_chunk_ids ?? "[]");
|
|
23087
|
+
} catch {
|
|
23088
|
+
continue;
|
|
23089
|
+
}
|
|
23090
|
+
if (!Array.isArray(ids)) continue;
|
|
23091
|
+
const key = [...new Set(ids.filter((id) => typeof id === "string"))].sort().join("\0");
|
|
23092
|
+
if (key !== wanted) continue;
|
|
23093
|
+
const stored = await vaultMemoryRawToStored(
|
|
23094
|
+
raw,
|
|
23095
|
+
ctx.walletAddress,
|
|
23096
|
+
ctx.signMessage,
|
|
23097
|
+
ctx.embeddedWalletSigner
|
|
23098
|
+
);
|
|
23099
|
+
if (normalizeForDedupe(stored.content) === target) return stored.uniqueId;
|
|
23100
|
+
}
|
|
23101
|
+
return null;
|
|
23102
|
+
}
|
|
23080
23103
|
|
|
23081
23104
|
// src/lib/memory/decay.ts
|
|
23082
23105
|
init_logger();
|
|
@@ -23101,6 +23124,10 @@ var DEFAULT_DECAY_POLICY = {
|
|
|
23101
23124
|
hardDeleteWindowMs: HARD_DELETE_WINDOW_MS,
|
|
23102
23125
|
pastEventGraceMs: PAST_EVENT_GRACE_MS
|
|
23103
23126
|
};
|
|
23127
|
+
function lastActivityAt(m) {
|
|
23128
|
+
const observedAt = Number.isFinite(m.lastObservedAt) ? m.lastObservedAt : m.updatedAt;
|
|
23129
|
+
return Math.max(m.updatedAt, observedAt);
|
|
23130
|
+
}
|
|
23104
23131
|
function resolvePolicy(policy) {
|
|
23105
23132
|
if (!policy) return DEFAULT_DECAY_POLICY;
|
|
23106
23133
|
return {
|
|
@@ -23140,8 +23167,7 @@ function classifyDecay(m, now, policy) {
|
|
|
23140
23167
|
if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
|
|
23141
23168
|
if (m.eventTimeEnd >= now) return "keep";
|
|
23142
23169
|
}
|
|
23143
|
-
|
|
23144
|
-
if (now - Math.max(m.updatedAt, observedAt) > ttlForType(m.factType, resolved)) return "archive";
|
|
23170
|
+
if (now - lastActivityAt(m) > ttlForType(m.factType, resolved)) return "archive";
|
|
23145
23171
|
return "keep";
|
|
23146
23172
|
}
|
|
23147
23173
|
|
|
@@ -28701,6 +28727,9 @@ var MANUAL_FACT_TYPES = [
|
|
|
28701
28727
|
function normalizeManualFactType(value) {
|
|
28702
28728
|
return typeof value === "string" && MANUAL_FACT_TYPES.includes(value) ? value : void 0;
|
|
28703
28729
|
}
|
|
28730
|
+
function isSharedScope(scope) {
|
|
28731
|
+
return scope === "shared";
|
|
28732
|
+
}
|
|
28704
28733
|
async function notifyWritten(options, input, outcome) {
|
|
28705
28734
|
try {
|
|
28706
28735
|
await options?.onWritten?.({ input, outcome });
|
|
@@ -28769,14 +28798,17 @@ function createMemoryVaultTool(vaultCtx, options, embeddingOptions, cache) {
|
|
|
28769
28798
|
try {
|
|
28770
28799
|
const isUpdate = !!id;
|
|
28771
28800
|
let previousContent;
|
|
28801
|
+
const scope = options?.scope ?? "private";
|
|
28772
28802
|
if (isUpdate) {
|
|
28773
28803
|
const existing = await getVaultMemoryOp(vaultCtx, id);
|
|
28774
28804
|
if (!existing) {
|
|
28775
28805
|
return `Error: Memory with ID "${id}" not found. Creating a new memory instead would require a separate call without an ID.`;
|
|
28776
28806
|
}
|
|
28807
|
+
if (isSharedScope(existing.scope) !== isSharedScope(scope)) {
|
|
28808
|
+
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.`;
|
|
28809
|
+
}
|
|
28777
28810
|
previousContent = existing.content;
|
|
28778
28811
|
}
|
|
28779
|
-
const scope = options?.scope ?? "private";
|
|
28780
28812
|
const operation = {
|
|
28781
28813
|
action: isUpdate ? "update" : "add",
|
|
28782
28814
|
content,
|
|
@@ -29986,6 +30018,24 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
29986
30018
|
const tRetain = Date.now();
|
|
29987
30019
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
29988
30020
|
try {
|
|
30021
|
+
if (isQuarantined) {
|
|
30022
|
+
const existingId = await findQuarantinedDuplicateOp(
|
|
30023
|
+
retainCtx.vaultCtx,
|
|
30024
|
+
candidate.content,
|
|
30025
|
+
candidate.sourceMessageIds,
|
|
30026
|
+
// The same defaults retain() writes the row with.
|
|
30027
|
+
{ scope: options.scope ?? "private", folderId: options.folderId ?? null }
|
|
30028
|
+
).catch(() => null);
|
|
30029
|
+
if (existingId) {
|
|
30030
|
+
quarantinedInfo.push({
|
|
30031
|
+
candidate,
|
|
30032
|
+
memoryId: existingId,
|
|
30033
|
+
reason,
|
|
30034
|
+
signature
|
|
30035
|
+
});
|
|
30036
|
+
continue;
|
|
30037
|
+
}
|
|
30038
|
+
}
|
|
29989
30039
|
const result = await retain(candidate.content, retainCtx, {
|
|
29990
30040
|
source: "auto-extracted",
|
|
29991
30041
|
sourceChunkIds: candidate.sourceMessageIds,
|
|
@@ -30436,7 +30486,7 @@ function createLlmDecayClassifier(options) {
|
|
|
30436
30486
|
if (!content || content.trim().length === 0) return ruleVerdict;
|
|
30437
30487
|
const trimmed = content.trim().slice(0, MAX_CONTENT_CHARS);
|
|
30438
30488
|
const safe = redactor ? (await redactor.redactTextAsync(trimmed)).text : trimmed;
|
|
30439
|
-
const meta = `factType: ${input.factType ?? "none"}; ageDays: ${ageDays(input
|
|
30489
|
+
const meta = `factType: ${input.factType ?? "none"}; ageDays: ${ageDays(lastActivityAt(input), now)}`;
|
|
30440
30490
|
let parsed;
|
|
30441
30491
|
try {
|
|
30442
30492
|
parsed = await callPortalJsonCompletion({
|
|
@@ -30469,9 +30519,9 @@ Should this be kept or archived?`,
|
|
|
30469
30519
|
}
|
|
30470
30520
|
};
|
|
30471
30521
|
}
|
|
30472
|
-
function ageDays(
|
|
30473
|
-
if (!Number.isFinite(
|
|
30474
|
-
return Math.max(0, Math.floor((now -
|
|
30522
|
+
function ageDays(activityAt, now) {
|
|
30523
|
+
if (!Number.isFinite(activityAt) || !Number.isFinite(now)) return 0;
|
|
30524
|
+
return Math.max(0, Math.floor((now - activityAt) / (24 * 60 * 60 * 1e3)));
|
|
30475
30525
|
}
|
|
30476
30526
|
function parseVerdict(parsed) {
|
|
30477
30527
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
@@ -30525,7 +30575,7 @@ function createDecaySweeper(options) {
|
|
|
30525
30575
|
if (!classifier || !isBorderline(input)) return ruleVerdict;
|
|
30526
30576
|
if (input.id) {
|
|
30527
30577
|
const cached = classifierCache.get(input.id);
|
|
30528
|
-
if (cached && cached.
|
|
30578
|
+
if (cached && cached.activityAt === lastActivityAt(input)) return cached.verdict;
|
|
30529
30579
|
}
|
|
30530
30580
|
if (sweep.classifierCalls >= maxClassifierCalls) {
|
|
30531
30581
|
if (!sweep.ceilingLogged) {
|
|
@@ -30539,7 +30589,7 @@ function createDecaySweeper(options) {
|
|
|
30539
30589
|
sweep.classifierCalls++;
|
|
30540
30590
|
try {
|
|
30541
30591
|
const verdict = await classifier.classify(input, ruleVerdict, now);
|
|
30542
|
-
if (input.id) classifierCache.set(input.id, {
|
|
30592
|
+
if (input.id) classifierCache.set(input.id, { activityAt: lastActivityAt(input), verdict });
|
|
30543
30593
|
return verdict;
|
|
30544
30594
|
} catch (err) {
|
|
30545
30595
|
getLogger().warn(
|
|
@@ -31287,7 +31337,10 @@ function reviewedMemoryIdsSignature(ids) {
|
|
|
31287
31337
|
return [...new Set(ids)].sort().join("\n");
|
|
31288
31338
|
}
|
|
31289
31339
|
function facetsSignature(facets) {
|
|
31290
|
-
return
|
|
31340
|
+
return [
|
|
31341
|
+
JSON.stringify(FACET_SYSTEM_PROMPT),
|
|
31342
|
+
...facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance, facetResponseSchema(f.key)])).sort()
|
|
31343
|
+
].join("\n");
|
|
31291
31344
|
}
|
|
31292
31345
|
function trendsEqual(a, b) {
|
|
31293
31346
|
if (!a) return false;
|
|
@@ -31509,7 +31562,7 @@ var FACET_SYSTEM_PROMPT = `You are writing one section of a person's shareable p
|
|
|
31509
31562
|
Rules:
|
|
31510
31563
|
- Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
|
|
31511
31564
|
- If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
|
|
31512
|
-
- Write in
|
|
31565
|
+
- Write in a pronoun-free profile voice: lead with the descriptor or the verb ("Thoughtful, detail-oriented builder. Values clear communication."). Never use "they"/"he"/"she", "I", "you", or the person's name \u2014 the person publishes this as their own profile, and strangers read it there.
|
|
31513
31566
|
- Be concise and specific; no preamble, hedging, or meta-commentary.
|
|
31514
31567
|
- Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
|
|
31515
31568
|
function buildFacetUserInstructions(facet) {
|
|
@@ -32184,6 +32237,37 @@ async function assembleMemoryContext(options) {
|
|
|
32184
32237
|
var import_watermelondb24 = __toESM(require_watermelondb());
|
|
32185
32238
|
var MAX_JOBS_PER_PASS = 3;
|
|
32186
32239
|
var MAX_ATTEMPTS = 3;
|
|
32240
|
+
var MAX_FAILED_SESSIONS = 3;
|
|
32241
|
+
var MIN_FAILED_SESSION_GAP_MS = 60 * 60 * 1e3;
|
|
32242
|
+
var REQUEST_REJECTED_STATUSES = /* @__PURE__ */ new Set([400, 404, 413, 422]);
|
|
32243
|
+
var CONTENT_FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
32244
|
+
"empty-content",
|
|
32245
|
+
"invalid-json",
|
|
32246
|
+
"null-completion",
|
|
32247
|
+
"body-parse-failed"
|
|
32248
|
+
]);
|
|
32249
|
+
var BACKOFF_ALLOWANCE_MS = 2100;
|
|
32250
|
+
var RETAIN_BUDGET_MS = 12e4;
|
|
32251
|
+
function defaultBatchTimeoutMs(extract) {
|
|
32252
|
+
const attempts = Math.max(1, extract.maxAttempts ?? 3);
|
|
32253
|
+
const calls = (extract.timeoutMs ?? 6e4) * attempts + BACKOFF_ALLOWANCE_MS * (attempts - 1);
|
|
32254
|
+
const extraction = extract.totalTimeoutMs !== void 0 ? Math.min(extract.totalTimeoutMs, calls) : calls;
|
|
32255
|
+
return extraction + RETAIN_BUDGET_MS;
|
|
32256
|
+
}
|
|
32257
|
+
var BatchFailureError = class extends Error {
|
|
32258
|
+
constructor(message, terminal) {
|
|
32259
|
+
super(message);
|
|
32260
|
+
this.terminal = terminal;
|
|
32261
|
+
}
|
|
32262
|
+
};
|
|
32263
|
+
var AccountFailureError = class extends Error {
|
|
32264
|
+
};
|
|
32265
|
+
var LockedSourcesError = class extends Error {
|
|
32266
|
+
constructor(persistentIds) {
|
|
32267
|
+
super("Source messages are locked; retained for retry");
|
|
32268
|
+
this.persistentIds = persistentIds;
|
|
32269
|
+
}
|
|
32270
|
+
};
|
|
32187
32271
|
function idsOf(job) {
|
|
32188
32272
|
return JSON.parse(String(job._getRaw("message_ids")));
|
|
32189
32273
|
}
|
|
@@ -32191,6 +32275,16 @@ function seqOf(job) {
|
|
|
32191
32275
|
const raw = job._getRaw("watermark_seq");
|
|
32192
32276
|
return typeof raw === "number" && raw > 0 ? raw : void 0;
|
|
32193
32277
|
}
|
|
32278
|
+
function failedSessionsOf(job, head) {
|
|
32279
|
+
if (job._getRaw("failed_head") !== head) return 0;
|
|
32280
|
+
const raw = job._getRaw("failed_sessions");
|
|
32281
|
+
return typeof raw === "number" && raw > 0 ? raw : 0;
|
|
32282
|
+
}
|
|
32283
|
+
function failedAtOf(job, head) {
|
|
32284
|
+
if (job._getRaw("failed_head") !== head) return void 0;
|
|
32285
|
+
const raw = job._getRaw("failed_at");
|
|
32286
|
+
return typeof raw === "number" ? raw : void 0;
|
|
32287
|
+
}
|
|
32194
32288
|
function createDurableAutoExtractor(options) {
|
|
32195
32289
|
const vault = options.retainCtx.vaultCtx;
|
|
32196
32290
|
const database = vault.database;
|
|
@@ -32213,6 +32307,9 @@ function createDurableAutoExtractor(options) {
|
|
|
32213
32307
|
let writes = Promise.resolve();
|
|
32214
32308
|
let storeFailures = 0;
|
|
32215
32309
|
const attempts = /* @__PURE__ */ new Map();
|
|
32310
|
+
const sessionFailures = /* @__PURE__ */ new Map();
|
|
32311
|
+
const skippedThisSession = /* @__PURE__ */ new Set();
|
|
32312
|
+
const clock = options.now ?? Date.now;
|
|
32216
32313
|
const reportError = (error, conversationId) => {
|
|
32217
32314
|
try {
|
|
32218
32315
|
options.onError?.(error instanceof Error ? error : new Error(String(error)), conversationId);
|
|
@@ -32241,6 +32338,7 @@ function createDurableAutoExtractor(options) {
|
|
|
32241
32338
|
}
|
|
32242
32339
|
async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
|
|
32243
32340
|
let watchdog;
|
|
32341
|
+
let cancelled = false;
|
|
32244
32342
|
const model = options.modelForScope?.(jobScope) ?? options.extract.model;
|
|
32245
32343
|
try {
|
|
32246
32344
|
return await Promise.race([
|
|
@@ -32251,7 +32349,7 @@ function createDurableAutoExtractor(options) {
|
|
|
32251
32349
|
...options.retainCtx,
|
|
32252
32350
|
vaultCtx: {
|
|
32253
32351
|
...vault,
|
|
32254
|
-
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
|
|
32352
|
+
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
|
|
32255
32353
|
}
|
|
32256
32354
|
},
|
|
32257
32355
|
// The model follows the job's scope, not the mode alive at drain time.
|
|
@@ -32271,14 +32369,116 @@ function createDurableAutoExtractor(options) {
|
|
|
32271
32369
|
new Promise((_resolve, reject) => {
|
|
32272
32370
|
watchdog = setTimeout(
|
|
32273
32371
|
() => reject(new Error("Extraction did not settle in time; retained for retry")),
|
|
32274
|
-
options.batchTimeoutMs ??
|
|
32372
|
+
options.batchTimeoutMs ?? defaultBatchTimeoutMs(options.extract)
|
|
32275
32373
|
);
|
|
32276
32374
|
})
|
|
32277
32375
|
]);
|
|
32278
32376
|
} finally {
|
|
32377
|
+
cancelled = true;
|
|
32279
32378
|
if (watchdog !== void 0) clearTimeout(watchdog);
|
|
32280
32379
|
}
|
|
32281
32380
|
}
|
|
32381
|
+
async function acknowledge(job, ids, loaded, abandoned) {
|
|
32382
|
+
attempts.delete(job.id);
|
|
32383
|
+
sessionFailures.delete(job.id);
|
|
32384
|
+
skippedThisSession.delete(job.id);
|
|
32385
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
32386
|
+
for (const message of loaded)
|
|
32387
|
+
if (message && message.messageId > 0) sequences.set(message.uniqueId, message.messageId);
|
|
32388
|
+
await database.write(async () => {
|
|
32389
|
+
const current = idsOf(job);
|
|
32390
|
+
const acknowledged = ids.filter((id) => current.includes(id));
|
|
32391
|
+
if (!acknowledged.length) return;
|
|
32392
|
+
const done = new Set(acknowledged);
|
|
32393
|
+
const remaining = current.filter((id) => !done.has(id));
|
|
32394
|
+
await job.update((r) => {
|
|
32395
|
+
r._setRaw(
|
|
32396
|
+
"message_ids",
|
|
32397
|
+
JSON.stringify(
|
|
32398
|
+
remaining.length ? [...abandoned ? [] : acknowledged.slice(-2), ...remaining] : []
|
|
32399
|
+
)
|
|
32400
|
+
);
|
|
32401
|
+
const newest = acknowledged[acknowledged.length - 1];
|
|
32402
|
+
r._setRaw("watermark", newest);
|
|
32403
|
+
const sequence = sequences.get(newest);
|
|
32404
|
+
if (sequence !== void 0) {
|
|
32405
|
+
const prior = seqOf(job);
|
|
32406
|
+
r._setRaw("watermark_seq", prior === void 0 ? sequence : Math.max(prior, sequence));
|
|
32407
|
+
}
|
|
32408
|
+
r._setRaw("failed_sessions", abandoned ? MAX_FAILED_SESSIONS : null);
|
|
32409
|
+
r._setRaw("failed_head", abandoned ? newest : null);
|
|
32410
|
+
r._setRaw("failed_at", null);
|
|
32411
|
+
});
|
|
32412
|
+
});
|
|
32413
|
+
}
|
|
32414
|
+
async function recordFailure(job, ids, loaded, error, conversationId) {
|
|
32415
|
+
const head = ids[0];
|
|
32416
|
+
if (!head || !loaded.some(Boolean)) return false;
|
|
32417
|
+
if (error instanceof AccountFailureError) {
|
|
32418
|
+
skippedThisSession.add(job.id);
|
|
32419
|
+
return false;
|
|
32420
|
+
}
|
|
32421
|
+
const counted = error instanceof BatchFailureError || error instanceof LockedSourcesError && error.persistentIds.length > 0;
|
|
32422
|
+
if (!counted) return false;
|
|
32423
|
+
const prior = sessionFailures.get(job.id);
|
|
32424
|
+
const count = prior?.head === head ? prior.count + 1 : 1;
|
|
32425
|
+
sessionFailures.set(job.id, { head, count });
|
|
32426
|
+
const terminal = error instanceof BatchFailureError && error.terminal;
|
|
32427
|
+
if (!terminal && count < MAX_ATTEMPTS) return false;
|
|
32428
|
+
skippedThisSession.add(job.id);
|
|
32429
|
+
const now = clock();
|
|
32430
|
+
const lastCounted = failedAtOf(job, head);
|
|
32431
|
+
if (lastCounted !== void 0 && now - lastCounted < MIN_FAILED_SESSION_GAP_MS) return false;
|
|
32432
|
+
const failures = failedSessionsOf(job, head) + 1;
|
|
32433
|
+
if (failures < MAX_FAILED_SESSIONS) {
|
|
32434
|
+
await database.write(
|
|
32435
|
+
() => job.update((r) => {
|
|
32436
|
+
r._setRaw("failed_sessions", failures);
|
|
32437
|
+
r._setRaw("failed_head", head);
|
|
32438
|
+
r._setRaw("failed_at", now);
|
|
32439
|
+
})
|
|
32440
|
+
);
|
|
32441
|
+
return false;
|
|
32442
|
+
}
|
|
32443
|
+
if (error instanceof LockedSourcesError) {
|
|
32444
|
+
const dropped = new Set(error.persistentIds);
|
|
32445
|
+
let newest;
|
|
32446
|
+
for (const message of loaded)
|
|
32447
|
+
if (message && dropped.has(message.uniqueId) && message.messageId > (newest?.seq ?? 0))
|
|
32448
|
+
newest = { id: message.uniqueId, seq: message.messageId };
|
|
32449
|
+
const advance = newest !== void 0 && newest.seq > (seqOf(job) ?? 0) ? newest : void 0;
|
|
32450
|
+
await database.write(
|
|
32451
|
+
() => job.update((r) => {
|
|
32452
|
+
r._setRaw("message_ids", JSON.stringify(idsOf(job).filter((id) => !dropped.has(id))));
|
|
32453
|
+
if (advance) {
|
|
32454
|
+
r._setRaw("watermark", advance.id);
|
|
32455
|
+
r._setRaw("watermark_seq", advance.seq);
|
|
32456
|
+
}
|
|
32457
|
+
r._setRaw("failed_sessions", advance ? MAX_FAILED_SESSIONS : null);
|
|
32458
|
+
r._setRaw("failed_head", advance ? advance.id : null);
|
|
32459
|
+
r._setRaw("failed_at", null);
|
|
32460
|
+
})
|
|
32461
|
+
);
|
|
32462
|
+
attempts.delete(job.id);
|
|
32463
|
+
sessionFailures.delete(job.id);
|
|
32464
|
+
skippedThisSession.delete(job.id);
|
|
32465
|
+
reportError(
|
|
32466
|
+
new Error(
|
|
32467
|
+
`Dropped ${dropped.size} source id(s) that stayed locked for ${failures} sessions to unblock extraction`
|
|
32468
|
+
),
|
|
32469
|
+
conversationId
|
|
32470
|
+
);
|
|
32471
|
+
return true;
|
|
32472
|
+
}
|
|
32473
|
+
await acknowledge(job, ids, loaded, true);
|
|
32474
|
+
reportError(
|
|
32475
|
+
new Error(
|
|
32476
|
+
`Abandoned an extraction batch of ${ids.length} source id(s) after it failed in ${failures} sessions`
|
|
32477
|
+
),
|
|
32478
|
+
conversationId
|
|
32479
|
+
);
|
|
32480
|
+
return true;
|
|
32481
|
+
}
|
|
32282
32482
|
async function drain() {
|
|
32283
32483
|
if (disposed || running) return;
|
|
32284
32484
|
running = true;
|
|
@@ -32293,21 +32493,23 @@ function createDurableAutoExtractor(options) {
|
|
|
32293
32493
|
for (const job of pending) {
|
|
32294
32494
|
if (disposed) break;
|
|
32295
32495
|
const attempted = attempts.get(job.id) ?? 0;
|
|
32296
|
-
if (attempted >= MAX_ATTEMPTS) continue;
|
|
32496
|
+
if (attempted >= MAX_ATTEMPTS || skippedThisSession.has(job.id)) continue;
|
|
32297
32497
|
if (processed >= MAX_JOBS_PER_PASS) {
|
|
32298
32498
|
scheduleAfterSuccess = true;
|
|
32299
32499
|
break;
|
|
32300
32500
|
}
|
|
32301
32501
|
processed++;
|
|
32302
32502
|
const conversationId = String(job._getRaw("conversation_id"));
|
|
32503
|
+
let ids = [];
|
|
32504
|
+
let loaded = [];
|
|
32303
32505
|
try {
|
|
32304
32506
|
const conversations = await storage.conversationsCollection.query(import_watermelondb24.Q.where("conversation_id", conversationId), import_watermelondb24.Q.where("is_deleted", false)).fetchCount();
|
|
32305
32507
|
if (!conversations) {
|
|
32306
32508
|
await database.write(() => job.destroyPermanently());
|
|
32307
32509
|
continue;
|
|
32308
32510
|
}
|
|
32309
|
-
|
|
32310
|
-
|
|
32511
|
+
ids = idsOf(job).slice(0, 20);
|
|
32512
|
+
loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
|
|
32311
32513
|
const missing = ids.filter((_id, index) => loaded[index] === null);
|
|
32312
32514
|
if (missing.length) {
|
|
32313
32515
|
if (attempted < MAX_ATTEMPTS - 1)
|
|
@@ -32331,17 +32533,21 @@ function createDurableAutoExtractor(options) {
|
|
|
32331
32533
|
);
|
|
32332
32534
|
continue;
|
|
32333
32535
|
}
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32536
|
+
const locked = loaded.filter((message) => message && isEncrypted(message.content));
|
|
32537
|
+
if (locked.length) {
|
|
32538
|
+
throw new LockedSourcesError(
|
|
32539
|
+
locked.flatMap(
|
|
32540
|
+
(message) => message.decryptionStatus === "auth_mismatch" || message.decryptionStatus === "invalid_payload" ? [message.uniqueId] : []
|
|
32541
|
+
)
|
|
32542
|
+
);
|
|
32338
32543
|
}
|
|
32339
32544
|
const messages = loaded.flatMap(
|
|
32340
32545
|
(m) => m && m.conversationId === conversationId && (m.role === "user" || m.role === "assistant") ? [{ id: m.uniqueId, role: m.role, content: m.content }] : []
|
|
32341
32546
|
);
|
|
32342
32547
|
let success = messages.length === 0;
|
|
32548
|
+
let result;
|
|
32343
32549
|
if (messages.length) {
|
|
32344
|
-
|
|
32550
|
+
result = await extractBatch(
|
|
32345
32551
|
messages,
|
|
32346
32552
|
conversationId,
|
|
32347
32553
|
ids,
|
|
@@ -32351,41 +32557,35 @@ function createDurableAutoExtractor(options) {
|
|
|
32351
32557
|
success = result.failedCount === 0 && result.outcome !== "empty-after-retry";
|
|
32352
32558
|
options.onTurnComplete?.(result);
|
|
32353
32559
|
}
|
|
32354
|
-
if (!success)
|
|
32355
|
-
|
|
32356
|
-
|
|
32357
|
-
|
|
32358
|
-
|
|
32359
|
-
|
|
32360
|
-
|
|
32361
|
-
|
|
32362
|
-
const acknowledged = ids.filter((id) => current.includes(id));
|
|
32363
|
-
if (!acknowledged.length) return;
|
|
32364
|
-
const done = new Set(acknowledged);
|
|
32365
|
-
const remaining = current.filter((id) => !done.has(id));
|
|
32366
|
-
if (remaining.length) {
|
|
32367
|
-
await job.update(
|
|
32368
|
-
(r) => r._setRaw("message_ids", JSON.stringify([...acknowledged.slice(-2), ...remaining]))
|
|
32369
|
-
);
|
|
32370
|
-
} else await job.update((r) => r._setRaw("message_ids", "[]"));
|
|
32371
|
-
const newest = acknowledged[acknowledged.length - 1];
|
|
32372
|
-
await job.update((r) => {
|
|
32373
|
-
r._setRaw("watermark", newest);
|
|
32374
|
-
const sequence = sequences.get(newest);
|
|
32375
|
-
if (sequence !== void 0) {
|
|
32376
|
-
const prior = seqOf(job);
|
|
32377
|
-
r._setRaw(
|
|
32378
|
-
"watermark_seq",
|
|
32379
|
-
prior === void 0 ? sequence : Math.max(prior, sequence)
|
|
32560
|
+
if (!success) {
|
|
32561
|
+
const failure = result?.failure;
|
|
32562
|
+
if (failure?.reason === "http-terminal") {
|
|
32563
|
+
const status = failure.httpStatus;
|
|
32564
|
+
if (status !== void 0 && REQUEST_REJECTED_STATUSES.has(status))
|
|
32565
|
+
throw new BatchFailureError(
|
|
32566
|
+
`Extraction rejected the batch (HTTP ${status}); not retried this session`,
|
|
32567
|
+
true
|
|
32380
32568
|
);
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32569
|
+
throw new AccountFailureError(
|
|
32570
|
+
`Extraction refused for the account (HTTP ${status ?? "unknown"}); retried next session`
|
|
32571
|
+
);
|
|
32572
|
+
}
|
|
32573
|
+
if ((result?.failedCount ?? 0) > 0 || failure !== void 0 && CONTENT_FAILURE_REASONS.has(failure.reason))
|
|
32574
|
+
throw new BatchFailureError("Extraction batch incomplete; retained for retry", false);
|
|
32575
|
+
throw new Error("Extraction batch incomplete; retained for retry");
|
|
32576
|
+
}
|
|
32577
|
+
await acknowledge(job, ids, loaded, false);
|
|
32384
32578
|
scheduleAfterSuccess = true;
|
|
32385
32579
|
} catch (error) {
|
|
32386
32580
|
attempts.set(job.id, attempted + 1);
|
|
32387
32581
|
retry = true;
|
|
32388
32582
|
reportError(error, conversationId);
|
|
32583
|
+
try {
|
|
32584
|
+
if (await recordFailure(job, ids, loaded, error, conversationId))
|
|
32585
|
+
scheduleAfterSuccess = true;
|
|
32586
|
+
} catch (recordError) {
|
|
32587
|
+
reportError(recordError, conversationId);
|
|
32588
|
+
}
|
|
32389
32589
|
}
|
|
32390
32590
|
}
|
|
32391
32591
|
} catch (error) {
|
|
@@ -32455,7 +32655,7 @@ function createDurableAutoExtractor(options) {
|
|
|
32455
32655
|
const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
|
|
32456
32656
|
if (start >= ids.length) return;
|
|
32457
32657
|
const ownWatermark = job?._getRaw("watermark");
|
|
32458
|
-
const atOwnWatermark = !!job && boundary >= 0 && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
|
|
32658
|
+
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));
|
|
32459
32659
|
const overlapStart = atOwnWatermark ? Math.max(0, boundary - 1) : start;
|
|
32460
32660
|
const fresh = ids.slice(overlapStart).filter((id) => !foreignSources.has(id));
|
|
32461
32661
|
if (!fresh.length) return;
|