@anuma/sdk 6.21.1 → 6.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expo/{chunk-CIMZZ7GQ.mjs → chunk-6BULYLRY.mjs} +92 -8
- package/dist/expo/index.cjs +761 -162
- package/dist/expo/index.d.mts +151 -9
- package/dist/expo/index.d.ts +151 -9
- package/dist/expo/index.mjs +516 -14
- package/dist/{react/recall-7Q3V337H.mjs → expo/recall-CUTJWQDQ.mjs} +1 -1
- package/dist/memory/context.cjs +183 -0
- package/dist/memory/context.d.mts +936 -0
- package/dist/memory/context.d.ts +936 -0
- package/dist/memory/context.mjs +157 -0
- package/dist/react/{chunk-AVYB5IXC.mjs → chunk-GPLMMMZN.mjs} +1 -1
- package/dist/react/{chunk-JRVAUZHM.mjs → chunk-K4N52PSK.mjs} +93 -9
- package/dist/react/{consolidate-URHGJT7I.mjs → consolidate-XKS6IZFC.mjs} +2 -2
- package/dist/react/index.cjs +876 -270
- package/dist/react/index.d.mts +209 -9
- package/dist/react/index.d.ts +209 -9
- package/dist/react/index.mjs +569 -65
- package/dist/{expo/recall-FQQGE6A2.mjs → react/recall-LFHIWONV.mjs} +2 -2
- package/dist/server/{chunk-4DSDATYG.mjs → chunk-FEDRFVHD.mjs} +182 -98
- package/dist/server/index.cjs +815 -216
- package/dist/server/index.d.mts +150 -5
- package/dist/server/index.d.ts +150 -5
- package/dist/server/index.mjs +509 -7
- package/dist/server/{recall-HA4UX4BB.mjs → recall-Z2WJGXOL.mjs} +1 -1
- package/dist/telemetry/index.d.mts +7 -1
- package/dist/telemetry/index.d.ts +7 -1
- package/dist/tools/index.cjs +347 -1
- package/dist/tools/index.d.mts +167 -1
- package/dist/tools/index.d.ts +167 -1
- package/dist/tools/index.mjs +343 -1
- package/package.json +8 -1
- package/dist/react/{chunk-WUG6GTHO.mjs → chunk-MHZB6KIX.mjs} +8 -8
package/dist/server/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ENTITY_FANOUT,
|
|
9
9
|
ENTITY_KINDS,
|
|
10
10
|
EncryptionKeyMissingError,
|
|
11
|
+
ExtractionJob,
|
|
11
12
|
LRUCache,
|
|
12
13
|
MAX_HOPS,
|
|
13
14
|
NODE_BUDGET,
|
|
@@ -54,6 +55,7 @@ import {
|
|
|
54
55
|
decryptJsonField,
|
|
55
56
|
deleteAllVaultMemoriesForUserOp,
|
|
56
57
|
deleteConversationOp,
|
|
58
|
+
deleteMessageOp,
|
|
57
59
|
deleteVaultMemoryOp,
|
|
58
60
|
eagerEmbedContent,
|
|
59
61
|
embedAllMessages,
|
|
@@ -141,7 +143,7 @@ import {
|
|
|
141
143
|
updateVaultMemoryOp,
|
|
142
144
|
upsertMessageOp,
|
|
143
145
|
vaultMemoryToStored
|
|
144
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-FEDRFVHD.mjs";
|
|
145
147
|
import {
|
|
146
148
|
createStreamingDeAnonymizer,
|
|
147
149
|
notifyConsolidationFallback,
|
|
@@ -330,7 +332,7 @@ var require_children = __commonJS({
|
|
|
330
332
|
var _makeDecorator = _interopRequireDefault(require_makeDecorator());
|
|
331
333
|
var _logError = _interopRequireDefault(require_logError());
|
|
332
334
|
var _invariant = _interopRequireDefault(require_invariant());
|
|
333
|
-
var
|
|
335
|
+
var Q8 = _interopRequireWildcard(require_QueryDescription());
|
|
334
336
|
function _getRequireWildcardCache(e) {
|
|
335
337
|
if ("function" != typeof WeakMap) return null;
|
|
336
338
|
var r = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap();
|
|
@@ -364,7 +366,7 @@ var require_children = __commonJS({
|
|
|
364
366
|
var childCollection = model.collections.get(childTable);
|
|
365
367
|
var association = model.constructor.associations[childTable];
|
|
366
368
|
(0, _invariant.default)(association && "has_many" === association.type, "@children decorator used for a table that's not has_many");
|
|
367
|
-
var query = childCollection.query(
|
|
369
|
+
var query = childCollection.query(Q8.where(association.foreignKey, model.id));
|
|
368
370
|
that._childrenQueryCache[childTable] = query;
|
|
369
371
|
return query;
|
|
370
372
|
},
|
|
@@ -1334,10 +1336,25 @@ __decorateClass([
|
|
|
1334
1336
|
], VaultFolder.prototype, "context", 2);
|
|
1335
1337
|
|
|
1336
1338
|
// src/lib/db/schema.ts
|
|
1337
|
-
var SDK_SCHEMA_VERSION =
|
|
1339
|
+
var SDK_SCHEMA_VERSION = 46;
|
|
1338
1340
|
var sdkSchema = (0, import_watermelondb12.appSchema)({
|
|
1339
1341
|
version: SDK_SCHEMA_VERSION,
|
|
1340
1342
|
tables: [
|
|
1343
|
+
(0, import_watermelondb12.tableSchema)({
|
|
1344
|
+
name: "memory_extraction_jobs",
|
|
1345
|
+
columns: [
|
|
1346
|
+
{ name: "owner_key", type: "string", isIndexed: true },
|
|
1347
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
1348
|
+
{ name: "scope", type: "string" },
|
|
1349
|
+
{ name: "message_ids", type: "string" },
|
|
1350
|
+
{ name: "watermark", type: "string", isOptional: true },
|
|
1351
|
+
// Deletion-proof anchor: history.message_id of the newest acknowledged
|
|
1352
|
+
// source. `watermark` alone is an id, and deleting that message erased
|
|
1353
|
+
// the anchor — see the column note in extractionJobs/models.ts.
|
|
1354
|
+
{ name: "watermark_seq", type: "number", isOptional: true },
|
|
1355
|
+
{ name: "folder_id", type: "string", isOptional: true }
|
|
1356
|
+
]
|
|
1357
|
+
}),
|
|
1341
1358
|
// Chat storage tables
|
|
1342
1359
|
(0, import_watermelondb12.tableSchema)({
|
|
1343
1360
|
name: "history",
|
|
@@ -2342,10 +2359,28 @@ var sdkMigrations = (0, import_migrations.schemaMigrations)({
|
|
|
2342
2359
|
columns: [{ name: "media", type: "string", isOptional: true }]
|
|
2343
2360
|
})
|
|
2344
2361
|
]
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
toVersion: 46,
|
|
2365
|
+
steps: [
|
|
2366
|
+
(0, import_migrations.createTable)({
|
|
2367
|
+
name: "memory_extraction_jobs",
|
|
2368
|
+
columns: [
|
|
2369
|
+
{ name: "owner_key", type: "string", isIndexed: true },
|
|
2370
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
2371
|
+
{ name: "scope", type: "string" },
|
|
2372
|
+
{ name: "message_ids", type: "string" },
|
|
2373
|
+
{ name: "watermark", type: "string", isOptional: true },
|
|
2374
|
+
{ name: "watermark_seq", type: "number", isOptional: true },
|
|
2375
|
+
{ name: "folder_id", type: "string", isOptional: true }
|
|
2376
|
+
]
|
|
2377
|
+
})
|
|
2378
|
+
]
|
|
2345
2379
|
}
|
|
2346
2380
|
]
|
|
2347
2381
|
});
|
|
2348
2382
|
var sdkModelClasses = [
|
|
2383
|
+
ExtractionJob,
|
|
2349
2384
|
Message,
|
|
2350
2385
|
Conversation,
|
|
2351
2386
|
ConversationSummary,
|
|
@@ -3696,7 +3731,8 @@ function classifyDecay(m, now, policy) {
|
|
|
3696
3731
|
if (m.eventTimeEnd < now - resolved.pastEventGraceMs) return "archive";
|
|
3697
3732
|
if (m.eventTimeEnd >= now) return "keep";
|
|
3698
3733
|
}
|
|
3699
|
-
|
|
3734
|
+
const observedAt = Number.isFinite(m.lastObservedAt) ? m.lastObservedAt : m.updatedAt;
|
|
3735
|
+
if (now - Math.max(m.updatedAt, observedAt) > ttlForType(m.factType, resolved)) return "archive";
|
|
3700
3736
|
return "keep";
|
|
3701
3737
|
}
|
|
3702
3738
|
|
|
@@ -5557,6 +5593,7 @@ async function retain(content, ctx, options = {}) {
|
|
|
5557
5593
|
const updated = await updateVaultMemoryOp(ctx.vaultCtx, targetId, {
|
|
5558
5594
|
content: existing.content,
|
|
5559
5595
|
proofCountIncrement: 1,
|
|
5596
|
+
observationSourceIds: options.sourceChunkIds,
|
|
5560
5597
|
sourceChunkIds: mergedSourceIds,
|
|
5561
5598
|
// resurrect encodes the decay gate: ACTIVE target → { preserveUpdatedAt:
|
|
5562
5599
|
// true } (main's normal re-observation path — bump proof_count without
|
|
@@ -5800,6 +5837,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
5800
5837
|
const updated = await updateVaultMemoryOp(ctx.vaultCtx, decision.targetId, {
|
|
5801
5838
|
content: existing.content,
|
|
5802
5839
|
proofCountIncrement: 1,
|
|
5840
|
+
observationSourceIds: options.sourceChunkIds,
|
|
5803
5841
|
sourceChunkIds: mergedSourceIds,
|
|
5804
5842
|
// ACTIVE target → { preserveUpdatedAt: true } (main's normal re-observation
|
|
5805
5843
|
// path); ARCHIVED (non-superseded, non-deleted) → { restore: true } and no
|
|
@@ -5849,6 +5887,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
5849
5887
|
const updated = await updateVaultMemoryOp(ctx.vaultCtx, decision.targetId, {
|
|
5850
5888
|
content: decision.content,
|
|
5851
5889
|
proofCountIncrement: 1,
|
|
5890
|
+
observationSourceIds: options.sourceChunkIds,
|
|
5852
5891
|
sourceChunkIds: mergedSourceIds,
|
|
5853
5892
|
embedding: JSON.stringify(newEmbedding),
|
|
5854
5893
|
embeddingModel: consolidatedModel,
|
|
@@ -6485,7 +6524,7 @@ function createAutoExtractor(options) {
|
|
|
6485
6524
|
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
6486
6525
|
}
|
|
6487
6526
|
});
|
|
6488
|
-
if (outcome !== "empty-after-retry") {
|
|
6527
|
+
if (outcome !== "empty-after-retry" && failedCount === 0) {
|
|
6489
6528
|
const advancedTo = window2[window2.length - 1].id;
|
|
6490
6529
|
stateFor(conversationId).watermark = advancedTo;
|
|
6491
6530
|
persistCursor(conversationId, messages, advancedTo, contiguous);
|
|
@@ -6640,6 +6679,7 @@ function toDecayInput(c) {
|
|
|
6640
6679
|
eventTimeEnd: c.eventTimeEnd,
|
|
6641
6680
|
eventTimeKind: c.eventTimeKind,
|
|
6642
6681
|
updatedAt: c.updatedAt,
|
|
6682
|
+
lastObservedAt: c.lastObservedAt,
|
|
6643
6683
|
archivedAt: c.archivedAt,
|
|
6644
6684
|
source: c.source,
|
|
6645
6685
|
trustTier: c.trustTier
|
|
@@ -6721,7 +6761,8 @@ function createDecaySweeper(options) {
|
|
|
6721
6761
|
for (const c of toArchive) {
|
|
6722
6762
|
const ok = await archiveVaultMemoryOp(vaultCtx, c.uniqueId, {
|
|
6723
6763
|
now,
|
|
6724
|
-
expectedUpdatedAt: c.updatedAt
|
|
6764
|
+
expectedUpdatedAt: c.updatedAt,
|
|
6765
|
+
expectedLastObservedAt: c.lastObservedAt ?? null
|
|
6725
6766
|
});
|
|
6726
6767
|
if (ok) archived++;
|
|
6727
6768
|
}
|
|
@@ -6966,6 +7007,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
6966
7007
|
budget: defaultBudget,
|
|
6967
7008
|
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
6968
7009
|
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
7010
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
6969
7011
|
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
6970
7012
|
...toolOptions?.excludeConversationId && {
|
|
6971
7013
|
excludeConversationId: toolOptions.excludeConversationId
|
|
@@ -8155,6 +8197,462 @@ function createMessageSourceResolver(ctx) {
|
|
|
8155
8197
|
};
|
|
8156
8198
|
}
|
|
8157
8199
|
|
|
8200
|
+
// src/lib/memory/context.ts
|
|
8201
|
+
function shouldRecallMemory(query) {
|
|
8202
|
+
const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
|
|
8203
|
+
return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
|
|
8204
|
+
normalized
|
|
8205
|
+
);
|
|
8206
|
+
}
|
|
8207
|
+
async function assembleMemoryContext(options) {
|
|
8208
|
+
const { query, recall: recall2, loadFacts, loadSessionRefs } = options;
|
|
8209
|
+
const memoryIds = options.memoryIds ?? options.recallOptions?.memoryIds;
|
|
8210
|
+
const ranked = shouldRecallMemory(query);
|
|
8211
|
+
const degraded = [];
|
|
8212
|
+
const guard = async (lane, run, fallback) => {
|
|
8213
|
+
try {
|
|
8214
|
+
return await run();
|
|
8215
|
+
} catch (error) {
|
|
8216
|
+
degraded.push(lane);
|
|
8217
|
+
try {
|
|
8218
|
+
options.onDegraded?.(lane, error);
|
|
8219
|
+
} catch {
|
|
8220
|
+
}
|
|
8221
|
+
return fallback;
|
|
8222
|
+
}
|
|
8223
|
+
};
|
|
8224
|
+
const scoped = memoryIds !== void 0 || options.recallOptions?.scopes !== void 0 || options.recallOptions?.folderId !== void 0;
|
|
8225
|
+
const allowed = memoryIds !== void 0 ? new Set(memoryIds) : void 0;
|
|
8226
|
+
const filters = {
|
|
8227
|
+
...options.recallOptions?.scopes && { scopes: options.recallOptions.scopes },
|
|
8228
|
+
...options.recallOptions?.folderId !== void 0 && {
|
|
8229
|
+
folderId: options.recallOptions.folderId
|
|
8230
|
+
}
|
|
8231
|
+
};
|
|
8232
|
+
const active = (m) => !m.isDeleted && !m.archivedAt && !m.supersededBy && m.trustTier !== "quarantined" && (!allowed || allowed.has(m.uniqueId));
|
|
8233
|
+
const asItem = (m, lane, score = 1) => ({
|
|
8234
|
+
id: m.uniqueId,
|
|
8235
|
+
content: m.content,
|
|
8236
|
+
score,
|
|
8237
|
+
lane,
|
|
8238
|
+
sourceIds: m.sourceChunkIds ?? []
|
|
8239
|
+
});
|
|
8240
|
+
const empty = {
|
|
8241
|
+
memories: [],
|
|
8242
|
+
usedBudget: options.recallOptions?.budget ?? "low",
|
|
8243
|
+
reranked: false,
|
|
8244
|
+
candidateCount: 0
|
|
8245
|
+
};
|
|
8246
|
+
const [facts, episodes, profile, session] = await Promise.all([
|
|
8247
|
+
ranked ? guard(
|
|
8248
|
+
"fact",
|
|
8249
|
+
() => recall2(query, { ...options.recallOptions, types: ["fact"], limit: 20, memoryIds }),
|
|
8250
|
+
empty
|
|
8251
|
+
) : empty,
|
|
8252
|
+
ranked && !scoped && options.includeEpisodes !== false ? guard(
|
|
8253
|
+
"episode",
|
|
8254
|
+
() => recall2(query, {
|
|
8255
|
+
...options.recallOptions,
|
|
8256
|
+
types: ["chunk"],
|
|
8257
|
+
limit: 4,
|
|
8258
|
+
minScore: 0.5,
|
|
8259
|
+
onDiagnostics: void 0
|
|
8260
|
+
}),
|
|
8261
|
+
empty
|
|
8262
|
+
) : empty,
|
|
8263
|
+
loadFacts ? guard(
|
|
8264
|
+
"profile",
|
|
8265
|
+
async () => {
|
|
8266
|
+
const stable = await loadFacts({
|
|
8267
|
+
...filters,
|
|
8268
|
+
memoryIds,
|
|
8269
|
+
factTypes: ["identity", "preference", "constraint", "relationship"],
|
|
8270
|
+
limit: 8
|
|
8271
|
+
});
|
|
8272
|
+
const dynamic = await loadFacts({
|
|
8273
|
+
...filters,
|
|
8274
|
+
memoryIds,
|
|
8275
|
+
factTypes: ["ongoing_context", "plan"],
|
|
8276
|
+
limit: 4
|
|
8277
|
+
});
|
|
8278
|
+
return [...stable, ...dynamic].filter(active).map((m) => asItem(m, "profile"));
|
|
8279
|
+
},
|
|
8280
|
+
[]
|
|
8281
|
+
) : [],
|
|
8282
|
+
loadFacts && loadSessionRefs ? guard(
|
|
8283
|
+
"session",
|
|
8284
|
+
async () => {
|
|
8285
|
+
const refs = (await loadSessionRefs()).filter((r) => !allowed || allowed.has(r.memoryId)).sort((a, b) => b.score - a.score).slice(0, 10);
|
|
8286
|
+
if (!refs.length) return [];
|
|
8287
|
+
const rows = await loadFacts({ ...filters, memoryIds: refs.map((r) => r.memoryId) });
|
|
8288
|
+
const byId = new Map(rows.filter(active).map((m) => [m.uniqueId, m]));
|
|
8289
|
+
return refs.flatMap((ref) => {
|
|
8290
|
+
const m = byId.get(ref.memoryId);
|
|
8291
|
+
return m ? [asItem(m, "session", ref.score)] : [];
|
|
8292
|
+
});
|
|
8293
|
+
},
|
|
8294
|
+
[]
|
|
8295
|
+
) : []
|
|
8296
|
+
]);
|
|
8297
|
+
const toItems = (result, lane) => result.memories.filter((m) => !allowed || allowed.has(m.id)).map((m) => ({
|
|
8298
|
+
id: m.id,
|
|
8299
|
+
content: m.content,
|
|
8300
|
+
score: m.score,
|
|
8301
|
+
lane,
|
|
8302
|
+
...lane === "fact" && { recalled: true },
|
|
8303
|
+
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
|
|
8304
|
+
}));
|
|
8305
|
+
const recalledFacts = toItems(facts, "fact");
|
|
8306
|
+
const recalledScores = /* @__PURE__ */ new Map();
|
|
8307
|
+
const recalledContentScores = /* @__PURE__ */ new Map();
|
|
8308
|
+
for (const item of recalledFacts) {
|
|
8309
|
+
recalledScores.set(item.id, Math.max(recalledScores.get(item.id) ?? -Infinity, item.score));
|
|
8310
|
+
const content = item.content.trim();
|
|
8311
|
+
recalledContentScores.set(
|
|
8312
|
+
content,
|
|
8313
|
+
Math.max(recalledContentScores.get(content) ?? -Infinity, item.score)
|
|
8314
|
+
);
|
|
8315
|
+
}
|
|
8316
|
+
const rankedProfile = profile.map((item) => {
|
|
8317
|
+
const score = Math.max(
|
|
8318
|
+
recalledScores.get(item.id) ?? -Infinity,
|
|
8319
|
+
recalledContentScores.get(item.content.trim()) ?? -Infinity
|
|
8320
|
+
);
|
|
8321
|
+
return score === -Infinity ? item : { ...item, score, recalled: true };
|
|
8322
|
+
});
|
|
8323
|
+
const lanes = [
|
|
8324
|
+
[rankedProfile, 2e3],
|
|
8325
|
+
[recalledFacts, 6e3],
|
|
8326
|
+
[toItems(episodes, "episode"), 3e3],
|
|
8327
|
+
[session, 1e3]
|
|
8328
|
+
];
|
|
8329
|
+
const items = [];
|
|
8330
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
8331
|
+
const seenContent = /* @__PURE__ */ new Set();
|
|
8332
|
+
let remaining = Math.max(0, options.maxChars ?? 12e3);
|
|
8333
|
+
let truncated = false;
|
|
8334
|
+
for (const [candidates, laneBudget] of lanes) {
|
|
8335
|
+
let laneRemaining = Math.min(laneBudget, remaining);
|
|
8336
|
+
for (const item of candidates) {
|
|
8337
|
+
const content = item.content?.trim();
|
|
8338
|
+
const key = `${item.lane === "episode" ? "episode" : "fact"}:${item.id}`;
|
|
8339
|
+
if (!content || seenIds.has(key) || seenContent.has(content)) continue;
|
|
8340
|
+
if (content.length > laneRemaining) {
|
|
8341
|
+
truncated = true;
|
|
8342
|
+
continue;
|
|
8343
|
+
}
|
|
8344
|
+
items.push({ ...item, content });
|
|
8345
|
+
seenIds.add(key);
|
|
8346
|
+
seenContent.add(content);
|
|
8347
|
+
remaining -= content.length;
|
|
8348
|
+
laneRemaining -= content.length;
|
|
8349
|
+
}
|
|
8350
|
+
}
|
|
8351
|
+
return { items, ranked, rankedCount: recalledFacts.length, degraded, truncated };
|
|
8352
|
+
}
|
|
8353
|
+
|
|
8354
|
+
// src/lib/memory/durableExtraction.ts
|
|
8355
|
+
var import_watermelondb20 = __toESM(require_watermelondb());
|
|
8356
|
+
var MAX_JOBS_PER_PASS = 3;
|
|
8357
|
+
var MAX_ATTEMPTS = 3;
|
|
8358
|
+
function idsOf(job) {
|
|
8359
|
+
return JSON.parse(String(job._getRaw("message_ids")));
|
|
8360
|
+
}
|
|
8361
|
+
function seqOf(job) {
|
|
8362
|
+
const raw = job._getRaw("watermark_seq");
|
|
8363
|
+
return typeof raw === "number" && raw > 0 ? raw : void 0;
|
|
8364
|
+
}
|
|
8365
|
+
function createDurableAutoExtractor(options) {
|
|
8366
|
+
const vault = options.retainCtx.vaultCtx;
|
|
8367
|
+
const database = vault.database;
|
|
8368
|
+
const owner = vault.userId ?? vault.walletAddress;
|
|
8369
|
+
if (!owner) throw new Error("Durable extraction requires a user or wallet owner");
|
|
8370
|
+
const jobs = database.get(ExtractionJob.table);
|
|
8371
|
+
const storage = {
|
|
8372
|
+
database,
|
|
8373
|
+
messagesCollection: database.get("history"),
|
|
8374
|
+
conversationsCollection: database.get("conversations"),
|
|
8375
|
+
walletAddress: vault.walletAddress,
|
|
8376
|
+
signMessage: vault.signMessage,
|
|
8377
|
+
embeddedWalletSigner: vault.embeddedWalletSigner
|
|
8378
|
+
};
|
|
8379
|
+
const folderId = options.folderId ?? null;
|
|
8380
|
+
let disposed = false;
|
|
8381
|
+
let running = false;
|
|
8382
|
+
let wakeRequested = false;
|
|
8383
|
+
let timer;
|
|
8384
|
+
let writes = Promise.resolve();
|
|
8385
|
+
let storeFailures = 0;
|
|
8386
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
8387
|
+
const reportError = (error, conversationId) => {
|
|
8388
|
+
try {
|
|
8389
|
+
options.onError?.(error instanceof Error ? error : new Error(String(error)), conversationId);
|
|
8390
|
+
} catch {
|
|
8391
|
+
}
|
|
8392
|
+
};
|
|
8393
|
+
const currentScope = () => {
|
|
8394
|
+
try {
|
|
8395
|
+
const value = typeof options.scope === "function" ? options.scope() : options.scope;
|
|
8396
|
+
return value ?? "private";
|
|
8397
|
+
} catch (error) {
|
|
8398
|
+
reportError(error);
|
|
8399
|
+
return "private";
|
|
8400
|
+
}
|
|
8401
|
+
};
|
|
8402
|
+
function schedule(delay) {
|
|
8403
|
+
if (disposed) return;
|
|
8404
|
+
if (running || timer !== void 0) {
|
|
8405
|
+
if (running) wakeRequested = true;
|
|
8406
|
+
return;
|
|
8407
|
+
}
|
|
8408
|
+
timer = setTimeout(() => {
|
|
8409
|
+
timer = void 0;
|
|
8410
|
+
void drain();
|
|
8411
|
+
}, delay);
|
|
8412
|
+
}
|
|
8413
|
+
async function extractBatch(messages, conversationId, ids, jobScope, jobFolderId) {
|
|
8414
|
+
let watchdog;
|
|
8415
|
+
try {
|
|
8416
|
+
return await Promise.race([
|
|
8417
|
+
new Promise((resolve, reject) => {
|
|
8418
|
+
const worker = createAutoExtractor({
|
|
8419
|
+
...options,
|
|
8420
|
+
retainCtx: {
|
|
8421
|
+
...options.retainCtx,
|
|
8422
|
+
vaultCtx: {
|
|
8423
|
+
...vault,
|
|
8424
|
+
canWrite: async () => !disposed && await storage.conversationsCollection.query(import_watermelondb20.Q.where("conversation_id", conversationId), import_watermelondb20.Q.where("is_deleted", false)).fetchCount() > 0 && await storage.messagesCollection.query(import_watermelondb20.Q.where("conversation_id", conversationId), import_watermelondb20.Q.where("id", import_watermelondb20.Q.oneOf(ids))).fetchCount() === ids.length && (!vault.canWrite || await vault.canWrite()) && !disposed
|
|
8425
|
+
}
|
|
8426
|
+
},
|
|
8427
|
+
// Never publish a private queued observation after a mode flip.
|
|
8428
|
+
scope: jobScope,
|
|
8429
|
+
folderId: jobFolderId,
|
|
8430
|
+
cursorStore: void 0,
|
|
8431
|
+
windowSize: 20,
|
|
8432
|
+
maxWindowSize: 20,
|
|
8433
|
+
onTurnComplete: resolve,
|
|
8434
|
+
onError: reject
|
|
8435
|
+
});
|
|
8436
|
+
worker.processTurn(messages, conversationId);
|
|
8437
|
+
worker.dispose();
|
|
8438
|
+
}),
|
|
8439
|
+
new Promise((_resolve, reject) => {
|
|
8440
|
+
watchdog = setTimeout(
|
|
8441
|
+
() => reject(new Error("Extraction did not settle in time; retained for retry")),
|
|
8442
|
+
options.batchTimeoutMs ?? 18e4
|
|
8443
|
+
);
|
|
8444
|
+
})
|
|
8445
|
+
]);
|
|
8446
|
+
} finally {
|
|
8447
|
+
if (watchdog !== void 0) clearTimeout(watchdog);
|
|
8448
|
+
}
|
|
8449
|
+
}
|
|
8450
|
+
async function drain() {
|
|
8451
|
+
if (disposed || running) return;
|
|
8452
|
+
running = true;
|
|
8453
|
+
wakeRequested = false;
|
|
8454
|
+
let retry = false;
|
|
8455
|
+
let scheduleAfterSuccess = false;
|
|
8456
|
+
try {
|
|
8457
|
+
await writes;
|
|
8458
|
+
const pending = await jobs.query(import_watermelondb20.Q.where("owner_key", owner), import_watermelondb20.Q.where("message_ids", import_watermelondb20.Q.notEq("[]"))).fetch();
|
|
8459
|
+
storeFailures = 0;
|
|
8460
|
+
let processed = 0;
|
|
8461
|
+
for (const job of pending) {
|
|
8462
|
+
if (disposed) break;
|
|
8463
|
+
const attempted = attempts.get(job.id) ?? 0;
|
|
8464
|
+
if (attempted >= MAX_ATTEMPTS) continue;
|
|
8465
|
+
if (processed >= MAX_JOBS_PER_PASS) {
|
|
8466
|
+
scheduleAfterSuccess = true;
|
|
8467
|
+
break;
|
|
8468
|
+
}
|
|
8469
|
+
processed++;
|
|
8470
|
+
const conversationId = String(job._getRaw("conversation_id"));
|
|
8471
|
+
try {
|
|
8472
|
+
const conversations = await storage.conversationsCollection.query(import_watermelondb20.Q.where("conversation_id", conversationId), import_watermelondb20.Q.where("is_deleted", false)).fetchCount();
|
|
8473
|
+
if (!conversations) {
|
|
8474
|
+
await database.write(() => job.destroyPermanently());
|
|
8475
|
+
continue;
|
|
8476
|
+
}
|
|
8477
|
+
const ids = idsOf(job).slice(0, 20);
|
|
8478
|
+
const loaded = await Promise.all(ids.map((id) => getMessageOp(storage, id)));
|
|
8479
|
+
const missing = ids.filter((_id, index) => loaded[index] === null);
|
|
8480
|
+
if (missing.length) {
|
|
8481
|
+
if (attempted < MAX_ATTEMPTS - 1)
|
|
8482
|
+
throw new Error("Source messages not yet available; retained for retry");
|
|
8483
|
+
const dropped = new Set(missing);
|
|
8484
|
+
await database.write(
|
|
8485
|
+
() => job.update(
|
|
8486
|
+
(r) => r._setRaw(
|
|
8487
|
+
"message_ids",
|
|
8488
|
+
JSON.stringify(idsOf(job).filter((id) => !dropped.has(id)))
|
|
8489
|
+
)
|
|
8490
|
+
)
|
|
8491
|
+
);
|
|
8492
|
+
attempts.delete(job.id);
|
|
8493
|
+
scheduleAfterSuccess = true;
|
|
8494
|
+
reportError(
|
|
8495
|
+
new Error(
|
|
8496
|
+
`Dropped ${missing.length} unresolvable source id(s) to unblock extraction`
|
|
8497
|
+
),
|
|
8498
|
+
conversationId
|
|
8499
|
+
);
|
|
8500
|
+
continue;
|
|
8501
|
+
}
|
|
8502
|
+
if (loaded.some(
|
|
8503
|
+
(message) => message && (message.decryptionStatus || isEncrypted(message.content))
|
|
8504
|
+
)) {
|
|
8505
|
+
throw new Error("Source messages are locked; retained for retry");
|
|
8506
|
+
}
|
|
8507
|
+
const messages = loaded.flatMap(
|
|
8508
|
+
(m) => m && m.conversationId === conversationId && (m.role === "user" || m.role === "assistant") ? [{ id: m.uniqueId, role: m.role, content: m.content }] : []
|
|
8509
|
+
);
|
|
8510
|
+
let success = messages.length === 0;
|
|
8511
|
+
if (messages.length) {
|
|
8512
|
+
const result = await extractBatch(
|
|
8513
|
+
messages,
|
|
8514
|
+
conversationId,
|
|
8515
|
+
ids,
|
|
8516
|
+
currentScope() === "private" ? "private" : String(job._getRaw("scope")),
|
|
8517
|
+
job._getRaw("folder_id") ?? null
|
|
8518
|
+
);
|
|
8519
|
+
success = result.failedCount === 0 && result.outcome !== "empty-after-retry";
|
|
8520
|
+
options.onTurnComplete?.(result);
|
|
8521
|
+
}
|
|
8522
|
+
if (!success) throw new Error("Extraction batch incomplete; retained for retry");
|
|
8523
|
+
attempts.delete(job.id);
|
|
8524
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
8525
|
+
for (const message of loaded)
|
|
8526
|
+
if (message && message.messageId > 0)
|
|
8527
|
+
sequences.set(message.uniqueId, message.messageId);
|
|
8528
|
+
await database.write(async () => {
|
|
8529
|
+
const current = idsOf(job);
|
|
8530
|
+
const acknowledged = ids.filter((id) => current.includes(id));
|
|
8531
|
+
if (!acknowledged.length) return;
|
|
8532
|
+
const done = new Set(acknowledged);
|
|
8533
|
+
const remaining = current.filter((id) => !done.has(id));
|
|
8534
|
+
if (remaining.length) {
|
|
8535
|
+
await job.update(
|
|
8536
|
+
(r) => r._setRaw("message_ids", JSON.stringify([...acknowledged.slice(-2), ...remaining]))
|
|
8537
|
+
);
|
|
8538
|
+
} else await job.update((r) => r._setRaw("message_ids", "[]"));
|
|
8539
|
+
const newest = acknowledged[acknowledged.length - 1];
|
|
8540
|
+
await job.update((r) => {
|
|
8541
|
+
r._setRaw("watermark", newest);
|
|
8542
|
+
const sequence = sequences.get(newest);
|
|
8543
|
+
if (sequence !== void 0) {
|
|
8544
|
+
const prior = seqOf(job);
|
|
8545
|
+
r._setRaw(
|
|
8546
|
+
"watermark_seq",
|
|
8547
|
+
prior === void 0 ? sequence : Math.max(prior, sequence)
|
|
8548
|
+
);
|
|
8549
|
+
}
|
|
8550
|
+
});
|
|
8551
|
+
});
|
|
8552
|
+
scheduleAfterSuccess = true;
|
|
8553
|
+
} catch (error) {
|
|
8554
|
+
attempts.set(job.id, attempted + 1);
|
|
8555
|
+
retry = true;
|
|
8556
|
+
reportError(error, conversationId);
|
|
8557
|
+
}
|
|
8558
|
+
}
|
|
8559
|
+
} catch (error) {
|
|
8560
|
+
retry = ++storeFailures < 3;
|
|
8561
|
+
reportError(error);
|
|
8562
|
+
} finally {
|
|
8563
|
+
running = false;
|
|
8564
|
+
const woken = wakeRequested;
|
|
8565
|
+
wakeRequested = false;
|
|
8566
|
+
if (retry || scheduleAfterSuccess || woken)
|
|
8567
|
+
schedule(retry ? options.retryDelayMs ?? 3e4 : 0);
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
schedule(0);
|
|
8571
|
+
return {
|
|
8572
|
+
processTurn(messages, conversationId) {
|
|
8573
|
+
if (disposed || !conversationId || !messages.length) return false;
|
|
8574
|
+
const ids = messages.map((m) => m.id);
|
|
8575
|
+
const scope = currentScope();
|
|
8576
|
+
writes = writes.then(
|
|
8577
|
+
() => database.write(async () => {
|
|
8578
|
+
const existing = await jobs.query(import_watermelondb20.Q.where("owner_key", owner), import_watermelondb20.Q.where("conversation_id", conversationId)).fetch();
|
|
8579
|
+
const job = existing.find(
|
|
8580
|
+
(candidate) => candidate._getRaw("scope") === scope && (candidate._getRaw("folder_id") ?? null) === folderId
|
|
8581
|
+
);
|
|
8582
|
+
const positions = new Map(ids.map((id, index) => [id, index]));
|
|
8583
|
+
const rows = await storage.messagesCollection.query(import_watermelondb20.Q.where("id", import_watermelondb20.Q.oneOf(ids))).fetch();
|
|
8584
|
+
const sequences = /* @__PURE__ */ new Map();
|
|
8585
|
+
for (const row of rows) {
|
|
8586
|
+
const sequence = Number(row._getRaw("message_id"));
|
|
8587
|
+
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
8588
|
+
}
|
|
8589
|
+
let boundary = -1;
|
|
8590
|
+
let observedSeq = 0;
|
|
8591
|
+
const foreignSources = /* @__PURE__ */ new Set();
|
|
8592
|
+
for (const prior of existing) {
|
|
8593
|
+
const sources = [...idsOf(prior), String(prior._getRaw("watermark") ?? "")];
|
|
8594
|
+
for (const id of sources) {
|
|
8595
|
+
boundary = Math.max(boundary, positions.get(id) ?? -1);
|
|
8596
|
+
if (prior !== job) foreignSources.add(id);
|
|
8597
|
+
}
|
|
8598
|
+
observedSeq = Math.max(observedSeq, seqOf(prior) ?? 0);
|
|
8599
|
+
for (const id of idsOf(prior))
|
|
8600
|
+
observedSeq = Math.max(observedSeq, sequences.get(id) ?? 0);
|
|
8601
|
+
}
|
|
8602
|
+
if (observedSeq > 0)
|
|
8603
|
+
ids.forEach((id, index) => {
|
|
8604
|
+
const sequence = sequences.get(id);
|
|
8605
|
+
if (sequence !== void 0 && sequence <= observedSeq)
|
|
8606
|
+
boundary = Math.max(boundary, index);
|
|
8607
|
+
});
|
|
8608
|
+
if (!existing.length && options.cursorStore) {
|
|
8609
|
+
try {
|
|
8610
|
+
const cursor = options.cursorStore.get(conversationId);
|
|
8611
|
+
if (cursor) boundary = Math.max(boundary, positions.get(cursor) ?? -1);
|
|
8612
|
+
} catch (error) {
|
|
8613
|
+
reportError(error, conversationId);
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
if (boundary < 0 && existing.some((prior) => idsOf(prior).length > 0 || seqOf(prior) !== void 0)) {
|
|
8617
|
+
reportError(
|
|
8618
|
+
new Error("Extraction boundary unresolved; skipped rather than re-observing"),
|
|
8619
|
+
conversationId
|
|
8620
|
+
);
|
|
8621
|
+
return;
|
|
8622
|
+
}
|
|
8623
|
+
const start = boundary >= 0 ? boundary + 1 : Math.max(0, ids.length - Math.max(1, options.windowSize ?? 6));
|
|
8624
|
+
if (start >= ids.length) return;
|
|
8625
|
+
const ownWatermark = job?._getRaw("watermark");
|
|
8626
|
+
const atOwnWatermark = !!job && boundary >= 0 && (ids[boundary] === ownWatermark || sequences.get(ids[boundary]) !== void 0 && sequences.get(ids[boundary]) === seqOf(job));
|
|
8627
|
+
const overlapStart = atOwnWatermark ? Math.max(0, boundary - 1) : start;
|
|
8628
|
+
const fresh = ids.slice(overlapStart).filter((id) => !foreignSources.has(id));
|
|
8629
|
+
if (!fresh.length) return;
|
|
8630
|
+
if (job) {
|
|
8631
|
+
await job.update(
|
|
8632
|
+
(r) => r._setRaw("message_ids", JSON.stringify([.../* @__PURE__ */ new Set([...idsOf(job), ...fresh])]))
|
|
8633
|
+
);
|
|
8634
|
+
attempts.delete(job.id);
|
|
8635
|
+
} else {
|
|
8636
|
+
await jobs.create((r) => {
|
|
8637
|
+
r._setRaw("owner_key", owner);
|
|
8638
|
+
r._setRaw("conversation_id", conversationId);
|
|
8639
|
+
r._setRaw("scope", scope);
|
|
8640
|
+
r._setRaw("folder_id", folderId);
|
|
8641
|
+
r._setRaw("message_ids", JSON.stringify([...new Set(fresh)]));
|
|
8642
|
+
});
|
|
8643
|
+
}
|
|
8644
|
+
})
|
|
8645
|
+
).then(() => schedule(options.debounceMs ?? 2e4)).catch((error) => reportError(error, conversationId));
|
|
8646
|
+
return true;
|
|
8647
|
+
},
|
|
8648
|
+
isProcessing: () => running,
|
|
8649
|
+
dispose() {
|
|
8650
|
+
disposed = true;
|
|
8651
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8652
|
+
}
|
|
8653
|
+
};
|
|
8654
|
+
}
|
|
8655
|
+
|
|
8158
8656
|
// src/tools/appBuilderPrompt.ts
|
|
8159
8657
|
var APP_BUILDER_PROMPT = `App Builder tools (create_file, patch_file, and the rest described below) are available this turn, but they are OPTIONAL \u2014 having them does not mean you should build anything.
|
|
8160
8658
|
|
|
@@ -13813,6 +14311,7 @@ export {
|
|
|
13813
14311
|
activatedToolSetNames,
|
|
13814
14312
|
analyzeStyle,
|
|
13815
14313
|
archiveVaultMemoryOp,
|
|
14314
|
+
assembleMemoryContext,
|
|
13816
14315
|
backfillMemoryTopicsOp,
|
|
13817
14316
|
buildConnectorGuidance,
|
|
13818
14317
|
buildDeniedToolsRider,
|
|
@@ -13847,6 +14346,7 @@ export {
|
|
|
13847
14346
|
createConversationOp,
|
|
13848
14347
|
createCryptoPricePreProcessor,
|
|
13849
14348
|
createDecaySweeper,
|
|
14349
|
+
createDurableAutoExtractor,
|
|
13850
14350
|
createLlmDecayClassifier,
|
|
13851
14351
|
createLlmNeighborRefiner,
|
|
13852
14352
|
createMediaBatchOp,
|
|
@@ -13880,6 +14380,7 @@ export {
|
|
|
13880
14380
|
deleteMediaByConversationOp,
|
|
13881
14381
|
deleteMediaByMessageOp,
|
|
13882
14382
|
deleteMediaOp,
|
|
14383
|
+
deleteMessageOp,
|
|
13883
14384
|
deleteModelPreferenceOp,
|
|
13884
14385
|
deleteProjectOp,
|
|
13885
14386
|
deleteUserPreferenceOp,
|
|
@@ -14004,6 +14505,7 @@ export {
|
|
|
14004
14505
|
setUserPreferenceOp,
|
|
14005
14506
|
shouldAnalyzeStyle,
|
|
14006
14507
|
shouldChunkMessage,
|
|
14508
|
+
shouldRecallMemory,
|
|
14007
14509
|
shouldRefreshTools,
|
|
14008
14510
|
splitMessagesAtThreshold,
|
|
14009
14511
|
stampTopicsExtractedAtOp,
|
|
@@ -429,7 +429,13 @@ type RecallDegradation =
|
|
|
429
429
|
| "graph-lane-failed"
|
|
430
430
|
/** The W6 temporal side lane threw and was dropped. Same posture as the graph
|
|
431
431
|
* lane: recall still returned, ranked without the temporal signal. */
|
|
432
|
-
| "temporal-lane-failed"
|
|
432
|
+
| "temporal-lane-failed"
|
|
433
|
+
/** {@link RecallOptions.memoryIds} was set and `types` asked for `"chunk"`.
|
|
434
|
+
* Conversation excerpts have no equivalent membership filter, so running the
|
|
435
|
+
* chunk lane would escape the topic/folder scope — it is dropped instead.
|
|
436
|
+
* Without this signal a scoped caller asking for excerpts got an empty
|
|
437
|
+
* result indistinguishable from "the vault had no match". */
|
|
438
|
+
| "chunks-scope-restricted";
|
|
433
439
|
/**
|
|
434
440
|
* Why a recall returned nothing. `""` when it returned something, so the field
|
|
435
441
|
* is always present and groupable rather than being absent on the healthy path.
|