@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.
@@ -20,7 +20,7 @@ import {
20
20
  taskTypeHeader,
21
21
  validateEndpointOverride,
22
22
  withInternalFlowMarker
23
- } from "./chunk-4SOUKJD5.mjs";
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 encoder = new TextEncoder();
58
- const data = encoder.encode(verifier);
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/chat/reservationReceipts.ts
5092
- var ACTIONS = [
5093
- {
5094
- kind: "booking",
5095
- tool: "anuma_book_restaurant",
5096
- alreadyDone: /^Tool "[^"]*" failed: ALREADY_BOOKED:/
5097
- },
5098
- {
5099
- kind: "cancel",
5100
- tool: "anuma_cancel_reservation",
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
- var NOT_AN_ATTEMPT = /^Tool "[^"]*" (has already been called|was already called)/;
5105
- var BOOKING_OUTCOME_UNKNOWN = /^Tool "[^"]*" failed: BOOKING_OUTCOME_UNKNOWN:/;
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
- function outcome(action, output, result) {
5138
- if (action.alreadyDone.test(output)) return { status: "already_done" };
5139
- const reason = text2(result.error);
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 parseObject(json5) {
5150
- try {
5151
- const parsed = JSON.parse(json5);
5152
- return parsed && typeof parsed === "object" ? parsed : {};
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 text2(value) {
5158
- if (typeof value === "number") return String(value);
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 missingConfirmFields(action, parameters) {
5199
- const requirement = confirmRequirement(action);
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
- // src/tools/walletPaymentConstants.ts
5210
- var SELECT_PAYMENT_WALLET_TOOL_NAME = "select_payment_wallet";
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
- const {
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 output;
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/tools/serverTools.ts
5351
- var DEFAULT_CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1e3;
5352
- var SERVER_TOOLS_CACHE_KEY = "sdk_server_tools_cache";
5353
- var CACHE_VERSION = "1.3";
5354
- var MIN_CONTENT_LENGTH_FOR_TOOLS = 5;
5355
- var MAX_CLIENT_TOOLS_AFTER_FILTER = 10;
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 toCompletionsFormat(tool) {
5154
+ function createRemoteTopicPersistence(request) {
5393
5155
  return {
5394
- type: "function",
5395
- function: {
5396
- name: tool.name,
5397
- description: tool.description,
5398
- parameters: tool.parameters
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 toResponsesFormat(tool) {
5403
- return {
5404
- type: "function",
5405
- name: tool.name,
5406
- description: tool.description,
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
- const cached = localStorage.getItem(SERVER_TOOLS_CACHE_KEY);
5414
- if (!cached) return null;
5415
- const parsed = JSON.parse(cached);
5416
- if (parsed.version !== CACHE_VERSION) {
5417
- removeLocalStorageCache();
5418
- return null;
5419
- }
5420
- return parsed;
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
- function isCacheExpired(cache, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) {
5426
- if (!cache) return true;
5427
- return Date.now() - cache.timestamp > expirationMs;
5428
- }
5429
- function buildCacheEntry(tools, checksum, etag) {
5214
+
5215
+ // src/lib/memory/store/remoteWork.ts
5216
+ function createRemoteWorkPersistence(request) {
5430
5217
  return {
5431
- tools,
5432
- timestamp: Date.now(),
5433
- version: CACHE_VERSION,
5434
- ...checksum && { checksum },
5435
- ...etag && { etag }
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 writeLocalStorageCache(entry) {
5439
- if (typeof localStorage === "undefined") return;
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
- localStorage.setItem(SERVER_TOOLS_CACHE_KEY, JSON.stringify(entry));
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
- getLogger().warn("[serverTools] Failed to cache tools:", error);
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 removeLocalStorageCache() {
5447
- if (typeof localStorage === "undefined") return;
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
- function shouldRefreshTools(responseChecksum, cache = localStorageToolsCache) {
5463
- if (!responseChecksum) {
5464
- return false;
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
- const decide = (cachedChecksum2) => !cachedChecksum2 || cachedChecksum2 !== responseChecksum;
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 Error(inStreamErr);
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 Error(inStreamErr);
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 Error(`${label}${message || "Upstream request failed"}`);
8475
+ throw new ProviderStreamError(
8476
+ `${label}${message || "Upstream request failed"}`,
8477
+ code || void 0
8478
+ );
7596
8479
  }
7597
- throw new Error("Upstream request failed (response.failed)");
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/db/chat/summaryOperations.ts
9927
- import { Q as Q3 } from "@nozbe/watermelondb";
9928
- function createSummaryContext(database, encryption = {}) {
9929
- return {
9930
- ...encryption,
9931
- database,
9932
- summariesCollection: database.get("conversation_summaries")
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 Q4 } from "@nozbe/watermelondb";
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(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
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(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
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(Q4.where("conversation_id", conversationId), Q4.sortBy("created_at", Q4.asc)).fetch();
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(Q4.where("conversation_id", conversationId)).fetch();
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 text4(value) {
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 = text4(record2.data.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: text4(record2.data.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
- return plain(
10180
- Object.fromEntries(
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, text4(r.data.role)]));
10204
- return records.map((r) => {
10205
- const parentMessageId = text4(r.data.parentMessageId);
10206
- const model = text4(r.data.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: text4(r.data.role) ?? "",
10213
- createdAt: new Date(r.createdAt),
10214
- ...parentMessageId ? { parentMessageId } : {},
10215
- ...model ? { model } : {},
10216
- ...artifact ? { content: text4(r.data.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
- var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
10426
- function topicWeight(topic2) {
10427
- return topic2.source === "user" ? 1 : topic2.weight ?? 1;
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 primaryTopic(topics) {
10430
- let best = null;
10431
- for (const topic2 of topics ?? []) {
10432
- if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
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 best;
11091
+ return plain({ links: current });
10435
11092
  }
10436
- function parseTopicWeight(value) {
10437
- return typeof value === "number" && value > 0 && value <= 1 ? value : void 0;
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 serializeTopics(topics) {
10440
- return JSON.stringify(topics);
11102
+ function bytes(value) {
11103
+ return new TextEncoder().encode(JSON.stringify(value)).length;
10441
11104
  }
10442
- function parseTopics(value) {
10443
- if (typeof value !== "string" || value.length === 0) return null;
10444
- let parsed;
10445
- try {
10446
- parsed = JSON.parse(value);
10447
- } catch {
10448
- return null;
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
- if (!Array.isArray(parsed)) return null;
10451
- const out = [];
10452
- for (const raw of parsed) {
10453
- if (typeof raw !== "object" || raw === null) continue;
10454
- const { name, kind, source, weight } = raw;
10455
- if (typeof name !== "string" || name.trim().length === 0) continue;
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 out;
11122
+ return { ...record2, data: { ...data, truncated } };
10465
11123
  }
10466
-
10467
- // src/lib/memory/store/remoteTopics.ts
10468
- function topic(value) {
10469
- const t = value;
10470
- 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")
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 createRemoteTopicPersistence(request) {
10475
- return {
10476
- topics: async () => {
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
- async function createRemoteTopic(persistence, name) {
10518
- const canonical = normalizeEntityName(name);
10519
- if (!canonical) return null;
10520
- const existing = (await persistence.topics()).find((t) => t.name === canonical);
10521
- if (existing && !existing.is_deleted) return canonical;
10522
- try {
10523
- await persistence.putTopic(
10524
- { name: canonical, is_deleted: false, ...existing?.kind && { kind: existing.kind } },
10525
- existing?.version ?? 0
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
- } catch (error) {
10528
- if (!(error instanceof RemoteMemoryError) || error.code !== "version_conflict") throw error;
10529
- const current = (await persistence.topics()).find((t) => t.name === canonical);
10530
- if (!current || current.is_deleted) throw error;
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
- async function withRemoteWorkLease(persistence, kind, processorVersion, task) {
10560
- const lease = await persistence.claimWork(kind, processorVersion);
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/memory/store/remotePersistence.ts
10596
- var RemoteMemoryError = class extends Error {
10597
- constructor(message, status, code, retryAfterMs, rateLimitBucket) {
10598
- super(message);
10599
- this.status = status;
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 memoryRecord(value) {
10680
- 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))) {
10681
- throw new Error("Invalid nearby private-memory response");
10682
- }
10683
- return value;
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 createNearbyRequest(options, apiPath = "/api/private-memories") {
10686
- const base = new URL(options.baseUrl);
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
- async function createRemoteMemoryPersistence(options) {
10786
- if (!options.keyId || options.keyId.length > 256)
10787
- throw new Error("A canonical keyId is required");
10788
- const request = createNearbyRequest(options);
10789
- const account = await request("/account", {}, options.signal);
10790
- if (!record(account) || account.state !== "active") {
10791
- throw new RemoteMemoryError(
10792
- "Complete and verify migration before using remote memories",
10793
- 409,
10794
- "memory_not_active"
10795
- );
10796
- }
10797
- if (account.key_id !== options.keyId) {
10798
- throw new RemoteMemoryError(
10799
- "The canonical memory key does not match this account",
10800
- 409,
10801
- "key_mismatch"
10802
- );
10803
- }
10804
- const snapshots = /* @__PURE__ */ new WeakMap();
10805
- const decode = async (value) => {
10806
- const item = memoryRecord(value);
10807
- const decrypt = async (ciphertext) => {
10808
- if (!ciphertextPattern.test(ciphertext))
10809
- throw new Error("Nearby returned unencrypted memory content");
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
- item
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
- if (unknown.length) {
10884
- throw new RemoteMemoryError(
10885
- `Unknown private-memory fields (${unknown.join(", ")}); refusing to forward them`,
10886
- 400,
10887
- "unknown_field"
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
- memory = { ...memory, ...memory.embedding && { embedding: [...memory.embedding] } };
10891
- const path = memoryPath(memory.memory_id);
10892
- const snapshot = typeof expectedVersion === "number" ? void 0 : snapshots.get(expectedVersion);
10893
- if (typeof expectedVersion !== "number" && (!snapshot || snapshot.id !== memory.memory_id || snapshot.version !== expectedVersion.version))
10894
- throw new Error(
10895
- "Expected snapshot must be an unmodified version from this persistence instance"
10896
- );
10897
- expectedVersion = typeof expectedVersion === "number" ? expectedVersion : snapshot.version;
10898
- if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
10899
- throw new Error("expectedVersion must be a nonnegative integer");
10900
- const conflict = () => new RemoteMemoryError("The memory changed since it was read", 409, "version_conflict");
10901
- let existing;
10902
- if (expectedVersion !== 0 && !snapshot) {
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
- existing = memoryRecord(await request(path, {}, signal));
11286
+ await requestEncryptionKey(ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
10905
11287
  } catch (error) {
10906
- if (error instanceof RemoteMemoryError && error.status === 404) throw conflict();
10907
- throw error;
11288
+ getLogger().warn("Failed to request the encryption key for the summary:", error);
10908
11289
  }
10909
11290
  }
10910
- if (existing && existing.version !== expectedVersion) throw conflict();
10911
- const decoded = existing ? await decode(existing).catch(() => void 0) : void 0;
10912
- const reusableContent = snapshot?.encryptedContent ?? existing?.memory.content;
10913
- const content = reusableContent && (snapshot?.content ?? decoded?.memory.content) === memory.content ? reusableContent : await encryptField2(memory.content);
10914
- 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);
10915
- return {
10916
- body: {
10917
- expected_version: expectedVersion,
10918
- memory: { ...memory, content, ...kindValue !== void 0 && { kind_value: kindValue } }
10919
- },
10920
- content: memory.content,
10921
- kindValue: memory.kind_value
10922
- };
10923
- };
10924
- const fetchCandidates = async (embedding2, candidateOptions, withCounts) => {
10925
- const limit = candidateOptions.limit ?? 100;
10926
- if (!Number.isInteger(limit) || limit < 1 || limit > 100)
10927
- throw new Error("Candidate limit must be 1\u2013100");
10928
- for (const [key3, max] of candidateListLimits) {
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
- const result = await request(
10934
- "/candidates",
10935
- {
10936
- method: "POST",
10937
- body: JSON.stringify({
10938
- embedding: embedding2,
10939
- limit,
10940
- force_ids: candidateOptions.force_ids ?? [],
10941
- ...withCounts && { with_counts: true },
10942
- ...Object.fromEntries(
10943
- [
10944
- "scopes",
10945
- "memory_ids",
10946
- "embedding_model",
10947
- "strict_model",
10948
- "include_deleted",
10949
- "deleted_only"
10950
- ].filter(
10951
- (key3) => candidateOptions[key3] !== void 0
10952
- ).map((key3) => [key3, candidateOptions[key3]])
10953
- ),
10954
- ...candidateOptions.fact_types !== void 0 && {
10955
- fact_types: candidateOptions.fact_types
10956
- },
10957
- ...candidateOptions.include_archived !== void 0 && {
10958
- include_archived: candidateOptions.include_archived
10959
- },
10960
- ...candidateOptions.include_quarantined !== void 0 && {
10961
- include_quarantined: candidateOptions.include_quarantined
10962
- },
10963
- ...candidateOptions.include_superseded !== void 0 && {
10964
- include_superseded: candidateOptions.include_superseded
10965
- }
10966
- })
10967
- },
10968
- candidateOptions.signal
10969
- );
10970
- if (!record(result) || !Array.isArray(result.items))
10971
- throw new Error("Invalid nearby candidate response");
10972
- return result;
10973
- };
10974
- const fetchQueryPage = async (queryOptions) => {
10975
- const { signal, ...query } = queryOptions;
10976
- if (query.limit !== void 0 && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200))
10977
- throw new Error("Page limit must be 1\u2013200");
10978
- for (const [key3, max] of queryListLimits) {
10979
- const values = query[key3];
10980
- if (values && values.length > max)
10981
- throw new Error(`Query ${key3} accepts at most ${max} entries (got ${values.length})`);
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
- ...createRemoteTopicPersistence(request),
11011
- ...createRemoteWorkPersistence(request),
11012
- supportsRealtimeChanges: account.realtime_changes === true,
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
- // src/lib/chatStore/types.ts
11136
- var ChatStoreError = class extends Error {
11137
- constructor(message, status, code, retryAfterMs) {
11138
- super(message);
11139
- this.status = status;
11140
- this.code = code;
11141
- this.retryAfterMs = retryAfterMs;
11142
- this.name = "ChatStoreError";
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 live = (await store.getMany(gone.map(ref2), signal)).filter((s) => !s.record.isDeleted);
11871
- const tombstoning = new Set(live.map((s) => key(s.record)));
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 live.map(({ record: record2, version }) => {
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 [live, archived, quarantined] = await Promise.all([
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, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
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 > maxAttempts) {
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-7QUL3TOE.mjs");
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 live = async (id2) => {
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 live(id2);
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 live(id2)),
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([live(id2), live(supersededById)]);
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 live(id2);
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 result = await runToolLoop({
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
- pendingResumeRef.current = {
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 { content, thinking } = extractAssistantText(data);
31814
- let parentMessageId = ctx.userMessageUniqueId;
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 (!viaQueue) return backend.upsertMessage(rowOpts);
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: detachedResult.resume ?? null,
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: detachedResult.resume ?? null,
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,