@anuma/sdk 10.8.0 → 10.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expo/index.cjs +313 -166
- package/dist/expo/index.d.mts +35 -1
- package/dist/expo/index.d.ts +35 -1
- package/dist/expo/index.mjs +298 -158
- package/dist/index.cjs +165 -16
- package/dist/index.d.mts +223 -1
- package/dist/index.d.ts +223 -1
- package/dist/index.mjs +158 -16
- package/dist/react/index.cjs +316 -169
- package/dist/react/index.d.mts +35 -1
- package/dist/react/index.d.ts +35 -1
- package/dist/react/index.mjs +301 -161
- package/dist/server/index.d.mts +12 -0
- package/dist/server/index.d.ts +12 -0
- package/package.json +2 -2
package/dist/expo/index.mjs
CHANGED
|
@@ -9923,118 +9923,19 @@ var xhrTransport = (options) => {
|
|
|
9923
9923
|
return { stream: createStream() };
|
|
9924
9924
|
};
|
|
9925
9925
|
|
|
9926
|
-
// src/lib/
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
}
|
|
9935
|
-
function modelToStored(record2, summary) {
|
|
9936
|
-
return {
|
|
9937
|
-
uniqueId: record2.id,
|
|
9938
|
-
conversationId: record2.conversationId,
|
|
9939
|
-
summary,
|
|
9940
|
-
summarizedUpTo: record2.summarizedUpTo,
|
|
9941
|
-
tokenCount: record2.tokenCount,
|
|
9942
|
-
createdAt: record2.createdAt,
|
|
9943
|
-
updatedAt: record2.updatedAt
|
|
9944
|
-
};
|
|
9945
|
-
}
|
|
9946
|
-
async function getConversationSummaryOp(ctx, conversationId) {
|
|
9947
|
-
const results = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
|
|
9948
|
-
if (results.length === 0) return null;
|
|
9949
|
-
const record2 = results[0];
|
|
9950
|
-
const stored2 = modelToStored(record2, record2.summary);
|
|
9951
|
-
if (ctx.walletAddress && isEncrypted(stored2.summary)) {
|
|
9952
|
-
if (ctx.signMessage) {
|
|
9953
|
-
try {
|
|
9954
|
-
await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
9955
|
-
} catch (error) {
|
|
9956
|
-
getLogger().warn("Failed to request the encryption key for the summary:", error);
|
|
9957
|
-
}
|
|
9958
|
-
}
|
|
9959
|
-
stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
|
|
9960
|
-
} else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
|
|
9961
|
-
try {
|
|
9962
|
-
const encryptedSummary = await encryptField(
|
|
9963
|
-
stored2.summary,
|
|
9964
|
-
ctx.walletAddress,
|
|
9965
|
-
ctx.signMessage,
|
|
9966
|
-
ctx.embeddedWalletSigner
|
|
9967
|
-
);
|
|
9968
|
-
await ctx.database.write(async () => {
|
|
9969
|
-
const current = await ctx.summariesCollection.query(Q3.where("id", record2.id)).fetch();
|
|
9970
|
-
if (current[0]?.summary === stored2.summary) {
|
|
9971
|
-
await current[0].update((rec) => {
|
|
9972
|
-
rec.summary = encryptedSummary;
|
|
9973
|
-
});
|
|
9974
|
-
}
|
|
9975
|
-
});
|
|
9976
|
-
} catch (error) {
|
|
9977
|
-
getLogger().warn("Failed to encrypt the legacy summary:", error);
|
|
9978
|
-
}
|
|
9926
|
+
// src/lib/chatStore/types.ts
|
|
9927
|
+
var ChatStoreError = class extends Error {
|
|
9928
|
+
constructor(message, status, code, retryAfterMs) {
|
|
9929
|
+
super(message);
|
|
9930
|
+
this.status = status;
|
|
9931
|
+
this.code = code;
|
|
9932
|
+
this.retryAfterMs = retryAfterMs;
|
|
9933
|
+
this.name = "ChatStoreError";
|
|
9979
9934
|
}
|
|
9980
|
-
return stored2;
|
|
9981
|
-
}
|
|
9982
|
-
async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
|
|
9983
|
-
const revision2 = getConversationRevision(ctx.database, conversationId);
|
|
9984
|
-
let record2;
|
|
9985
|
-
const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
|
|
9986
|
-
await ctx.database.write(async () => {
|
|
9987
|
-
assertConversationRevision(ctx.database, conversationId, revision2);
|
|
9988
|
-
const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
|
|
9989
|
-
if (existing.length > 0) {
|
|
9990
|
-
record2 = await existing[0].update((rec) => {
|
|
9991
|
-
rec.summary = encryptedSummary;
|
|
9992
|
-
rec.summarizedUpTo = summarizedUpTo;
|
|
9993
|
-
rec.tokenCount = tokenCount;
|
|
9994
|
-
});
|
|
9995
|
-
} else {
|
|
9996
|
-
record2 = await ctx.summariesCollection.create((rec) => {
|
|
9997
|
-
rec.conversationId = conversationId;
|
|
9998
|
-
rec.summary = encryptedSummary;
|
|
9999
|
-
rec.summarizedUpTo = summarizedUpTo;
|
|
10000
|
-
rec.tokenCount = tokenCount;
|
|
10001
|
-
});
|
|
10002
|
-
}
|
|
10003
|
-
});
|
|
10004
|
-
return modelToStored(record2, summary);
|
|
10005
|
-
}
|
|
10006
|
-
async function deleteConversationSummaryOp(ctx, conversationId) {
|
|
10007
|
-
await ctx.database.write(async () => {
|
|
10008
|
-
const existing = await ctx.summariesCollection.query(Q3.where("conversation_id", conversationId)).fetch();
|
|
10009
|
-
for (const record2 of existing) {
|
|
10010
|
-
await record2.destroyPermanently();
|
|
10011
|
-
}
|
|
10012
|
-
});
|
|
10013
|
-
}
|
|
10014
|
-
|
|
10015
|
-
// src/lib/db/conversationMemory/models.ts
|
|
10016
|
-
import { Model as Model3 } from "@nozbe/watermelondb";
|
|
10017
|
-
import { date as date2, field as field2, readonly, text as text3 } from "@nozbe/watermelondb/decorators";
|
|
10018
|
-
var ConversationMemory = class extends Model3 {
|
|
10019
9935
|
};
|
|
10020
|
-
ConversationMemory.table = "conversation_memory";
|
|
10021
|
-
ConversationMemory.associations = {};
|
|
10022
|
-
__decorateClass([
|
|
10023
|
-
text3("conversation_id")
|
|
10024
|
-
], ConversationMemory.prototype, "conversationId", 2);
|
|
10025
|
-
__decorateClass([
|
|
10026
|
-
text3("memory_id")
|
|
10027
|
-
], ConversationMemory.prototype, "memoryId", 2);
|
|
10028
|
-
__decorateClass([
|
|
10029
|
-
field2("score")
|
|
10030
|
-
], ConversationMemory.prototype, "score", 2);
|
|
10031
|
-
__decorateClass([
|
|
10032
|
-
readonly,
|
|
10033
|
-
date2("created_at")
|
|
10034
|
-
], ConversationMemory.prototype, "createdAt", 2);
|
|
10035
9936
|
|
|
10036
9937
|
// src/lib/db/conversationMemory/operations.ts
|
|
10037
|
-
import { Q as
|
|
9938
|
+
import { Q as Q3 } from "@nozbe/watermelondb";
|
|
10038
9939
|
var MAX_PER_CONVERSATION = 200;
|
|
10039
9940
|
function conversationMemoryToStored(row) {
|
|
10040
9941
|
return {
|
|
@@ -10049,7 +9950,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
|
|
|
10049
9950
|
if (!conversationId || items.length === 0) return;
|
|
10050
9951
|
const now = Date.now();
|
|
10051
9952
|
await ctx.database.write(async () => {
|
|
10052
|
-
const existing = await ctx.conversationMemoryCollection.query(
|
|
9953
|
+
const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10053
9954
|
const seen = new Set(existing.map((r) => r.memoryId));
|
|
10054
9955
|
const batchSeen = /* @__PURE__ */ new Set();
|
|
10055
9956
|
const deduped = [];
|
|
@@ -10086,7 +9987,7 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
|
|
|
10086
9987
|
if (!conversationId || turn.size === 0) return;
|
|
10087
9988
|
const kept = new Map([...turn].slice(-MAX_PER_CONVERSATION));
|
|
10088
9989
|
await ctx.database.write(async () => {
|
|
10089
|
-
const existing = await ctx.conversationMemoryCollection.query(
|
|
9990
|
+
const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10090
9991
|
const earlier = existing.filter((r) => !turn.has(r.memoryId));
|
|
10091
9992
|
const linked = new Set(existing.map((r) => r.memoryId));
|
|
10092
9993
|
await ctx.database.batch(
|
|
@@ -10111,13 +10012,13 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
|
|
|
10111
10012
|
}
|
|
10112
10013
|
async function getConversationMemoriesOp(ctx, conversationId) {
|
|
10113
10014
|
if (!conversationId) return [];
|
|
10114
|
-
const rows = await ctx.conversationMemoryCollection.query(
|
|
10015
|
+
const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10115
10016
|
return rows.map(conversationMemoryToStored);
|
|
10116
10017
|
}
|
|
10117
10018
|
async function clearConversationMemoriesOp(ctx, conversationId) {
|
|
10118
10019
|
if (!conversationId) return;
|
|
10119
10020
|
await ctx.database.write(async () => {
|
|
10120
|
-
const rows = await ctx.conversationMemoryCollection.query(
|
|
10021
|
+
const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId)).fetch();
|
|
10121
10022
|
if (rows.length === 0) return;
|
|
10122
10023
|
await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
|
|
10123
10024
|
});
|
|
@@ -10132,15 +10033,37 @@ var maxChunks = 16;
|
|
|
10132
10033
|
var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
|
|
10133
10034
|
var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
|
|
10134
10035
|
var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
|
|
10036
|
+
var maxBubbles = 3;
|
|
10037
|
+
var maxTurnIdBytes = 128;
|
|
10038
|
+
var maxBubbleBytes = 16384;
|
|
10039
|
+
var anumaOrigins = /* @__PURE__ */ new Set(["intro", "reply"]);
|
|
10135
10040
|
function plain(value) {
|
|
10136
10041
|
return JSON.parse(JSON.stringify(value ?? {}));
|
|
10137
10042
|
}
|
|
10138
|
-
function
|
|
10043
|
+
function text3(value) {
|
|
10139
10044
|
return typeof value === "string" ? value : void 0;
|
|
10140
10045
|
}
|
|
10141
10046
|
function number(value) {
|
|
10142
10047
|
return typeof value === "number" ? value : void 0;
|
|
10143
10048
|
}
|
|
10049
|
+
function readAnumaTurn(value) {
|
|
10050
|
+
if (!isObject(value)) return void 0;
|
|
10051
|
+
const { turnId, bubbles, origin } = value;
|
|
10052
|
+
if (typeof turnId !== "string" || turnId.length === 0 || new TextEncoder().encode(turnId).length > maxTurnIdBytes)
|
|
10053
|
+
return void 0;
|
|
10054
|
+
if (bubbles !== void 0 && (!Array.isArray(bubbles) || bubbles.length < 1 || bubbles.length > maxBubbles || !bubbles.every((b) => typeof b === "string") || bubbles.reduce((sum, b) => sum + new TextEncoder().encode(b).length, 0) > maxBubbleBytes))
|
|
10055
|
+
return void 0;
|
|
10056
|
+
if (origin !== void 0 && (typeof origin !== "string" || !anumaOrigins.has(origin)))
|
|
10057
|
+
return void 0;
|
|
10058
|
+
return {
|
|
10059
|
+
turnId,
|
|
10060
|
+
...bubbles !== void 0 ? { bubbles: [...bubbles] } : {},
|
|
10061
|
+
...origin !== void 0 ? { origin } : {}
|
|
10062
|
+
};
|
|
10063
|
+
}
|
|
10064
|
+
function isObject(value) {
|
|
10065
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10066
|
+
}
|
|
10144
10067
|
function conversationSortKey(data, createdAt) {
|
|
10145
10068
|
const pinnedAt = number(data.pinnedAt);
|
|
10146
10069
|
if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
|
|
@@ -10161,12 +10084,12 @@ function conversationContext(data) {
|
|
|
10161
10084
|
}
|
|
10162
10085
|
function toConversation(record2) {
|
|
10163
10086
|
const pinnedAt = number(record2.data.pinnedAt);
|
|
10164
|
-
const projectId =
|
|
10087
|
+
const projectId = text3(record2.data.projectId);
|
|
10165
10088
|
const context = conversationContext(record2.data);
|
|
10166
10089
|
return {
|
|
10167
10090
|
uniqueId: record2.id,
|
|
10168
10091
|
conversationId: record2.id,
|
|
10169
|
-
title:
|
|
10092
|
+
title: text3(record2.data.title) ?? "",
|
|
10170
10093
|
...projectId ? { projectId } : {},
|
|
10171
10094
|
createdAt: new Date(record2.createdAt),
|
|
10172
10095
|
updatedAt: new Date(record2.updatedAt),
|
|
@@ -10175,18 +10098,28 @@ function toConversation(record2) {
|
|
|
10175
10098
|
...context ? { context } : {}
|
|
10176
10099
|
};
|
|
10177
10100
|
}
|
|
10101
|
+
function anumaTurnData(value) {
|
|
10102
|
+
if (value === void 0) return void 0;
|
|
10103
|
+
const turn = readAnumaTurn(value);
|
|
10104
|
+
if (!turn) throw new ChatStoreError("Invalid Anuma turn", 0, "invalid_record");
|
|
10105
|
+
return turn;
|
|
10106
|
+
}
|
|
10178
10107
|
function messageData(options) {
|
|
10179
|
-
|
|
10180
|
-
|
|
10108
|
+
const anuma = anumaTurnData(options.anuma);
|
|
10109
|
+
return plain({
|
|
10110
|
+
...Object.fromEntries(
|
|
10181
10111
|
Object.entries(options).filter(
|
|
10182
|
-
([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
|
|
10112
|
+
([key3, value]) => key3 !== "anuma" && !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
|
|
10183
10113
|
)
|
|
10184
|
-
)
|
|
10185
|
-
|
|
10114
|
+
),
|
|
10115
|
+
...anuma ? { anuma } : {}
|
|
10116
|
+
});
|
|
10186
10117
|
}
|
|
10187
10118
|
function toMessage(record2) {
|
|
10188
|
-
const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
|
|
10119
|
+
const { chunkOffsets: _offsets, truncated: _truncated, anuma: rawAnuma, ...fields } = record2.data;
|
|
10120
|
+
const anuma = readAnumaTurn(rawAnuma);
|
|
10189
10121
|
return {
|
|
10122
|
+
...anuma ? { anuma } : {},
|
|
10190
10123
|
...fields,
|
|
10191
10124
|
uniqueId: record2.id,
|
|
10192
10125
|
messageId: record2.sortKey,
|
|
@@ -10200,20 +10133,20 @@ function chunkOffsets(record2) {
|
|
|
10200
10133
|
return Array.isArray(offsets) ? offsets : void 0;
|
|
10201
10134
|
}
|
|
10202
10135
|
function toSkeletons(records) {
|
|
10203
|
-
const roleById = new Map(records.map((r) => [r.id,
|
|
10136
|
+
const roleById = new Map(records.map((r) => [r.id, text3(r.data.role)]));
|
|
10204
10137
|
return records.map((r) => {
|
|
10205
|
-
const parentMessageId =
|
|
10206
|
-
const model =
|
|
10138
|
+
const parentMessageId = text3(r.data.parentMessageId);
|
|
10139
|
+
const model = text3(r.data.model);
|
|
10207
10140
|
const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
|
|
10208
10141
|
return {
|
|
10209
10142
|
uniqueId: r.id,
|
|
10210
10143
|
messageId: r.sortKey,
|
|
10211
10144
|
conversationId: r.conversationId,
|
|
10212
|
-
role:
|
|
10145
|
+
role: text3(r.data.role) ?? "",
|
|
10213
10146
|
createdAt: new Date(r.createdAt),
|
|
10214
10147
|
...parentMessageId ? { parentMessageId } : {},
|
|
10215
10148
|
...model ? { model } : {},
|
|
10216
|
-
...artifact ? { content:
|
|
10149
|
+
...artifact ? { content: text3(r.data.content) ?? "" } : {}
|
|
10217
10150
|
};
|
|
10218
10151
|
});
|
|
10219
10152
|
}
|
|
@@ -10221,8 +10154,8 @@ function toSummary(record2) {
|
|
|
10221
10154
|
return {
|
|
10222
10155
|
uniqueId: record2.id,
|
|
10223
10156
|
conversationId: record2.id,
|
|
10224
|
-
summary:
|
|
10225
|
-
summarizedUpTo:
|
|
10157
|
+
summary: text3(record2.data.summary) ?? "",
|
|
10158
|
+
summarizedUpTo: text3(record2.data.summarizedUpTo) ?? "",
|
|
10226
10159
|
tokenCount: number(record2.data.tokenCount) ?? 0,
|
|
10227
10160
|
createdAt: new Date(record2.createdAt),
|
|
10228
10161
|
updatedAt: new Date(record2.updatedAt)
|
|
@@ -10273,7 +10206,7 @@ function fitRecord(record2) {
|
|
|
10273
10206
|
delete data[field10];
|
|
10274
10207
|
truncated.push(field10);
|
|
10275
10208
|
}
|
|
10276
|
-
const content =
|
|
10209
|
+
const content = text3(data.content) ?? "";
|
|
10277
10210
|
const over = bytes({ ...record2, data }) - recordBudgetBytes;
|
|
10278
10211
|
if (over > 0) {
|
|
10279
10212
|
const encoded = new TextEncoder().encode(content);
|
|
@@ -10317,6 +10250,215 @@ function fitChunks(chunks3) {
|
|
|
10317
10250
|
return out;
|
|
10318
10251
|
}
|
|
10319
10252
|
|
|
10253
|
+
// src/lib/chatStore/anuma.ts
|
|
10254
|
+
var ANUMA_CONVERSATION_ID = "anuma-1on1";
|
|
10255
|
+
var maxAttempts = 3;
|
|
10256
|
+
function anumaTurnContent(bubbles) {
|
|
10257
|
+
return bubbles.join("\n\n");
|
|
10258
|
+
}
|
|
10259
|
+
function anumaLastReadSortKey(conversation) {
|
|
10260
|
+
const record2 = conversation && "record" in conversation ? conversation.record : conversation;
|
|
10261
|
+
const state = record2?.data.anuma;
|
|
10262
|
+
if (!isObject(state)) return 0;
|
|
10263
|
+
const sortKey = state.lastReadSortKey;
|
|
10264
|
+
return Number.isSafeInteger(sortKey) && sortKey > 0 ? sortKey : 0;
|
|
10265
|
+
}
|
|
10266
|
+
function conflict(error) {
|
|
10267
|
+
return error instanceof ChatStoreError && error.code === "version_conflict";
|
|
10268
|
+
}
|
|
10269
|
+
function live(snapshot) {
|
|
10270
|
+
if (snapshot?.record.isDeleted)
|
|
10271
|
+
throw new ChatStoreError("Anuma conversation was deleted", 409, "conversation_deleted");
|
|
10272
|
+
return snapshot;
|
|
10273
|
+
}
|
|
10274
|
+
async function ensureAnumaConversation(store, options = {}) {
|
|
10275
|
+
const existing = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
|
|
10276
|
+
if (existing) return existing;
|
|
10277
|
+
const at = (options.now ?? Date.now)();
|
|
10278
|
+
const data = plain({ title: "Anuma", lastActivityAt: at, anuma: { lastReadSortKey: 0 } });
|
|
10279
|
+
try {
|
|
10280
|
+
const [created] = await store.putMany(
|
|
10281
|
+
[
|
|
10282
|
+
{
|
|
10283
|
+
expectedVersion: 0,
|
|
10284
|
+
record: {
|
|
10285
|
+
kind: "conversation",
|
|
10286
|
+
id: ANUMA_CONVERSATION_ID,
|
|
10287
|
+
conversationId: "",
|
|
10288
|
+
sortKey: conversationSortKey(data, at),
|
|
10289
|
+
createdAt: at,
|
|
10290
|
+
updatedAt: at,
|
|
10291
|
+
isDeleted: false,
|
|
10292
|
+
data
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
],
|
|
10296
|
+
options.signal
|
|
10297
|
+
);
|
|
10298
|
+
return created;
|
|
10299
|
+
} catch (error) {
|
|
10300
|
+
if (!conflict(error)) throw error;
|
|
10301
|
+
const winner = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
|
|
10302
|
+
if (!winner) throw error;
|
|
10303
|
+
return winner;
|
|
10304
|
+
}
|
|
10305
|
+
}
|
|
10306
|
+
async function markAnumaRead(store, sortKey, options = {}) {
|
|
10307
|
+
if (!Number.isSafeInteger(sortKey) || sortKey < 0)
|
|
10308
|
+
throw new ChatStoreError("Invalid Anuma read marker", 0, "invalid_record");
|
|
10309
|
+
const now = options.now ?? Date.now;
|
|
10310
|
+
for (let attempt = 1; ; attempt++) {
|
|
10311
|
+
const current = await ensureAnumaConversation(store, options);
|
|
10312
|
+
const read = anumaLastReadSortKey(current);
|
|
10313
|
+
if (sortKey <= read) return current;
|
|
10314
|
+
const record2 = current.record;
|
|
10315
|
+
const state = isObject(record2.data.anuma) ? record2.data.anuma : {};
|
|
10316
|
+
const data = plain({ ...record2.data, anuma: { ...state, lastReadSortKey: sortKey } });
|
|
10317
|
+
try {
|
|
10318
|
+
const [written] = await store.putMany(
|
|
10319
|
+
[
|
|
10320
|
+
{
|
|
10321
|
+
expectedVersion: current.version,
|
|
10322
|
+
record: {
|
|
10323
|
+
...record2,
|
|
10324
|
+
data,
|
|
10325
|
+
sortKey: conversationSortKey(data, record2.createdAt),
|
|
10326
|
+
updatedAt: Math.max(record2.updatedAt, now())
|
|
10327
|
+
}
|
|
10328
|
+
}
|
|
10329
|
+
],
|
|
10330
|
+
options.signal
|
|
10331
|
+
);
|
|
10332
|
+
return written;
|
|
10333
|
+
} catch (error) {
|
|
10334
|
+
if (!conflict(error) || attempt >= maxAttempts) throw error;
|
|
10335
|
+
}
|
|
10336
|
+
}
|
|
10337
|
+
}
|
|
10338
|
+
function countUnreadAnumaTurns(messages, lastReadSortKey) {
|
|
10339
|
+
const answered = Math.max(
|
|
10340
|
+
0,
|
|
10341
|
+
...messages.filter((m) => m.role === "user").map((m) => m.messageId)
|
|
10342
|
+
);
|
|
10343
|
+
const floor = Math.max(lastReadSortKey, answered);
|
|
10344
|
+
const turns = /* @__PURE__ */ new Set();
|
|
10345
|
+
for (const m of messages) {
|
|
10346
|
+
if (m.role !== "assistant" || m.messageId <= floor) continue;
|
|
10347
|
+
turns.add(m.anuma?.turnId ? `turn:${m.anuma.turnId}` : `message:${m.uniqueId}`);
|
|
10348
|
+
}
|
|
10349
|
+
return turns.size;
|
|
10350
|
+
}
|
|
10351
|
+
|
|
10352
|
+
// src/lib/db/chat/summaryOperations.ts
|
|
10353
|
+
import { Q as Q4 } from "@nozbe/watermelondb";
|
|
10354
|
+
function createSummaryContext(database, encryption = {}) {
|
|
10355
|
+
return {
|
|
10356
|
+
...encryption,
|
|
10357
|
+
database,
|
|
10358
|
+
summariesCollection: database.get("conversation_summaries")
|
|
10359
|
+
};
|
|
10360
|
+
}
|
|
10361
|
+
function modelToStored(record2, summary) {
|
|
10362
|
+
return {
|
|
10363
|
+
uniqueId: record2.id,
|
|
10364
|
+
conversationId: record2.conversationId,
|
|
10365
|
+
summary,
|
|
10366
|
+
summarizedUpTo: record2.summarizedUpTo,
|
|
10367
|
+
tokenCount: record2.tokenCount,
|
|
10368
|
+
createdAt: record2.createdAt,
|
|
10369
|
+
updatedAt: record2.updatedAt
|
|
10370
|
+
};
|
|
10371
|
+
}
|
|
10372
|
+
async function getConversationSummaryOp(ctx, conversationId) {
|
|
10373
|
+
const results = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
10374
|
+
if (results.length === 0) return null;
|
|
10375
|
+
const record2 = results[0];
|
|
10376
|
+
const stored2 = modelToStored(record2, record2.summary);
|
|
10377
|
+
if (ctx.walletAddress && isEncrypted(stored2.summary)) {
|
|
10378
|
+
if (ctx.signMessage) {
|
|
10379
|
+
try {
|
|
10380
|
+
await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
10381
|
+
} catch (error) {
|
|
10382
|
+
getLogger().warn("Failed to request the encryption key for the summary:", error);
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
|
|
10386
|
+
} else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
|
|
10387
|
+
try {
|
|
10388
|
+
const encryptedSummary = await encryptField(
|
|
10389
|
+
stored2.summary,
|
|
10390
|
+
ctx.walletAddress,
|
|
10391
|
+
ctx.signMessage,
|
|
10392
|
+
ctx.embeddedWalletSigner
|
|
10393
|
+
);
|
|
10394
|
+
await ctx.database.write(async () => {
|
|
10395
|
+
const current = await ctx.summariesCollection.query(Q4.where("id", record2.id)).fetch();
|
|
10396
|
+
if (current[0]?.summary === stored2.summary) {
|
|
10397
|
+
await current[0].update((rec) => {
|
|
10398
|
+
rec.summary = encryptedSummary;
|
|
10399
|
+
});
|
|
10400
|
+
}
|
|
10401
|
+
});
|
|
10402
|
+
} catch (error) {
|
|
10403
|
+
getLogger().warn("Failed to encrypt the legacy summary:", error);
|
|
10404
|
+
}
|
|
10405
|
+
}
|
|
10406
|
+
return stored2;
|
|
10407
|
+
}
|
|
10408
|
+
async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
|
|
10409
|
+
const revision2 = getConversationRevision(ctx.database, conversationId);
|
|
10410
|
+
let record2;
|
|
10411
|
+
const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
|
|
10412
|
+
await ctx.database.write(async () => {
|
|
10413
|
+
assertConversationRevision(ctx.database, conversationId, revision2);
|
|
10414
|
+
const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
10415
|
+
if (existing.length > 0) {
|
|
10416
|
+
record2 = await existing[0].update((rec) => {
|
|
10417
|
+
rec.summary = encryptedSummary;
|
|
10418
|
+
rec.summarizedUpTo = summarizedUpTo;
|
|
10419
|
+
rec.tokenCount = tokenCount;
|
|
10420
|
+
});
|
|
10421
|
+
} else {
|
|
10422
|
+
record2 = await ctx.summariesCollection.create((rec) => {
|
|
10423
|
+
rec.conversationId = conversationId;
|
|
10424
|
+
rec.summary = encryptedSummary;
|
|
10425
|
+
rec.summarizedUpTo = summarizedUpTo;
|
|
10426
|
+
rec.tokenCount = tokenCount;
|
|
10427
|
+
});
|
|
10428
|
+
}
|
|
10429
|
+
});
|
|
10430
|
+
return modelToStored(record2, summary);
|
|
10431
|
+
}
|
|
10432
|
+
async function deleteConversationSummaryOp(ctx, conversationId) {
|
|
10433
|
+
await ctx.database.write(async () => {
|
|
10434
|
+
const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
10435
|
+
for (const record2 of existing) {
|
|
10436
|
+
await record2.destroyPermanently();
|
|
10437
|
+
}
|
|
10438
|
+
});
|
|
10439
|
+
}
|
|
10440
|
+
|
|
10441
|
+
// src/lib/db/conversationMemory/models.ts
|
|
10442
|
+
import { Model as Model3 } from "@nozbe/watermelondb";
|
|
10443
|
+
import { date as date2, field as field2, readonly, text as text4 } from "@nozbe/watermelondb/decorators";
|
|
10444
|
+
var ConversationMemory = class extends Model3 {
|
|
10445
|
+
};
|
|
10446
|
+
ConversationMemory.table = "conversation_memory";
|
|
10447
|
+
ConversationMemory.associations = {};
|
|
10448
|
+
__decorateClass([
|
|
10449
|
+
text4("conversation_id")
|
|
10450
|
+
], ConversationMemory.prototype, "conversationId", 2);
|
|
10451
|
+
__decorateClass([
|
|
10452
|
+
text4("memory_id")
|
|
10453
|
+
], ConversationMemory.prototype, "memoryId", 2);
|
|
10454
|
+
__decorateClass([
|
|
10455
|
+
field2("score")
|
|
10456
|
+
], ConversationMemory.prototype, "score", 2);
|
|
10457
|
+
__decorateClass([
|
|
10458
|
+
readonly,
|
|
10459
|
+
date2("created_at")
|
|
10460
|
+
], ConversationMemory.prototype, "createdAt", 2);
|
|
10461
|
+
|
|
10320
10462
|
// src/lib/chatStore/chatImports.ts
|
|
10321
10463
|
var importBatch = 100;
|
|
10322
10464
|
var importChunks = 200;
|
|
@@ -10897,17 +11039,17 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
10897
11039
|
expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
|
|
10898
11040
|
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
10899
11041
|
throw new Error("expectedVersion must be a nonnegative integer");
|
|
10900
|
-
const
|
|
11042
|
+
const conflict2 = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
|
|
10901
11043
|
let existing;
|
|
10902
11044
|
if (expectedVersion !== 0 && !snapshot) {
|
|
10903
11045
|
try {
|
|
10904
11046
|
existing = memoryRecord(await request(path, {}, signal));
|
|
10905
11047
|
} catch (error) {
|
|
10906
|
-
if (error instanceof RemoteMemoryError && error.status === 404) throw
|
|
11048
|
+
if (error instanceof RemoteMemoryError && error.status === 404) throw conflict2();
|
|
10907
11049
|
throw error;
|
|
10908
11050
|
}
|
|
10909
11051
|
}
|
|
10910
|
-
if (existing && existing.version !== expectedVersion) throw
|
|
11052
|
+
if (existing && existing.version !== expectedVersion) throw conflict2();
|
|
10911
11053
|
const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
|
|
10912
11054
|
const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
|
|
10913
11055
|
const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
|
|
@@ -11132,17 +11274,6 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
11132
11274
|
};
|
|
11133
11275
|
}
|
|
11134
11276
|
|
|
11135
|
-
// src/lib/chatStore/types.ts
|
|
11136
|
-
var ChatStoreError = class extends Error {
|
|
11137
|
-
constructor(message, status, code, retryAfterMs) {
|
|
11138
|
-
super(message);
|
|
11139
|
-
this.status = status;
|
|
11140
|
-
this.code = code;
|
|
11141
|
-
this.retryAfterMs = retryAfterMs;
|
|
11142
|
-
this.name = "ChatStoreError";
|
|
11143
|
-
}
|
|
11144
|
-
};
|
|
11145
|
-
|
|
11146
11277
|
// src/lib/chatStore/remote.ts
|
|
11147
11278
|
var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
|
|
11148
11279
|
var cipherPattern = /^enc:v\d+:(?:[0-9a-f]{2}){28,}$/i;
|
|
@@ -11867,10 +11998,10 @@ async function deletedSinceUpload(store, journal, local, signal) {
|
|
|
11867
11998
|
const present = /* @__PURE__ */ new Set([...local.records.map((r) => key(r.record)), ...local.skipped.keys()]);
|
|
11868
11999
|
const gone = [...journal.keys].filter((k) => !present.has(k));
|
|
11869
12000
|
if (gone.length === 0) return [];
|
|
11870
|
-
const
|
|
11871
|
-
const tombstoning = new Set(
|
|
12001
|
+
const live2 = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
|
|
12002
|
+
const tombstoning = new Set(live2.map((s) => key(s.record)));
|
|
11872
12003
|
await journal.drop(gone.filter((k) => !tombstoning.has(k)));
|
|
11873
|
-
return
|
|
12004
|
+
return live2.map(({ record: record2, version }) => {
|
|
11874
12005
|
const empty = emptied[record2.kind];
|
|
11875
12006
|
return {
|
|
11876
12007
|
record: {
|
|
@@ -12530,12 +12661,13 @@ var resumableFields = [
|
|
|
12530
12661
|
"error",
|
|
12531
12662
|
"thoughtProcess",
|
|
12532
12663
|
"thinking",
|
|
12533
|
-
"toolCallEvents"
|
|
12664
|
+
"toolCallEvents",
|
|
12665
|
+
"anuma"
|
|
12534
12666
|
];
|
|
12535
12667
|
function resumedFields(options) {
|
|
12536
12668
|
return plain(
|
|
12537
12669
|
Object.fromEntries(
|
|
12538
|
-
resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, options[f]])
|
|
12670
|
+
resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, f === "anuma" ? anumaTurnData(options.anuma) : options[f]])
|
|
12539
12671
|
)
|
|
12540
12672
|
);
|
|
12541
12673
|
}
|
|
@@ -13189,7 +13321,7 @@ function createVaultChatBackend(options) {
|
|
|
13189
13321
|
},
|
|
13190
13322
|
async getConversations(query) {
|
|
13191
13323
|
await available2();
|
|
13192
|
-
return all("conversation").map(toConversation).filter(
|
|
13324
|
+
return all("conversation").filter((record2) => record2.id !== ANUMA_CONVERSATION_ID).map(toConversation).filter(
|
|
13193
13325
|
(c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
|
|
13194
13326
|
).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
13195
13327
|
},
|
|
@@ -13232,6 +13364,7 @@ function createVaultChatBackend(options) {
|
|
|
13232
13364
|
return !!record2 && !record2.isDeleted;
|
|
13233
13365
|
},
|
|
13234
13366
|
async deleteConversation(conversationId) {
|
|
13367
|
+
if (conversationId === ANUMA_CONVERSATION_ID) return false;
|
|
13235
13368
|
const record2 = await conversation(conversationId);
|
|
13236
13369
|
if (!record2 || record2.isDeleted) return false;
|
|
13237
13370
|
for (const [k, patch] of pending)
|
|
@@ -16238,7 +16371,7 @@ async function countActiveVaultMemoriesOp(ctx) {
|
|
|
16238
16371
|
return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
|
|
16239
16372
|
}
|
|
16240
16373
|
async function getVaultMemoryCountsOp(ctx) {
|
|
16241
|
-
const [
|
|
16374
|
+
const [live2, archived, quarantined] = await Promise.all([
|
|
16242
16375
|
countActiveVaultMemoriesOp(ctx),
|
|
16243
16376
|
ctx.vaultMemoryCollection.query(
|
|
16244
16377
|
...baseVaultConditions(ctx, { includeArchived: true }),
|
|
@@ -16249,7 +16382,7 @@ async function getVaultMemoryCountsOp(ctx) {
|
|
|
16249
16382
|
Q6.where("trust_tier", "quarantined")
|
|
16250
16383
|
).fetchCount()
|
|
16251
16384
|
]);
|
|
16252
|
-
return { live, archived, quarantined };
|
|
16385
|
+
return { live: live2, archived, quarantined };
|
|
16253
16386
|
}
|
|
16254
16387
|
async function getActiveVaultMemoryIdsOp(ctx, ids) {
|
|
16255
16388
|
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -17534,12 +17667,12 @@ var WalletPoller = class {
|
|
|
17534
17667
|
* @param maxAttempts - Maximum polling attempts before giving up (default: 60)
|
|
17535
17668
|
* @returns Stop function to cancel polling
|
|
17536
17669
|
*/
|
|
17537
|
-
startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS,
|
|
17670
|
+
startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts2 = DEFAULT_MAX_ATTEMPTS) {
|
|
17538
17671
|
this.stop();
|
|
17539
17672
|
this.attempts = 0;
|
|
17540
17673
|
const poll = async () => {
|
|
17541
17674
|
this.attempts++;
|
|
17542
|
-
if (this.attempts >
|
|
17675
|
+
if (this.attempts > maxAttempts2) {
|
|
17543
17676
|
this.stop();
|
|
17544
17677
|
return;
|
|
17545
17678
|
}
|
|
@@ -24900,13 +25033,13 @@ function createRemoteMemoryStore(options) {
|
|
|
24900
25033
|
});
|
|
24901
25034
|
return run;
|
|
24902
25035
|
};
|
|
24903
|
-
const
|
|
25036
|
+
const live2 = async (id2) => {
|
|
24904
25037
|
const record2 = await persistence.get(id2);
|
|
24905
25038
|
return record2 && !record2.memory.is_deleted ? record2 : null;
|
|
24906
25039
|
};
|
|
24907
25040
|
const mutateLocked = async (id2, change, edit, writeOptions) => {
|
|
24908
25041
|
writeOptions?.signal?.throwIfAborted();
|
|
24909
|
-
const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await
|
|
25042
|
+
const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live2(id2);
|
|
24910
25043
|
if (!record2) return null;
|
|
24911
25044
|
if (record2.memory.is_deleted) return null;
|
|
24912
25045
|
if (writeOptions?.canWrite && !await writeOptions.canWrite())
|
|
@@ -25704,7 +25837,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25704
25837
|
}, clear);
|
|
25705
25838
|
return snapshot.then((topics) => structuredClone(topics));
|
|
25706
25839
|
},
|
|
25707
|
-
get: async (id2) => asStored(await
|
|
25840
|
+
get: async (id2) => asStored(await live2(id2)),
|
|
25708
25841
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
25709
25842
|
(record2) => stored(record2.memory)
|
|
25710
25843
|
),
|
|
@@ -25789,7 +25922,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25789
25922
|
return serialized(
|
|
25790
25923
|
first,
|
|
25791
25924
|
() => serialized(second, async () => {
|
|
25792
|
-
const [old, next] = await Promise.all([
|
|
25925
|
+
const [old, next] = await Promise.all([live2(id2), live2(supersededById)]);
|
|
25793
25926
|
if (!old || !next || old.memory.superseded_by || next.memory.superseded_by) return false;
|
|
25794
25927
|
if (old.memory.kind !== void 0 && old.memory.kind !== null) return false;
|
|
25795
25928
|
const saved = await persistence.putMany([
|
|
@@ -25810,7 +25943,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25810
25943
|
(memory) => memory.archived_at === void 0 || memory.archived_at === null ? { ...memory, archived_at: Date.now() } : null
|
|
25811
25944
|
),
|
|
25812
25945
|
restore: (id2) => serialized(id2, async () => {
|
|
25813
|
-
const record2 = await
|
|
25946
|
+
const record2 = await live2(id2);
|
|
25814
25947
|
if (!record2) return false;
|
|
25815
25948
|
if (record2.memory.archived_at === void 0 || record2.memory.archived_at === null)
|
|
25816
25949
|
return true;
|
|
@@ -32956,6 +33089,7 @@ function useChatStorage(options) {
|
|
|
32956
33089
|
};
|
|
32957
33090
|
}
|
|
32958
33091
|
export {
|
|
33092
|
+
ANUMA_CONVERSATION_ID,
|
|
32959
33093
|
CHUNKS_DISCARDED_ORIGIN,
|
|
32960
33094
|
Conversation as ChatConversation,
|
|
32961
33095
|
Message as ChatMessage,
|
|
@@ -33015,6 +33149,8 @@ export {
|
|
|
33015
33149
|
WALLET_PAYMENT_TOOL_NAME,
|
|
33016
33150
|
WalletPoller,
|
|
33017
33151
|
addConversationMemoriesOp,
|
|
33152
|
+
anumaLastReadSortKey,
|
|
33153
|
+
anumaTurnContent,
|
|
33018
33154
|
archiveVaultMemoryOp,
|
|
33019
33155
|
assembleMemoryContext,
|
|
33020
33156
|
attachFileContextToLastUserMessage,
|
|
@@ -33048,6 +33184,7 @@ export {
|
|
|
33048
33184
|
consoleLogger,
|
|
33049
33185
|
conversationMemoryToStored,
|
|
33050
33186
|
cosineInt8,
|
|
33187
|
+
countUnreadAnumaTurns,
|
|
33051
33188
|
createAutoExtractor,
|
|
33052
33189
|
createChunkVectorCache,
|
|
33053
33190
|
createCryptoPricePreProcessor,
|
|
@@ -33096,6 +33233,7 @@ export {
|
|
|
33096
33233
|
embedMessage,
|
|
33097
33234
|
encodeChunkVector,
|
|
33098
33235
|
encryptToolCallEventsOp,
|
|
33236
|
+
ensureAnumaConversation,
|
|
33099
33237
|
ensureDefaultFoldersOp,
|
|
33100
33238
|
exchangeNotionCode,
|
|
33101
33239
|
extractAndLinkEntitiesForMemoriesOp,
|
|
@@ -33136,6 +33274,7 @@ export {
|
|
|
33136
33274
|
isRerankerAvailable,
|
|
33137
33275
|
loadSettings,
|
|
33138
33276
|
localChatBackend,
|
|
33277
|
+
markAnumaRead,
|
|
33139
33278
|
maskScopedEmbeddingCache,
|
|
33140
33279
|
maxVaultAttachmentBytes,
|
|
33141
33280
|
memoryCipher,
|
|
@@ -33151,6 +33290,7 @@ export {
|
|
|
33151
33290
|
quantizeEmbedding,
|
|
33152
33291
|
queueManager,
|
|
33153
33292
|
rankProfileCandidates,
|
|
33293
|
+
readAnumaTurn,
|
|
33154
33294
|
recall,
|
|
33155
33295
|
reencryptMemoryField,
|
|
33156
33296
|
reflect,
|