@anuma/sdk 10.8.0 → 10.10.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/{consolidate-7QUL3TOE.mjs → consolidate-QBV3DDHU.mjs} +1 -1
- package/dist/expo/index.cjs +2370 -1898
- package/dist/expo/index.d.mts +1017 -893
- package/dist/expo/index.d.ts +1017 -893
- package/dist/expo/index.mjs +1886 -1425
- 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 +431 -254
- package/dist/react/index.d.mts +747 -712
- package/dist/react/index.d.ts +747 -712
- package/dist/react/index.mjs +416 -246
- package/dist/server/index.cjs +19 -13
- package/dist/server/index.d.mts +16 -2
- package/dist/server/index.d.ts +16 -2
- package/dist/server/index.mjs +19 -13
- package/package.json +2 -2
- package/dist/expo/{chunk-4SOUKJD5.mjs → chunk-VNTEHEGI.mjs} +957 -957
package/dist/expo/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
taskTypeHeader,
|
|
21
21
|
validateEndpointOverride,
|
|
22
22
|
withInternalFlowMarker
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-VNTEHEGI.mjs";
|
|
24
24
|
import {
|
|
25
25
|
BASE_URL,
|
|
26
26
|
MCP_R2_DOMAIN,
|
|
@@ -54,8 +54,8 @@ function generateCodeVerifier() {
|
|
|
54
54
|
return base64UrlEncode(array);
|
|
55
55
|
}
|
|
56
56
|
async function generateCodeChallenge(verifier) {
|
|
57
|
-
const
|
|
58
|
-
const data =
|
|
57
|
+
const encoder2 = new TextEncoder();
|
|
58
|
+
const data = encoder2.encode(verifier);
|
|
59
59
|
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
60
60
|
return base64UrlEncode(new Uint8Array(hash));
|
|
61
61
|
}
|
|
@@ -3031,7 +3031,7 @@ async function updateMessageErrorOp(ctx, uniqueId, error) {
|
|
|
3031
3031
|
}
|
|
3032
3032
|
await ctx.database.write(async () => {
|
|
3033
3033
|
await message.update((msg) => {
|
|
3034
|
-
msg._setRaw("error", error);
|
|
3034
|
+
msg._setRaw("error", error ?? "");
|
|
3035
3035
|
});
|
|
3036
3036
|
});
|
|
3037
3037
|
return messageToStored(message, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
@@ -5088,382 +5088,1255 @@ function createCryptoPricePreProcessor(options = {}) {
|
|
|
5088
5088
|
};
|
|
5089
5089
|
}
|
|
5090
5090
|
|
|
5091
|
-
// src/lib/
|
|
5092
|
-
var
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
alreadyDone: /^Tool "[^"]*" failed: ALREADY_CANCELLED:/
|
|
5102
|
-
}
|
|
5091
|
+
// src/lib/db/entities/types.ts
|
|
5092
|
+
var ENTITY_KINDS = [
|
|
5093
|
+
"person",
|
|
5094
|
+
"organization",
|
|
5095
|
+
"place",
|
|
5096
|
+
"event",
|
|
5097
|
+
"product",
|
|
5098
|
+
"thing",
|
|
5099
|
+
"concept",
|
|
5100
|
+
"other"
|
|
5103
5101
|
];
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
var CANCEL_OUTCOME_UNKNOWN = "CANCEL_OUTCOME_UNKNOWN";
|
|
5107
|
-
function extractReservationReceipts(toolCallEvents) {
|
|
5108
|
-
const latest = /* @__PURE__ */ new Map();
|
|
5109
|
-
for (const event of toolCallEvents ?? []) {
|
|
5110
|
-
const action = ACTIONS.find((a) => event.name?.endsWith(a.tool));
|
|
5111
|
-
const output = event.output ?? "";
|
|
5112
|
-
if (!action || !output || hasEncryptedToolPayload(event) || NOT_AN_ATTEMPT.test(output))
|
|
5113
|
-
continue;
|
|
5114
|
-
const kept = latest.get(action.kind)?.status;
|
|
5115
|
-
if (kept === "made") continue;
|
|
5116
|
-
const receipt = toReceipt(action, event.arguments ?? "", output);
|
|
5117
|
-
if (kept === "unknown" && receipt.status !== "made") continue;
|
|
5118
|
-
latest.set(action.kind, receipt);
|
|
5119
|
-
}
|
|
5120
|
-
return [...latest.values()];
|
|
5121
|
-
}
|
|
5122
|
-
function toReceipt(action, argumentsJson, output) {
|
|
5123
|
-
const args = parseObject(argumentsJson);
|
|
5124
|
-
const result = parseObject(output);
|
|
5125
|
-
return {
|
|
5126
|
-
kind: action.kind,
|
|
5127
|
-
venueName: text2(args.venue_name) ?? "",
|
|
5128
|
-
day: text2(args.day) ?? "",
|
|
5129
|
-
time: text2(args.time) ?? "",
|
|
5130
|
-
partySize: count(args.party_size),
|
|
5131
|
-
reservationId: text2(result.reservation_id) ?? text2(args.reservation_id),
|
|
5132
|
-
resyUrl: text2(result.reservations_url),
|
|
5133
|
-
feeSummary: text2(result.fee_summary),
|
|
5134
|
-
...outcome(action, output, result)
|
|
5135
|
-
};
|
|
5102
|
+
function normalizeEntityName(name) {
|
|
5103
|
+
return name.trim().toLowerCase();
|
|
5136
5104
|
}
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
if (action.kind === "booking") {
|
|
5141
|
-
if (BOOKING_OUTCOME_UNKNOWN.test(output)) return { status: "unknown" };
|
|
5142
|
-
const held = result.success === true || text2(result.reservation_id) !== void 0;
|
|
5143
|
-
return held ? { status: "made" } : { status: "not_made", reason };
|
|
5144
|
-
}
|
|
5145
|
-
if (result.error_code === CANCEL_OUTCOME_UNKNOWN) return { status: "unknown", reason };
|
|
5146
|
-
const cancelled = result.success === true && !result.error && !result.error_code;
|
|
5147
|
-
return cancelled ? { status: "made" } : { status: "not_made", reason };
|
|
5105
|
+
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
5106
|
+
function topicWeight(topic2) {
|
|
5107
|
+
return topic2.source === "user" ? 1 : topic2.weight ?? 1;
|
|
5148
5108
|
}
|
|
5149
|
-
function
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
} catch {
|
|
5154
|
-
return {};
|
|
5109
|
+
function primaryTopic(topics) {
|
|
5110
|
+
let best = null;
|
|
5111
|
+
for (const topic2 of topics ?? []) {
|
|
5112
|
+
if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
|
|
5155
5113
|
}
|
|
5114
|
+
return best;
|
|
5156
5115
|
}
|
|
5157
|
-
function
|
|
5158
|
-
|
|
5159
|
-
return typeof value === "string" && value ? value : void 0;
|
|
5160
|
-
}
|
|
5161
|
-
function count(value) {
|
|
5162
|
-
const n = typeof value === "string" && value.trim() ? Number(value) : value;
|
|
5163
|
-
return typeof n === "number" && Number.isFinite(n) ? n : void 0;
|
|
5164
|
-
}
|
|
5165
|
-
|
|
5166
|
-
// src/tools/confirmConstants.ts
|
|
5167
|
-
var CONFIRM_TOOL_NAME = "prompt_user_confirm";
|
|
5168
|
-
var BOOK_RESTAURANT = {
|
|
5169
|
-
tool: "AnumaPaymentsMCP-anuma_book_restaurant",
|
|
5170
|
-
status: "confirmed_not_booked",
|
|
5171
|
-
fields: ["venue_id", "config_id", "party_size", "day", "venue_name", "time"]
|
|
5172
|
-
};
|
|
5173
|
-
var CANCEL_RESERVATION = {
|
|
5174
|
-
tool: "AnumaPaymentsMCP-anuma_cancel_reservation",
|
|
5175
|
-
status: "confirmed_not_cancelled",
|
|
5176
|
-
fields: [
|
|
5177
|
-
"reservation_id",
|
|
5178
|
-
"venue_name",
|
|
5179
|
-
"day",
|
|
5180
|
-
"time",
|
|
5181
|
-
"party_size",
|
|
5182
|
-
"fee_applies",
|
|
5183
|
-
"fee_amount"
|
|
5184
|
-
]
|
|
5185
|
-
};
|
|
5186
|
-
var CONFIRM_REQUIRED_FIELDS = /* @__PURE__ */ new Map([
|
|
5187
|
-
["book_restaurant", BOOK_RESTAURANT],
|
|
5188
|
-
["anuma_book_restaurant", BOOK_RESTAURANT],
|
|
5189
|
-
["anumapaymentsmcp-anuma_book_restaurant", BOOK_RESTAURANT],
|
|
5190
|
-
["cancel_reservation", CANCEL_RESERVATION],
|
|
5191
|
-
["anuma_cancel_reservation", CANCEL_RESERVATION],
|
|
5192
|
-
["anumapaymentsmcp-anuma_cancel_reservation", CANCEL_RESERVATION]
|
|
5193
|
-
]);
|
|
5194
|
-
function confirmRequirement(action) {
|
|
5195
|
-
if (typeof action !== "string") return void 0;
|
|
5196
|
-
return CONFIRM_REQUIRED_FIELDS.get(action.trim().toLowerCase());
|
|
5116
|
+
function parseTopicWeight(value) {
|
|
5117
|
+
return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
|
|
5197
5118
|
}
|
|
5198
|
-
function
|
|
5199
|
-
|
|
5200
|
-
if (!requirement) return [];
|
|
5201
|
-
const shown = /* @__PURE__ */ new Set();
|
|
5202
|
-
for (const parameter of Array.isArray(parameters) ? parameters : []) {
|
|
5203
|
-
const { name, value } = parameter ?? {};
|
|
5204
|
-
if (typeof name === "string" && typeof value === "string" && value.trim()) shown.add(name);
|
|
5205
|
-
}
|
|
5206
|
-
return requirement.fields.filter((field10) => !shown.has(field10));
|
|
5119
|
+
function serializeTopics(topics) {
|
|
5120
|
+
return JSON.stringify(topics);
|
|
5207
5121
|
}
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
var WALLET_PAYMENT_TOOL_NAME = "prompt_wallet_payment";
|
|
5212
|
-
var FLIGHT_BOOK_TOOL_SET = "flight-book";
|
|
5213
|
-
var FLIGHT_SERVER_TOOL_NAMES = [
|
|
5214
|
-
"AnumaPaymentsMCP-anuma_search_flights",
|
|
5215
|
-
"AnumaPaymentsMCP-anuma_flight_offer_details",
|
|
5216
|
-
"AnumaPaymentsMCP-anuma_create_flight_intent",
|
|
5217
|
-
"AnumaPaymentsMCP-anuma_get_flight_booking"
|
|
5218
|
-
];
|
|
5219
|
-
|
|
5220
|
-
// src/lib/storage/mcpImages.ts
|
|
5221
|
-
var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5222
|
-
"AnumaMediaMCP-anuma_create_image",
|
|
5223
|
-
"anuma_create_image",
|
|
5224
|
-
"AnumaImageMCP-generate_cloud_image",
|
|
5225
|
-
"AnumaImageMCP-edit_cloud_image",
|
|
5226
|
-
"generate_cloud_image",
|
|
5227
|
-
"edit_cloud_image"
|
|
5228
|
-
]);
|
|
5229
|
-
function toolOutputForModel(name, output) {
|
|
5230
|
-
if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
|
|
5122
|
+
function parseTopics(value) {
|
|
5123
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
5124
|
+
let parsed;
|
|
5231
5125
|
try {
|
|
5232
|
-
|
|
5233
|
-
imageUrl: _imageUrl,
|
|
5234
|
-
url: _url,
|
|
5235
|
-
output_images: _outputImages,
|
|
5236
|
-
...rest
|
|
5237
|
-
} = JSON.parse(output);
|
|
5238
|
-
return JSON.stringify(rest);
|
|
5126
|
+
parsed = JSON.parse(value);
|
|
5239
5127
|
} catch {
|
|
5240
|
-
return
|
|
5128
|
+
return null;
|
|
5129
|
+
}
|
|
5130
|
+
if (!Array.isArray(parsed)) return null;
|
|
5131
|
+
const out = [];
|
|
5132
|
+
for (const raw of parsed) {
|
|
5133
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
5134
|
+
const { name, kind, source, weight } = raw;
|
|
5135
|
+
if (typeof name !== "string" || name.trim().length === 0) continue;
|
|
5136
|
+
const parsedWeight = parseTopicWeight(weight);
|
|
5137
|
+
out.push({
|
|
5138
|
+
name,
|
|
5139
|
+
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
5140
|
+
source: source === "user" ? "user" : "auto",
|
|
5141
|
+
...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
|
|
5142
|
+
});
|
|
5241
5143
|
}
|
|
5144
|
+
return out;
|
|
5242
5145
|
}
|
|
5243
|
-
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
|
|
5244
|
-
var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
|
|
5245
|
-
|
|
5246
|
-
// src/tools/appBuilderPrompt.ts
|
|
5247
|
-
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.
|
|
5248
|
-
|
|
5249
|
-
Build ONLY when the user gives an explicit instruction to build, create, generate, or change an app, tool, or UI \u2014 e.g. "build a todo app", "make a snake game", "add a footer to the app", "change the background to blue". A bare app name or feature reference is NOT a build instruction: if the user just says "todo app", "a snake game", or "qr code", or asks a question about an app, do NOT generate anything \u2014 reply normally, or ask what they'd like you to build. The same holds for greetings, questions, stories, explanations, advice, or code that isn't a runnable app: respond normally and do not create files. When it's unclear whether the user actually wants an app built, ask instead of building.
|
|
5250
|
-
|
|
5251
|
-
Once the user HAS given an explicit build instruction, you are in App Builder mode: you produce polished, production-quality React apps that feel designed \u2014 not generic.
|
|
5252
|
-
|
|
5253
|
-
DESIGN: on the initial build, before writing code, briefly state (2-3 sentences) what you want this app to feel like \u2014 name the specific fonts, the palette in hex, and one signature detail. Be concrete: "Fraunces display + Inter body + JetBrains Mono metadata, terracotta #b75432 accent on bone #f3efe6, paper-grain texture via inline SVG noise" is the kind of specificity that anchors the build. Default to nothing: don't pick "safe modern productivity" unless the brief literally asks for utilitarian. On later turns, state a design brief only when the change is visual (a restyle, new theme, or new UI); for a text, logic, data, or behavior-only change, skip the brief and just make the change.
|
|
5254
|
-
|
|
5255
|
-
EDIT SCOPE: Change only the files and lines required by the current user request. A title rename is a text edit, not a design change. Preserve styles, dependencies, and unrelated logic on text edits. Design feedback does not authorize unrelated edits.
|
|
5256
|
-
|
|
5257
|
-
WORKFLOW:
|
|
5258
|
-
1. NEVER output code as text or markdown. ALWAYS use tools.
|
|
5259
|
-
2. Initial build: state your design brief, then create_file with a "files" array writing ALL files in one call. The preview renders automatically.
|
|
5260
|
-
3. patch_file requires read_file for that path earlier in the conversation (or create_file in the same conversation, which counts). read_file output is numbered "42: <text>" \u2014 the prefix is display-only; never copy it into "find". Each "find" must match exactly one location \u2014 include 2-3 lines of surrounding context. Pass multiple patches per call to change distinct locations.
|
|
5261
|
-
4. Prefer patch_file for changes to existing files \u2014 surgical, reviewable, preserves surrounding code. Reserve create_file overwrites for substantial restructuring. Inserts: find the line before, replace with that line + the new line. Deletes: empty replace.
|
|
5262
|
-
5. On the initial build, when adding new UI, or after user-requested design changes, call critique_design first and then audit_design. Critique checks taste, hierarchy, and distinctiveness. Audit checks tokens, focus states, aria-labels, alt text, and heading order. Patch only issues within the current user request. For text, logic, or data edits, skip both design tools.
|
|
5263
|
-
6. Before declaring done, call verify_app. The host's preview runtime reports whether your app actually mounted and surfaces any errors it captured \u2014 hallucinated imports, syntax the bundler rejected, undefined components, runtime crashes. If \`rendered\` is false or \`errors\` is non-empty, fix every error before the next message. If the response includes a \`note\` that verify_app isn't wired in this host, skip runtime verification. An unavailable runtime verifier does not authorize design changes. Use audit_design and critique_design only for the initial build, adding new UI, or user-requested design changes.
|
|
5264
|
-
7. Keep responses to a sentence or two. Exception: when responding to critique_design, write a substantive paragraph per rubric question.
|
|
5265
|
-
|
|
5266
|
-
STRUCTURE:
|
|
5267
|
-
- App.js: default-export React component. For non-trivial apps, define small co-located helpers (Header, CardList, Card, EditDialog, \u2026) above the default export rather than one giant App.
|
|
5268
|
-
- App.css: all styles, imported in App.js. No inline style objects.
|
|
5269
|
-
- package.json: list ALL imported packages including react. No CDN script tags. Versions are auto-pinned.
|
|
5270
|
-
- Do NOT create index.js or index.html (auto-generated).
|
|
5271
|
-
|
|
5272
|
-
STATE & PERSISTENCE:
|
|
5273
|
-
- The code runs in the user's browser \u2014 standard browser APIs available (localStorage, URL hash, Date, fetch).
|
|
5274
|
-
- Persist user-created data (todos, cards, notes, settings) with localStorage so it survives refresh. Use a versioned key ("kanban.v1") and JSON.parse inside try/catch on read.
|
|
5275
|
-
- Treat persisted data as untrusted and possibly stale: a saved record may predate a field you just added (a timestamp, id, or setting), so default-fill or skip missing fields on read \u2014 never assume a loaded record has every field the current code expects. Bump the key version when the shape changes.
|
|
5276
|
-
- Lift state to App when multiple children need it; keep it local otherwise. Extract non-trivial logic into custom hooks (useTodos, useTimer).
|
|
5277
|
-
- useReducer for state shapes with several interrelated fields (cards + columns + filters + drag state).
|
|
5278
|
-
|
|
5279
|
-
FILE & IMAGE UPLOAD:
|
|
5280
|
-
- Picker: \`<input type="file" accept="image/*" multiple>\` (vary accept by use case: \`.csv,text/csv\`, \`.txt,.md,text/plain\`, etc.).
|
|
5281
|
-
- Drag-and-drop: call \`e.preventDefault()\` in onDragOver (otherwise drop never fires); read files from \`event.dataTransfer.files\`. Drop and picker should coexist.
|
|
5282
|
-
- Previews: \`<img src={URL.createObjectURL(file)} />\`. Call \`URL.revokeObjectURL\` on unmount or before replacing \u2014 otherwise the browser leaks memory.
|
|
5283
|
-
- localStorage cannot hold large binary. Persist only metadata for big files; re-upload on next visit (or use IndexedDB if you really need durable binary storage).
|
|
5284
|
-
- Validate size and type before processing; reject files larger than the app can hold in memory.
|
|
5285
|
-
|
|
5286
|
-
AI CAPABILITIES \u2014 \`window.app.complete(prompt: string): Promise<string>\` is available in this environment. Build for the real call.
|
|
5287
|
-
- When the app's core purpose is conversation, generation, grading, recommendation, or any natural-language task, this IS the implementation \u2014 call it. NEVER fake it: no hardcoded or canned replies, no random or \`setTimeout\` "thinking", no demo-mode stub. A chat app, tutor, or assistant that doesn't call \`window.app.complete\` is wrong, not a placeholder.
|
|
5288
|
-
- Don't force it where it isn't needed \u2014 a calculator, todo list, or timer has no reason to call it. Match the implementation to what the app actually does.
|
|
5289
|
-
- Async: wrap in try/catch, show a loading state, and disable dependent inputs while waiting. Surface failures gracefully ("Couldn't reach the AI \u2014 try again").
|
|
5290
|
-
- Structure prompts with clear sections (Role / Context / Task / Format) and pass the relevant state (e.g. the running conversation) as context. Render the response as untrusted text: \`white-space: pre-wrap\`, never dangerouslySetInnerHTML.
|
|
5291
|
-
- Skip the call only when the answer is genuinely static \u2014 no language or reasoning needed. Cache repeated prompts in component state when sensible.
|
|
5292
|
-
|
|
5293
|
-
CODE QUALITY \u2014 write as a senior engineer would:
|
|
5294
|
-
- Handle edge cases (empty inputs, division by zero, invalid data). Show helpful error states, not crashes.
|
|
5295
|
-
- Never let formatting throw during render: guard \`new Date(x)\` / \`Intl\` / \`toLocaleString\` / \`.toFixed\` against missing or invalid values (e.g. \`Number.isNaN(d.getTime())\`) and return a fallback \u2014 one bad value from storage or state must not blank the whole app.
|
|
5296
|
-
- Semantic HTML: real headings, labels, buttons (not divs with onClick), form elements.
|
|
5297
|
-
- Accessible: aria-labels on icon buttons, focus-visible styles, keyboard navigable.
|
|
5298
|
-
- Extract into custom hooks or helpers when a component grows past ~80 lines.
|
|
5299
|
-
|
|
5300
|
-
STYLING \u2014 Tailwind is the CHASSIS, custom CSS carries the IDENTITY:
|
|
5301
|
-
- Tailwind (via Play CDN) for layout, spacing, state (hover/focus), responsive prefixes \u2014 uniform structure.
|
|
5302
|
-
- App.css carries the identity: @import the Google Fonts, declare CSS custom properties (--bg, --panel, --card, --ink, --muted, --accent, --hairline, --radius, --radius-sm, --shadow-card, --shadow-card-hi) in :root, gradients/textures/signature animations Tailwind can't express. Reference tokens from Tailwind via arbitrary values (\`className="bg-[var(--bg)]"\`) when needed. A well-designed app has 60-200 lines of App.css \u2014 not zero.
|
|
5303
|
-
- Modern CSS is how apps stop reading as generic-AI-output: \`color-mix(in oklab, var(--accent) 14%, transparent)\` for tinting, \`oklch()\` / \`oklab()\` for perceptually-uniform color, \`:has()\`, \`conic-gradient\` / \`radial-gradient\`, \`backdrop-filter\`, \`@container\` queries, inline SVG \`feTurbulence\` for texture.
|
|
5304
|
-
- Don't pull additional CSS frameworks. Tailwind + custom CSS is enough.
|
|
5305
|
-
|
|
5306
|
-
VISUAL DESIGN \u2014 every app should look like a real product, not a wireframe:
|
|
5307
|
-
- Let the chosen aesthetic drive every decision. "Editorial" wants italic display type and restrained color; "retro arcade" wants chunky shadows and saturated gradients. No safe-modern defaults.
|
|
5308
|
-
- Atmospheric depth: subtle radial wash, paper grain (inline SVG noise), warm/cool tint \u2014 every canvas should feel like a designed space, not a #fff rectangle.
|
|
5309
|
-
- Three shadow tiers (rest / hover / active). Pair drop shadows with inset highlights (\`box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 6px 14px -10px ...\`) for tactile feel.
|
|
5310
|
-
- Typography hierarchy: display font for headlines (italic accents on key words via \`<em>\`), body font for content, mono for metadata/labels \u2014 different families telegraph different information types.
|
|
5311
|
-
- Interactive polish: hover/active/focus on every clickable element, 200-300ms cubic-bezier transitions, keyboard shortcuts where natural (Esc to cancel, Cmd/Ctrl+Enter to commit), focus-visible rings keyed to the accent.
|
|
5312
|
-
- Light/dark mode if reasonable: a single CSS variable inversion under \`[data-theme="dark"]\`.
|
|
5313
|
-
- Customization affordance for multi-session apps: small "Tweaks" panel with theme / density / accent. Skip for throwaway demos.
|
|
5314
|
-
- Responsive: Tailwind responsive prefixes (\`sm:\`, \`md:\`, \`lg:\`) and \`@media\` in App.css. Mobile-first.`;
|
|
5315
|
-
|
|
5316
|
-
// src/tools/document/documentBuilderPrompt.ts
|
|
5317
|
-
var DOCUMENT_BUILDER_PROMPT = `Document tools (create_document, read_document, patch_document) are available this turn, but they are OPTIONAL \u2014 having them does not mean you should produce a document.
|
|
5318
|
-
|
|
5319
|
-
Use them ONLY when the user explicitly asks you to draft, write, compose, or generate a document \u2014 a contract, agreement, NDA, letter, cover letter, memo, essay, report, brief, proposal, or resume \u2014 e.g. "draft me a service agreement", "write a cover letter for this job", "make a one-page memo about X". A bare topic, a question, a request for an explanation, or a request for a slide deck or an interactive app is NOT a document instruction: respond normally and do not call these tools. When it's unclear whether the user wants a finished document, ask instead.
|
|
5320
|
-
|
|
5321
|
-
When you DO author a document, the document body goes entirely into the tool \u2014 NEVER write the document text as chat prose. The tool renders your source to a real, paginated PDF and attaches it to your reply automatically. AFTER the tool returns, briefly confirm in ONE short sentence what you made (e.g. "Drafted a cover letter for the product manager role \u2014 see the attached PDF."). Do not restate the document's contents.
|
|
5322
|
-
|
|
5323
|
-
AUTHORING \u2014 write the "source" as a single @react-pdf/renderer JSX expression (this is react-pdf, NOT HTML and NOT a slide deck):
|
|
5324
|
-
- Root is exactly one <Document> with at least one <Page>. Its only children are <Page> elements. Add more <Page> elements for longer documents \u2014 react-pdf also paginates overflow automatically.
|
|
5325
|
-
- Inside a <Page>: use <View> as flex containers and <Text> for ALL text. Raw text is only allowed inside <Text> (and <Link>); a <View> or <Page> may not contain bare text.
|
|
5326
|
-
- <Text> may nest <Text>, <Link>, or <Note> for inline styling, hyperlinks, and (rarely) a PDF comment annotation. A <Link> src must be an ordinary web link \u2014 http(s), mailto, tel, or a relative/anchor link; javascript:/data:/file: links are rejected. <Image> is self-closing and its src MUST be an inline "data:" URI \u2014 remote image URLs are rejected for privacy. Vector primitives (<Rect>/<Circle>/<Path>/etc.) must live inside an <Svg>.
|
|
5327
|
-
- Styling is react-pdf style objects only: style={{ marginTop: 12, fontSize: 11, fontFamily: "Times-Roman", flexDirection: "column" }}. Flexbox only \u2014 there is NO CSS grid, float, or box-shadow. Use camelCase react-pdf keys (margin/padding, flex*, width/height, color/backgroundColor, border*, text*, etc.).
|
|
5328
|
-
- fontFamily must be a built-in PDF font: Helvetica, Times-Roman, or Courier (with -Bold / -Oblique / -Italic / -BoldOblique variants), or Symbol / ZapfDingbats. Custom fonts are not supported.
|
|
5329
|
-
- LITERAL VALUES ONLY: no variables, no JS expressions, no .map(), no event handlers (onClick, \u2026), no spreads. Every attribute is a literal string, number, boolean, or a style object of literals.
|
|
5330
|
-
|
|
5331
|
-
A minimal example:
|
|
5332
|
-
|
|
5333
|
-
<Document>
|
|
5334
|
-
<Page size="A4" style={{ paddingVertical: 56, paddingHorizontal: 64, fontFamily: "Times-Roman", fontSize: 11, lineHeight: 1.5 }}>
|
|
5335
|
-
<Text style={{ fontSize: 18, fontFamily: "Times-Bold", marginBottom: 16, textAlign: "center" }}>MUTUAL NON-DISCLOSURE AGREEMENT</Text>
|
|
5336
|
-
<Text style={{ marginBottom: 10 }}>This Mutual Non-Disclosure Agreement (the "Agreement") is entered into as of the date of last signature below.</Text>
|
|
5337
|
-
<View style={{ marginTop: 14 }}>
|
|
5338
|
-
<Text style={{ fontFamily: "Times-Bold", marginBottom: 4 }}>1. Confidential Information</Text>
|
|
5339
|
-
<Text>Each party may disclose certain confidential and proprietary information to the other party.</Text>
|
|
5340
|
-
</View>
|
|
5341
|
-
</Page>
|
|
5342
|
-
</Document>
|
|
5343
|
-
|
|
5344
|
-
WORKFLOW:
|
|
5345
|
-
1. To create or fully restructure a document, call create_document with the complete source.
|
|
5346
|
-
2. To revise an existing document, call read_document first (its output is line-numbered "42: <text>" \u2014 the prefix is display-only, never copy it into a "find"), then patch_document with small find/replace edits. Each "find" must match exactly one location; add 2-3 lines of surrounding context if needed. Prefer patching over rewriting.
|
|
5347
|
-
3. To attach several separate documents in one reply, call create_document multiple times with distinct documentId slugs (e.g. "nda", "cover-letter"). Each renders to its own PDF.
|
|
5348
|
-
4. If a tool reports a DSL error (with line:col) or a render failure, fix the source and retry \u2014 do not give up or fall back to writing the document as chat text.`;
|
|
5349
5146
|
|
|
5350
|
-
// src/lib/
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
var CLIENT_TOOLS_MIN_SIMILARITY = 0.53;
|
|
5357
|
-
var CLIENT_TOOLS_RELEVANCE_RATIO = 0.75;
|
|
5358
|
-
function isNewToolFormat(tool) {
|
|
5359
|
-
return "schema" in tool && tool.schema !== void 0;
|
|
5360
|
-
}
|
|
5361
|
-
function isNewResponseFormat(response) {
|
|
5362
|
-
return "checksum" in response && "tools" in response && typeof response.checksum === "string";
|
|
5363
|
-
}
|
|
5364
|
-
function convertServerToolsResponse(response) {
|
|
5365
|
-
let toolsMap;
|
|
5366
|
-
let checksum;
|
|
5367
|
-
if (isNewResponseFormat(response)) {
|
|
5368
|
-
toolsMap = response.tools;
|
|
5369
|
-
checksum = response.checksum;
|
|
5370
|
-
} else {
|
|
5371
|
-
toolsMap = response;
|
|
5372
|
-
}
|
|
5373
|
-
const tools = Object.values(toolsMap).map((tool) => {
|
|
5374
|
-
if (isNewToolFormat(tool)) {
|
|
5375
|
-
return {
|
|
5376
|
-
type: "function",
|
|
5377
|
-
name: tool.schema.name,
|
|
5378
|
-
description: tool.schema.description,
|
|
5379
|
-
parameters: tool.schema.parameters,
|
|
5380
|
-
...tool.embedding && { embedding: tool.embedding }
|
|
5381
|
-
};
|
|
5382
|
-
}
|
|
5383
|
-
return {
|
|
5384
|
-
type: "function",
|
|
5385
|
-
name: tool.name,
|
|
5386
|
-
description: tool.description,
|
|
5387
|
-
parameters: tool.parameters
|
|
5388
|
-
};
|
|
5389
|
-
});
|
|
5390
|
-
return { tools, checksum };
|
|
5147
|
+
// src/lib/memory/store/remoteTopics.ts
|
|
5148
|
+
function topic(value) {
|
|
5149
|
+
const t = value;
|
|
5150
|
+
if (!t || typeof t.name !== "string" || !t.name || !Number.isSafeInteger(t.version) || t.version < 1 || typeof t.is_deleted !== "boolean" || typeof t.server_updated_at !== "string" || t.kind !== void 0 && typeof t.kind !== "string")
|
|
5151
|
+
throw new Error("Invalid nearby topic");
|
|
5152
|
+
return t;
|
|
5391
5153
|
}
|
|
5392
|
-
function
|
|
5154
|
+
function createRemoteTopicPersistence(request) {
|
|
5393
5155
|
return {
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5156
|
+
topics: async () => {
|
|
5157
|
+
const out = [];
|
|
5158
|
+
let cursor;
|
|
5159
|
+
do {
|
|
5160
|
+
const page = await request(
|
|
5161
|
+
"/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
|
|
5162
|
+
{}
|
|
5163
|
+
);
|
|
5164
|
+
if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
|
|
5165
|
+
throw new Error("Invalid nearby topic page");
|
|
5166
|
+
out.push(...page.items.map(topic));
|
|
5167
|
+
cursor = page.next_cursor;
|
|
5168
|
+
} while (cursor);
|
|
5169
|
+
return out;
|
|
5170
|
+
},
|
|
5171
|
+
putTopic: async (value, expectedVersion) => {
|
|
5172
|
+
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
5173
|
+
throw new Error("Expected topic version must be a nonnegative integer");
|
|
5174
|
+
return topic(
|
|
5175
|
+
await request("/topics", {
|
|
5176
|
+
method: "PUT",
|
|
5177
|
+
body: JSON.stringify({ topic: value, expected_version: expectedVersion })
|
|
5178
|
+
})
|
|
5179
|
+
);
|
|
5180
|
+
},
|
|
5181
|
+
importTopics: async (topics, signal) => {
|
|
5182
|
+
for (let i = 0; i < topics.length; i += 50) {
|
|
5183
|
+
signal?.throwIfAborted();
|
|
5184
|
+
await request(
|
|
5185
|
+
"/topics/import",
|
|
5186
|
+
{
|
|
5187
|
+
method: "POST",
|
|
5188
|
+
body: JSON.stringify({ topics: topics.slice(i, i + 50) })
|
|
5189
|
+
},
|
|
5190
|
+
signal
|
|
5191
|
+
);
|
|
5192
|
+
}
|
|
5193
|
+
signal?.throwIfAborted();
|
|
5399
5194
|
}
|
|
5400
5195
|
};
|
|
5401
5196
|
}
|
|
5402
|
-
function
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
parameters: tool.parameters
|
|
5408
|
-
};
|
|
5409
|
-
}
|
|
5410
|
-
function getCachedServerTools() {
|
|
5411
|
-
if (typeof localStorage === "undefined") return null;
|
|
5197
|
+
async function createRemoteTopic(persistence, name) {
|
|
5198
|
+
const canonical = normalizeEntityName(name);
|
|
5199
|
+
if (!canonical) return null;
|
|
5200
|
+
const existing = (await persistence.topics()).find((t) => t.name === canonical);
|
|
5201
|
+
if (existing && !existing.is_deleted) return canonical;
|
|
5412
5202
|
try {
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
} catch {
|
|
5422
|
-
return null;
|
|
5203
|
+
await persistence.putTopic(
|
|
5204
|
+
{ name: canonical, is_deleted: false, ...existing?.kind && { kind: existing.kind } },
|
|
5205
|
+
existing?.version ?? 0
|
|
5206
|
+
);
|
|
5207
|
+
} catch (error) {
|
|
5208
|
+
if (!(error instanceof RemoteMemoryError) || error.code !== "version_conflict") throw error;
|
|
5209
|
+
const current = (await persistence.topics()).find((t) => t.name === canonical);
|
|
5210
|
+
if (!current || current.is_deleted) throw error;
|
|
5423
5211
|
}
|
|
5212
|
+
return canonical;
|
|
5424
5213
|
}
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
}
|
|
5429
|
-
function buildCacheEntry(tools, checksum, etag) {
|
|
5214
|
+
|
|
5215
|
+
// src/lib/memory/store/remoteWork.ts
|
|
5216
|
+
function createRemoteWorkPersistence(request) {
|
|
5430
5217
|
return {
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5218
|
+
claimWork: async (kind, processorVersion, token) => {
|
|
5219
|
+
const result = await request("/work/claim", {
|
|
5220
|
+
method: "POST",
|
|
5221
|
+
body: JSON.stringify({
|
|
5222
|
+
kind,
|
|
5223
|
+
processor_version: processorVersion,
|
|
5224
|
+
...token && { token }
|
|
5225
|
+
})
|
|
5226
|
+
});
|
|
5227
|
+
if (!result || typeof result !== "object") throw new Error("Invalid nearby work lease");
|
|
5228
|
+
const lease = result.lease;
|
|
5229
|
+
if (lease === void 0) return null;
|
|
5230
|
+
if (!lease || lease.kind !== kind || typeof lease.token !== "string" || !lease.token || lease.processor_version !== processorVersion || typeof lease.expires_at !== "string" || !Number.isFinite(Date.parse(lease.expires_at)))
|
|
5231
|
+
throw new Error("Invalid nearby work lease");
|
|
5232
|
+
return lease;
|
|
5233
|
+
},
|
|
5234
|
+
releaseWork: async (kind, token) => {
|
|
5235
|
+
await request("/work/release", { method: "POST", body: JSON.stringify({ kind, token }) });
|
|
5236
|
+
}
|
|
5436
5237
|
};
|
|
5437
5238
|
}
|
|
5438
|
-
function
|
|
5439
|
-
|
|
5239
|
+
async function withRemoteWorkLease(persistence, kind, processorVersion, task) {
|
|
5240
|
+
const lease = await persistence.claimWork(kind, processorVersion);
|
|
5241
|
+
if (!lease) return null;
|
|
5242
|
+
const controller = new AbortController();
|
|
5243
|
+
const leaseLostError = new RemoteMemoryError("Maintenance lease lost", 409, "work_lease_expired");
|
|
5244
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
5245
|
+
controller.signal.addEventListener("abort", () => reject(leaseLostError), {
|
|
5246
|
+
once: true
|
|
5247
|
+
});
|
|
5248
|
+
});
|
|
5249
|
+
let renewing;
|
|
5250
|
+
const timer = setInterval(() => {
|
|
5251
|
+
if (renewing || controller.signal.aborted) return;
|
|
5252
|
+
renewing = persistence.claimWork(kind, processorVersion, lease.token).then((renewed) => {
|
|
5253
|
+
if (!renewed) controller.abort(leaseLostError);
|
|
5254
|
+
}).catch((error) => getLogger().warn("[memory/work] Lease renewal failed:", error)).finally(() => {
|
|
5255
|
+
renewing = void 0;
|
|
5256
|
+
});
|
|
5257
|
+
}, 3e4);
|
|
5440
5258
|
try {
|
|
5441
|
-
|
|
5259
|
+
const result = await Promise.race([task(lease.token, controller.signal), cancelled]);
|
|
5260
|
+
await renewing;
|
|
5261
|
+
return controller.signal.aborted ? null : result;
|
|
5442
5262
|
} catch (error) {
|
|
5443
|
-
|
|
5263
|
+
if (controller.signal.aborted || isRemoteWorkLeaseExpired(error)) return null;
|
|
5264
|
+
throw error;
|
|
5265
|
+
} finally {
|
|
5266
|
+
clearInterval(timer);
|
|
5267
|
+
await renewing;
|
|
5268
|
+
await persistence.releaseWork(kind, lease.token).catch((error) => getLogger().warn("[memory/work] Lease release failed:", error));
|
|
5444
5269
|
}
|
|
5445
5270
|
}
|
|
5446
|
-
function
|
|
5447
|
-
|
|
5448
|
-
localStorage.removeItem(SERVER_TOOLS_CACHE_KEY);
|
|
5449
|
-
}
|
|
5450
|
-
var localStorageToolsCache = {
|
|
5451
|
-
get: getCachedServerTools,
|
|
5452
|
-
set: writeLocalStorageCache,
|
|
5453
|
-
clear: removeLocalStorageCache
|
|
5454
|
-
};
|
|
5455
|
-
function clearServerToolsCache(cache = localStorageToolsCache) {
|
|
5456
|
-
return cache.clear?.();
|
|
5457
|
-
}
|
|
5458
|
-
function getToolsChecksum2(cache = localStorageToolsCache) {
|
|
5459
|
-
const cached = cache.get();
|
|
5460
|
-
return cached instanceof Promise ? cached.then((c) => c?.checksum) : cached?.checksum;
|
|
5271
|
+
function isRemoteWorkLeaseExpired(error) {
|
|
5272
|
+
return error instanceof RemoteMemoryError && error.code === "work_lease_expired";
|
|
5461
5273
|
}
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5274
|
+
|
|
5275
|
+
// src/lib/memory/store/remotePersistence.ts
|
|
5276
|
+
var RemoteMemoryError = class extends Error {
|
|
5277
|
+
constructor(message, status, code, retryAfterMs, rateLimitBucket) {
|
|
5278
|
+
super(message);
|
|
5279
|
+
this.status = status;
|
|
5280
|
+
this.code = code;
|
|
5281
|
+
this.retryAfterMs = retryAfterMs;
|
|
5282
|
+
this.rateLimitBucket = rateLimitBucket;
|
|
5283
|
+
this.name = "RemoteMemoryError";
|
|
5465
5284
|
}
|
|
5466
|
-
|
|
5285
|
+
};
|
|
5286
|
+
var ciphertextPattern = /^enc:v\d+:[0-9a-f]+$/i;
|
|
5287
|
+
var memoryFields = {
|
|
5288
|
+
memory_id: true,
|
|
5289
|
+
content: true,
|
|
5290
|
+
scope: true,
|
|
5291
|
+
created_at: true,
|
|
5292
|
+
updated_at: true,
|
|
5293
|
+
is_deleted: true,
|
|
5294
|
+
embedding: true,
|
|
5295
|
+
embedding_model: true,
|
|
5296
|
+
kind: true,
|
|
5297
|
+
kind_value: true,
|
|
5298
|
+
level: true,
|
|
5299
|
+
folder_id: true,
|
|
5300
|
+
source_chunk_ids: true,
|
|
5301
|
+
proof_count: true,
|
|
5302
|
+
source: true,
|
|
5303
|
+
event_time_start: true,
|
|
5304
|
+
event_time_end: true,
|
|
5305
|
+
event_time_kind: true,
|
|
5306
|
+
topics_user_managed: true,
|
|
5307
|
+
topics: true,
|
|
5308
|
+
topics_updated_at: true,
|
|
5309
|
+
media: true,
|
|
5310
|
+
topics_extracted_at: true,
|
|
5311
|
+
topics_extracted_version: true,
|
|
5312
|
+
superseded_by: true,
|
|
5313
|
+
superseded_at: true,
|
|
5314
|
+
last_observed_at: true,
|
|
5315
|
+
fact_type: true,
|
|
5316
|
+
archived_at: true,
|
|
5317
|
+
trust_tier: true,
|
|
5318
|
+
visibility: true,
|
|
5319
|
+
twin_opt_in: true,
|
|
5320
|
+
published_at: true,
|
|
5321
|
+
geohash: true
|
|
5322
|
+
};
|
|
5323
|
+
var candidateListLimits = [
|
|
5324
|
+
["scopes", 20],
|
|
5325
|
+
["fact_types", 20],
|
|
5326
|
+
["memory_ids", 1e3],
|
|
5327
|
+
["force_ids", 100]
|
|
5328
|
+
];
|
|
5329
|
+
var queryListLimits = [
|
|
5330
|
+
["scopes", 20],
|
|
5331
|
+
["fact_types", 20],
|
|
5332
|
+
["visibility", 20],
|
|
5333
|
+
["memory_ids", 1e3],
|
|
5334
|
+
["topics", 100]
|
|
5335
|
+
];
|
|
5336
|
+
var queryFields = [
|
|
5337
|
+
"include_deleted",
|
|
5338
|
+
"include_archived",
|
|
5339
|
+
"include_quarantined",
|
|
5340
|
+
"include_superseded",
|
|
5341
|
+
"archived_only",
|
|
5342
|
+
"scopes",
|
|
5343
|
+
"fact_types",
|
|
5344
|
+
"visibility",
|
|
5345
|
+
"memory_ids",
|
|
5346
|
+
"topics",
|
|
5347
|
+
"updated_after",
|
|
5348
|
+
"topics_due",
|
|
5349
|
+
"topics_extracted",
|
|
5350
|
+
"event_time_overlaps",
|
|
5351
|
+
"order",
|
|
5352
|
+
"cursor",
|
|
5353
|
+
"limit",
|
|
5354
|
+
"include_embeddings"
|
|
5355
|
+
];
|
|
5356
|
+
function record(value) {
|
|
5357
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5358
|
+
}
|
|
5359
|
+
function memoryRecord(value) {
|
|
5360
|
+
if (!record(value) || !record(value.memory) || typeof value.memory.memory_id !== "string" || typeof value.memory.content !== "string" || typeof value.memory.scope !== "string" || typeof value.memory.created_at !== "number" || typeof value.memory.updated_at !== "number" || typeof value.memory.is_deleted !== "boolean" || !Number.isSafeInteger(value.version) || value.version < 1 || typeof value.server_updated_at !== "string" || value.score !== void 0 && (typeof value.score !== "number" || !Number.isFinite(value.score))) {
|
|
5361
|
+
throw new Error("Invalid nearby private-memory response");
|
|
5362
|
+
}
|
|
5363
|
+
return value;
|
|
5364
|
+
}
|
|
5365
|
+
function createNearbyRequest(options, apiPath = "/api/private-memories") {
|
|
5366
|
+
const base = new URL(options.baseUrl);
|
|
5367
|
+
if (!/^https?:$/.test(base.protocol) || base.username || base.password || base.search || base.hash) {
|
|
5368
|
+
throw new Error(
|
|
5369
|
+
"baseUrl must be an HTTP(S) origin or base path without credentials, query or fragment"
|
|
5370
|
+
);
|
|
5371
|
+
}
|
|
5372
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
5373
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
5374
|
+
throw new Error("timeoutMs must be a positive number");
|
|
5375
|
+
const root = base.href.replace(/\/$/, "") + apiPath;
|
|
5376
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
5377
|
+
const cooldowns = /* @__PURE__ */ new Map();
|
|
5378
|
+
const memoryBudget = (path, method) => {
|
|
5379
|
+
if (method === "PUT")
|
|
5380
|
+
return path === "/topics" ? "private-memory-import" : "private-memory-write";
|
|
5381
|
+
if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
|
|
5382
|
+
return "private-memory-import";
|
|
5383
|
+
return "private-memory-read";
|
|
5384
|
+
};
|
|
5385
|
+
const request = async (path, init = {}, signal) => {
|
|
5386
|
+
const controller = new AbortController();
|
|
5387
|
+
let timedOut = false;
|
|
5388
|
+
const timer = setTimeout(() => {
|
|
5389
|
+
timedOut = true;
|
|
5390
|
+
controller.abort();
|
|
5391
|
+
}, timeoutMs);
|
|
5392
|
+
const forwardAbort = () => controller.abort(signal?.reason);
|
|
5393
|
+
if (signal?.aborted) forwardAbort();
|
|
5394
|
+
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
5395
|
+
let rejectAborted;
|
|
5396
|
+
const aborted2 = new Promise((_, reject) => {
|
|
5397
|
+
rejectAborted = () => reject(
|
|
5398
|
+
controller.signal.reason instanceof Error ? controller.signal.reason : new Error("Nearby request aborted")
|
|
5399
|
+
);
|
|
5400
|
+
if (controller.signal.aborted) rejectAborted();
|
|
5401
|
+
else controller.signal.addEventListener("abort", rejectAborted, { once: true });
|
|
5402
|
+
});
|
|
5403
|
+
aborted2.catch(() => void 0);
|
|
5404
|
+
try {
|
|
5405
|
+
const token = await Promise.race([options.getToken(), aborted2]);
|
|
5406
|
+
if (controller.signal.aborted) await aborted2;
|
|
5407
|
+
if (!token) throw new RemoteMemoryError("Authentication required", 401);
|
|
5408
|
+
const pathname = path.split("?")[0];
|
|
5409
|
+
const route = `${init.method ?? "GET"} ${pathname}`;
|
|
5410
|
+
const budgets = ["*", route];
|
|
5411
|
+
if (apiPath === "/api/private-memories")
|
|
5412
|
+
budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
|
|
5413
|
+
for (const bucket of budgets) {
|
|
5414
|
+
const cooldown = cooldowns.get(bucket);
|
|
5415
|
+
if (!cooldown) continue;
|
|
5416
|
+
const remaining = cooldown.until - Date.now();
|
|
5417
|
+
if (remaining > 0)
|
|
5418
|
+
throw new RemoteMemoryError(
|
|
5419
|
+
cooldown.error.message,
|
|
5420
|
+
429,
|
|
5421
|
+
cooldown.error.code,
|
|
5422
|
+
remaining,
|
|
5423
|
+
cooldown.error.rateLimitBucket
|
|
5424
|
+
);
|
|
5425
|
+
cooldowns.delete(bucket);
|
|
5426
|
+
}
|
|
5427
|
+
const response = await fetcher(root + path, {
|
|
5428
|
+
...init,
|
|
5429
|
+
headers: {
|
|
5430
|
+
Authorization: `Bearer ${token}`,
|
|
5431
|
+
...init.body ? { "Content-Type": "application/json" } : {}
|
|
5432
|
+
},
|
|
5433
|
+
redirect: "error",
|
|
5434
|
+
signal: controller.signal
|
|
5435
|
+
});
|
|
5436
|
+
if (!response.ok) {
|
|
5437
|
+
const body = await response.json().catch(() => null);
|
|
5438
|
+
const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
5439
|
+
const error = new RemoteMemoryError(
|
|
5440
|
+
record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
|
|
5441
|
+
response.status,
|
|
5442
|
+
record(body) && typeof body.code === "string" ? body.code : void 0,
|
|
5443
|
+
retryAfter ?? void 0,
|
|
5444
|
+
record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
|
|
5445
|
+
);
|
|
5446
|
+
if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
|
|
5447
|
+
const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
|
|
5448
|
+
cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
|
|
5449
|
+
}
|
|
5450
|
+
throw error;
|
|
5451
|
+
}
|
|
5452
|
+
if (response.status === 204) return null;
|
|
5453
|
+
return await response.json();
|
|
5454
|
+
} catch (error) {
|
|
5455
|
+
if (timedOut) throw new RemoteMemoryError("Nearby request timed out", 408, "timeout");
|
|
5456
|
+
throw error;
|
|
5457
|
+
} finally {
|
|
5458
|
+
clearTimeout(timer);
|
|
5459
|
+
signal?.removeEventListener("abort", forwardAbort);
|
|
5460
|
+
if (rejectAborted) controller.signal.removeEventListener("abort", rejectAborted);
|
|
5461
|
+
}
|
|
5462
|
+
};
|
|
5463
|
+
return request;
|
|
5464
|
+
}
|
|
5465
|
+
async function createRemoteMemoryPersistence(options) {
|
|
5466
|
+
if (!options.keyId || options.keyId.length > 256)
|
|
5467
|
+
throw new Error("A canonical keyId is required");
|
|
5468
|
+
const request = createNearbyRequest(options);
|
|
5469
|
+
const account = await request("/account", {}, options.signal);
|
|
5470
|
+
if (!record(account) || account.state !== "active") {
|
|
5471
|
+
throw new RemoteMemoryError(
|
|
5472
|
+
"Complete and verify migration before using remote memories",
|
|
5473
|
+
409,
|
|
5474
|
+
"memory_not_active"
|
|
5475
|
+
);
|
|
5476
|
+
}
|
|
5477
|
+
if (account.key_id !== options.keyId) {
|
|
5478
|
+
throw new RemoteMemoryError(
|
|
5479
|
+
"The canonical memory key does not match this account",
|
|
5480
|
+
409,
|
|
5481
|
+
"key_mismatch"
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5484
|
+
const snapshots = /* @__PURE__ */ new WeakMap();
|
|
5485
|
+
const decode = async (value) => {
|
|
5486
|
+
const item = memoryRecord(value);
|
|
5487
|
+
const decrypt = async (ciphertext) => {
|
|
5488
|
+
if (!ciphertextPattern.test(ciphertext))
|
|
5489
|
+
throw new Error("Nearby returned unencrypted memory content");
|
|
5490
|
+
const plaintext = await options.decrypt(ciphertext);
|
|
5491
|
+
if (plaintext === ciphertext) throw new Error("Memory decryption failed");
|
|
5492
|
+
return plaintext;
|
|
5493
|
+
};
|
|
5494
|
+
const decoded = {
|
|
5495
|
+
...item,
|
|
5496
|
+
memory: {
|
|
5497
|
+
...item.memory,
|
|
5498
|
+
content: item.memory.is_deleted && item.memory.content === "" ? "" : await decrypt(item.memory.content),
|
|
5499
|
+
...item.memory.kind_value !== void 0 && {
|
|
5500
|
+
kind_value: await decrypt(item.memory.kind_value)
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5503
|
+
};
|
|
5504
|
+
return remember(decoded, item);
|
|
5505
|
+
};
|
|
5506
|
+
const remember = (decoded, item) => {
|
|
5507
|
+
snapshots.set(decoded, {
|
|
5508
|
+
id: item.memory.memory_id,
|
|
5509
|
+
version: item.version,
|
|
5510
|
+
content: decoded.memory.content,
|
|
5511
|
+
kindValue: decoded.memory.kind_value,
|
|
5512
|
+
encryptedContent: item.memory.content,
|
|
5513
|
+
encryptedKindValue: item.memory.kind_value
|
|
5514
|
+
});
|
|
5515
|
+
return decoded;
|
|
5516
|
+
};
|
|
5517
|
+
const decodeAll = async (values) => {
|
|
5518
|
+
const records = values.map(memoryRecord);
|
|
5519
|
+
const settled = await Promise.allSettled(records.map(decode));
|
|
5520
|
+
const items = [];
|
|
5521
|
+
const failed = [];
|
|
5522
|
+
settled.forEach((result, i) => {
|
|
5523
|
+
if (result.status === "fulfilled") items.push(result.value);
|
|
5524
|
+
else
|
|
5525
|
+
failed.push({
|
|
5526
|
+
memory_id: records[i].memory.memory_id,
|
|
5527
|
+
version: records[i].version,
|
|
5528
|
+
error: result.reason instanceof Error ? result.reason : new Error(String(result.reason))
|
|
5529
|
+
});
|
|
5530
|
+
});
|
|
5531
|
+
return { items, failed };
|
|
5532
|
+
};
|
|
5533
|
+
const decodeCommitted = async (value, write) => {
|
|
5534
|
+
const item = memoryRecord(value);
|
|
5535
|
+
if (item.memory.memory_id !== write.body.memory.memory_id || item.memory.content !== write.body.memory.content || item.memory.kind_value !== write.body.memory.kind_value)
|
|
5536
|
+
return decode(item);
|
|
5537
|
+
return remember(
|
|
5538
|
+
{
|
|
5539
|
+
...item,
|
|
5540
|
+
memory: {
|
|
5541
|
+
...item.memory,
|
|
5542
|
+
content: write.content,
|
|
5543
|
+
...write.kindValue !== void 0 && { kind_value: write.kindValue }
|
|
5544
|
+
}
|
|
5545
|
+
},
|
|
5546
|
+
item
|
|
5547
|
+
);
|
|
5548
|
+
};
|
|
5549
|
+
const encryptField2 = async (plaintext) => {
|
|
5550
|
+
const ciphertext = await options.encrypt(plaintext);
|
|
5551
|
+
if (ciphertext === plaintext || !ciphertextPattern.test(ciphertext))
|
|
5552
|
+
throw new Error("Field encryption must return SDK ciphertext; refusing to upload plaintext");
|
|
5553
|
+
return ciphertext;
|
|
5554
|
+
};
|
|
5555
|
+
const memoryPath = (id2) => {
|
|
5556
|
+
if (!id2 || id2.length > 128) throw new Error("memory_id must be 1\u2013128 characters");
|
|
5557
|
+
return "/memories/" + encodeURIComponent(id2);
|
|
5558
|
+
};
|
|
5559
|
+
const prepareWrite = async (memory, expectedVersion, signal) => {
|
|
5560
|
+
const unknown = Object.keys(memory).filter(
|
|
5561
|
+
(key3) => !Object.prototype.hasOwnProperty.call(memoryFields, key3)
|
|
5562
|
+
);
|
|
5563
|
+
if (unknown.length) {
|
|
5564
|
+
throw new RemoteMemoryError(
|
|
5565
|
+
`Unknown private-memory fields (${unknown.join(", ")}); refusing to forward them`,
|
|
5566
|
+
400,
|
|
5567
|
+
"unknown_field"
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
memory = { ...memory, ...memory.embedding && { embedding: [...memory.embedding] } };
|
|
5571
|
+
const path = memoryPath(memory.memory_id);
|
|
5572
|
+
const snapshot = typeof expectedVersion === "number" ? void 0 : snapshots.get(expectedVersion);
|
|
5573
|
+
if (typeof expectedVersion !== "number" && (!snapshot || snapshot.id !== memory.memory_id || snapshot.version !== expectedVersion.version))
|
|
5574
|
+
throw new Error(
|
|
5575
|
+
"Expected snapshot must be an unmodified version from this persistence instance"
|
|
5576
|
+
);
|
|
5577
|
+
expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
|
|
5578
|
+
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
5579
|
+
throw new Error("expectedVersion must be a nonnegative integer");
|
|
5580
|
+
const conflict2 = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
|
|
5581
|
+
let existing;
|
|
5582
|
+
if (expectedVersion !== 0 && !snapshot) {
|
|
5583
|
+
try {
|
|
5584
|
+
existing = memoryRecord(await request(path, {}, signal));
|
|
5585
|
+
} catch (error) {
|
|
5586
|
+
if (error instanceof RemoteMemoryError && error.status === 404) throw conflict2();
|
|
5587
|
+
throw error;
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
if (existing && existing.version !== expectedVersion) throw conflict2();
|
|
5591
|
+
const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
|
|
5592
|
+
const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
|
|
5593
|
+
const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
|
|
5594
|
+
const kindValue = memory.kind_value === void 0 ? void 0 : (snapshot?.kindValue ?? decoded?.memory.kind_value) === memory.kind_value ? snapshot?.encryptedKindValue ?? existing.memory.kind_value : await encryptField2(memory.kind_value);
|
|
5595
|
+
return {
|
|
5596
|
+
body: {
|
|
5597
|
+
expected_version: expectedVersion,
|
|
5598
|
+
memory: { ...memory, content, ...kindValue !== void 0 && { kind_value: kindValue } }
|
|
5599
|
+
},
|
|
5600
|
+
content: memory.content,
|
|
5601
|
+
kindValue: memory.kind_value
|
|
5602
|
+
};
|
|
5603
|
+
};
|
|
5604
|
+
const fetchCandidates = async (embedding2, candidateOptions, withCounts) => {
|
|
5605
|
+
const limit = candidateOptions.limit ?? 100;
|
|
5606
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
5607
|
+
throw new Error("Candidate limit must be 1\u2013100");
|
|
5608
|
+
for (const [key3, max] of candidateListLimits) {
|
|
5609
|
+
const values = candidateOptions[key3];
|
|
5610
|
+
if (values && values.length > max)
|
|
5611
|
+
throw new Error(`Candidate ${key3} accepts at most ${max} entries (got ${values.length})`);
|
|
5612
|
+
}
|
|
5613
|
+
const result = await request(
|
|
5614
|
+
"/candidates",
|
|
5615
|
+
{
|
|
5616
|
+
method: "POST",
|
|
5617
|
+
body: JSON.stringify({
|
|
5618
|
+
embedding: embedding2,
|
|
5619
|
+
limit,
|
|
5620
|
+
force_ids: candidateOptions.force_ids ?? [],
|
|
5621
|
+
...withCounts && { with_counts: true },
|
|
5622
|
+
...Object.fromEntries(
|
|
5623
|
+
[
|
|
5624
|
+
"scopes",
|
|
5625
|
+
"memory_ids",
|
|
5626
|
+
"embedding_model",
|
|
5627
|
+
"strict_model",
|
|
5628
|
+
"include_deleted",
|
|
5629
|
+
"deleted_only"
|
|
5630
|
+
].filter(
|
|
5631
|
+
(key3) => candidateOptions[key3] !== void 0
|
|
5632
|
+
).map((key3) => [key3, candidateOptions[key3]])
|
|
5633
|
+
),
|
|
5634
|
+
...candidateOptions.fact_types !== void 0 && {
|
|
5635
|
+
fact_types: candidateOptions.fact_types
|
|
5636
|
+
},
|
|
5637
|
+
...candidateOptions.include_archived !== void 0 && {
|
|
5638
|
+
include_archived: candidateOptions.include_archived
|
|
5639
|
+
},
|
|
5640
|
+
...candidateOptions.include_quarantined !== void 0 && {
|
|
5641
|
+
include_quarantined: candidateOptions.include_quarantined
|
|
5642
|
+
},
|
|
5643
|
+
...candidateOptions.include_superseded !== void 0 && {
|
|
5644
|
+
include_superseded: candidateOptions.include_superseded
|
|
5645
|
+
}
|
|
5646
|
+
})
|
|
5647
|
+
},
|
|
5648
|
+
candidateOptions.signal
|
|
5649
|
+
);
|
|
5650
|
+
if (!record(result) || !Array.isArray(result.items))
|
|
5651
|
+
throw new Error("Invalid nearby candidate response");
|
|
5652
|
+
return result;
|
|
5653
|
+
};
|
|
5654
|
+
const fetchQueryPage = async (queryOptions) => {
|
|
5655
|
+
const { signal, ...query } = queryOptions;
|
|
5656
|
+
if (query.limit !== void 0 && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200))
|
|
5657
|
+
throw new Error("Page limit must be 1\u2013200");
|
|
5658
|
+
for (const [key3, max] of queryListLimits) {
|
|
5659
|
+
const values = query[key3];
|
|
5660
|
+
if (values && values.length > max)
|
|
5661
|
+
throw new Error(`Query ${key3} accepts at most ${max} entries (got ${values.length})`);
|
|
5662
|
+
}
|
|
5663
|
+
const page = await request(
|
|
5664
|
+
"/memories/query",
|
|
5665
|
+
{
|
|
5666
|
+
method: "POST",
|
|
5667
|
+
body: JSON.stringify(
|
|
5668
|
+
Object.fromEntries(
|
|
5669
|
+
queryFields.filter((key3) => query[key3] !== void 0).map((key3) => [key3, query[key3]])
|
|
5670
|
+
)
|
|
5671
|
+
)
|
|
5672
|
+
},
|
|
5673
|
+
signal
|
|
5674
|
+
);
|
|
5675
|
+
if (!record(page) || !Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string" || page.changes_cursor !== void 0 && typeof page.changes_cursor !== "string")
|
|
5676
|
+
throw new Error("Invalid nearby private-memory page");
|
|
5677
|
+
return page;
|
|
5678
|
+
};
|
|
5679
|
+
const candidateSet = async (embedding2, candidateOptions = {}) => {
|
|
5680
|
+
const result = await fetchCandidates(embedding2, candidateOptions, true);
|
|
5681
|
+
if (!Number.isSafeInteger(result.total_count) || result.total_count < 0 || !Number.isSafeInteger(result.unavailable_count) || result.unavailable_count < 0)
|
|
5682
|
+
throw new Error("Invalid nearby candidate response");
|
|
5683
|
+
return {
|
|
5684
|
+
...await decodeAll(result.items),
|
|
5685
|
+
total_count: result.total_count,
|
|
5686
|
+
unavailable_count: result.unavailable_count
|
|
5687
|
+
};
|
|
5688
|
+
};
|
|
5689
|
+
return {
|
|
5690
|
+
...createRemoteTopicPersistence(request),
|
|
5691
|
+
...createRemoteWorkPersistence(request),
|
|
5692
|
+
supportsRealtimeChanges: account.realtime_changes === true,
|
|
5693
|
+
counts: async () => {
|
|
5694
|
+
const result = await request("/counts", {});
|
|
5695
|
+
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
5696
|
+
(key3) => !Number.isSafeInteger(result[key3]) || result[key3] < 0
|
|
5697
|
+
))
|
|
5698
|
+
throw new Error("Invalid nearby memory counts");
|
|
5699
|
+
return {
|
|
5700
|
+
live: result.live,
|
|
5701
|
+
archived: result.archived,
|
|
5702
|
+
quarantined: result.quarantined
|
|
5703
|
+
};
|
|
5704
|
+
},
|
|
5705
|
+
temporal: async (window2, limit = 100) => {
|
|
5706
|
+
if (!Number.isFinite(window2.start) || !Number.isFinite(window2.end) || window2.end <= window2.start || !Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
5707
|
+
throw new Error("Temporal window must have end > start and limit must be 1\u2013100");
|
|
5708
|
+
const result = await request("/temporal", {
|
|
5709
|
+
method: "POST",
|
|
5710
|
+
body: JSON.stringify({ ...window2, limit })
|
|
5711
|
+
});
|
|
5712
|
+
if (!record(result) || !Array.isArray(result.items) || result.items.some(
|
|
5713
|
+
(item) => !record(item) || typeof item.memory_id !== "string" || !item.memory_id || typeof item.score !== "number" || !Number.isFinite(item.score) || item.score <= 0 || item.score > 1
|
|
5714
|
+
))
|
|
5715
|
+
throw new Error("Invalid nearby temporal candidates");
|
|
5716
|
+
return result.items.map((item) => item.memory_id);
|
|
5717
|
+
},
|
|
5718
|
+
get: async (id2, signal) => {
|
|
5719
|
+
try {
|
|
5720
|
+
return await decode(await request(memoryPath(id2), {}, signal));
|
|
5721
|
+
} catch (error) {
|
|
5722
|
+
if (error instanceof RemoteMemoryError && error.status === 404) return null;
|
|
5723
|
+
throw error;
|
|
5724
|
+
}
|
|
5725
|
+
},
|
|
5726
|
+
list: async (listOptions = {}) => {
|
|
5727
|
+
const { signal, ...query } = listOptions;
|
|
5728
|
+
if (query.limit !== void 0 && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) {
|
|
5729
|
+
throw new Error("Page limit must be 1\u2013200");
|
|
5730
|
+
}
|
|
5731
|
+
const params = new URLSearchParams();
|
|
5732
|
+
for (const key3 of [
|
|
5733
|
+
"cursor",
|
|
5734
|
+
"limit",
|
|
5735
|
+
"include_deleted",
|
|
5736
|
+
"include_archived",
|
|
5737
|
+
"include_quarantined",
|
|
5738
|
+
"include_superseded",
|
|
5739
|
+
"include_embeddings"
|
|
5740
|
+
]) {
|
|
5741
|
+
if (query[key3] !== void 0) params.set(key3, String(query[key3]));
|
|
5742
|
+
}
|
|
5743
|
+
const page = await request("/memories?" + params.toString(), {}, signal);
|
|
5744
|
+
if (!record(page) || !Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string") {
|
|
5745
|
+
throw new Error("Invalid nearby private-memory page");
|
|
5746
|
+
}
|
|
5747
|
+
return {
|
|
5748
|
+
...await decodeAll(page.items),
|
|
5749
|
+
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor }
|
|
5750
|
+
};
|
|
5751
|
+
},
|
|
5752
|
+
put: async (memory, expectedVersion, signal) => {
|
|
5753
|
+
const write = await prepareWrite(memory, expectedVersion, signal);
|
|
5754
|
+
return decodeCommitted(
|
|
5755
|
+
await request(
|
|
5756
|
+
memoryPath(write.body.memory.memory_id),
|
|
5757
|
+
{
|
|
5758
|
+
method: "PUT",
|
|
5759
|
+
body: JSON.stringify({ key_id: options.keyId, ...write.body })
|
|
5760
|
+
},
|
|
5761
|
+
signal
|
|
5762
|
+
),
|
|
5763
|
+
write
|
|
5764
|
+
);
|
|
5765
|
+
},
|
|
5766
|
+
putMany: async (writes, signal, leaseToken) => {
|
|
5767
|
+
if (writes.length < 1 || writes.length > 50 || new Set(writes.map((w) => w.memory.memory_id)).size !== writes.length)
|
|
5768
|
+
throw new Error("Batch must contain 1\u201350 distinct memory ids");
|
|
5769
|
+
const prepared = await Promise.all(
|
|
5770
|
+
writes.map((w) => prepareWrite(w.memory, w.expectedVersion, signal))
|
|
5771
|
+
);
|
|
5772
|
+
const result = await request(
|
|
5773
|
+
"/memories/batch",
|
|
5774
|
+
{
|
|
5775
|
+
method: "POST",
|
|
5776
|
+
body: JSON.stringify({
|
|
5777
|
+
key_id: options.keyId,
|
|
5778
|
+
writes: prepared.map((w) => w.body),
|
|
5779
|
+
...leaseToken && { lease_token: leaseToken }
|
|
5780
|
+
})
|
|
5781
|
+
},
|
|
5782
|
+
signal
|
|
5783
|
+
);
|
|
5784
|
+
if (!record(result) || !Array.isArray(result.items) || result.items.length !== prepared.length)
|
|
5785
|
+
throw new Error("Invalid nearby batch response");
|
|
5786
|
+
return Promise.all(result.items.map((item, i) => decodeCommitted(item, prepared[i])));
|
|
5787
|
+
},
|
|
5788
|
+
query: async (queryOptions = {}) => {
|
|
5789
|
+
const page = await fetchQueryPage(queryOptions);
|
|
5790
|
+
return {
|
|
5791
|
+
...await decodeAll(page.items),
|
|
5792
|
+
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor },
|
|
5793
|
+
...page.changes_cursor !== void 0 && { changes_cursor: page.changes_cursor }
|
|
5794
|
+
};
|
|
5795
|
+
},
|
|
5796
|
+
queryMetadata: async (queryOptions = {}) => {
|
|
5797
|
+
const page = await fetchQueryPage(queryOptions);
|
|
5798
|
+
return {
|
|
5799
|
+
items: page.items.map((item) => {
|
|
5800
|
+
const {
|
|
5801
|
+
memory: { content: _content, kind_value: _kindValue, ...memory },
|
|
5802
|
+
version,
|
|
5803
|
+
server_updated_at
|
|
5804
|
+
} = memoryRecord(item);
|
|
5805
|
+
return { memory, version, server_updated_at };
|
|
5806
|
+
}),
|
|
5807
|
+
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor }
|
|
5808
|
+
};
|
|
5809
|
+
},
|
|
5810
|
+
candidateSet,
|
|
5811
|
+
candidates: async (embedding2, candidateOptions = {}) => decodeAll((await fetchCandidates(embedding2, candidateOptions, false)).items)
|
|
5812
|
+
};
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5815
|
+
// src/lib/chat/inbox.ts
|
|
5816
|
+
var ENVELOPE_ALG = "ECDH-ES+HKDF-SHA256+A256GCM";
|
|
5817
|
+
var HKDF_INFO_PREFIX = "anuma-inbox-v1:";
|
|
5818
|
+
var encoder = new TextEncoder();
|
|
5819
|
+
function toBase64Url(bytes4) {
|
|
5820
|
+
let binary = "";
|
|
5821
|
+
for (const byte of bytes4) binary += String.fromCharCode(byte);
|
|
5822
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
5823
|
+
}
|
|
5824
|
+
function fromBase64Url(value) {
|
|
5825
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]*$/.test(value)) {
|
|
5826
|
+
throw new Error("Inbox envelope field is not base64url");
|
|
5827
|
+
}
|
|
5828
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
5829
|
+
const binary = atob(padded + "=".repeat((4 - padded.length % 4) % 4));
|
|
5830
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
5831
|
+
}
|
|
5832
|
+
function isInboxItem(value) {
|
|
5833
|
+
if (!value || typeof value !== "object") return false;
|
|
5834
|
+
const v = value;
|
|
5835
|
+
return typeof v.id === "string" && typeof v.sealed === "string";
|
|
5836
|
+
}
|
|
5837
|
+
function createVaultInbox(options) {
|
|
5838
|
+
const request = createNearbyRequest(options, "/api/private-chats");
|
|
5839
|
+
return {
|
|
5840
|
+
putPublicKey: async (publicKey) => {
|
|
5841
|
+
await request("/public-key", {
|
|
5842
|
+
method: "PUT",
|
|
5843
|
+
body: JSON.stringify({ public_key: publicKey })
|
|
5844
|
+
});
|
|
5845
|
+
},
|
|
5846
|
+
list: async () => {
|
|
5847
|
+
const body = await request("/inbox");
|
|
5848
|
+
return Array.isArray(body?.items) ? body.items.filter(isInboxItem) : [];
|
|
5849
|
+
},
|
|
5850
|
+
remove: async (id2) => {
|
|
5851
|
+
await request(`/inbox/${encodeURIComponent(id2)}`, { method: "DELETE" });
|
|
5852
|
+
}
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
async function exportInboxPublicKey(publicKey) {
|
|
5856
|
+
return toBase64Url(new Uint8Array(await crypto.subtle.exportKey("raw", publicKey)));
|
|
5857
|
+
}
|
|
5858
|
+
async function openInboxItem(item, keyPair) {
|
|
5859
|
+
const envelope = JSON.parse(item.sealed);
|
|
5860
|
+
if (envelope.v !== 1 || envelope.alg !== ENVELOPE_ALG) {
|
|
5861
|
+
throw new Error("Unsupported inbox envelope");
|
|
5862
|
+
}
|
|
5863
|
+
const epk = fromBase64Url(envelope.epk);
|
|
5864
|
+
const iv = fromBase64Url(envelope.iv);
|
|
5865
|
+
const ciphertext = fromBase64Url(envelope.ct);
|
|
5866
|
+
if (epk.length !== 65 || iv.length !== 12) throw new Error("Malformed inbox envelope");
|
|
5867
|
+
const ephemeral = await crypto.subtle.importKey(
|
|
5868
|
+
"raw",
|
|
5869
|
+
epk,
|
|
5870
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
5871
|
+
false,
|
|
5872
|
+
[]
|
|
5873
|
+
);
|
|
5874
|
+
const shared = await crypto.subtle.deriveBits(
|
|
5875
|
+
{ name: "ECDH", public: ephemeral },
|
|
5876
|
+
keyPair.privateKey,
|
|
5877
|
+
256
|
|
5878
|
+
);
|
|
5879
|
+
const recipient = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
5880
|
+
const salt = new Uint8Array(epk.length + recipient.length);
|
|
5881
|
+
salt.set(epk);
|
|
5882
|
+
salt.set(recipient, epk.length);
|
|
5883
|
+
const ikm = await crypto.subtle.importKey("raw", shared, "HKDF", false, ["deriveKey"]);
|
|
5884
|
+
const key3 = await crypto.subtle.deriveKey(
|
|
5885
|
+
{ name: "HKDF", hash: "SHA-256", salt, info: encoder.encode(HKDF_INFO_PREFIX + item.id) },
|
|
5886
|
+
ikm,
|
|
5887
|
+
{ name: "AES-GCM", length: 256 },
|
|
5888
|
+
false,
|
|
5889
|
+
["decrypt"]
|
|
5890
|
+
);
|
|
5891
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
5892
|
+
{ name: "AES-GCM", iv, additionalData: encoder.encode(item.id) },
|
|
5893
|
+
key3,
|
|
5894
|
+
ciphertext
|
|
5895
|
+
);
|
|
5896
|
+
const opened = JSON.parse(new TextDecoder().decode(plaintext));
|
|
5897
|
+
if (opened.v !== 1 || opened.inference_id !== item.id || !opened.response) {
|
|
5898
|
+
throw new Error("Inbox item does not match its id");
|
|
5899
|
+
}
|
|
5900
|
+
return opened;
|
|
5901
|
+
}
|
|
5902
|
+
function inboxReplaces(row, opened) {
|
|
5903
|
+
if (row.error) return true;
|
|
5904
|
+
return row.wasStopped === true && opened.status === "completed";
|
|
5905
|
+
}
|
|
5906
|
+
async function clearTurnErrors(backend, answer) {
|
|
5907
|
+
const parentId = answer.parentMessageId;
|
|
5908
|
+
if (parentId) {
|
|
5909
|
+
const [parent] = await backend.getMessagesByIds([parentId]);
|
|
5910
|
+
if (parent?.error) await backend.updateMessageError(parentId, null);
|
|
5911
|
+
}
|
|
5912
|
+
if (!answer.error) return answer;
|
|
5913
|
+
return await backend.updateMessageError(answer.uniqueId, null) ?? answer;
|
|
5914
|
+
}
|
|
5915
|
+
async function deliverInboxAnswers(options) {
|
|
5916
|
+
const { inbox, keyPair, backend, store } = options;
|
|
5917
|
+
const results = [];
|
|
5918
|
+
for (const item of await inbox.list()) {
|
|
5919
|
+
let opened;
|
|
5920
|
+
try {
|
|
5921
|
+
opened = await openInboxItem(item, keyPair);
|
|
5922
|
+
} catch (err) {
|
|
5923
|
+
results.push({
|
|
5924
|
+
id: item.id,
|
|
5925
|
+
conversationId: null,
|
|
5926
|
+
status: "unreadable",
|
|
5927
|
+
assistantMessage: null,
|
|
5928
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5929
|
+
});
|
|
5930
|
+
continue;
|
|
5931
|
+
}
|
|
5932
|
+
const base = { id: item.id, conversationId: opened.conversation_id };
|
|
5933
|
+
try {
|
|
5934
|
+
const messageId = opened.assistant_message_id ?? `msg_bg_${opened.inference_id}`;
|
|
5935
|
+
const [found] = await backend.getMessagesByIds([messageId]);
|
|
5936
|
+
const existing = found && inboxReplaces(found, opened) ? void 0 : found;
|
|
5937
|
+
if (!found) {
|
|
5938
|
+
const parent = opened.parent_message_id;
|
|
5939
|
+
const ownsTurn = parent ? (await backend.getMessagesByIds([parent])).length > 0 : await backend.getConversation(opened.conversation_id) !== null;
|
|
5940
|
+
if (!ownsTurn) {
|
|
5941
|
+
results.push({ ...base, status: "elsewhere", assistantMessage: null });
|
|
5942
|
+
continue;
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
const stored2 = existing ?? await store(opened, messageId);
|
|
5946
|
+
await inbox.remove(item.id);
|
|
5947
|
+
results.push({
|
|
5948
|
+
...base,
|
|
5949
|
+
status: existing ? "delivered" : "stored",
|
|
5950
|
+
assistantMessage: stored2
|
|
5951
|
+
});
|
|
5952
|
+
} catch (err) {
|
|
5953
|
+
results.push({
|
|
5954
|
+
...base,
|
|
5955
|
+
status: "retry",
|
|
5956
|
+
assistantMessage: null,
|
|
5957
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5958
|
+
});
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
return results;
|
|
5962
|
+
}
|
|
5963
|
+
|
|
5964
|
+
// src/lib/chat/reservationReceipts.ts
|
|
5965
|
+
var ACTIONS = [
|
|
5966
|
+
{
|
|
5967
|
+
kind: "booking",
|
|
5968
|
+
tool: "anuma_book_restaurant",
|
|
5969
|
+
alreadyDone: /^Tool "[^"]*" failed: ALREADY_BOOKED:/
|
|
5970
|
+
},
|
|
5971
|
+
{
|
|
5972
|
+
kind: "cancel",
|
|
5973
|
+
tool: "anuma_cancel_reservation",
|
|
5974
|
+
alreadyDone: /^Tool "[^"]*" failed: ALREADY_CANCELLED:/
|
|
5975
|
+
}
|
|
5976
|
+
];
|
|
5977
|
+
var NOT_AN_ATTEMPT = /^Tool "[^"]*" (has already been called|was already called)/;
|
|
5978
|
+
var BOOKING_OUTCOME_UNKNOWN = /^Tool "[^"]*" failed: BOOKING_OUTCOME_UNKNOWN:/;
|
|
5979
|
+
var CANCEL_OUTCOME_UNKNOWN = "CANCEL_OUTCOME_UNKNOWN";
|
|
5980
|
+
function extractReservationReceipts(toolCallEvents) {
|
|
5981
|
+
const latest = /* @__PURE__ */ new Map();
|
|
5982
|
+
for (const event of toolCallEvents ?? []) {
|
|
5983
|
+
const action = ACTIONS.find((a) => event.name?.endsWith(a.tool));
|
|
5984
|
+
const output = event.output ?? "";
|
|
5985
|
+
if (!action || !output || hasEncryptedToolPayload(event) || NOT_AN_ATTEMPT.test(output))
|
|
5986
|
+
continue;
|
|
5987
|
+
const kept = latest.get(action.kind)?.status;
|
|
5988
|
+
if (kept === "made") continue;
|
|
5989
|
+
const receipt = toReceipt(action, event.arguments ?? "", output);
|
|
5990
|
+
if (kept === "unknown" && receipt.status !== "made") continue;
|
|
5991
|
+
latest.set(action.kind, receipt);
|
|
5992
|
+
}
|
|
5993
|
+
return [...latest.values()];
|
|
5994
|
+
}
|
|
5995
|
+
function toReceipt(action, argumentsJson, output) {
|
|
5996
|
+
const args = parseObject(argumentsJson);
|
|
5997
|
+
const result = parseObject(output);
|
|
5998
|
+
return {
|
|
5999
|
+
kind: action.kind,
|
|
6000
|
+
venueName: text2(args.venue_name) ?? "",
|
|
6001
|
+
day: text2(args.day) ?? "",
|
|
6002
|
+
time: text2(args.time) ?? "",
|
|
6003
|
+
partySize: count(args.party_size),
|
|
6004
|
+
reservationId: text2(result.reservation_id) ?? text2(args.reservation_id),
|
|
6005
|
+
resyUrl: text2(result.reservations_url),
|
|
6006
|
+
feeSummary: text2(result.fee_summary),
|
|
6007
|
+
...outcome(action, output, result)
|
|
6008
|
+
};
|
|
6009
|
+
}
|
|
6010
|
+
function outcome(action, output, result) {
|
|
6011
|
+
if (action.alreadyDone.test(output)) return { status: "already_done" };
|
|
6012
|
+
const reason = text2(result.error);
|
|
6013
|
+
if (action.kind === "booking") {
|
|
6014
|
+
if (BOOKING_OUTCOME_UNKNOWN.test(output)) return { status: "unknown" };
|
|
6015
|
+
const held = result.success === true || text2(result.reservation_id) !== void 0;
|
|
6016
|
+
return held ? { status: "made" } : { status: "not_made", reason };
|
|
6017
|
+
}
|
|
6018
|
+
if (result.error_code === CANCEL_OUTCOME_UNKNOWN) return { status: "unknown", reason };
|
|
6019
|
+
const cancelled = result.success === true && !result.error && !result.error_code;
|
|
6020
|
+
return cancelled ? { status: "made" } : { status: "not_made", reason };
|
|
6021
|
+
}
|
|
6022
|
+
function parseObject(json5) {
|
|
6023
|
+
try {
|
|
6024
|
+
const parsed = JSON.parse(json5);
|
|
6025
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
6026
|
+
} catch {
|
|
6027
|
+
return {};
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
function text2(value) {
|
|
6031
|
+
if (typeof value === "number") return String(value);
|
|
6032
|
+
return typeof value === "string" && value ? value : void 0;
|
|
6033
|
+
}
|
|
6034
|
+
function count(value) {
|
|
6035
|
+
const n = typeof value === "string" && value.trim() ? Number(value) : value;
|
|
6036
|
+
return typeof n === "number" && Number.isFinite(n) ? n : void 0;
|
|
6037
|
+
}
|
|
6038
|
+
|
|
6039
|
+
// src/tools/confirmConstants.ts
|
|
6040
|
+
var CONFIRM_TOOL_NAME = "prompt_user_confirm";
|
|
6041
|
+
var BOOK_RESTAURANT = {
|
|
6042
|
+
tool: "AnumaPaymentsMCP-anuma_book_restaurant",
|
|
6043
|
+
status: "confirmed_not_booked",
|
|
6044
|
+
fields: ["venue_id", "config_id", "party_size", "day", "venue_name", "time"]
|
|
6045
|
+
};
|
|
6046
|
+
var CANCEL_RESERVATION = {
|
|
6047
|
+
tool: "AnumaPaymentsMCP-anuma_cancel_reservation",
|
|
6048
|
+
status: "confirmed_not_cancelled",
|
|
6049
|
+
fields: [
|
|
6050
|
+
"reservation_id",
|
|
6051
|
+
"venue_name",
|
|
6052
|
+
"day",
|
|
6053
|
+
"time",
|
|
6054
|
+
"party_size",
|
|
6055
|
+
"fee_applies",
|
|
6056
|
+
"fee_amount"
|
|
6057
|
+
]
|
|
6058
|
+
};
|
|
6059
|
+
var CONFIRM_REQUIRED_FIELDS = /* @__PURE__ */ new Map([
|
|
6060
|
+
["book_restaurant", BOOK_RESTAURANT],
|
|
6061
|
+
["anuma_book_restaurant", BOOK_RESTAURANT],
|
|
6062
|
+
["anumapaymentsmcp-anuma_book_restaurant", BOOK_RESTAURANT],
|
|
6063
|
+
["cancel_reservation", CANCEL_RESERVATION],
|
|
6064
|
+
["anuma_cancel_reservation", CANCEL_RESERVATION],
|
|
6065
|
+
["anumapaymentsmcp-anuma_cancel_reservation", CANCEL_RESERVATION]
|
|
6066
|
+
]);
|
|
6067
|
+
function confirmRequirement(action) {
|
|
6068
|
+
if (typeof action !== "string") return void 0;
|
|
6069
|
+
return CONFIRM_REQUIRED_FIELDS.get(action.trim().toLowerCase());
|
|
6070
|
+
}
|
|
6071
|
+
function missingConfirmFields(action, parameters) {
|
|
6072
|
+
const requirement = confirmRequirement(action);
|
|
6073
|
+
if (!requirement) return [];
|
|
6074
|
+
const shown = /* @__PURE__ */ new Set();
|
|
6075
|
+
for (const parameter of Array.isArray(parameters) ? parameters : []) {
|
|
6076
|
+
const { name, value } = parameter ?? {};
|
|
6077
|
+
if (typeof name === "string" && typeof value === "string" && value.trim()) shown.add(name);
|
|
6078
|
+
}
|
|
6079
|
+
return requirement.fields.filter((field10) => !shown.has(field10));
|
|
6080
|
+
}
|
|
6081
|
+
|
|
6082
|
+
// src/tools/walletPaymentConstants.ts
|
|
6083
|
+
var SELECT_PAYMENT_WALLET_TOOL_NAME = "select_payment_wallet";
|
|
6084
|
+
var WALLET_PAYMENT_TOOL_NAME = "prompt_wallet_payment";
|
|
6085
|
+
var FLIGHT_BOOK_TOOL_SET = "flight-book";
|
|
6086
|
+
var FLIGHT_SERVER_TOOL_NAMES = [
|
|
6087
|
+
"AnumaPaymentsMCP-anuma_search_flights",
|
|
6088
|
+
"AnumaPaymentsMCP-anuma_flight_offer_details",
|
|
6089
|
+
"AnumaPaymentsMCP-anuma_create_flight_intent",
|
|
6090
|
+
"AnumaPaymentsMCP-anuma_get_flight_booking"
|
|
6091
|
+
];
|
|
6092
|
+
|
|
6093
|
+
// src/lib/storage/mcpImages.ts
|
|
6094
|
+
var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6095
|
+
"AnumaMediaMCP-anuma_create_image",
|
|
6096
|
+
"anuma_create_image",
|
|
6097
|
+
"AnumaImageMCP-generate_cloud_image",
|
|
6098
|
+
"AnumaImageMCP-edit_cloud_image",
|
|
6099
|
+
"generate_cloud_image",
|
|
6100
|
+
"edit_cloud_image"
|
|
6101
|
+
]);
|
|
6102
|
+
function toolOutputForModel(name, output) {
|
|
6103
|
+
if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
|
|
6104
|
+
try {
|
|
6105
|
+
const {
|
|
6106
|
+
imageUrl: _imageUrl,
|
|
6107
|
+
url: _url,
|
|
6108
|
+
output_images: _outputImages,
|
|
6109
|
+
...rest
|
|
6110
|
+
} = JSON.parse(output);
|
|
6111
|
+
return JSON.stringify(rest);
|
|
6112
|
+
} catch {
|
|
6113
|
+
return output;
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
|
|
6117
|
+
var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
|
|
6118
|
+
|
|
6119
|
+
// src/tools/appBuilderPrompt.ts
|
|
6120
|
+
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.
|
|
6121
|
+
|
|
6122
|
+
Build ONLY when the user gives an explicit instruction to build, create, generate, or change an app, tool, or UI \u2014 e.g. "build a todo app", "make a snake game", "add a footer to the app", "change the background to blue". A bare app name or feature reference is NOT a build instruction: if the user just says "todo app", "a snake game", or "qr code", or asks a question about an app, do NOT generate anything \u2014 reply normally, or ask what they'd like you to build. The same holds for greetings, questions, stories, explanations, advice, or code that isn't a runnable app: respond normally and do not create files. When it's unclear whether the user actually wants an app built, ask instead of building.
|
|
6123
|
+
|
|
6124
|
+
Once the user HAS given an explicit build instruction, you are in App Builder mode: you produce polished, production-quality React apps that feel designed \u2014 not generic.
|
|
6125
|
+
|
|
6126
|
+
DESIGN: on the initial build, before writing code, briefly state (2-3 sentences) what you want this app to feel like \u2014 name the specific fonts, the palette in hex, and one signature detail. Be concrete: "Fraunces display + Inter body + JetBrains Mono metadata, terracotta #b75432 accent on bone #f3efe6, paper-grain texture via inline SVG noise" is the kind of specificity that anchors the build. Default to nothing: don't pick "safe modern productivity" unless the brief literally asks for utilitarian. On later turns, state a design brief only when the change is visual (a restyle, new theme, or new UI); for a text, logic, data, or behavior-only change, skip the brief and just make the change.
|
|
6127
|
+
|
|
6128
|
+
EDIT SCOPE: Change only the files and lines required by the current user request. A title rename is a text edit, not a design change. Preserve styles, dependencies, and unrelated logic on text edits. Design feedback does not authorize unrelated edits.
|
|
6129
|
+
|
|
6130
|
+
WORKFLOW:
|
|
6131
|
+
1. NEVER output code as text or markdown. ALWAYS use tools.
|
|
6132
|
+
2. Initial build: state your design brief, then create_file with a "files" array writing ALL files in one call. The preview renders automatically.
|
|
6133
|
+
3. patch_file requires read_file for that path earlier in the conversation (or create_file in the same conversation, which counts). read_file output is numbered "42: <text>" \u2014 the prefix is display-only; never copy it into "find". Each "find" must match exactly one location \u2014 include 2-3 lines of surrounding context. Pass multiple patches per call to change distinct locations.
|
|
6134
|
+
4. Prefer patch_file for changes to existing files \u2014 surgical, reviewable, preserves surrounding code. Reserve create_file overwrites for substantial restructuring. Inserts: find the line before, replace with that line + the new line. Deletes: empty replace.
|
|
6135
|
+
5. On the initial build, when adding new UI, or after user-requested design changes, call critique_design first and then audit_design. Critique checks taste, hierarchy, and distinctiveness. Audit checks tokens, focus states, aria-labels, alt text, and heading order. Patch only issues within the current user request. For text, logic, or data edits, skip both design tools.
|
|
6136
|
+
6. Before declaring done, call verify_app. The host's preview runtime reports whether your app actually mounted and surfaces any errors it captured \u2014 hallucinated imports, syntax the bundler rejected, undefined components, runtime crashes. If \`rendered\` is false or \`errors\` is non-empty, fix every error before the next message. If the response includes a \`note\` that verify_app isn't wired in this host, skip runtime verification. An unavailable runtime verifier does not authorize design changes. Use audit_design and critique_design only for the initial build, adding new UI, or user-requested design changes.
|
|
6137
|
+
7. Keep responses to a sentence or two. Exception: when responding to critique_design, write a substantive paragraph per rubric question.
|
|
6138
|
+
|
|
6139
|
+
STRUCTURE:
|
|
6140
|
+
- App.js: default-export React component. For non-trivial apps, define small co-located helpers (Header, CardList, Card, EditDialog, \u2026) above the default export rather than one giant App.
|
|
6141
|
+
- App.css: all styles, imported in App.js. No inline style objects.
|
|
6142
|
+
- package.json: list ALL imported packages including react. No CDN script tags. Versions are auto-pinned.
|
|
6143
|
+
- Do NOT create index.js or index.html (auto-generated).
|
|
6144
|
+
|
|
6145
|
+
STATE & PERSISTENCE:
|
|
6146
|
+
- The code runs in the user's browser \u2014 standard browser APIs available (localStorage, URL hash, Date, fetch).
|
|
6147
|
+
- Persist user-created data (todos, cards, notes, settings) with localStorage so it survives refresh. Use a versioned key ("kanban.v1") and JSON.parse inside try/catch on read.
|
|
6148
|
+
- Treat persisted data as untrusted and possibly stale: a saved record may predate a field you just added (a timestamp, id, or setting), so default-fill or skip missing fields on read \u2014 never assume a loaded record has every field the current code expects. Bump the key version when the shape changes.
|
|
6149
|
+
- Lift state to App when multiple children need it; keep it local otherwise. Extract non-trivial logic into custom hooks (useTodos, useTimer).
|
|
6150
|
+
- useReducer for state shapes with several interrelated fields (cards + columns + filters + drag state).
|
|
6151
|
+
|
|
6152
|
+
FILE & IMAGE UPLOAD:
|
|
6153
|
+
- Picker: \`<input type="file" accept="image/*" multiple>\` (vary accept by use case: \`.csv,text/csv\`, \`.txt,.md,text/plain\`, etc.).
|
|
6154
|
+
- Drag-and-drop: call \`e.preventDefault()\` in onDragOver (otherwise drop never fires); read files from \`event.dataTransfer.files\`. Drop and picker should coexist.
|
|
6155
|
+
- Previews: \`<img src={URL.createObjectURL(file)} />\`. Call \`URL.revokeObjectURL\` on unmount or before replacing \u2014 otherwise the browser leaks memory.
|
|
6156
|
+
- localStorage cannot hold large binary. Persist only metadata for big files; re-upload on next visit (or use IndexedDB if you really need durable binary storage).
|
|
6157
|
+
- Validate size and type before processing; reject files larger than the app can hold in memory.
|
|
6158
|
+
|
|
6159
|
+
AI CAPABILITIES \u2014 \`window.app.complete(prompt: string): Promise<string>\` is available in this environment. Build for the real call.
|
|
6160
|
+
- When the app's core purpose is conversation, generation, grading, recommendation, or any natural-language task, this IS the implementation \u2014 call it. NEVER fake it: no hardcoded or canned replies, no random or \`setTimeout\` "thinking", no demo-mode stub. A chat app, tutor, or assistant that doesn't call \`window.app.complete\` is wrong, not a placeholder.
|
|
6161
|
+
- Don't force it where it isn't needed \u2014 a calculator, todo list, or timer has no reason to call it. Match the implementation to what the app actually does.
|
|
6162
|
+
- Async: wrap in try/catch, show a loading state, and disable dependent inputs while waiting. Surface failures gracefully ("Couldn't reach the AI \u2014 try again").
|
|
6163
|
+
- Structure prompts with clear sections (Role / Context / Task / Format) and pass the relevant state (e.g. the running conversation) as context. Render the response as untrusted text: \`white-space: pre-wrap\`, never dangerouslySetInnerHTML.
|
|
6164
|
+
- Skip the call only when the answer is genuinely static \u2014 no language or reasoning needed. Cache repeated prompts in component state when sensible.
|
|
6165
|
+
|
|
6166
|
+
CODE QUALITY \u2014 write as a senior engineer would:
|
|
6167
|
+
- Handle edge cases (empty inputs, division by zero, invalid data). Show helpful error states, not crashes.
|
|
6168
|
+
- Never let formatting throw during render: guard \`new Date(x)\` / \`Intl\` / \`toLocaleString\` / \`.toFixed\` against missing or invalid values (e.g. \`Number.isNaN(d.getTime())\`) and return a fallback \u2014 one bad value from storage or state must not blank the whole app.
|
|
6169
|
+
- Semantic HTML: real headings, labels, buttons (not divs with onClick), form elements.
|
|
6170
|
+
- Accessible: aria-labels on icon buttons, focus-visible styles, keyboard navigable.
|
|
6171
|
+
- Extract into custom hooks or helpers when a component grows past ~80 lines.
|
|
6172
|
+
|
|
6173
|
+
STYLING \u2014 Tailwind is the CHASSIS, custom CSS carries the IDENTITY:
|
|
6174
|
+
- Tailwind (via Play CDN) for layout, spacing, state (hover/focus), responsive prefixes \u2014 uniform structure.
|
|
6175
|
+
- App.css carries the identity: @import the Google Fonts, declare CSS custom properties (--bg, --panel, --card, --ink, --muted, --accent, --hairline, --radius, --radius-sm, --shadow-card, --shadow-card-hi) in :root, gradients/textures/signature animations Tailwind can't express. Reference tokens from Tailwind via arbitrary values (\`className="bg-[var(--bg)]"\`) when needed. A well-designed app has 60-200 lines of App.css \u2014 not zero.
|
|
6176
|
+
- Modern CSS is how apps stop reading as generic-AI-output: \`color-mix(in oklab, var(--accent) 14%, transparent)\` for tinting, \`oklch()\` / \`oklab()\` for perceptually-uniform color, \`:has()\`, \`conic-gradient\` / \`radial-gradient\`, \`backdrop-filter\`, \`@container\` queries, inline SVG \`feTurbulence\` for texture.
|
|
6177
|
+
- Don't pull additional CSS frameworks. Tailwind + custom CSS is enough.
|
|
6178
|
+
|
|
6179
|
+
VISUAL DESIGN \u2014 every app should look like a real product, not a wireframe:
|
|
6180
|
+
- Let the chosen aesthetic drive every decision. "Editorial" wants italic display type and restrained color; "retro arcade" wants chunky shadows and saturated gradients. No safe-modern defaults.
|
|
6181
|
+
- Atmospheric depth: subtle radial wash, paper grain (inline SVG noise), warm/cool tint \u2014 every canvas should feel like a designed space, not a #fff rectangle.
|
|
6182
|
+
- Three shadow tiers (rest / hover / active). Pair drop shadows with inset highlights (\`box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 6px 14px -10px ...\`) for tactile feel.
|
|
6183
|
+
- Typography hierarchy: display font for headlines (italic accents on key words via \`<em>\`), body font for content, mono for metadata/labels \u2014 different families telegraph different information types.
|
|
6184
|
+
- Interactive polish: hover/active/focus on every clickable element, 200-300ms cubic-bezier transitions, keyboard shortcuts where natural (Esc to cancel, Cmd/Ctrl+Enter to commit), focus-visible rings keyed to the accent.
|
|
6185
|
+
- Light/dark mode if reasonable: a single CSS variable inversion under \`[data-theme="dark"]\`.
|
|
6186
|
+
- Customization affordance for multi-session apps: small "Tweaks" panel with theme / density / accent. Skip for throwaway demos.
|
|
6187
|
+
- Responsive: Tailwind responsive prefixes (\`sm:\`, \`md:\`, \`lg:\`) and \`@media\` in App.css. Mobile-first.`;
|
|
6188
|
+
|
|
6189
|
+
// src/tools/document/documentBuilderPrompt.ts
|
|
6190
|
+
var DOCUMENT_BUILDER_PROMPT = `Document tools (create_document, read_document, patch_document) are available this turn, but they are OPTIONAL \u2014 having them does not mean you should produce a document.
|
|
6191
|
+
|
|
6192
|
+
Use them ONLY when the user explicitly asks you to draft, write, compose, or generate a document \u2014 a contract, agreement, NDA, letter, cover letter, memo, essay, report, brief, proposal, or resume \u2014 e.g. "draft me a service agreement", "write a cover letter for this job", "make a one-page memo about X". A bare topic, a question, a request for an explanation, or a request for a slide deck or an interactive app is NOT a document instruction: respond normally and do not call these tools. When it's unclear whether the user wants a finished document, ask instead.
|
|
6193
|
+
|
|
6194
|
+
When you DO author a document, the document body goes entirely into the tool \u2014 NEVER write the document text as chat prose. The tool renders your source to a real, paginated PDF and attaches it to your reply automatically. AFTER the tool returns, briefly confirm in ONE short sentence what you made (e.g. "Drafted a cover letter for the product manager role \u2014 see the attached PDF."). Do not restate the document's contents.
|
|
6195
|
+
|
|
6196
|
+
AUTHORING \u2014 write the "source" as a single @react-pdf/renderer JSX expression (this is react-pdf, NOT HTML and NOT a slide deck):
|
|
6197
|
+
- Root is exactly one <Document> with at least one <Page>. Its only children are <Page> elements. Add more <Page> elements for longer documents \u2014 react-pdf also paginates overflow automatically.
|
|
6198
|
+
- Inside a <Page>: use <View> as flex containers and <Text> for ALL text. Raw text is only allowed inside <Text> (and <Link>); a <View> or <Page> may not contain bare text.
|
|
6199
|
+
- <Text> may nest <Text>, <Link>, or <Note> for inline styling, hyperlinks, and (rarely) a PDF comment annotation. A <Link> src must be an ordinary web link \u2014 http(s), mailto, tel, or a relative/anchor link; javascript:/data:/file: links are rejected. <Image> is self-closing and its src MUST be an inline "data:" URI \u2014 remote image URLs are rejected for privacy. Vector primitives (<Rect>/<Circle>/<Path>/etc.) must live inside an <Svg>.
|
|
6200
|
+
- Styling is react-pdf style objects only: style={{ marginTop: 12, fontSize: 11, fontFamily: "Times-Roman", flexDirection: "column" }}. Flexbox only \u2014 there is NO CSS grid, float, or box-shadow. Use camelCase react-pdf keys (margin/padding, flex*, width/height, color/backgroundColor, border*, text*, etc.).
|
|
6201
|
+
- fontFamily must be a built-in PDF font: Helvetica, Times-Roman, or Courier (with -Bold / -Oblique / -Italic / -BoldOblique variants), or Symbol / ZapfDingbats. Custom fonts are not supported.
|
|
6202
|
+
- LITERAL VALUES ONLY: no variables, no JS expressions, no .map(), no event handlers (onClick, \u2026), no spreads. Every attribute is a literal string, number, boolean, or a style object of literals.
|
|
6203
|
+
|
|
6204
|
+
A minimal example:
|
|
6205
|
+
|
|
6206
|
+
<Document>
|
|
6207
|
+
<Page size="A4" style={{ paddingVertical: 56, paddingHorizontal: 64, fontFamily: "Times-Roman", fontSize: 11, lineHeight: 1.5 }}>
|
|
6208
|
+
<Text style={{ fontSize: 18, fontFamily: "Times-Bold", marginBottom: 16, textAlign: "center" }}>MUTUAL NON-DISCLOSURE AGREEMENT</Text>
|
|
6209
|
+
<Text style={{ marginBottom: 10 }}>This Mutual Non-Disclosure Agreement (the "Agreement") is entered into as of the date of last signature below.</Text>
|
|
6210
|
+
<View style={{ marginTop: 14 }}>
|
|
6211
|
+
<Text style={{ fontFamily: "Times-Bold", marginBottom: 4 }}>1. Confidential Information</Text>
|
|
6212
|
+
<Text>Each party may disclose certain confidential and proprietary information to the other party.</Text>
|
|
6213
|
+
</View>
|
|
6214
|
+
</Page>
|
|
6215
|
+
</Document>
|
|
6216
|
+
|
|
6217
|
+
WORKFLOW:
|
|
6218
|
+
1. To create or fully restructure a document, call create_document with the complete source.
|
|
6219
|
+
2. To revise an existing document, call read_document first (its output is line-numbered "42: <text>" \u2014 the prefix is display-only, never copy it into a "find"), then patch_document with small find/replace edits. Each "find" must match exactly one location; add 2-3 lines of surrounding context if needed. Prefer patching over rewriting.
|
|
6220
|
+
3. To attach several separate documents in one reply, call create_document multiple times with distinct documentId slugs (e.g. "nda", "cover-letter"). Each renders to its own PDF.
|
|
6221
|
+
4. If a tool reports a DSL error (with line:col) or a render failure, fix the source and retry \u2014 do not give up or fall back to writing the document as chat text.`;
|
|
6222
|
+
|
|
6223
|
+
// src/lib/tools/serverTools.ts
|
|
6224
|
+
var DEFAULT_CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1e3;
|
|
6225
|
+
var SERVER_TOOLS_CACHE_KEY = "sdk_server_tools_cache";
|
|
6226
|
+
var CACHE_VERSION = "1.3";
|
|
6227
|
+
var MIN_CONTENT_LENGTH_FOR_TOOLS = 5;
|
|
6228
|
+
var MAX_CLIENT_TOOLS_AFTER_FILTER = 10;
|
|
6229
|
+
var CLIENT_TOOLS_MIN_SIMILARITY = 0.53;
|
|
6230
|
+
var CLIENT_TOOLS_RELEVANCE_RATIO = 0.75;
|
|
6231
|
+
function isNewToolFormat(tool) {
|
|
6232
|
+
return "schema" in tool && tool.schema !== void 0;
|
|
6233
|
+
}
|
|
6234
|
+
function isNewResponseFormat(response) {
|
|
6235
|
+
return "checksum" in response && "tools" in response && typeof response.checksum === "string";
|
|
6236
|
+
}
|
|
6237
|
+
function convertServerToolsResponse(response) {
|
|
6238
|
+
let toolsMap;
|
|
6239
|
+
let checksum;
|
|
6240
|
+
if (isNewResponseFormat(response)) {
|
|
6241
|
+
toolsMap = response.tools;
|
|
6242
|
+
checksum = response.checksum;
|
|
6243
|
+
} else {
|
|
6244
|
+
toolsMap = response;
|
|
6245
|
+
}
|
|
6246
|
+
const tools = Object.values(toolsMap).map((tool) => {
|
|
6247
|
+
if (isNewToolFormat(tool)) {
|
|
6248
|
+
return {
|
|
6249
|
+
type: "function",
|
|
6250
|
+
name: tool.schema.name,
|
|
6251
|
+
description: tool.schema.description,
|
|
6252
|
+
parameters: tool.schema.parameters,
|
|
6253
|
+
...tool.embedding && { embedding: tool.embedding }
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
return {
|
|
6257
|
+
type: "function",
|
|
6258
|
+
name: tool.name,
|
|
6259
|
+
description: tool.description,
|
|
6260
|
+
parameters: tool.parameters
|
|
6261
|
+
};
|
|
6262
|
+
});
|
|
6263
|
+
return { tools, checksum };
|
|
6264
|
+
}
|
|
6265
|
+
function toCompletionsFormat(tool) {
|
|
6266
|
+
return {
|
|
6267
|
+
type: "function",
|
|
6268
|
+
function: {
|
|
6269
|
+
name: tool.name,
|
|
6270
|
+
description: tool.description,
|
|
6271
|
+
parameters: tool.parameters
|
|
6272
|
+
}
|
|
6273
|
+
};
|
|
6274
|
+
}
|
|
6275
|
+
function toResponsesFormat(tool) {
|
|
6276
|
+
return {
|
|
6277
|
+
type: "function",
|
|
6278
|
+
name: tool.name,
|
|
6279
|
+
description: tool.description,
|
|
6280
|
+
parameters: tool.parameters
|
|
6281
|
+
};
|
|
6282
|
+
}
|
|
6283
|
+
function getCachedServerTools() {
|
|
6284
|
+
if (typeof localStorage === "undefined") return null;
|
|
6285
|
+
try {
|
|
6286
|
+
const cached = localStorage.getItem(SERVER_TOOLS_CACHE_KEY);
|
|
6287
|
+
if (!cached) return null;
|
|
6288
|
+
const parsed = JSON.parse(cached);
|
|
6289
|
+
if (parsed.version !== CACHE_VERSION) {
|
|
6290
|
+
removeLocalStorageCache();
|
|
6291
|
+
return null;
|
|
6292
|
+
}
|
|
6293
|
+
return parsed;
|
|
6294
|
+
} catch {
|
|
6295
|
+
return null;
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
function isCacheExpired(cache, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) {
|
|
6299
|
+
if (!cache) return true;
|
|
6300
|
+
return Date.now() - cache.timestamp > expirationMs;
|
|
6301
|
+
}
|
|
6302
|
+
function buildCacheEntry(tools, checksum, etag) {
|
|
6303
|
+
return {
|
|
6304
|
+
tools,
|
|
6305
|
+
timestamp: Date.now(),
|
|
6306
|
+
version: CACHE_VERSION,
|
|
6307
|
+
...checksum && { checksum },
|
|
6308
|
+
...etag && { etag }
|
|
6309
|
+
};
|
|
6310
|
+
}
|
|
6311
|
+
function writeLocalStorageCache(entry) {
|
|
6312
|
+
if (typeof localStorage === "undefined") return;
|
|
6313
|
+
try {
|
|
6314
|
+
localStorage.setItem(SERVER_TOOLS_CACHE_KEY, JSON.stringify(entry));
|
|
6315
|
+
} catch (error) {
|
|
6316
|
+
getLogger().warn("[serverTools] Failed to cache tools:", error);
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
function removeLocalStorageCache() {
|
|
6320
|
+
if (typeof localStorage === "undefined") return;
|
|
6321
|
+
localStorage.removeItem(SERVER_TOOLS_CACHE_KEY);
|
|
6322
|
+
}
|
|
6323
|
+
var localStorageToolsCache = {
|
|
6324
|
+
get: getCachedServerTools,
|
|
6325
|
+
set: writeLocalStorageCache,
|
|
6326
|
+
clear: removeLocalStorageCache
|
|
6327
|
+
};
|
|
6328
|
+
function clearServerToolsCache(cache = localStorageToolsCache) {
|
|
6329
|
+
return cache.clear?.();
|
|
6330
|
+
}
|
|
6331
|
+
function getToolsChecksum2(cache = localStorageToolsCache) {
|
|
6332
|
+
const cached = cache.get();
|
|
6333
|
+
return cached instanceof Promise ? cached.then((c) => c?.checksum) : cached?.checksum;
|
|
6334
|
+
}
|
|
6335
|
+
function shouldRefreshTools(responseChecksum, cache = localStorageToolsCache) {
|
|
6336
|
+
if (!responseChecksum) {
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6339
|
+
const decide = (cachedChecksum2) => !cachedChecksum2 || cachedChecksum2 !== responseChecksum;
|
|
5467
6340
|
const cachedChecksum = getToolsChecksum2(cache);
|
|
5468
6341
|
return cachedChecksum instanceof Promise ? cachedChecksum.then(decide) : decide(cachedChecksum);
|
|
5469
6342
|
}
|
|
@@ -6438,6 +7311,13 @@ function createErrorResult(message, onError) {
|
|
|
6438
7311
|
function isAbortError(err) {
|
|
6439
7312
|
return err instanceof Error && err.name === "AbortError";
|
|
6440
7313
|
}
|
|
7314
|
+
var ProviderStreamError = class extends Error {
|
|
7315
|
+
constructor(message, code) {
|
|
7316
|
+
super(message);
|
|
7317
|
+
this.name = "ProviderStreamError";
|
|
7318
|
+
this.code = code;
|
|
7319
|
+
}
|
|
7320
|
+
};
|
|
6441
7321
|
function getInStreamErrorMessage(chunk) {
|
|
6442
7322
|
if (!chunk || typeof chunk !== "object") return null;
|
|
6443
7323
|
const obj = chunk;
|
|
@@ -6724,7 +7604,7 @@ var CompletionsStrategy = class {
|
|
|
6724
7604
|
processStreamChunk(chunk, accumulator) {
|
|
6725
7605
|
const result = { content: null, thinking: null };
|
|
6726
7606
|
const inStreamErr = getInStreamErrorMessage(chunk);
|
|
6727
|
-
if (inStreamErr) throw new
|
|
7607
|
+
if (inStreamErr) throw new ProviderStreamError(inStreamErr);
|
|
6728
7608
|
const rawChunk = chunk;
|
|
6729
7609
|
const typedChunk = rawChunk.response && rawChunk.type === "response" ? rawChunk.response : chunk;
|
|
6730
7610
|
if (typedChunk.id && !accumulator.responseId) {
|
|
@@ -7575,7 +8455,7 @@ var ResponsesStrategy = class {
|
|
|
7575
8455
|
const result = { content: null, thinking: null };
|
|
7576
8456
|
const typedChunk = chunk;
|
|
7577
8457
|
const inStreamErr = getInStreamErrorMessage(chunk);
|
|
7578
|
-
if (inStreamErr) throw new
|
|
8458
|
+
if (inStreamErr) throw new ProviderStreamError(inStreamErr);
|
|
7579
8459
|
if (typedChunk.type === "response.failed") {
|
|
7580
8460
|
const resp = typedChunk.response;
|
|
7581
8461
|
const err = resp?.error;
|
|
@@ -7592,9 +8472,12 @@ var ResponsesStrategy = class {
|
|
|
7592
8472
|
}
|
|
7593
8473
|
}
|
|
7594
8474
|
const label = code ? `[${code}] ` : "";
|
|
7595
|
-
throw new
|
|
8475
|
+
throw new ProviderStreamError(
|
|
8476
|
+
`${label}${message || "Upstream request failed"}`,
|
|
8477
|
+
code || void 0
|
|
8478
|
+
);
|
|
7596
8479
|
}
|
|
7597
|
-
throw new
|
|
8480
|
+
throw new ProviderStreamError("Upstream request failed (response.failed)");
|
|
7598
8481
|
}
|
|
7599
8482
|
if (typedChunk.type === "response" && typedChunk.response) {
|
|
7600
8483
|
const resp = typedChunk.response;
|
|
@@ -8218,13 +9101,6 @@ function isRetriableStreamError(err) {
|
|
|
8218
9101
|
if (msg.includes("connection refused") || msg.includes("connect error")) return true;
|
|
8219
9102
|
return false;
|
|
8220
9103
|
}
|
|
8221
|
-
var ProviderStreamError = class extends Error {
|
|
8222
|
-
constructor(message, code) {
|
|
8223
|
-
super(message);
|
|
8224
|
-
this.name = "ProviderStreamError";
|
|
8225
|
-
this.code = code;
|
|
8226
|
-
}
|
|
8227
|
-
};
|
|
8228
9104
|
function extractProviderStreamError(chunk) {
|
|
8229
9105
|
if (!chunk || typeof chunk !== "object") return null;
|
|
8230
9106
|
const errField = chunk.error;
|
|
@@ -8605,6 +9481,7 @@ async function runToolLoop(options) {
|
|
|
8605
9481
|
thinking: redactor.deAnonymize(acc.thinking)
|
|
8606
9482
|
}) : strategy.buildFinalResponse(acc);
|
|
8607
9483
|
const finalContentText = (acc) => redactor ? redactor.deAnonymize(acc.content) : acc.content;
|
|
9484
|
+
let streamingAccumulator = null;
|
|
8608
9485
|
try {
|
|
8609
9486
|
let sseError = null;
|
|
8610
9487
|
let apiTools = toolsToApiFormat(tools, resolved);
|
|
@@ -8756,6 +9633,7 @@ async function runToolLoop(options) {
|
|
|
8756
9633
|
}
|
|
8757
9634
|
});
|
|
8758
9635
|
accumulator = createStreamAccumulator(model || void 0);
|
|
9636
|
+
streamingAccumulator = accumulator;
|
|
8759
9637
|
contentSmoother = new StreamSmoother((text16) => {
|
|
8760
9638
|
if (emitData) emitData(text16);
|
|
8761
9639
|
}, smoothing);
|
|
@@ -9266,6 +10144,7 @@ Executing tool: ${toolInfo}
|
|
|
9266
10144
|
}
|
|
9267
10145
|
});
|
|
9268
10146
|
currentAccumulator = createStreamAccumulator(model || void 0);
|
|
10147
|
+
streamingAccumulator = currentAccumulator;
|
|
9269
10148
|
contContentSmoother = new StreamSmoother((text16) => {
|
|
9270
10149
|
if (emitData) emitData(text16);
|
|
9271
10150
|
}, smoothing);
|
|
@@ -9463,7 +10342,7 @@ Executing tool: ${toolInfo}
|
|
|
9463
10342
|
const statusCode = err instanceof Error && "statusCode" in err ? err.statusCode : void 0;
|
|
9464
10343
|
const connectionLost = !(err instanceof ProviderStreamError) && !(err instanceof SseError);
|
|
9465
10344
|
return {
|
|
9466
|
-
data: null,
|
|
10345
|
+
data: connectionLost && streamingAccumulator ? buildResponseFinal(streamingAccumulator) : null,
|
|
9467
10346
|
error: errorMsg,
|
|
9468
10347
|
statusCode,
|
|
9469
10348
|
...connectionLost ? { connectionLost: true } : {}
|
|
@@ -9480,6 +10359,33 @@ function streamReplayPath(inferenceId) {
|
|
|
9480
10359
|
function streamCancelPath(inferenceId) {
|
|
9481
10360
|
return `${streamReplayPath(inferenceId)}/cancel`;
|
|
9482
10361
|
}
|
|
10362
|
+
var ACTIVE_STREAMS_PATH = "/api/v1/chat/streams/active";
|
|
10363
|
+
function toActiveStream(value) {
|
|
10364
|
+
if (!value || typeof value !== "object") return null;
|
|
10365
|
+
const v = value;
|
|
10366
|
+
const text16 = (key3) => typeof v[key3] === "string" && v[key3] ? v[key3] : null;
|
|
10367
|
+
const inferenceId = text16("inference_id");
|
|
10368
|
+
if (!inferenceId || v.api_type !== "responses" && v.api_type !== "completions") return null;
|
|
10369
|
+
return {
|
|
10370
|
+
inferenceId,
|
|
10371
|
+
assistantMessageId: text16("assistant_message_id"),
|
|
10372
|
+
conversationId: text16("conversation_id"),
|
|
10373
|
+
parentMessageId: text16("parent_message_id"),
|
|
10374
|
+
apiType: v.api_type,
|
|
10375
|
+
model: text16("model") ?? void 0,
|
|
10376
|
+
startedAt: text16("started_at") ?? ""
|
|
10377
|
+
};
|
|
10378
|
+
}
|
|
10379
|
+
async function listActiveStreams(options) {
|
|
10380
|
+
const query = options.conversationId ? `?conversation_id=${encodeURIComponent(options.conversationId)}` : "";
|
|
10381
|
+
const response = await (options.fetch ?? globalThis.fetch)(
|
|
10382
|
+
`${options.baseUrl ?? BASE_URL}${ACTIVE_STREAMS_PATH}${query}`,
|
|
10383
|
+
{ headers: { Authorization: `Bearer ${options.token}` } }
|
|
10384
|
+
);
|
|
10385
|
+
if (!response.ok) throw new Error(`Active streams request failed (${response.status})`);
|
|
10386
|
+
const body = await response.json();
|
|
10387
|
+
return Array.isArray(body.streams) ? body.streams.map(toActiveStream).filter((s) => s !== null) : [];
|
|
10388
|
+
}
|
|
9483
10389
|
var StreamExpiredError = class extends Error {
|
|
9484
10390
|
constructor(inferenceId, message) {
|
|
9485
10391
|
super(message ?? `The resumable stream "${inferenceId}" has expired or no longer exists`);
|
|
@@ -9923,118 +10829,19 @@ var xhrTransport = (options) => {
|
|
|
9923
10829
|
return { stream: createStream() };
|
|
9924
10830
|
};
|
|
9925
10831
|
|
|
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
|
-
}
|
|
10832
|
+
// src/lib/chatStore/types.ts
|
|
10833
|
+
var ChatStoreError = class extends Error {
|
|
10834
|
+
constructor(message, status, code, retryAfterMs) {
|
|
10835
|
+
super(message);
|
|
10836
|
+
this.status = status;
|
|
10837
|
+
this.code = code;
|
|
10838
|
+
this.retryAfterMs = retryAfterMs;
|
|
10839
|
+
this.name = "ChatStoreError";
|
|
9979
10840
|
}
|
|
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
10841
|
};
|
|
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
10842
|
|
|
10036
10843
|
// src/lib/db/conversationMemory/operations.ts
|
|
10037
|
-
import { Q as
|
|
10844
|
+
import { Q as Q3 } from "@nozbe/watermelondb";
|
|
10038
10845
|
var MAX_PER_CONVERSATION = 200;
|
|
10039
10846
|
function conversationMemoryToStored(row) {
|
|
10040
10847
|
return {
|
|
@@ -10049,7 +10856,7 @@ async function addConversationMemoriesOp(ctx, conversationId, items) {
|
|
|
10049
10856
|
if (!conversationId || items.length === 0) return;
|
|
10050
10857
|
const now = Date.now();
|
|
10051
10858
|
await ctx.database.write(async () => {
|
|
10052
|
-
const existing = await ctx.conversationMemoryCollection.query(
|
|
10859
|
+
const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10053
10860
|
const seen = new Set(existing.map((r) => r.memoryId));
|
|
10054
10861
|
const batchSeen = /* @__PURE__ */ new Set();
|
|
10055
10862
|
const deduped = [];
|
|
@@ -10086,7 +10893,7 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
|
|
|
10086
10893
|
if (!conversationId || turn.size === 0) return;
|
|
10087
10894
|
const kept = new Map([...turn].slice(-MAX_PER_CONVERSATION));
|
|
10088
10895
|
await ctx.database.write(async () => {
|
|
10089
|
-
const existing = await ctx.conversationMemoryCollection.query(
|
|
10896
|
+
const existing = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10090
10897
|
const earlier = existing.filter((r) => !turn.has(r.memoryId));
|
|
10091
10898
|
const linked = new Set(existing.map((r) => r.memoryId));
|
|
10092
10899
|
await ctx.database.batch(
|
|
@@ -10111,13 +10918,13 @@ async function recordConversationMemoriesOp(ctx, conversationId, items, now = Da
|
|
|
10111
10918
|
}
|
|
10112
10919
|
async function getConversationMemoriesOp(ctx, conversationId) {
|
|
10113
10920
|
if (!conversationId) return [];
|
|
10114
|
-
const rows = await ctx.conversationMemoryCollection.query(
|
|
10921
|
+
const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId), Q3.sortBy("created_at", Q3.asc)).fetch();
|
|
10115
10922
|
return rows.map(conversationMemoryToStored);
|
|
10116
10923
|
}
|
|
10117
10924
|
async function clearConversationMemoriesOp(ctx, conversationId) {
|
|
10118
10925
|
if (!conversationId) return;
|
|
10119
10926
|
await ctx.database.write(async () => {
|
|
10120
|
-
const rows = await ctx.conversationMemoryCollection.query(
|
|
10927
|
+
const rows = await ctx.conversationMemoryCollection.query(Q3.where("conversation_id", conversationId)).fetch();
|
|
10121
10928
|
if (rows.length === 0) return;
|
|
10122
10929
|
await ctx.database.batch(...rows.map((r) => r.prepareDestroyPermanently()));
|
|
10123
10930
|
});
|
|
@@ -10132,15 +10939,37 @@ var maxChunks = 16;
|
|
|
10132
10939
|
var droppable = ["thoughtProcess", "thinking", "sources", "toolCallEvents"];
|
|
10133
10940
|
var messageMetadata = /* @__PURE__ */ new Set(["conversationId", "uniqueId", "vector", "chunks"]);
|
|
10134
10941
|
var alwaysStored = /* @__PURE__ */ new Set(["role", "content", "responseDuration"]);
|
|
10942
|
+
var maxBubbles = 3;
|
|
10943
|
+
var maxTurnIdBytes = 128;
|
|
10944
|
+
var maxBubbleBytes = 16384;
|
|
10945
|
+
var anumaOrigins = /* @__PURE__ */ new Set(["intro", "reply"]);
|
|
10135
10946
|
function plain(value) {
|
|
10136
10947
|
return JSON.parse(JSON.stringify(value ?? {}));
|
|
10137
10948
|
}
|
|
10138
|
-
function
|
|
10949
|
+
function text3(value) {
|
|
10139
10950
|
return typeof value === "string" ? value : void 0;
|
|
10140
10951
|
}
|
|
10141
10952
|
function number(value) {
|
|
10142
10953
|
return typeof value === "number" ? value : void 0;
|
|
10143
10954
|
}
|
|
10955
|
+
function readAnumaTurn(value) {
|
|
10956
|
+
if (!isObject(value)) return void 0;
|
|
10957
|
+
const { turnId, bubbles, origin } = value;
|
|
10958
|
+
if (typeof turnId !== "string" || turnId.length === 0 || new TextEncoder().encode(turnId).length > maxTurnIdBytes)
|
|
10959
|
+
return void 0;
|
|
10960
|
+
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))
|
|
10961
|
+
return void 0;
|
|
10962
|
+
if (origin !== void 0 && (typeof origin !== "string" || !anumaOrigins.has(origin)))
|
|
10963
|
+
return void 0;
|
|
10964
|
+
return {
|
|
10965
|
+
turnId,
|
|
10966
|
+
...bubbles !== void 0 ? { bubbles: [...bubbles] } : {},
|
|
10967
|
+
...origin !== void 0 ? { origin } : {}
|
|
10968
|
+
};
|
|
10969
|
+
}
|
|
10970
|
+
function isObject(value) {
|
|
10971
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10972
|
+
}
|
|
10144
10973
|
function conversationSortKey(data, createdAt) {
|
|
10145
10974
|
const pinnedAt = number(data.pinnedAt);
|
|
10146
10975
|
if (pinnedAt) return PINNED_SORT_BASE + pinnedAt;
|
|
@@ -10161,12 +10990,12 @@ function conversationContext(data) {
|
|
|
10161
10990
|
}
|
|
10162
10991
|
function toConversation(record2) {
|
|
10163
10992
|
const pinnedAt = number(record2.data.pinnedAt);
|
|
10164
|
-
const projectId =
|
|
10993
|
+
const projectId = text3(record2.data.projectId);
|
|
10165
10994
|
const context = conversationContext(record2.data);
|
|
10166
10995
|
return {
|
|
10167
10996
|
uniqueId: record2.id,
|
|
10168
10997
|
conversationId: record2.id,
|
|
10169
|
-
title:
|
|
10998
|
+
title: text3(record2.data.title) ?? "",
|
|
10170
10999
|
...projectId ? { projectId } : {},
|
|
10171
11000
|
createdAt: new Date(record2.createdAt),
|
|
10172
11001
|
updatedAt: new Date(record2.updatedAt),
|
|
@@ -10175,18 +11004,28 @@ function toConversation(record2) {
|
|
|
10175
11004
|
...context ? { context } : {}
|
|
10176
11005
|
};
|
|
10177
11006
|
}
|
|
11007
|
+
function anumaTurnData(value) {
|
|
11008
|
+
if (value === void 0) return void 0;
|
|
11009
|
+
const turn = readAnumaTurn(value);
|
|
11010
|
+
if (!turn) throw new ChatStoreError("Invalid Anuma turn", 0, "invalid_record");
|
|
11011
|
+
return turn;
|
|
11012
|
+
}
|
|
10178
11013
|
function messageData(options) {
|
|
10179
|
-
|
|
10180
|
-
|
|
11014
|
+
const anuma = anumaTurnData(options.anuma);
|
|
11015
|
+
return plain({
|
|
11016
|
+
...Object.fromEntries(
|
|
10181
11017
|
Object.entries(options).filter(
|
|
10182
|
-
([key3, value]) => !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
|
|
11018
|
+
([key3, value]) => key3 !== "anuma" && !messageMetadata.has(key3) && (!!value || alwaysStored.has(key3))
|
|
10183
11019
|
)
|
|
10184
|
-
)
|
|
10185
|
-
|
|
11020
|
+
),
|
|
11021
|
+
...anuma ? { anuma } : {}
|
|
11022
|
+
});
|
|
10186
11023
|
}
|
|
10187
11024
|
function toMessage(record2) {
|
|
10188
|
-
const { chunkOffsets: _offsets, truncated: _truncated, ...fields } = record2.data;
|
|
11025
|
+
const { chunkOffsets: _offsets, truncated: _truncated, anuma: rawAnuma, ...fields } = record2.data;
|
|
11026
|
+
const anuma = readAnumaTurn(rawAnuma);
|
|
10189
11027
|
return {
|
|
11028
|
+
...anuma ? { anuma } : {},
|
|
10190
11029
|
...fields,
|
|
10191
11030
|
uniqueId: record2.id,
|
|
10192
11031
|
messageId: record2.sortKey,
|
|
@@ -10200,948 +11039,422 @@ function chunkOffsets(record2) {
|
|
|
10200
11039
|
return Array.isArray(offsets) ? offsets : void 0;
|
|
10201
11040
|
}
|
|
10202
11041
|
function toSkeletons(records) {
|
|
10203
|
-
const roleById = new Map(records.map((r) => [r.id,
|
|
10204
|
-
return records.map((r) => {
|
|
10205
|
-
const parentMessageId =
|
|
10206
|
-
const model =
|
|
10207
|
-
const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
|
|
10208
|
-
return {
|
|
10209
|
-
uniqueId: r.id,
|
|
10210
|
-
messageId: r.sortKey,
|
|
10211
|
-
conversationId: r.conversationId,
|
|
10212
|
-
role:
|
|
10213
|
-
createdAt: new Date(r.createdAt),
|
|
10214
|
-
...parentMessageId ? { parentMessageId } : {},
|
|
10215
|
-
...model ? { model } : {},
|
|
10216
|
-
...artifact ? { content:
|
|
10217
|
-
};
|
|
10218
|
-
});
|
|
10219
|
-
}
|
|
10220
|
-
function toSummary(record2) {
|
|
10221
|
-
return {
|
|
10222
|
-
uniqueId: record2.id,
|
|
10223
|
-
conversationId: record2.id,
|
|
10224
|
-
summary: text4(record2.data.summary) ?? "",
|
|
10225
|
-
summarizedUpTo: text4(record2.data.summarizedUpTo) ?? "",
|
|
10226
|
-
tokenCount: number(record2.data.tokenCount) ?? 0,
|
|
10227
|
-
createdAt: new Date(record2.createdAt),
|
|
10228
|
-
updatedAt: new Date(record2.updatedAt)
|
|
10229
|
-
};
|
|
10230
|
-
}
|
|
10231
|
-
function links(data) {
|
|
10232
|
-
return Array.isArray(data.links) ? data.links : [];
|
|
10233
|
-
}
|
|
10234
|
-
function applyLinkOps(data, ops) {
|
|
10235
|
-
let current = links(data);
|
|
10236
|
-
for (const op of ops) {
|
|
10237
|
-
if ("clear" in op) {
|
|
10238
|
-
current = [];
|
|
10239
|
-
continue;
|
|
10240
|
-
}
|
|
10241
|
-
const byId = new Map(current.map((l) => [l.memoryId, l]));
|
|
10242
|
-
const turn = /* @__PURE__ */ new Set();
|
|
10243
|
-
op.add.forEach((item, i) => {
|
|
10244
|
-
if (!item.memoryId || turn.has(item.memoryId)) return;
|
|
10245
|
-
turn.add(item.memoryId);
|
|
10246
|
-
const known = byId.get(item.memoryId);
|
|
10247
|
-
if (known && known.createdAt >= op.at + i) return;
|
|
10248
|
-
byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
|
|
10249
|
-
});
|
|
10250
|
-
current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
|
|
10251
|
-
}
|
|
10252
|
-
return plain({ links: current });
|
|
10253
|
-
}
|
|
10254
|
-
function toLinks(record2) {
|
|
10255
|
-
return links(record2.data).map((l) => ({
|
|
10256
|
-
uniqueId: `${record2.id}:${l.memoryId}`,
|
|
10257
|
-
conversationId: record2.id,
|
|
10258
|
-
memoryId: l.memoryId,
|
|
10259
|
-
score: l.score,
|
|
10260
|
-
createdAt: new Date(l.createdAt)
|
|
10261
|
-
}));
|
|
10262
|
-
}
|
|
10263
|
-
function bytes(value) {
|
|
10264
|
-
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
10265
|
-
}
|
|
10266
|
-
function fitRecord(record2) {
|
|
10267
|
-
if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
|
|
10268
|
-
const data = { ...record2.data };
|
|
10269
|
-
const truncated = [];
|
|
10270
|
-
for (const field10 of droppable) {
|
|
10271
|
-
if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
|
|
10272
|
-
if (data[field10] === void 0) continue;
|
|
10273
|
-
delete data[field10];
|
|
10274
|
-
truncated.push(field10);
|
|
10275
|
-
}
|
|
10276
|
-
const content = text4(data.content) ?? "";
|
|
10277
|
-
const over = bytes({ ...record2, data }) - recordBudgetBytes;
|
|
10278
|
-
if (over > 0) {
|
|
10279
|
-
const encoded = new TextEncoder().encode(content);
|
|
10280
|
-
data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
|
|
10281
|
-
truncated.push("content");
|
|
10282
|
-
}
|
|
10283
|
-
return { ...record2, data: { ...data, truncated } };
|
|
10284
|
-
}
|
|
10285
|
-
function acceptedVector(vector2) {
|
|
10286
|
-
if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
|
|
10287
|
-
if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims).some((x) => x !== 0))
|
|
10288
|
-
return void 0;
|
|
10289
|
-
return [...vector2];
|
|
10290
|
-
}
|
|
10291
|
-
function normalized(vector2) {
|
|
10292
|
-
const norm = Math.hypot(...vector2) || 1;
|
|
10293
|
-
return vector2.map((x) => x / norm);
|
|
10294
|
-
}
|
|
10295
|
-
function fitChunks(chunks3) {
|
|
10296
|
-
const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
|
|
10297
|
-
if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
|
|
10298
|
-
if (usable.length <= maxChunks) return usable;
|
|
10299
|
-
const out = [];
|
|
10300
|
-
for (let g = 0; g < maxChunks; g++) {
|
|
10301
|
-
const group = usable.slice(
|
|
10302
|
-
Math.floor(g * usable.length / maxChunks),
|
|
10303
|
-
Math.floor((g + 1) * usable.length / maxChunks)
|
|
10304
|
-
);
|
|
10305
|
-
const dims = group[0].vector.length;
|
|
10306
|
-
const mean = new Array(dims).fill(0);
|
|
10307
|
-
for (const c of group) {
|
|
10308
|
-
const unit = normalized(c.vector);
|
|
10309
|
-
for (let d = 0; d < dims; d++) mean[d] += unit[d];
|
|
10310
|
-
}
|
|
10311
|
-
out.push({
|
|
10312
|
-
vector: normalized(mean),
|
|
10313
|
-
startOffset: group[0].startOffset,
|
|
10314
|
-
endOffset: group[group.length - 1].endOffset
|
|
10315
|
-
});
|
|
10316
|
-
}
|
|
10317
|
-
return out;
|
|
10318
|
-
}
|
|
10319
|
-
|
|
10320
|
-
// src/lib/chatStore/chatImports.ts
|
|
10321
|
-
var importBatch = 100;
|
|
10322
|
-
var importChunks = 200;
|
|
10323
|
-
var importPlaintextBytes = 39e5;
|
|
10324
|
-
function ms(date10, fallback = 1) {
|
|
10325
|
-
const value = date10?.getTime();
|
|
10326
|
-
return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
|
|
10327
|
-
}
|
|
10328
|
-
function bytes2(value) {
|
|
10329
|
-
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
10330
|
-
}
|
|
10331
|
-
function conversationRecord(conversation, messages) {
|
|
10332
|
-
const createdAt = ms(conversation.createdAt);
|
|
10333
|
-
const data = plain({
|
|
10334
|
-
...contextFields(conversation.context),
|
|
10335
|
-
title: conversation.title,
|
|
10336
|
-
projectId: conversation.projectId || void 0,
|
|
10337
|
-
pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
|
|
10338
|
-
lastActivityAt: Math.max(
|
|
10339
|
-
ms(conversation.updatedAt, createdAt),
|
|
10340
|
-
...messages.map((m) => ms(m.createdAt))
|
|
10341
|
-
)
|
|
10342
|
-
});
|
|
10343
|
-
return {
|
|
10344
|
-
record: {
|
|
10345
|
-
kind: "conversation",
|
|
10346
|
-
id: conversation.conversationId,
|
|
10347
|
-
conversationId: "",
|
|
10348
|
-
sortKey: conversationSortKey(data, createdAt),
|
|
10349
|
-
createdAt,
|
|
10350
|
-
updatedAt: ms(conversation.updatedAt, createdAt),
|
|
10351
|
-
isDeleted: false,
|
|
10352
|
-
data
|
|
10353
|
-
}
|
|
10354
|
-
};
|
|
10355
|
-
}
|
|
10356
|
-
function messageRecord(message) {
|
|
10357
|
-
const {
|
|
10358
|
-
uniqueId: _uniqueId,
|
|
10359
|
-
messageId: _messageId,
|
|
10360
|
-
createdAt: _createdAt,
|
|
10361
|
-
updatedAt: _updatedAt,
|
|
10362
|
-
decryptionStatus: _status,
|
|
10363
|
-
chunks: chunks3,
|
|
10364
|
-
vector: vector2,
|
|
10365
|
-
...fields
|
|
10366
|
-
} = message;
|
|
10367
|
-
const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
|
|
10368
|
-
const single = fitted ? void 0 : acceptedVector(vector2);
|
|
10369
|
-
const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
|
|
10370
|
-
const record2 = fitRecord({
|
|
10371
|
-
kind: "message",
|
|
10372
|
-
id: message.uniqueId,
|
|
10373
|
-
conversationId: message.conversationId,
|
|
10374
|
-
sortKey: message.messageId,
|
|
10375
|
-
createdAt: ms(message.createdAt),
|
|
10376
|
-
updatedAt: ms(message.updatedAt, ms(message.createdAt)),
|
|
10377
|
-
isDeleted: false,
|
|
10378
|
-
data: {
|
|
10379
|
-
...messageData(fields),
|
|
10380
|
-
...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
|
|
10381
|
-
}
|
|
10382
|
-
});
|
|
10383
|
-
return {
|
|
10384
|
-
record: record2,
|
|
10385
|
-
...embeddings2 ? { embeddings: embeddings2 } : {},
|
|
10386
|
-
...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
|
|
10387
|
-
};
|
|
10388
|
-
}
|
|
10389
|
-
function batches(records) {
|
|
10390
|
-
const out = [];
|
|
10391
|
-
let batch = [];
|
|
10392
|
-
let chunks3 = 0;
|
|
10393
|
-
let size = 0;
|
|
10394
|
-
for (const item of records) {
|
|
10395
|
-
const vectors = item.embeddings?.length ?? 0;
|
|
10396
|
-
const itemSize = bytes2(item.record);
|
|
10397
|
-
if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
|
|
10398
|
-
out.push(batch);
|
|
10399
|
-
batch = [];
|
|
10400
|
-
chunks3 = 0;
|
|
10401
|
-
size = 0;
|
|
10402
|
-
}
|
|
10403
|
-
batch.push(item);
|
|
10404
|
-
chunks3 += vectors;
|
|
10405
|
-
size += itemSize;
|
|
10406
|
-
}
|
|
10407
|
-
if (batch.length) out.push(batch);
|
|
10408
|
-
return out;
|
|
10409
|
-
}
|
|
10410
|
-
|
|
10411
|
-
// src/lib/db/entities/types.ts
|
|
10412
|
-
var ENTITY_KINDS = [
|
|
10413
|
-
"person",
|
|
10414
|
-
"organization",
|
|
10415
|
-
"place",
|
|
10416
|
-
"event",
|
|
10417
|
-
"product",
|
|
10418
|
-
"thing",
|
|
10419
|
-
"concept",
|
|
10420
|
-
"other"
|
|
10421
|
-
];
|
|
10422
|
-
function normalizeEntityName(name) {
|
|
10423
|
-
return name.trim().toLowerCase();
|
|
11042
|
+
const roleById = new Map(records.map((r) => [r.id, text3(r.data.role)]));
|
|
11043
|
+
return records.map((r) => {
|
|
11044
|
+
const parentMessageId = text3(r.data.parentMessageId);
|
|
11045
|
+
const model = text3(r.data.model);
|
|
11046
|
+
const artifact = r.data.role === "user" && !!parentMessageId && roleById.get(parentMessageId) === "user";
|
|
11047
|
+
return {
|
|
11048
|
+
uniqueId: r.id,
|
|
11049
|
+
messageId: r.sortKey,
|
|
11050
|
+
conversationId: r.conversationId,
|
|
11051
|
+
role: text3(r.data.role) ?? "",
|
|
11052
|
+
createdAt: new Date(r.createdAt),
|
|
11053
|
+
...parentMessageId ? { parentMessageId } : {},
|
|
11054
|
+
...model ? { model } : {},
|
|
11055
|
+
...artifact ? { content: text3(r.data.content) ?? "" } : {}
|
|
11056
|
+
};
|
|
11057
|
+
});
|
|
10424
11058
|
}
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
11059
|
+
function toSummary(record2) {
|
|
11060
|
+
return {
|
|
11061
|
+
uniqueId: record2.id,
|
|
11062
|
+
conversationId: record2.id,
|
|
11063
|
+
summary: text3(record2.data.summary) ?? "",
|
|
11064
|
+
summarizedUpTo: text3(record2.data.summarizedUpTo) ?? "",
|
|
11065
|
+
tokenCount: number(record2.data.tokenCount) ?? 0,
|
|
11066
|
+
createdAt: new Date(record2.createdAt),
|
|
11067
|
+
updatedAt: new Date(record2.updatedAt)
|
|
11068
|
+
};
|
|
10428
11069
|
}
|
|
10429
|
-
function
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
11070
|
+
function links(data) {
|
|
11071
|
+
return Array.isArray(data.links) ? data.links : [];
|
|
11072
|
+
}
|
|
11073
|
+
function applyLinkOps(data, ops) {
|
|
11074
|
+
let current = links(data);
|
|
11075
|
+
for (const op of ops) {
|
|
11076
|
+
if ("clear" in op) {
|
|
11077
|
+
current = [];
|
|
11078
|
+
continue;
|
|
11079
|
+
}
|
|
11080
|
+
const byId = new Map(current.map((l) => [l.memoryId, l]));
|
|
11081
|
+
const turn = /* @__PURE__ */ new Set();
|
|
11082
|
+
op.add.forEach((item, i) => {
|
|
11083
|
+
if (!item.memoryId || turn.has(item.memoryId)) return;
|
|
11084
|
+
turn.add(item.memoryId);
|
|
11085
|
+
const known = byId.get(item.memoryId);
|
|
11086
|
+
if (known && known.createdAt >= op.at + i) return;
|
|
11087
|
+
byId.set(item.memoryId, { memoryId: item.memoryId, score: item.score, createdAt: op.at + i });
|
|
11088
|
+
});
|
|
11089
|
+
current = [...byId.values()].sort((a, b) => a.createdAt - b.createdAt).slice(-MAX_PER_CONVERSATION);
|
|
10433
11090
|
}
|
|
10434
|
-
return
|
|
11091
|
+
return plain({ links: current });
|
|
10435
11092
|
}
|
|
10436
|
-
function
|
|
10437
|
-
return
|
|
11093
|
+
function toLinks(record2) {
|
|
11094
|
+
return links(record2.data).map((l) => ({
|
|
11095
|
+
uniqueId: `${record2.id}:${l.memoryId}`,
|
|
11096
|
+
conversationId: record2.id,
|
|
11097
|
+
memoryId: l.memoryId,
|
|
11098
|
+
score: l.score,
|
|
11099
|
+
createdAt: new Date(l.createdAt)
|
|
11100
|
+
}));
|
|
10438
11101
|
}
|
|
10439
|
-
function
|
|
10440
|
-
return JSON.stringify(
|
|
11102
|
+
function bytes(value) {
|
|
11103
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
10441
11104
|
}
|
|
10442
|
-
function
|
|
10443
|
-
if (
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
11105
|
+
function fitRecord(record2) {
|
|
11106
|
+
if (record2.kind !== "message" || bytes(record2) <= recordBudgetBytes) return record2;
|
|
11107
|
+
const data = { ...record2.data };
|
|
11108
|
+
const truncated = [];
|
|
11109
|
+
for (const field10 of droppable) {
|
|
11110
|
+
if (bytes({ ...record2, data }) <= recordBudgetBytes) break;
|
|
11111
|
+
if (data[field10] === void 0) continue;
|
|
11112
|
+
delete data[field10];
|
|
11113
|
+
truncated.push(field10);
|
|
10449
11114
|
}
|
|
10450
|
-
|
|
10451
|
-
const
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
const parsedWeight = parseTopicWeight(weight);
|
|
10457
|
-
out.push({
|
|
10458
|
-
name,
|
|
10459
|
-
...typeof kind === "string" && kind.length > 0 ? { kind } : {},
|
|
10460
|
-
source: source === "user" ? "user" : "auto",
|
|
10461
|
-
...parsedWeight !== void 0 ? { weight: parsedWeight } : {}
|
|
10462
|
-
});
|
|
11115
|
+
const content = text3(data.content) ?? "";
|
|
11116
|
+
const over = bytes({ ...record2, data }) - recordBudgetBytes;
|
|
11117
|
+
if (over > 0) {
|
|
11118
|
+
const encoded = new TextEncoder().encode(content);
|
|
11119
|
+
data.content = new TextDecoder().decode(encoded.slice(0, Math.max(0, encoded.length - over - 64))).replace(/�$/, "");
|
|
11120
|
+
truncated.push("content");
|
|
10463
11121
|
}
|
|
10464
|
-
return
|
|
11122
|
+
return { ...record2, data: { ...data, truncated } };
|
|
10465
11123
|
}
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
throw new Error("Invalid nearby topic");
|
|
10472
|
-
return t;
|
|
11124
|
+
function acceptedVector(vector2) {
|
|
11125
|
+
if (!vector2 || !acceptedDims.has(vector2.length)) return void 0;
|
|
11126
|
+
if (!vector2.every(Number.isFinite) || !vector2.slice(0, storedDims).some((x) => x !== 0))
|
|
11127
|
+
return void 0;
|
|
11128
|
+
return [...vector2];
|
|
10473
11129
|
}
|
|
10474
|
-
function
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
const out = [];
|
|
10478
|
-
let cursor;
|
|
10479
|
-
do {
|
|
10480
|
-
const page = await request(
|
|
10481
|
-
"/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
|
|
10482
|
-
{}
|
|
10483
|
-
);
|
|
10484
|
-
if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
|
|
10485
|
-
throw new Error("Invalid nearby topic page");
|
|
10486
|
-
out.push(...page.items.map(topic));
|
|
10487
|
-
cursor = page.next_cursor;
|
|
10488
|
-
} while (cursor);
|
|
10489
|
-
return out;
|
|
10490
|
-
},
|
|
10491
|
-
putTopic: async (value, expectedVersion) => {
|
|
10492
|
-
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
10493
|
-
throw new Error("Expected topic version must be a nonnegative integer");
|
|
10494
|
-
return topic(
|
|
10495
|
-
await request("/topics", {
|
|
10496
|
-
method: "PUT",
|
|
10497
|
-
body: JSON.stringify({ topic: value, expected_version: expectedVersion })
|
|
10498
|
-
})
|
|
10499
|
-
);
|
|
10500
|
-
},
|
|
10501
|
-
importTopics: async (topics, signal) => {
|
|
10502
|
-
for (let i = 0; i < topics.length; i += 50) {
|
|
10503
|
-
signal?.throwIfAborted();
|
|
10504
|
-
await request(
|
|
10505
|
-
"/topics/import",
|
|
10506
|
-
{
|
|
10507
|
-
method: "POST",
|
|
10508
|
-
body: JSON.stringify({ topics: topics.slice(i, i + 50) })
|
|
10509
|
-
},
|
|
10510
|
-
signal
|
|
10511
|
-
);
|
|
10512
|
-
}
|
|
10513
|
-
signal?.throwIfAborted();
|
|
10514
|
-
}
|
|
10515
|
-
};
|
|
11130
|
+
function normalized(vector2) {
|
|
11131
|
+
const norm = Math.hypot(...vector2) || 1;
|
|
11132
|
+
return vector2.map((x) => x / norm);
|
|
10516
11133
|
}
|
|
10517
|
-
|
|
10518
|
-
const
|
|
10519
|
-
if (!
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
11134
|
+
function fitChunks(chunks3) {
|
|
11135
|
+
const usable = chunks3.map((c) => ({ ...c, vector: acceptedVector(c.vector) }));
|
|
11136
|
+
if (usable.length === 0 || usable.some((c) => !c.vector)) return void 0;
|
|
11137
|
+
if (usable.length <= maxChunks) return usable;
|
|
11138
|
+
const out = [];
|
|
11139
|
+
for (let g = 0; g < maxChunks; g++) {
|
|
11140
|
+
const group = usable.slice(
|
|
11141
|
+
Math.floor(g * usable.length / maxChunks),
|
|
11142
|
+
Math.floor((g + 1) * usable.length / maxChunks)
|
|
10526
11143
|
);
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
const
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
return canonical;
|
|
10533
|
-
}
|
|
10534
|
-
|
|
10535
|
-
// src/lib/memory/store/remoteWork.ts
|
|
10536
|
-
function createRemoteWorkPersistence(request) {
|
|
10537
|
-
return {
|
|
10538
|
-
claimWork: async (kind, processorVersion, token) => {
|
|
10539
|
-
const result = await request("/work/claim", {
|
|
10540
|
-
method: "POST",
|
|
10541
|
-
body: JSON.stringify({
|
|
10542
|
-
kind,
|
|
10543
|
-
processor_version: processorVersion,
|
|
10544
|
-
...token && { token }
|
|
10545
|
-
})
|
|
10546
|
-
});
|
|
10547
|
-
if (!result || typeof result !== "object") throw new Error("Invalid nearby work lease");
|
|
10548
|
-
const lease = result.lease;
|
|
10549
|
-
if (lease === void 0) return null;
|
|
10550
|
-
if (!lease || lease.kind !== kind || typeof lease.token !== "string" || !lease.token || lease.processor_version !== processorVersion || typeof lease.expires_at !== "string" || !Number.isFinite(Date.parse(lease.expires_at)))
|
|
10551
|
-
throw new Error("Invalid nearby work lease");
|
|
10552
|
-
return lease;
|
|
10553
|
-
},
|
|
10554
|
-
releaseWork: async (kind, token) => {
|
|
10555
|
-
await request("/work/release", { method: "POST", body: JSON.stringify({ kind, token }) });
|
|
11144
|
+
const dims = group[0].vector.length;
|
|
11145
|
+
const mean = new Array(dims).fill(0);
|
|
11146
|
+
for (const c of group) {
|
|
11147
|
+
const unit = normalized(c.vector);
|
|
11148
|
+
for (let d = 0; d < dims; d++) mean[d] += unit[d];
|
|
10556
11149
|
}
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
if (!lease) return null;
|
|
10562
|
-
const controller = new AbortController();
|
|
10563
|
-
const leaseLostError = new RemoteMemoryError("Maintenance lease lost", 409, "work_lease_expired");
|
|
10564
|
-
const cancelled = new Promise((_resolve, reject) => {
|
|
10565
|
-
controller.signal.addEventListener("abort", () => reject(leaseLostError), {
|
|
10566
|
-
once: true
|
|
10567
|
-
});
|
|
10568
|
-
});
|
|
10569
|
-
let renewing;
|
|
10570
|
-
const timer = setInterval(() => {
|
|
10571
|
-
if (renewing || controller.signal.aborted) return;
|
|
10572
|
-
renewing = persistence.claimWork(kind, processorVersion, lease.token).then((renewed) => {
|
|
10573
|
-
if (!renewed) controller.abort(leaseLostError);
|
|
10574
|
-
}).catch((error) => getLogger().warn("[memory/work] Lease renewal failed:", error)).finally(() => {
|
|
10575
|
-
renewing = void 0;
|
|
11150
|
+
out.push({
|
|
11151
|
+
vector: normalized(mean),
|
|
11152
|
+
startOffset: group[0].startOffset,
|
|
11153
|
+
endOffset: group[group.length - 1].endOffset
|
|
10576
11154
|
});
|
|
10577
|
-
}, 3e4);
|
|
10578
|
-
try {
|
|
10579
|
-
const result = await Promise.race([task(lease.token, controller.signal), cancelled]);
|
|
10580
|
-
await renewing;
|
|
10581
|
-
return controller.signal.aborted ? null : result;
|
|
10582
|
-
} catch (error) {
|
|
10583
|
-
if (controller.signal.aborted || isRemoteWorkLeaseExpired(error)) return null;
|
|
10584
|
-
throw error;
|
|
10585
|
-
} finally {
|
|
10586
|
-
clearInterval(timer);
|
|
10587
|
-
await renewing;
|
|
10588
|
-
await persistence.releaseWork(kind, lease.token).catch((error) => getLogger().warn("[memory/work] Lease release failed:", error));
|
|
10589
11155
|
}
|
|
10590
|
-
|
|
10591
|
-
function isRemoteWorkLeaseExpired(error) {
|
|
10592
|
-
return error instanceof RemoteMemoryError && error.code === "work_lease_expired";
|
|
11156
|
+
return out;
|
|
10593
11157
|
}
|
|
10594
11158
|
|
|
10595
|
-
// src/lib/
|
|
10596
|
-
var
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
this.code = code;
|
|
10601
|
-
this.retryAfterMs = retryAfterMs;
|
|
10602
|
-
this.rateLimitBucket = rateLimitBucket;
|
|
10603
|
-
this.name = "RemoteMemoryError";
|
|
10604
|
-
}
|
|
10605
|
-
};
|
|
10606
|
-
var ciphertextPattern = /^enc:v\d+:[0-9a-f]+$/i;
|
|
10607
|
-
var memoryFields = {
|
|
10608
|
-
memory_id: true,
|
|
10609
|
-
content: true,
|
|
10610
|
-
scope: true,
|
|
10611
|
-
created_at: true,
|
|
10612
|
-
updated_at: true,
|
|
10613
|
-
is_deleted: true,
|
|
10614
|
-
embedding: true,
|
|
10615
|
-
embedding_model: true,
|
|
10616
|
-
kind: true,
|
|
10617
|
-
kind_value: true,
|
|
10618
|
-
level: true,
|
|
10619
|
-
folder_id: true,
|
|
10620
|
-
source_chunk_ids: true,
|
|
10621
|
-
proof_count: true,
|
|
10622
|
-
source: true,
|
|
10623
|
-
event_time_start: true,
|
|
10624
|
-
event_time_end: true,
|
|
10625
|
-
event_time_kind: true,
|
|
10626
|
-
topics_user_managed: true,
|
|
10627
|
-
topics: true,
|
|
10628
|
-
topics_updated_at: true,
|
|
10629
|
-
media: true,
|
|
10630
|
-
topics_extracted_at: true,
|
|
10631
|
-
topics_extracted_version: true,
|
|
10632
|
-
superseded_by: true,
|
|
10633
|
-
superseded_at: true,
|
|
10634
|
-
last_observed_at: true,
|
|
10635
|
-
fact_type: true,
|
|
10636
|
-
archived_at: true,
|
|
10637
|
-
trust_tier: true,
|
|
10638
|
-
visibility: true,
|
|
10639
|
-
twin_opt_in: true,
|
|
10640
|
-
published_at: true,
|
|
10641
|
-
geohash: true
|
|
10642
|
-
};
|
|
10643
|
-
var candidateListLimits = [
|
|
10644
|
-
["scopes", 20],
|
|
10645
|
-
["fact_types", 20],
|
|
10646
|
-
["memory_ids", 1e3],
|
|
10647
|
-
["force_ids", 100]
|
|
10648
|
-
];
|
|
10649
|
-
var queryListLimits = [
|
|
10650
|
-
["scopes", 20],
|
|
10651
|
-
["fact_types", 20],
|
|
10652
|
-
["visibility", 20],
|
|
10653
|
-
["memory_ids", 1e3],
|
|
10654
|
-
["topics", 100]
|
|
10655
|
-
];
|
|
10656
|
-
var queryFields = [
|
|
10657
|
-
"include_deleted",
|
|
10658
|
-
"include_archived",
|
|
10659
|
-
"include_quarantined",
|
|
10660
|
-
"include_superseded",
|
|
10661
|
-
"archived_only",
|
|
10662
|
-
"scopes",
|
|
10663
|
-
"fact_types",
|
|
10664
|
-
"visibility",
|
|
10665
|
-
"memory_ids",
|
|
10666
|
-
"topics",
|
|
10667
|
-
"updated_after",
|
|
10668
|
-
"topics_due",
|
|
10669
|
-
"topics_extracted",
|
|
10670
|
-
"event_time_overlaps",
|
|
10671
|
-
"order",
|
|
10672
|
-
"cursor",
|
|
10673
|
-
"limit",
|
|
10674
|
-
"include_embeddings"
|
|
10675
|
-
];
|
|
10676
|
-
function record(value) {
|
|
10677
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11159
|
+
// src/lib/chatStore/anuma.ts
|
|
11160
|
+
var ANUMA_CONVERSATION_ID = "anuma-1on1";
|
|
11161
|
+
var maxAttempts = 3;
|
|
11162
|
+
function anumaTurnContent(bubbles) {
|
|
11163
|
+
return bubbles.join("\n\n");
|
|
10678
11164
|
}
|
|
10679
|
-
function
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
11165
|
+
function anumaLastReadSortKey(conversation) {
|
|
11166
|
+
const record2 = conversation && "record" in conversation ? conversation.record : conversation;
|
|
11167
|
+
const state = record2?.data.anuma;
|
|
11168
|
+
if (!isObject(state)) return 0;
|
|
11169
|
+
const sortKey = state.lastReadSortKey;
|
|
11170
|
+
return Number.isSafeInteger(sortKey) && sortKey > 0 ? sortKey : 0;
|
|
10684
11171
|
}
|
|
10685
|
-
function
|
|
10686
|
-
|
|
10687
|
-
if (!/^https?:$/.test(base.protocol) || base.username || base.password || base.search || base.hash) {
|
|
10688
|
-
throw new Error(
|
|
10689
|
-
"baseUrl must be an HTTP(S) origin or base path without credentials, query or fragment"
|
|
10690
|
-
);
|
|
10691
|
-
}
|
|
10692
|
-
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
10693
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
10694
|
-
throw new Error("timeoutMs must be a positive number");
|
|
10695
|
-
const root = base.href.replace(/\/$/, "") + apiPath;
|
|
10696
|
-
const fetcher = options.fetch ?? globalThis.fetch;
|
|
10697
|
-
const cooldowns = /* @__PURE__ */ new Map();
|
|
10698
|
-
const memoryBudget = (path, method) => {
|
|
10699
|
-
if (method === "PUT")
|
|
10700
|
-
return path === "/topics" ? "private-memory-import" : "private-memory-write";
|
|
10701
|
-
if (method === "POST" && ["/import", "/memories/batch", "/topics/import"].includes(path))
|
|
10702
|
-
return "private-memory-import";
|
|
10703
|
-
return "private-memory-read";
|
|
10704
|
-
};
|
|
10705
|
-
const request = async (path, init = {}, signal) => {
|
|
10706
|
-
const controller = new AbortController();
|
|
10707
|
-
let timedOut = false;
|
|
10708
|
-
const timer = setTimeout(() => {
|
|
10709
|
-
timedOut = true;
|
|
10710
|
-
controller.abort();
|
|
10711
|
-
}, timeoutMs);
|
|
10712
|
-
const forwardAbort = () => controller.abort(signal?.reason);
|
|
10713
|
-
if (signal?.aborted) forwardAbort();
|
|
10714
|
-
else signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
10715
|
-
let rejectAborted;
|
|
10716
|
-
const aborted2 = new Promise((_, reject) => {
|
|
10717
|
-
rejectAborted = () => reject(
|
|
10718
|
-
controller.signal.reason instanceof Error ? controller.signal.reason : new Error("Nearby request aborted")
|
|
10719
|
-
);
|
|
10720
|
-
if (controller.signal.aborted) rejectAborted();
|
|
10721
|
-
else controller.signal.addEventListener("abort", rejectAborted, { once: true });
|
|
10722
|
-
});
|
|
10723
|
-
aborted2.catch(() => void 0);
|
|
10724
|
-
try {
|
|
10725
|
-
const token = await Promise.race([options.getToken(), aborted2]);
|
|
10726
|
-
if (controller.signal.aborted) await aborted2;
|
|
10727
|
-
if (!token) throw new RemoteMemoryError("Authentication required", 401);
|
|
10728
|
-
const pathname = path.split("?")[0];
|
|
10729
|
-
const route = `${init.method ?? "GET"} ${pathname}`;
|
|
10730
|
-
const budgets = ["*", route];
|
|
10731
|
-
if (apiPath === "/api/private-memories")
|
|
10732
|
-
budgets.push("private-memory-ip", memoryBudget(pathname, init.method ?? "GET"));
|
|
10733
|
-
for (const bucket of budgets) {
|
|
10734
|
-
const cooldown = cooldowns.get(bucket);
|
|
10735
|
-
if (!cooldown) continue;
|
|
10736
|
-
const remaining = cooldown.until - Date.now();
|
|
10737
|
-
if (remaining > 0)
|
|
10738
|
-
throw new RemoteMemoryError(
|
|
10739
|
-
cooldown.error.message,
|
|
10740
|
-
429,
|
|
10741
|
-
cooldown.error.code,
|
|
10742
|
-
remaining,
|
|
10743
|
-
cooldown.error.rateLimitBucket
|
|
10744
|
-
);
|
|
10745
|
-
cooldowns.delete(bucket);
|
|
10746
|
-
}
|
|
10747
|
-
const response = await fetcher(root + path, {
|
|
10748
|
-
...init,
|
|
10749
|
-
headers: {
|
|
10750
|
-
Authorization: `Bearer ${token}`,
|
|
10751
|
-
...init.body ? { "Content-Type": "application/json" } : {}
|
|
10752
|
-
},
|
|
10753
|
-
redirect: "error",
|
|
10754
|
-
signal: controller.signal
|
|
10755
|
-
});
|
|
10756
|
-
if (!response.ok) {
|
|
10757
|
-
const body = await response.json().catch(() => null);
|
|
10758
|
-
const retryAfter = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
10759
|
-
const error = new RemoteMemoryError(
|
|
10760
|
-
record(body) && typeof body.detail === "string" ? body.detail : `Nearby request failed (${response.status})`,
|
|
10761
|
-
response.status,
|
|
10762
|
-
record(body) && typeof body.code === "string" ? body.code : void 0,
|
|
10763
|
-
retryAfter ?? void 0,
|
|
10764
|
-
record(body) && typeof body.rate_limit_bucket === "string" ? body.rate_limit_bucket : void 0
|
|
10765
|
-
);
|
|
10766
|
-
if (response.status === 429 && retryAfter !== null && retryAfter > 0 && (!error.code || error.code === "rate_limited")) {
|
|
10767
|
-
const bucket = error.rateLimitBucket && budgets.includes(error.rateLimitBucket) ? error.rateLimitBucket : record(body) && (body.limit_scope === "account" || body.limit_scope === "ip") && !error.rateLimitBucket ? "*" : route;
|
|
10768
|
-
cooldowns.set(bucket, { until: Date.now() + retryAfter, error });
|
|
10769
|
-
}
|
|
10770
|
-
throw error;
|
|
10771
|
-
}
|
|
10772
|
-
if (response.status === 204) return null;
|
|
10773
|
-
return await response.json();
|
|
10774
|
-
} catch (error) {
|
|
10775
|
-
if (timedOut) throw new RemoteMemoryError("Nearby request timed out", 408, "timeout");
|
|
10776
|
-
throw error;
|
|
10777
|
-
} finally {
|
|
10778
|
-
clearTimeout(timer);
|
|
10779
|
-
signal?.removeEventListener("abort", forwardAbort);
|
|
10780
|
-
if (rejectAborted) controller.signal.removeEventListener("abort", rejectAborted);
|
|
10781
|
-
}
|
|
10782
|
-
};
|
|
10783
|
-
return request;
|
|
11172
|
+
function conflict(error) {
|
|
11173
|
+
return error instanceof ChatStoreError && error.code === "version_conflict";
|
|
10784
11174
|
}
|
|
10785
|
-
|
|
10786
|
-
if (
|
|
10787
|
-
throw new
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
const plaintext = await options.decrypt(ciphertext);
|
|
10811
|
-
if (plaintext === ciphertext) throw new Error("Memory decryption failed");
|
|
10812
|
-
return plaintext;
|
|
10813
|
-
};
|
|
10814
|
-
const decoded = {
|
|
10815
|
-
...item,
|
|
10816
|
-
memory: {
|
|
10817
|
-
...item.memory,
|
|
10818
|
-
content: item.memory.is_deleted && item.memory.content === "" ? "" : await decrypt(item.memory.content),
|
|
10819
|
-
...item.memory.kind_value !== void 0 && {
|
|
10820
|
-
kind_value: await decrypt(item.memory.kind_value)
|
|
10821
|
-
}
|
|
10822
|
-
}
|
|
10823
|
-
};
|
|
10824
|
-
return remember(decoded, item);
|
|
10825
|
-
};
|
|
10826
|
-
const remember = (decoded, item) => {
|
|
10827
|
-
snapshots.set(decoded, {
|
|
10828
|
-
id: item.memory.memory_id,
|
|
10829
|
-
version: item.version,
|
|
10830
|
-
content: decoded.memory.content,
|
|
10831
|
-
kindValue: decoded.memory.kind_value,
|
|
10832
|
-
encryptedContent: item.memory.content,
|
|
10833
|
-
encryptedKindValue: item.memory.kind_value
|
|
10834
|
-
});
|
|
10835
|
-
return decoded;
|
|
10836
|
-
};
|
|
10837
|
-
const decodeAll = async (values) => {
|
|
10838
|
-
const records = values.map(memoryRecord);
|
|
10839
|
-
const settled = await Promise.allSettled(records.map(decode));
|
|
10840
|
-
const items = [];
|
|
10841
|
-
const failed = [];
|
|
10842
|
-
settled.forEach((result, i) => {
|
|
10843
|
-
if (result.status === "fulfilled") items.push(result.value);
|
|
10844
|
-
else
|
|
10845
|
-
failed.push({
|
|
10846
|
-
memory_id: records[i].memory.memory_id,
|
|
10847
|
-
version: records[i].version,
|
|
10848
|
-
error: result.reason instanceof Error ? result.reason : new Error(String(result.reason))
|
|
10849
|
-
});
|
|
10850
|
-
});
|
|
10851
|
-
return { items, failed };
|
|
10852
|
-
};
|
|
10853
|
-
const decodeCommitted = async (value, write) => {
|
|
10854
|
-
const item = memoryRecord(value);
|
|
10855
|
-
if (item.memory.memory_id !== write.body.memory.memory_id || item.memory.content !== write.body.memory.content || item.memory.kind_value !== write.body.memory.kind_value)
|
|
10856
|
-
return decode(item);
|
|
10857
|
-
return remember(
|
|
10858
|
-
{
|
|
10859
|
-
...item,
|
|
10860
|
-
memory: {
|
|
10861
|
-
...item.memory,
|
|
10862
|
-
content: write.content,
|
|
10863
|
-
...write.kindValue !== void 0 && { kind_value: write.kindValue }
|
|
11175
|
+
function live(snapshot) {
|
|
11176
|
+
if (snapshot?.record.isDeleted)
|
|
11177
|
+
throw new ChatStoreError("Anuma conversation was deleted", 409, "conversation_deleted");
|
|
11178
|
+
return snapshot;
|
|
11179
|
+
}
|
|
11180
|
+
async function ensureAnumaConversation(store, options = {}) {
|
|
11181
|
+
const existing = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
|
|
11182
|
+
if (existing) return existing;
|
|
11183
|
+
const at = (options.now ?? Date.now)();
|
|
11184
|
+
const data = plain({ title: "Anuma", lastActivityAt: at, anuma: { lastReadSortKey: 0 } });
|
|
11185
|
+
try {
|
|
11186
|
+
const [created] = await store.putMany(
|
|
11187
|
+
[
|
|
11188
|
+
{
|
|
11189
|
+
expectedVersion: 0,
|
|
11190
|
+
record: {
|
|
11191
|
+
kind: "conversation",
|
|
11192
|
+
id: ANUMA_CONVERSATION_ID,
|
|
11193
|
+
conversationId: "",
|
|
11194
|
+
sortKey: conversationSortKey(data, at),
|
|
11195
|
+
createdAt: at,
|
|
11196
|
+
updatedAt: at,
|
|
11197
|
+
isDeleted: false,
|
|
11198
|
+
data
|
|
11199
|
+
}
|
|
10864
11200
|
}
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
);
|
|
10868
|
-
};
|
|
10869
|
-
const encryptField2 = async (plaintext) => {
|
|
10870
|
-
const ciphertext = await options.encrypt(plaintext);
|
|
10871
|
-
if (ciphertext === plaintext || !ciphertextPattern.test(ciphertext))
|
|
10872
|
-
throw new Error("Field encryption must return SDK ciphertext; refusing to upload plaintext");
|
|
10873
|
-
return ciphertext;
|
|
10874
|
-
};
|
|
10875
|
-
const memoryPath = (id2) => {
|
|
10876
|
-
if (!id2 || id2.length > 128) throw new Error("memory_id must be 1\u2013128 characters");
|
|
10877
|
-
return "/memories/" + encodeURIComponent(id2);
|
|
10878
|
-
};
|
|
10879
|
-
const prepareWrite = async (memory, expectedVersion, signal) => {
|
|
10880
|
-
const unknown = Object.keys(memory).filter(
|
|
10881
|
-
(key3) => !Object.prototype.hasOwnProperty.call(memoryFields, key3)
|
|
11201
|
+
],
|
|
11202
|
+
options.signal
|
|
10882
11203
|
);
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
|
|
10887
|
-
|
|
11204
|
+
return created;
|
|
11205
|
+
} catch (error) {
|
|
11206
|
+
if (!conflict(error)) throw error;
|
|
11207
|
+
const winner = live(await store.get("conversation", ANUMA_CONVERSATION_ID, options.signal));
|
|
11208
|
+
if (!winner) throw error;
|
|
11209
|
+
return winner;
|
|
11210
|
+
}
|
|
11211
|
+
}
|
|
11212
|
+
async function markAnumaRead(store, sortKey, options = {}) {
|
|
11213
|
+
if (!Number.isSafeInteger(sortKey) || sortKey < 0)
|
|
11214
|
+
throw new ChatStoreError("Invalid Anuma read marker", 0, "invalid_record");
|
|
11215
|
+
const now = options.now ?? Date.now;
|
|
11216
|
+
for (let attempt = 1; ; attempt++) {
|
|
11217
|
+
const current = await ensureAnumaConversation(store, options);
|
|
11218
|
+
const read = anumaLastReadSortKey(current);
|
|
11219
|
+
if (sortKey <= read) return current;
|
|
11220
|
+
const record2 = current.record;
|
|
11221
|
+
const state = isObject(record2.data.anuma) ? record2.data.anuma : {};
|
|
11222
|
+
const data = plain({ ...record2.data, anuma: { ...state, lastReadSortKey: sortKey } });
|
|
11223
|
+
try {
|
|
11224
|
+
const [written] = await store.putMany(
|
|
11225
|
+
[
|
|
11226
|
+
{
|
|
11227
|
+
expectedVersion: current.version,
|
|
11228
|
+
record: {
|
|
11229
|
+
...record2,
|
|
11230
|
+
data,
|
|
11231
|
+
sortKey: conversationSortKey(data, record2.createdAt),
|
|
11232
|
+
updatedAt: Math.max(record2.updatedAt, now())
|
|
11233
|
+
}
|
|
11234
|
+
}
|
|
11235
|
+
],
|
|
11236
|
+
options.signal
|
|
10888
11237
|
);
|
|
11238
|
+
return written;
|
|
11239
|
+
} catch (error) {
|
|
11240
|
+
if (!conflict(error) || attempt >= maxAttempts) throw error;
|
|
10889
11241
|
}
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
function countUnreadAnumaTurns(messages, lastReadSortKey) {
|
|
11245
|
+
const answered = Math.max(
|
|
11246
|
+
0,
|
|
11247
|
+
...messages.filter((m) => m.role === "user").map((m) => m.messageId)
|
|
11248
|
+
);
|
|
11249
|
+
const floor = Math.max(lastReadSortKey, answered);
|
|
11250
|
+
const turns = /* @__PURE__ */ new Set();
|
|
11251
|
+
for (const m of messages) {
|
|
11252
|
+
if (m.role !== "assistant" || m.messageId <= floor) continue;
|
|
11253
|
+
turns.add(m.anuma?.turnId ? `turn:${m.anuma.turnId}` : `message:${m.uniqueId}`);
|
|
11254
|
+
}
|
|
11255
|
+
return turns.size;
|
|
11256
|
+
}
|
|
11257
|
+
|
|
11258
|
+
// src/lib/db/chat/summaryOperations.ts
|
|
11259
|
+
import { Q as Q4 } from "@nozbe/watermelondb";
|
|
11260
|
+
function createSummaryContext(database, encryption = {}) {
|
|
11261
|
+
return {
|
|
11262
|
+
...encryption,
|
|
11263
|
+
database,
|
|
11264
|
+
summariesCollection: database.get("conversation_summaries")
|
|
11265
|
+
};
|
|
11266
|
+
}
|
|
11267
|
+
function modelToStored(record2, summary) {
|
|
11268
|
+
return {
|
|
11269
|
+
uniqueId: record2.id,
|
|
11270
|
+
conversationId: record2.conversationId,
|
|
11271
|
+
summary,
|
|
11272
|
+
summarizedUpTo: record2.summarizedUpTo,
|
|
11273
|
+
tokenCount: record2.tokenCount,
|
|
11274
|
+
createdAt: record2.createdAt,
|
|
11275
|
+
updatedAt: record2.updatedAt
|
|
11276
|
+
};
|
|
11277
|
+
}
|
|
11278
|
+
async function getConversationSummaryOp(ctx, conversationId) {
|
|
11279
|
+
const results = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
11280
|
+
if (results.length === 0) return null;
|
|
11281
|
+
const record2 = results[0];
|
|
11282
|
+
const stored2 = modelToStored(record2, record2.summary);
|
|
11283
|
+
if (ctx.walletAddress && isEncrypted(stored2.summary)) {
|
|
11284
|
+
if (ctx.signMessage) {
|
|
10903
11285
|
try {
|
|
10904
|
-
|
|
11286
|
+
await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
|
|
10905
11287
|
} catch (error) {
|
|
10906
|
-
|
|
10907
|
-
throw error;
|
|
11288
|
+
getLogger().warn("Failed to request the encryption key for the summary:", error);
|
|
10908
11289
|
}
|
|
10909
11290
|
}
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
const values = candidateOptions[key3];
|
|
10930
|
-
if (values && values.length > max)
|
|
10931
|
-
throw new Error(`Candidate ${key3} accepts at most ${max} entries (got ${values.length})`);
|
|
11291
|
+
stored2.summary = await decryptField(stored2.summary, ctx.walletAddress);
|
|
11292
|
+
} else if (ctx.walletAddress && ctx.signMessage && stored2.summary) {
|
|
11293
|
+
try {
|
|
11294
|
+
const encryptedSummary = await encryptField(
|
|
11295
|
+
stored2.summary,
|
|
11296
|
+
ctx.walletAddress,
|
|
11297
|
+
ctx.signMessage,
|
|
11298
|
+
ctx.embeddedWalletSigner
|
|
11299
|
+
);
|
|
11300
|
+
await ctx.database.write(async () => {
|
|
11301
|
+
const current = await ctx.summariesCollection.query(Q4.where("id", record2.id)).fetch();
|
|
11302
|
+
if (current[0]?.summary === stored2.summary) {
|
|
11303
|
+
await current[0].update((rec) => {
|
|
11304
|
+
rec.summary = encryptedSummary;
|
|
11305
|
+
});
|
|
11306
|
+
}
|
|
11307
|
+
});
|
|
11308
|
+
} catch (error) {
|
|
11309
|
+
getLogger().warn("Failed to encrypt the legacy summary:", error);
|
|
10932
11310
|
}
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
11311
|
+
}
|
|
11312
|
+
return stored2;
|
|
11313
|
+
}
|
|
11314
|
+
async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
|
|
11315
|
+
const revision2 = getConversationRevision(ctx.database, conversationId);
|
|
11316
|
+
let record2;
|
|
11317
|
+
const encryptedSummary = ctx.walletAddress ? await encryptField(summary, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner) : summary;
|
|
11318
|
+
await ctx.database.write(async () => {
|
|
11319
|
+
assertConversationRevision(ctx.database, conversationId, revision2);
|
|
11320
|
+
const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
11321
|
+
if (existing.length > 0) {
|
|
11322
|
+
record2 = await existing[0].update((rec) => {
|
|
11323
|
+
rec.summary = encryptedSummary;
|
|
11324
|
+
rec.summarizedUpTo = summarizedUpTo;
|
|
11325
|
+
rec.tokenCount = tokenCount;
|
|
11326
|
+
});
|
|
11327
|
+
} else {
|
|
11328
|
+
record2 = await ctx.summariesCollection.create((rec) => {
|
|
11329
|
+
rec.conversationId = conversationId;
|
|
11330
|
+
rec.summary = encryptedSummary;
|
|
11331
|
+
rec.summarizedUpTo = summarizedUpTo;
|
|
11332
|
+
rec.tokenCount = tokenCount;
|
|
11333
|
+
});
|
|
11334
|
+
}
|
|
11335
|
+
});
|
|
11336
|
+
return modelToStored(record2, summary);
|
|
11337
|
+
}
|
|
11338
|
+
async function deleteConversationSummaryOp(ctx, conversationId) {
|
|
11339
|
+
await ctx.database.write(async () => {
|
|
11340
|
+
const existing = await ctx.summariesCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
11341
|
+
for (const record2 of existing) {
|
|
11342
|
+
await record2.destroyPermanently();
|
|
11343
|
+
}
|
|
11344
|
+
});
|
|
11345
|
+
}
|
|
11346
|
+
|
|
11347
|
+
// src/lib/db/conversationMemory/models.ts
|
|
11348
|
+
import { Model as Model3 } from "@nozbe/watermelondb";
|
|
11349
|
+
import { date as date2, field as field2, readonly, text as text4 } from "@nozbe/watermelondb/decorators";
|
|
11350
|
+
var ConversationMemory = class extends Model3 {
|
|
11351
|
+
};
|
|
11352
|
+
ConversationMemory.table = "conversation_memory";
|
|
11353
|
+
ConversationMemory.associations = {};
|
|
11354
|
+
__decorateClass([
|
|
11355
|
+
text4("conversation_id")
|
|
11356
|
+
], ConversationMemory.prototype, "conversationId", 2);
|
|
11357
|
+
__decorateClass([
|
|
11358
|
+
text4("memory_id")
|
|
11359
|
+
], ConversationMemory.prototype, "memoryId", 2);
|
|
11360
|
+
__decorateClass([
|
|
11361
|
+
field2("score")
|
|
11362
|
+
], ConversationMemory.prototype, "score", 2);
|
|
11363
|
+
__decorateClass([
|
|
11364
|
+
readonly,
|
|
11365
|
+
date2("created_at")
|
|
11366
|
+
], ConversationMemory.prototype, "createdAt", 2);
|
|
11367
|
+
|
|
11368
|
+
// src/lib/chatStore/chatImports.ts
|
|
11369
|
+
var importBatch = 100;
|
|
11370
|
+
var importChunks = 200;
|
|
11371
|
+
var importPlaintextBytes = 39e5;
|
|
11372
|
+
function ms(date10, fallback = 1) {
|
|
11373
|
+
const value = date10?.getTime();
|
|
11374
|
+
return value && Number.isFinite(value) ? Math.max(1, Math.floor(value)) : fallback;
|
|
11375
|
+
}
|
|
11376
|
+
function bytes2(value) {
|
|
11377
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
11378
|
+
}
|
|
11379
|
+
function conversationRecord(conversation, messages) {
|
|
11380
|
+
const createdAt = ms(conversation.createdAt);
|
|
11381
|
+
const data = plain({
|
|
11382
|
+
...contextFields(conversation.context),
|
|
11383
|
+
title: conversation.title,
|
|
11384
|
+
projectId: conversation.projectId || void 0,
|
|
11385
|
+
pinnedAt: conversation.pinnedAt ? ms(conversation.pinnedAt) : void 0,
|
|
11386
|
+
lastActivityAt: Math.max(
|
|
11387
|
+
ms(conversation.updatedAt, createdAt),
|
|
11388
|
+
...messages.map((m) => ms(m.createdAt))
|
|
11389
|
+
)
|
|
11390
|
+
});
|
|
11391
|
+
return {
|
|
11392
|
+
record: {
|
|
11393
|
+
kind: "conversation",
|
|
11394
|
+
id: conversation.conversationId,
|
|
11395
|
+
conversationId: "",
|
|
11396
|
+
sortKey: conversationSortKey(data, createdAt),
|
|
11397
|
+
createdAt,
|
|
11398
|
+
updatedAt: ms(conversation.updatedAt, createdAt),
|
|
11399
|
+
isDeleted: false,
|
|
11400
|
+
data
|
|
10982
11401
|
}
|
|
10983
|
-
const page = await request(
|
|
10984
|
-
"/memories/query",
|
|
10985
|
-
{
|
|
10986
|
-
method: "POST",
|
|
10987
|
-
body: JSON.stringify(
|
|
10988
|
-
Object.fromEntries(
|
|
10989
|
-
queryFields.filter((key3) => query[key3] !== void 0).map((key3) => [key3, query[key3]])
|
|
10990
|
-
)
|
|
10991
|
-
)
|
|
10992
|
-
},
|
|
10993
|
-
signal
|
|
10994
|
-
);
|
|
10995
|
-
if (!record(page) || !Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string" || page.changes_cursor !== void 0 && typeof page.changes_cursor !== "string")
|
|
10996
|
-
throw new Error("Invalid nearby private-memory page");
|
|
10997
|
-
return page;
|
|
10998
|
-
};
|
|
10999
|
-
const candidateSet = async (embedding2, candidateOptions = {}) => {
|
|
11000
|
-
const result = await fetchCandidates(embedding2, candidateOptions, true);
|
|
11001
|
-
if (!Number.isSafeInteger(result.total_count) || result.total_count < 0 || !Number.isSafeInteger(result.unavailable_count) || result.unavailable_count < 0)
|
|
11002
|
-
throw new Error("Invalid nearby candidate response");
|
|
11003
|
-
return {
|
|
11004
|
-
...await decodeAll(result.items),
|
|
11005
|
-
total_count: result.total_count,
|
|
11006
|
-
unavailable_count: result.unavailable_count
|
|
11007
|
-
};
|
|
11008
11402
|
};
|
|
11403
|
+
}
|
|
11404
|
+
function messageRecord(message) {
|
|
11405
|
+
const {
|
|
11406
|
+
uniqueId: _uniqueId,
|
|
11407
|
+
messageId: _messageId,
|
|
11408
|
+
createdAt: _createdAt,
|
|
11409
|
+
updatedAt: _updatedAt,
|
|
11410
|
+
decryptionStatus: _status,
|
|
11411
|
+
chunks: chunks3,
|
|
11412
|
+
vector: vector2,
|
|
11413
|
+
...fields
|
|
11414
|
+
} = message;
|
|
11415
|
+
const fitted = chunks3?.length ? fitChunks(chunks3) : void 0;
|
|
11416
|
+
const single = fitted ? void 0 : acceptedVector(vector2);
|
|
11417
|
+
const embeddings2 = fitted?.map((c) => c.vector) ?? (single ? [single] : void 0);
|
|
11418
|
+
const record2 = fitRecord({
|
|
11419
|
+
kind: "message",
|
|
11420
|
+
id: message.uniqueId,
|
|
11421
|
+
conversationId: message.conversationId,
|
|
11422
|
+
sortKey: message.messageId,
|
|
11423
|
+
createdAt: ms(message.createdAt),
|
|
11424
|
+
updatedAt: ms(message.updatedAt, ms(message.createdAt)),
|
|
11425
|
+
isDeleted: false,
|
|
11426
|
+
data: {
|
|
11427
|
+
...messageData(fields),
|
|
11428
|
+
...fitted ? { chunkOffsets: fitted.map((c) => [c.startOffset, c.endOffset]) } : {}
|
|
11429
|
+
}
|
|
11430
|
+
});
|
|
11009
11431
|
return {
|
|
11010
|
-
|
|
11011
|
-
...
|
|
11012
|
-
|
|
11013
|
-
counts: async () => {
|
|
11014
|
-
const result = await request("/counts", {});
|
|
11015
|
-
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
11016
|
-
(key3) => !Number.isSafeInteger(result[key3]) || result[key3] < 0
|
|
11017
|
-
))
|
|
11018
|
-
throw new Error("Invalid nearby memory counts");
|
|
11019
|
-
return {
|
|
11020
|
-
live: result.live,
|
|
11021
|
-
archived: result.archived,
|
|
11022
|
-
quarantined: result.quarantined
|
|
11023
|
-
};
|
|
11024
|
-
},
|
|
11025
|
-
temporal: async (window2, limit = 100) => {
|
|
11026
|
-
if (!Number.isFinite(window2.start) || !Number.isFinite(window2.end) || window2.end <= window2.start || !Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
11027
|
-
throw new Error("Temporal window must have end > start and limit must be 1\u2013100");
|
|
11028
|
-
const result = await request("/temporal", {
|
|
11029
|
-
method: "POST",
|
|
11030
|
-
body: JSON.stringify({ ...window2, limit })
|
|
11031
|
-
});
|
|
11032
|
-
if (!record(result) || !Array.isArray(result.items) || result.items.some(
|
|
11033
|
-
(item) => !record(item) || typeof item.memory_id !== "string" || !item.memory_id || typeof item.score !== "number" || !Number.isFinite(item.score) || item.score <= 0 || item.score > 1
|
|
11034
|
-
))
|
|
11035
|
-
throw new Error("Invalid nearby temporal candidates");
|
|
11036
|
-
return result.items.map((item) => item.memory_id);
|
|
11037
|
-
},
|
|
11038
|
-
get: async (id2, signal) => {
|
|
11039
|
-
try {
|
|
11040
|
-
return await decode(await request(memoryPath(id2), {}, signal));
|
|
11041
|
-
} catch (error) {
|
|
11042
|
-
if (error instanceof RemoteMemoryError && error.status === 404) return null;
|
|
11043
|
-
throw error;
|
|
11044
|
-
}
|
|
11045
|
-
},
|
|
11046
|
-
list: async (listOptions = {}) => {
|
|
11047
|
-
const { signal, ...query } = listOptions;
|
|
11048
|
-
if (query.limit !== void 0 && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) {
|
|
11049
|
-
throw new Error("Page limit must be 1\u2013200");
|
|
11050
|
-
}
|
|
11051
|
-
const params = new URLSearchParams();
|
|
11052
|
-
for (const key3 of [
|
|
11053
|
-
"cursor",
|
|
11054
|
-
"limit",
|
|
11055
|
-
"include_deleted",
|
|
11056
|
-
"include_archived",
|
|
11057
|
-
"include_quarantined",
|
|
11058
|
-
"include_superseded",
|
|
11059
|
-
"include_embeddings"
|
|
11060
|
-
]) {
|
|
11061
|
-
if (query[key3] !== void 0) params.set(key3, String(query[key3]));
|
|
11062
|
-
}
|
|
11063
|
-
const page = await request("/memories?" + params.toString(), {}, signal);
|
|
11064
|
-
if (!record(page) || !Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string") {
|
|
11065
|
-
throw new Error("Invalid nearby private-memory page");
|
|
11066
|
-
}
|
|
11067
|
-
return {
|
|
11068
|
-
...await decodeAll(page.items),
|
|
11069
|
-
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor }
|
|
11070
|
-
};
|
|
11071
|
-
},
|
|
11072
|
-
put: async (memory, expectedVersion, signal) => {
|
|
11073
|
-
const write = await prepareWrite(memory, expectedVersion, signal);
|
|
11074
|
-
return decodeCommitted(
|
|
11075
|
-
await request(
|
|
11076
|
-
memoryPath(write.body.memory.memory_id),
|
|
11077
|
-
{
|
|
11078
|
-
method: "PUT",
|
|
11079
|
-
body: JSON.stringify({ key_id: options.keyId, ...write.body })
|
|
11080
|
-
},
|
|
11081
|
-
signal
|
|
11082
|
-
),
|
|
11083
|
-
write
|
|
11084
|
-
);
|
|
11085
|
-
},
|
|
11086
|
-
putMany: async (writes, signal, leaseToken) => {
|
|
11087
|
-
if (writes.length < 1 || writes.length > 50 || new Set(writes.map((w) => w.memory.memory_id)).size !== writes.length)
|
|
11088
|
-
throw new Error("Batch must contain 1\u201350 distinct memory ids");
|
|
11089
|
-
const prepared = await Promise.all(
|
|
11090
|
-
writes.map((w) => prepareWrite(w.memory, w.expectedVersion, signal))
|
|
11091
|
-
);
|
|
11092
|
-
const result = await request(
|
|
11093
|
-
"/memories/batch",
|
|
11094
|
-
{
|
|
11095
|
-
method: "POST",
|
|
11096
|
-
body: JSON.stringify({
|
|
11097
|
-
key_id: options.keyId,
|
|
11098
|
-
writes: prepared.map((w) => w.body),
|
|
11099
|
-
...leaseToken && { lease_token: leaseToken }
|
|
11100
|
-
})
|
|
11101
|
-
},
|
|
11102
|
-
signal
|
|
11103
|
-
);
|
|
11104
|
-
if (!record(result) || !Array.isArray(result.items) || result.items.length !== prepared.length)
|
|
11105
|
-
throw new Error("Invalid nearby batch response");
|
|
11106
|
-
return Promise.all(result.items.map((item, i) => decodeCommitted(item, prepared[i])));
|
|
11107
|
-
},
|
|
11108
|
-
query: async (queryOptions = {}) => {
|
|
11109
|
-
const page = await fetchQueryPage(queryOptions);
|
|
11110
|
-
return {
|
|
11111
|
-
...await decodeAll(page.items),
|
|
11112
|
-
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor },
|
|
11113
|
-
...page.changes_cursor !== void 0 && { changes_cursor: page.changes_cursor }
|
|
11114
|
-
};
|
|
11115
|
-
},
|
|
11116
|
-
queryMetadata: async (queryOptions = {}) => {
|
|
11117
|
-
const page = await fetchQueryPage(queryOptions);
|
|
11118
|
-
return {
|
|
11119
|
-
items: page.items.map((item) => {
|
|
11120
|
-
const {
|
|
11121
|
-
memory: { content: _content, kind_value: _kindValue, ...memory },
|
|
11122
|
-
version,
|
|
11123
|
-
server_updated_at
|
|
11124
|
-
} = memoryRecord(item);
|
|
11125
|
-
return { memory, version, server_updated_at };
|
|
11126
|
-
}),
|
|
11127
|
-
...page.next_cursor !== void 0 && { next_cursor: page.next_cursor }
|
|
11128
|
-
};
|
|
11129
|
-
},
|
|
11130
|
-
candidateSet,
|
|
11131
|
-
candidates: async (embedding2, candidateOptions = {}) => decodeAll((await fetchCandidates(embedding2, candidateOptions, false)).items)
|
|
11432
|
+
record: record2,
|
|
11433
|
+
...embeddings2 ? { embeddings: embeddings2 } : {},
|
|
11434
|
+
...embeddings2 && message.embeddingModel ? { embeddingModel: message.embeddingModel } : {}
|
|
11132
11435
|
};
|
|
11133
11436
|
}
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
|
|
11437
|
+
function batches(records) {
|
|
11438
|
+
const out = [];
|
|
11439
|
+
let batch = [];
|
|
11440
|
+
let chunks3 = 0;
|
|
11441
|
+
let size = 0;
|
|
11442
|
+
for (const item of records) {
|
|
11443
|
+
const vectors = item.embeddings?.length ?? 0;
|
|
11444
|
+
const itemSize = bytes2(item.record);
|
|
11445
|
+
if (batch.length > 0 && (batch.length >= importBatch || chunks3 + vectors > importChunks || size + itemSize > importPlaintextBytes)) {
|
|
11446
|
+
out.push(batch);
|
|
11447
|
+
batch = [];
|
|
11448
|
+
chunks3 = 0;
|
|
11449
|
+
size = 0;
|
|
11450
|
+
}
|
|
11451
|
+
batch.push(item);
|
|
11452
|
+
chunks3 += vectors;
|
|
11453
|
+
size += itemSize;
|
|
11143
11454
|
}
|
|
11144
|
-
|
|
11455
|
+
if (batch.length) out.push(batch);
|
|
11456
|
+
return out;
|
|
11457
|
+
}
|
|
11145
11458
|
|
|
11146
11459
|
// src/lib/chatStore/remote.ts
|
|
11147
11460
|
var kinds = /* @__PURE__ */ new Set(["conversation", "message", "summary", "project", "memory_links", "setting"]);
|
|
@@ -11867,10 +12180,10 @@ async function deletedSinceUpload(store, journal, local, signal) {
|
|
|
11867
12180
|
const present = /* @__PURE__ */ new Set([...local.records.map((r) => key(r.record)), ...local.skipped.keys()]);
|
|
11868
12181
|
const gone = [...journal.keys].filter((k) => !present.has(k));
|
|
11869
12182
|
if (gone.length === 0) return [];
|
|
11870
|
-
const
|
|
11871
|
-
const tombstoning = new Set(
|
|
12183
|
+
const live2 = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
|
|
12184
|
+
const tombstoning = new Set(live2.map((s) => key(s.record)));
|
|
11872
12185
|
await journal.drop(gone.filter((k) => !tombstoning.has(k)));
|
|
11873
|
-
return
|
|
12186
|
+
return live2.map(({ record: record2, version }) => {
|
|
11874
12187
|
const empty = emptied[record2.kind];
|
|
11875
12188
|
return {
|
|
11876
12189
|
record: {
|
|
@@ -12530,12 +12843,13 @@ var resumableFields = [
|
|
|
12530
12843
|
"error",
|
|
12531
12844
|
"thoughtProcess",
|
|
12532
12845
|
"thinking",
|
|
12533
|
-
"toolCallEvents"
|
|
12846
|
+
"toolCallEvents",
|
|
12847
|
+
"anuma"
|
|
12534
12848
|
];
|
|
12535
12849
|
function resumedFields(options) {
|
|
12536
12850
|
return plain(
|
|
12537
12851
|
Object.fromEntries(
|
|
12538
|
-
resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, options[f]])
|
|
12852
|
+
resumableFields.filter((f) => options[f] !== void 0).map((f) => [f, f === "anuma" ? anumaTurnData(options.anuma) : options[f]])
|
|
12539
12853
|
)
|
|
12540
12854
|
);
|
|
12541
12855
|
}
|
|
@@ -13189,7 +13503,7 @@ function createVaultChatBackend(options) {
|
|
|
13189
13503
|
},
|
|
13190
13504
|
async getConversations(query) {
|
|
13191
13505
|
await available2();
|
|
13192
|
-
return all("conversation").map(toConversation).filter(
|
|
13506
|
+
return all("conversation").filter((record2) => record2.id !== ANUMA_CONVERSATION_ID).map(toConversation).filter(
|
|
13193
13507
|
(c) => query?.projectId === void 0 || (query.projectId === null ? !c.projectId : c.projectId === query.projectId)
|
|
13194
13508
|
).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
13195
13509
|
},
|
|
@@ -13232,6 +13546,7 @@ function createVaultChatBackend(options) {
|
|
|
13232
13546
|
return !!record2 && !record2.isDeleted;
|
|
13233
13547
|
},
|
|
13234
13548
|
async deleteConversation(conversationId) {
|
|
13549
|
+
if (conversationId === ANUMA_CONVERSATION_ID) return false;
|
|
13235
13550
|
const record2 = await conversation(conversationId);
|
|
13236
13551
|
if (!record2 || record2.isDeleted) return false;
|
|
13237
13552
|
for (const [k, patch] of pending)
|
|
@@ -16238,7 +16553,7 @@ async function countActiveVaultMemoriesOp(ctx) {
|
|
|
16238
16553
|
return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
|
|
16239
16554
|
}
|
|
16240
16555
|
async function getVaultMemoryCountsOp(ctx) {
|
|
16241
|
-
const [
|
|
16556
|
+
const [live2, archived, quarantined] = await Promise.all([
|
|
16242
16557
|
countActiveVaultMemoriesOp(ctx),
|
|
16243
16558
|
ctx.vaultMemoryCollection.query(
|
|
16244
16559
|
...baseVaultConditions(ctx, { includeArchived: true }),
|
|
@@ -16249,7 +16564,7 @@ async function getVaultMemoryCountsOp(ctx) {
|
|
|
16249
16564
|
Q6.where("trust_tier", "quarantined")
|
|
16250
16565
|
).fetchCount()
|
|
16251
16566
|
]);
|
|
16252
|
-
return { live, archived, quarantined };
|
|
16567
|
+
return { live: live2, archived, quarantined };
|
|
16253
16568
|
}
|
|
16254
16569
|
async function getActiveVaultMemoryIdsOp(ctx, ids) {
|
|
16255
16570
|
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -17534,12 +17849,12 @@ var WalletPoller = class {
|
|
|
17534
17849
|
* @param maxAttempts - Maximum polling attempts before giving up (default: 60)
|
|
17535
17850
|
* @returns Stop function to cancel polling
|
|
17536
17851
|
*/
|
|
17537
|
-
startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS,
|
|
17852
|
+
startPolling(checkWallet, onWalletReady, intervalMs = DEFAULT_INTERVAL_MS, maxAttempts2 = DEFAULT_MAX_ATTEMPTS) {
|
|
17538
17853
|
this.stop();
|
|
17539
17854
|
this.attempts = 0;
|
|
17540
17855
|
const poll = async () => {
|
|
17541
17856
|
this.attempts++;
|
|
17542
|
-
if (this.attempts >
|
|
17857
|
+
if (this.attempts > maxAttempts2) {
|
|
17543
17858
|
this.stop();
|
|
17544
17859
|
return;
|
|
17545
17860
|
}
|
|
@@ -20513,7 +20828,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
20513
20828
|
content: m.content,
|
|
20514
20829
|
similarity: m.similarity
|
|
20515
20830
|
}));
|
|
20516
|
-
const { consolidateMemory: doConsolidate } = await import("./consolidate-
|
|
20831
|
+
const { consolidateMemory: doConsolidate } = await import("./consolidate-QBV3DDHU.mjs");
|
|
20517
20832
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
20518
20833
|
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
20519
20834
|
if (decision.action === "supersede" && decision.content) {
|
|
@@ -24900,13 +25215,13 @@ function createRemoteMemoryStore(options) {
|
|
|
24900
25215
|
});
|
|
24901
25216
|
return run;
|
|
24902
25217
|
};
|
|
24903
|
-
const
|
|
25218
|
+
const live2 = async (id2) => {
|
|
24904
25219
|
const record2 = await persistence.get(id2);
|
|
24905
25220
|
return record2 && !record2.memory.is_deleted ? record2 : null;
|
|
24906
25221
|
};
|
|
24907
25222
|
const mutateLocked = async (id2, change, edit, writeOptions) => {
|
|
24908
25223
|
writeOptions?.signal?.throwIfAborted();
|
|
24909
|
-
const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await
|
|
25224
|
+
const record2 = writeOptions?.signal ? await persistence.get(id2, writeOptions.signal) : await live2(id2);
|
|
24910
25225
|
if (!record2) return null;
|
|
24911
25226
|
if (record2.memory.is_deleted) return null;
|
|
24912
25227
|
if (writeOptions?.canWrite && !await writeOptions.canWrite())
|
|
@@ -25704,7 +26019,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25704
26019
|
}, clear);
|
|
25705
26020
|
return snapshot.then((topics) => structuredClone(topics));
|
|
25706
26021
|
},
|
|
25707
|
-
get: async (id2) => asStored(await
|
|
26022
|
+
get: async (id2) => asStored(await live2(id2)),
|
|
25708
26023
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
25709
26024
|
(record2) => stored(record2.memory)
|
|
25710
26025
|
),
|
|
@@ -25789,7 +26104,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25789
26104
|
return serialized(
|
|
25790
26105
|
first,
|
|
25791
26106
|
() => serialized(second, async () => {
|
|
25792
|
-
const [old, next] = await Promise.all([
|
|
26107
|
+
const [old, next] = await Promise.all([live2(id2), live2(supersededById)]);
|
|
25793
26108
|
if (!old || !next || old.memory.superseded_by || next.memory.superseded_by) return false;
|
|
25794
26109
|
if (old.memory.kind !== void 0 && old.memory.kind !== null) return false;
|
|
25795
26110
|
const saved = await persistence.putMany([
|
|
@@ -25810,7 +26125,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25810
26125
|
(memory) => memory.archived_at === void 0 || memory.archived_at === null ? { ...memory, archived_at: Date.now() } : null
|
|
25811
26126
|
),
|
|
25812
26127
|
restore: (id2) => serialized(id2, async () => {
|
|
25813
|
-
const record2 = await
|
|
26128
|
+
const record2 = await live2(id2);
|
|
25814
26129
|
if (!record2) return false;
|
|
25815
26130
|
if (record2.memory.archived_at === void 0 || record2.memory.archived_at === null)
|
|
25816
26131
|
return true;
|
|
@@ -29885,6 +30200,8 @@ function useChat(options) {
|
|
|
29885
30200
|
const requestId = ++requestIdRef.current;
|
|
29886
30201
|
const superseded = () => requestIdRef.current !== requestId;
|
|
29887
30202
|
pendingResumeRef.current = null;
|
|
30203
|
+
let liveRound = null;
|
|
30204
|
+
const deferredErrors = [];
|
|
29888
30205
|
setIsLoading(true);
|
|
29889
30206
|
try {
|
|
29890
30207
|
const tokenGetterValidation = validateTokenGetter(getToken);
|
|
@@ -29940,7 +30257,7 @@ function useChat(options) {
|
|
|
29940
30257
|
};
|
|
29941
30258
|
messagesWithContext = [toolGuidanceMessage, ...messagesWithContext];
|
|
29942
30259
|
}
|
|
29943
|
-
const
|
|
30260
|
+
const loopResult = await runToolLoop({
|
|
29944
30261
|
messages: messagesWithContext,
|
|
29945
30262
|
model,
|
|
29946
30263
|
token,
|
|
@@ -29964,12 +30281,13 @@ function useChat(options) {
|
|
|
29964
30281
|
transport: xhrTransport,
|
|
29965
30282
|
onStreamMeta: (meta) => {
|
|
29966
30283
|
const resolvedApiType = resolveApiType(requestApiType ?? defaultApiType, model);
|
|
29967
|
-
|
|
30284
|
+
liveRound = {
|
|
29968
30285
|
inferenceId: meta.inferenceId,
|
|
29969
30286
|
apiType: resolvedApiType,
|
|
29970
30287
|
model,
|
|
29971
30288
|
conversationId
|
|
29972
30289
|
};
|
|
30290
|
+
pendingResumeRef.current = liveRound;
|
|
29973
30291
|
if (onStreamMetaConsumer) {
|
|
29974
30292
|
try {
|
|
29975
30293
|
onStreamMetaConsumer({
|
|
@@ -29992,16 +30310,28 @@ function useChat(options) {
|
|
|
29992
30310
|
if (globalOnThinking) globalOnThinking(chunk);
|
|
29993
30311
|
},
|
|
29994
30312
|
onFinish,
|
|
29995
|
-
onError,
|
|
30313
|
+
onError: resumable ? (error) => deferredErrors.push(error) : onError,
|
|
29996
30314
|
onToolCall,
|
|
29997
30315
|
onServerToolCall,
|
|
29998
30316
|
onToolCallArgumentsDelta,
|
|
29999
|
-
onStepFinish
|
|
30317
|
+
onStepFinish: (event) => {
|
|
30318
|
+
liveRound = null;
|
|
30319
|
+
onStepFinish?.(event);
|
|
30320
|
+
},
|
|
30000
30321
|
preProcessors,
|
|
30001
30322
|
hooks,
|
|
30002
30323
|
piiRedaction: requestPiiRedaction ?? resolvedPiiRedaction,
|
|
30003
30324
|
onPiiRedacted
|
|
30004
30325
|
});
|
|
30326
|
+
const result = resumable && loopResult.error !== null && loopResult.connectionLost && liveRound ? {
|
|
30327
|
+
data: loopResult.data,
|
|
30328
|
+
error: "Request detached",
|
|
30329
|
+
detached: true,
|
|
30330
|
+
resume: liveRound
|
|
30331
|
+
} : loopResult;
|
|
30332
|
+
if (!("detached" in result && result.detached)) {
|
|
30333
|
+
deferredErrors.forEach((error) => onError?.(error));
|
|
30334
|
+
}
|
|
30005
30335
|
if (superseded()) {
|
|
30006
30336
|
} else if ("detached" in result && result.detached && result.resume) {
|
|
30007
30337
|
pendingResumeRef.current = result.resume;
|
|
@@ -30045,13 +30375,25 @@ function useChat(options) {
|
|
|
30045
30375
|
onStreamMetaConsumer
|
|
30046
30376
|
]
|
|
30047
30377
|
);
|
|
30378
|
+
const getActiveStreams = useCallback3(
|
|
30379
|
+
async (conversationId) => {
|
|
30380
|
+
const getterValidation = validateTokenGetter(getToken);
|
|
30381
|
+
if (!getterValidation.valid) throw new Error(getterValidation.message);
|
|
30382
|
+
const token = await getToken();
|
|
30383
|
+
const tokenValidation = validateToken(token);
|
|
30384
|
+
if (!tokenValidation.valid) throw new Error(tokenValidation.message);
|
|
30385
|
+
return listActiveStreams({ token, baseUrl, conversationId });
|
|
30386
|
+
},
|
|
30387
|
+
[getToken, baseUrl]
|
|
30388
|
+
);
|
|
30048
30389
|
return {
|
|
30049
30390
|
isLoading,
|
|
30050
30391
|
sendMessage,
|
|
30051
30392
|
stop,
|
|
30052
30393
|
cancel,
|
|
30053
30394
|
detach,
|
|
30054
|
-
resumeStream: resumeStream2
|
|
30395
|
+
resumeStream: resumeStream2,
|
|
30396
|
+
getActiveStreams
|
|
30055
30397
|
};
|
|
30056
30398
|
}
|
|
30057
30399
|
|
|
@@ -31107,6 +31449,7 @@ function useChatStorage(options) {
|
|
|
31107
31449
|
resumable = false,
|
|
31108
31450
|
onCancelResult,
|
|
31109
31451
|
onStreamMeta,
|
|
31452
|
+
inbox,
|
|
31110
31453
|
onToolSelection,
|
|
31111
31454
|
piiRedaction,
|
|
31112
31455
|
onPiiRedacted,
|
|
@@ -31574,13 +31917,19 @@ function useChatStorage(options) {
|
|
|
31574
31917
|
},
|
|
31575
31918
|
[vaultCtx, memoryStore]
|
|
31576
31919
|
);
|
|
31920
|
+
const activeReplyIdRef = useRef4(void 0);
|
|
31921
|
+
const handleStreamMeta = useCallback5(
|
|
31922
|
+
(meta) => onStreamMeta?.({ ...meta, assistantMessageId: activeReplyIdRef.current }),
|
|
31923
|
+
[onStreamMeta]
|
|
31924
|
+
);
|
|
31577
31925
|
const {
|
|
31578
31926
|
isLoading,
|
|
31579
31927
|
sendMessage: baseSendMessage,
|
|
31580
31928
|
stop: baseStop,
|
|
31581
31929
|
cancel: baseCancel,
|
|
31582
31930
|
detach,
|
|
31583
|
-
resumeStream: baseResumeStream
|
|
31931
|
+
resumeStream: baseResumeStream,
|
|
31932
|
+
getActiveStreams
|
|
31584
31933
|
} = useChat({
|
|
31585
31934
|
getToken,
|
|
31586
31935
|
baseUrl,
|
|
@@ -31594,7 +31943,7 @@ function useChatStorage(options) {
|
|
|
31594
31943
|
smoothing,
|
|
31595
31944
|
resumable,
|
|
31596
31945
|
onCancelResult,
|
|
31597
|
-
onStreamMeta,
|
|
31946
|
+
onStreamMeta: onStreamMeta ? handleStreamMeta : void 0,
|
|
31598
31947
|
piiRedaction: resolvedPiiRedaction,
|
|
31599
31948
|
onPiiRedacted,
|
|
31600
31949
|
onServerToolCall,
|
|
@@ -31810,10 +32159,13 @@ function useChatStorage(options) {
|
|
|
31810
32159
|
getTokenRef.current = getToken;
|
|
31811
32160
|
const finalizeResumedRow = useCallback5(
|
|
31812
32161
|
async (ctx, data, wasStopped, responseDuration, viaQueue = false) => {
|
|
31813
|
-
const
|
|
31814
|
-
|
|
32162
|
+
const extracted2 = extractAssistantText(data);
|
|
32163
|
+
const restore = ctx.restoreText ?? ((text16) => text16);
|
|
32164
|
+
const content = restore(extracted2.content);
|
|
32165
|
+
const thinking = extracted2.thinking ? restore(extracted2.thinking) : extracted2.thinking;
|
|
32166
|
+
let parentMessageId = ctx.parentless ? void 0 : ctx.userMessageUniqueId;
|
|
31815
32167
|
let knownIds = ctx.knownToolCallEventIds;
|
|
31816
|
-
if (ctx.convId && (!knownIds || !parentMessageId)) {
|
|
32168
|
+
if (ctx.convId && (!knownIds || !parentMessageId && !ctx.parentless)) {
|
|
31817
32169
|
let storedMessages = null;
|
|
31818
32170
|
try {
|
|
31819
32171
|
storedMessages = await getMessages(ctx.convId);
|
|
@@ -31831,7 +32183,7 @@ function useChatStorage(options) {
|
|
|
31831
32183
|
}
|
|
31832
32184
|
}
|
|
31833
32185
|
}
|
|
31834
|
-
if (!parentMessageId) {
|
|
32186
|
+
if (!parentMessageId && !ctx.parentless) {
|
|
31835
32187
|
for (let i = storedMessages.length - 1; i >= 0; i--) {
|
|
31836
32188
|
if (storedMessages[i].uniqueId !== ctx.assistantUniqueId) {
|
|
31837
32189
|
parentMessageId = storedMessages[i].uniqueId;
|
|
@@ -31870,7 +32222,15 @@ function useChatStorage(options) {
|
|
|
31870
32222
|
toolCallEvents: currentTurnToolCallEvents && currentTurnToolCallEvents.length > 0 ? currentTurnToolCallEvents : void 0,
|
|
31871
32223
|
uniqueId: ctx.assistantUniqueId
|
|
31872
32224
|
};
|
|
31873
|
-
if (
|
|
32225
|
+
if (wasStopped) {
|
|
32226
|
+
try {
|
|
32227
|
+
const [current] = await backend.getMessagesByIds([rowOpts.uniqueId]);
|
|
32228
|
+
if (current && !current.wasStopped && !current.error) return current;
|
|
32229
|
+
} catch (err) {
|
|
32230
|
+
getLogger().warn("[useChatStorage] Could not check for a stored answer:", err);
|
|
32231
|
+
}
|
|
32232
|
+
}
|
|
32233
|
+
if (!viaQueue) return clearTurnErrors(backend, await backend.upsertMessage(rowOpts));
|
|
31874
32234
|
const { result } = await writeOrQueue(
|
|
31875
32235
|
"createMessage",
|
|
31876
32236
|
rowOpts,
|
|
@@ -32485,6 +32845,7 @@ function useChatStorage(options) {
|
|
|
32485
32845
|
} catch {
|
|
32486
32846
|
}
|
|
32487
32847
|
}
|
|
32848
|
+
activeReplyIdRef.current = resumable ? effectiveAssistantUniqueId : void 0;
|
|
32488
32849
|
const result = await baseSendMessage({
|
|
32489
32850
|
messages: messagesToSend,
|
|
32490
32851
|
model,
|
|
@@ -32510,15 +32871,23 @@ function useChatStorage(options) {
|
|
|
32510
32871
|
imageModel,
|
|
32511
32872
|
conversationId: convId,
|
|
32512
32873
|
piiRedaction: callPiiRedaction,
|
|
32513
|
-
headers
|
|
32874
|
+
headers: resumable && effectiveAssistantUniqueId ? {
|
|
32875
|
+
...headers,
|
|
32876
|
+
"X-Parent-Message-ID": storedUserMessage.uniqueId,
|
|
32877
|
+
"X-Assistant-Message-ID": effectiveAssistantUniqueId
|
|
32878
|
+
} : headers
|
|
32514
32879
|
});
|
|
32880
|
+
if (activeReplyIdRef.current === effectiveAssistantUniqueId) {
|
|
32881
|
+
activeReplyIdRef.current = void 0;
|
|
32882
|
+
}
|
|
32515
32883
|
const responseDuration = (Date.now() - startTime) / 1e3;
|
|
32516
32884
|
const detachedResult = result;
|
|
32517
32885
|
if (detachedResult.detached) {
|
|
32518
32886
|
recordToolSetTurn(database, convId, matchedToolSets);
|
|
32519
32887
|
const rowId = effectiveAssistantUniqueId ?? `msg_${uuidv78()}`;
|
|
32888
|
+
const resume = detachedResult.resume ? { ...detachedResult.resume, assistantMessageId: rowId } : null;
|
|
32520
32889
|
pendingResumeRef.current = {
|
|
32521
|
-
handle:
|
|
32890
|
+
handle: resume,
|
|
32522
32891
|
convId,
|
|
32523
32892
|
userMessageUniqueId: storedUserMessage.uniqueId,
|
|
32524
32893
|
userMessageQueueId: userMsgQueueId,
|
|
@@ -32536,7 +32905,7 @@ function useChatStorage(options) {
|
|
|
32536
32905
|
data: detachedResult.data ?? null,
|
|
32537
32906
|
error: "Request detached",
|
|
32538
32907
|
detached: true,
|
|
32539
|
-
resume
|
|
32908
|
+
resume,
|
|
32540
32909
|
assistantUniqueId: rowId,
|
|
32541
32910
|
userMessage: storedUserMessage
|
|
32542
32911
|
};
|
|
@@ -32776,7 +33145,7 @@ function useChatStorage(options) {
|
|
|
32776
33145
|
handle: handleOverride,
|
|
32777
33146
|
convId: handleOverride.conversationId ?? currentConversationId ?? "",
|
|
32778
33147
|
userMessageUniqueId: "",
|
|
32779
|
-
assistantUniqueId: `msg_resume_${handleOverride.inferenceId}`,
|
|
33148
|
+
assistantUniqueId: handleOverride.assistantMessageId ?? `msg_resume_${handleOverride.inferenceId}`,
|
|
32780
33149
|
model: handleOverride.model,
|
|
32781
33150
|
imageModel: void 0,
|
|
32782
33151
|
sources: void 0,
|
|
@@ -32900,6 +33269,10 @@ function useChatStorage(options) {
|
|
|
32900
33269
|
retirementStopRequestedRef.current = true;
|
|
32901
33270
|
return;
|
|
32902
33271
|
}
|
|
33272
|
+
const replaying = activeResumeRef.current;
|
|
33273
|
+
if (replaying && !replaying.headless && replaying.context.handle) {
|
|
33274
|
+
baseCancel(replaying.context.handle);
|
|
33275
|
+
}
|
|
32903
33276
|
const pending = pendingResumeRef.current;
|
|
32904
33277
|
if (pending && pending.assistantUniqueId && !isResumingRef.current) {
|
|
32905
33278
|
const finalize = pending;
|
|
@@ -32924,13 +33297,90 @@ function useChatStorage(options) {
|
|
|
32924
33297
|
})();
|
|
32925
33298
|
}
|
|
32926
33299
|
baseStop();
|
|
32927
|
-
}, [baseStop, finalizeResumedRow]);
|
|
33300
|
+
}, [baseStop, baseCancel, finalizeResumedRow]);
|
|
33301
|
+
const watchStream = useCallback5(
|
|
33302
|
+
(stream) => baseResumeStream({
|
|
33303
|
+
inferenceId: stream.inferenceId,
|
|
33304
|
+
apiType: stream.apiType,
|
|
33305
|
+
model: stream.model,
|
|
33306
|
+
conversationId: stream.conversationId ?? void 0
|
|
33307
|
+
}),
|
|
33308
|
+
[baseResumeStream]
|
|
33309
|
+
);
|
|
33310
|
+
const registeredInboxKeyRef = useRef4(null);
|
|
33311
|
+
const deliverInbox = useCallback5(async () => {
|
|
33312
|
+
if (!inbox || !walletAddress || !signMessage) return [];
|
|
33313
|
+
const keyPair = await getKeyPair(walletAddress, signMessage, embeddedWalletSigner);
|
|
33314
|
+
const publicKey = await exportInboxPublicKey(keyPair.publicKey);
|
|
33315
|
+
const registered = registeredInboxKeyRef.current;
|
|
33316
|
+
if (registered?.inbox !== inbox || registered.publicKey !== publicKey) {
|
|
33317
|
+
await inbox.putPublicKey(publicKey);
|
|
33318
|
+
registeredInboxKeyRef.current = { inbox, publicKey };
|
|
33319
|
+
}
|
|
33320
|
+
const results = await deliverInboxAnswers({
|
|
33321
|
+
inbox,
|
|
33322
|
+
keyPair,
|
|
33323
|
+
backend,
|
|
33324
|
+
store: async (opened, messageId) => {
|
|
33325
|
+
const { redactor } = resolveCallPii(
|
|
33326
|
+
void 0,
|
|
33327
|
+
piiRedaction,
|
|
33328
|
+
() => getConversationRedactor(opened.conversation_id, nerDetectorRef.current)
|
|
33329
|
+
);
|
|
33330
|
+
const stored2 = await finalizeResumedRow(
|
|
33331
|
+
{
|
|
33332
|
+
handle: null,
|
|
33333
|
+
convId: opened.conversation_id,
|
|
33334
|
+
userMessageUniqueId: opened.parent_message_id ?? "",
|
|
33335
|
+
parentless: opened.parent_message_id === null,
|
|
33336
|
+
restoreText: redactor ? (text16) => redactor.deAnonymize(text16) : void 0,
|
|
33337
|
+
assistantUniqueId: messageId,
|
|
33338
|
+
model: opened.model,
|
|
33339
|
+
imageModel: void 0,
|
|
33340
|
+
sources: void 0,
|
|
33341
|
+
thoughtProcess: void 0,
|
|
33342
|
+
startTime: Date.now(),
|
|
33343
|
+
partialData: null
|
|
33344
|
+
},
|
|
33345
|
+
opened.response,
|
|
33346
|
+
opened.status === "tool_request",
|
|
33347
|
+
0
|
|
33348
|
+
);
|
|
33349
|
+
void embedMessageAsync(stored2, (text16) => redactor ? redactor.maskText(text16) : text16);
|
|
33350
|
+
return stored2;
|
|
33351
|
+
}
|
|
33352
|
+
});
|
|
33353
|
+
const pending = pendingResumeRef.current;
|
|
33354
|
+
const answered = results.some(
|
|
33355
|
+
(r) => (r.status === "stored" || r.status === "delivered") && r.assistantMessage?.uniqueId === pending?.assistantUniqueId
|
|
33356
|
+
);
|
|
33357
|
+
if (pending && answered) pendingResumeRef.current = null;
|
|
33358
|
+
return results;
|
|
33359
|
+
}, [
|
|
33360
|
+
inbox,
|
|
33361
|
+
walletAddress,
|
|
33362
|
+
signMessage,
|
|
33363
|
+
embeddedWalletSigner,
|
|
33364
|
+
backend,
|
|
33365
|
+
piiRedaction,
|
|
33366
|
+
getConversationRedactor,
|
|
33367
|
+
finalizeResumedRow,
|
|
33368
|
+
embedMessageAsync
|
|
33369
|
+
]);
|
|
33370
|
+
const detachWithReplyId = useCallback5(() => {
|
|
33371
|
+
const handle = detach();
|
|
33372
|
+
const replyId = activeReplyIdRef.current;
|
|
33373
|
+
return handle && replyId ? { ...handle, assistantMessageId: replyId } : handle;
|
|
33374
|
+
}, [detach]);
|
|
32928
33375
|
return {
|
|
32929
33376
|
isLoading,
|
|
32930
33377
|
sendMessage,
|
|
32931
33378
|
stop,
|
|
32932
|
-
detach,
|
|
33379
|
+
detach: detachWithReplyId,
|
|
32933
33380
|
resumeStream: resumeStream2,
|
|
33381
|
+
getActiveStreams,
|
|
33382
|
+
watchStream,
|
|
33383
|
+
deliverInbox,
|
|
32934
33384
|
conversationId: currentConversationId,
|
|
32935
33385
|
setConversationId: setCurrentConversationId,
|
|
32936
33386
|
createConversation,
|
|
@@ -32956,6 +33406,7 @@ function useChatStorage(options) {
|
|
|
32956
33406
|
};
|
|
32957
33407
|
}
|
|
32958
33408
|
export {
|
|
33409
|
+
ANUMA_CONVERSATION_ID,
|
|
32959
33410
|
CHUNKS_DISCARDED_ORIGIN,
|
|
32960
33411
|
Conversation as ChatConversation,
|
|
32961
33412
|
Message as ChatMessage,
|
|
@@ -33015,6 +33466,8 @@ export {
|
|
|
33015
33466
|
WALLET_PAYMENT_TOOL_NAME,
|
|
33016
33467
|
WalletPoller,
|
|
33017
33468
|
addConversationMemoriesOp,
|
|
33469
|
+
anumaLastReadSortKey,
|
|
33470
|
+
anumaTurnContent,
|
|
33018
33471
|
archiveVaultMemoryOp,
|
|
33019
33472
|
assembleMemoryContext,
|
|
33020
33473
|
attachFileContextToLastUserMessage,
|
|
@@ -33048,6 +33501,7 @@ export {
|
|
|
33048
33501
|
consoleLogger,
|
|
33049
33502
|
conversationMemoryToStored,
|
|
33050
33503
|
cosineInt8,
|
|
33504
|
+
countUnreadAnumaTurns,
|
|
33051
33505
|
createAutoExtractor,
|
|
33052
33506
|
createChunkVectorCache,
|
|
33053
33507
|
createCryptoPricePreProcessor,
|
|
@@ -33075,6 +33529,7 @@ export {
|
|
|
33075
33529
|
createVaultChatBackend,
|
|
33076
33530
|
createVaultEmbeddingCache,
|
|
33077
33531
|
createVaultFolderOp,
|
|
33532
|
+
createVaultInbox,
|
|
33078
33533
|
createVaultMemoriesBatchOp,
|
|
33079
33534
|
createVaultMemoryOp,
|
|
33080
33535
|
createWalletPaymentTool,
|
|
@@ -33096,8 +33551,10 @@ export {
|
|
|
33096
33551
|
embedMessage,
|
|
33097
33552
|
encodeChunkVector,
|
|
33098
33553
|
encryptToolCallEventsOp,
|
|
33554
|
+
ensureAnumaConversation,
|
|
33099
33555
|
ensureDefaultFoldersOp,
|
|
33100
33556
|
exchangeNotionCode,
|
|
33557
|
+
exportInboxPublicKey,
|
|
33101
33558
|
extractAndLinkEntitiesForMemoriesOp,
|
|
33102
33559
|
extractAndRetain,
|
|
33103
33560
|
extractEntitiesForMemories,
|
|
@@ -33134,8 +33591,10 @@ export {
|
|
|
33134
33591
|
isEncrypted,
|
|
33135
33592
|
isPiiRedactor,
|
|
33136
33593
|
isRerankerAvailable,
|
|
33594
|
+
listActiveStreams,
|
|
33137
33595
|
loadSettings,
|
|
33138
33596
|
localChatBackend,
|
|
33597
|
+
markAnumaRead,
|
|
33139
33598
|
maskScopedEmbeddingCache,
|
|
33140
33599
|
maxVaultAttachmentBytes,
|
|
33141
33600
|
memoryCipher,
|
|
@@ -33145,12 +33604,14 @@ export {
|
|
|
33145
33604
|
moveMemoriesToFolderOp,
|
|
33146
33605
|
noopLogger,
|
|
33147
33606
|
onKeyAvailable,
|
|
33607
|
+
openInboxItem,
|
|
33148
33608
|
parseMedia,
|
|
33149
33609
|
preEmbedVaultMemories,
|
|
33150
33610
|
primaryTopic,
|
|
33151
33611
|
quantizeEmbedding,
|
|
33152
33612
|
queueManager,
|
|
33153
33613
|
rankProfileCandidates,
|
|
33614
|
+
readAnumaTurn,
|
|
33154
33615
|
recall,
|
|
33155
33616
|
reencryptMemoryField,
|
|
33156
33617
|
reflect,
|