@anuma/sdk 10.0.2 → 10.0.4

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.
@@ -14346,1183 +14346,1183 @@ var init_logger = __esm({
14346
14346
  }
14347
14347
  });
14348
14348
 
14349
- // src/clientConfig.ts
14350
- var clientConfig_exports = {};
14351
- __export(clientConfig_exports, {
14352
- BASE_URL: () => BASE_URL,
14353
- MCP_R2_DOMAIN: () => MCP_R2_DOMAIN,
14354
- createClientConfig: () => createClientConfig,
14355
- resolveBaseUrl: () => resolveBaseUrl
14356
- });
14357
- function readGlobal(key) {
14358
- if (typeof globalThis === "undefined") return void 0;
14359
- const value = globalThis[key];
14360
- return typeof value === "string" && value.length > 0 ? value : void 0;
14361
- }
14362
- function resolveBaseUrl() {
14363
- const fromProcess = typeof process !== "undefined" ? process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || process.env.EXPO_PUBLIC_API_URL : void 0;
14364
- const fromGlobal = readGlobal("API_URL") ?? readGlobal("NEXT_PUBLIC_API_URL") ?? readGlobal("EXPO_PUBLIC_API_URL");
14365
- return (fromProcess || fromGlobal || DEFAULT_BASE_URL).replace(/\/+$/, "");
14366
- }
14367
- var DEFAULT_BASE_URL, BASE_URL, MCP_R2_DOMAIN, createClientConfig;
14368
- var init_clientConfig = __esm({
14369
- "src/clientConfig.ts"() {
14349
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js
14350
+ var require_action = __commonJS({
14351
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js"(exports2) {
14370
14352
  "use strict";
14371
- DEFAULT_BASE_URL = "https://portal.anuma-dev.ai";
14372
- BASE_URL = resolveBaseUrl();
14373
- MCP_R2_DOMAIN = "4cf0e0ea50b97e72386fcf2f92a2d4e8.r2.cloudflarestorage.com";
14374
- createClientConfig = (config) => ({
14375
- ...config,
14376
- baseUrl: BASE_URL
14377
- });
14353
+ var _interopRequireDefault = require_interopRequireDefault();
14354
+ exports2.__esModule = true;
14355
+ exports2.reader = reader;
14356
+ exports2.writer = writer;
14357
+ var _extends2 = _interopRequireDefault(require_extends());
14358
+ function writer(target, key, descriptor) {
14359
+ var actionName = "".concat(target.table || target.constructor.name, ".").concat(key);
14360
+ return (0, _extends2.default)({}, descriptor, {
14361
+ value: function(...args) {
14362
+ var _this = this;
14363
+ return this.database.write(function() {
14364
+ return descriptor.value.apply(_this, args);
14365
+ }, actionName);
14366
+ }
14367
+ });
14368
+ }
14369
+ function reader(target, key, descriptor) {
14370
+ var actionName = "".concat(target.table || target.constructor.name, ".").concat(key);
14371
+ return (0, _extends2.default)({}, descriptor, {
14372
+ value: function(...args) {
14373
+ var _this2 = this;
14374
+ return this.database.read(function() {
14375
+ return descriptor.value.apply(_this2, args);
14376
+ }, actionName);
14377
+ }
14378
+ });
14379
+ }
14378
14380
  }
14379
14381
  });
14380
14382
 
14381
- // src/lib/taskType.ts
14382
- function taskTypeHeader(taskType) {
14383
- return taskType ? { [TASK_TYPE_HEADER]: taskType } : {};
14384
- }
14385
- var TASK_TYPE_HEADER;
14386
- var init_taskType = __esm({
14387
- "src/lib/taskType.ts"() {
14383
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/children/index.js
14384
+ var require_children = __commonJS({
14385
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/children/index.js"(exports2) {
14388
14386
  "use strict";
14389
- TASK_TYPE_HEADER = "X-Anuma-Task-Type";
14387
+ var _interopRequireDefault = require_interopRequireDefault();
14388
+ exports2.__esModule = true;
14389
+ exports2.default = void 0;
14390
+ var _makeDecorator = _interopRequireDefault(require_makeDecorator());
14391
+ var _logError = _interopRequireDefault(require_logError());
14392
+ var _invariant = _interopRequireDefault(require_invariant());
14393
+ var Q13 = _interopRequireWildcard(require_QueryDescription());
14394
+ function _getRequireWildcardCache(e) {
14395
+ if ("function" != typeof WeakMap) return null;
14396
+ var r = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap();
14397
+ return (_getRequireWildcardCache = function _getRequireWildcardCache2(e2) {
14398
+ return e2 ? t : r;
14399
+ })(e);
14400
+ }
14401
+ function _interopRequireWildcard(e, r) {
14402
+ if (!r && e && e.__esModule) return e;
14403
+ if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };
14404
+ var t = _getRequireWildcardCache(r);
14405
+ if (t && t.has(e)) return t.get(e);
14406
+ var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor;
14407
+ for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
14408
+ var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;
14409
+ i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];
14410
+ }
14411
+ return n.default = e, t && t.set(e, n), n;
14412
+ }
14413
+ var children = (0, _makeDecorator.default)(function(childTable) {
14414
+ return function() {
14415
+ return {
14416
+ get: function() {
14417
+ var that = this;
14418
+ that._childrenQueryCache = that._childrenQueryCache || {};
14419
+ var cachedQuery = that._childrenQueryCache[childTable];
14420
+ if (cachedQuery) {
14421
+ return cachedQuery;
14422
+ }
14423
+ var model = that.asModel;
14424
+ var childCollection = model.collections.get(childTable);
14425
+ var association = model.constructor.associations[childTable];
14426
+ (0, _invariant.default)(association && "has_many" === association.type, "@children decorator used for a table that's not has_many");
14427
+ var query = childCollection.query(Q13.where(association.foreignKey, model.id));
14428
+ that._childrenQueryCache[childTable] = query;
14429
+ return query;
14430
+ },
14431
+ set: function() {
14432
+ (0, _logError.default)("Setter called on a @children-marked property");
14433
+ }
14434
+ };
14435
+ };
14436
+ });
14437
+ var _default = exports2.default = children;
14390
14438
  }
14391
14439
  });
14392
14440
 
14393
- // src/lib/pii/patterns.ts
14394
- function luhnCheck(digits) {
14395
- let sum = 0;
14396
- let alternate = false;
14397
- for (let i = digits.length - 1; i >= 0; i--) {
14398
- let n = parseInt(digits[i], 10);
14399
- if (alternate) {
14400
- n *= 2;
14401
- if (n > 9) n -= 9;
14402
- }
14403
- sum += n;
14404
- alternate = !alternate;
14405
- }
14406
- return sum % 10 === 0;
14407
- }
14408
- var PII_PATTERNS;
14409
- var init_patterns = __esm({
14410
- "src/lib/pii/patterns.ts"() {
14441
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/common.js
14442
+ var require_common2 = __commonJS({
14443
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/common.js"(exports2) {
14411
14444
  "use strict";
14412
- PII_PATTERNS = [
14413
- {
14414
- category: "EMAIL",
14415
- regex: /\b[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]{1,255}\.[a-zA-Z]{2,24}\b/g
14416
- },
14417
- {
14418
- category: "SSN",
14419
- regex: /\b\d{3}-\d{2}-\d{4}\b/g,
14420
- validate: (match) => {
14421
- const parts = match.split("-");
14422
- const area = parseInt(parts[0], 10);
14423
- return area !== 0 && area !== 666 && area < 900;
14424
- }
14425
- },
14426
- {
14427
- category: "SSN",
14428
- regex: /\b\d{3}[ ]?\d{2}[ ]?\d{4}\b/g,
14429
- context: /(?:\bssn\b|\bsocial\s+security(?:\s+(?:number|no\.?|#))?\b)[^\d\n]{0,12}$/i,
14430
- validate: (match) => {
14431
- const digits = match.replace(/\D/g, "");
14432
- if (digits.length !== 9) return false;
14433
- const area = parseInt(digits.slice(0, 3), 10);
14434
- return area !== 0 && area !== 666 && area < 900;
14435
- }
14436
- },
14437
- {
14438
- category: "CREDIT_CARD",
14439
- regex: /\b\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{1,7}\b|\b\d{13,19}\b/g,
14440
- validate: (match) => {
14441
- const digits = match.replace(/\D/g, "");
14442
- if (digits.length < 13 || digits.length > 19) return false;
14443
- return luhnCheck(digits);
14444
- }
14445
- },
14446
- {
14447
- category: "IP_ADDRESS",
14448
- regex: /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g,
14449
- validate: (match) => {
14450
- return match !== "0.0.0.0" && match !== "127.0.0.1" && match !== "255.255.255.255";
14451
- }
14452
- },
14453
- {
14454
- category: "IP_ADDRESS",
14455
- regex: /(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:))/g,
14456
- validate: (match) => match !== "::1" && match !== "::"
14457
- },
14458
- {
14459
- category: "PHONE",
14460
- regex: /(?:\+\d{1,3}[\s.-])?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\b|\+\d{10,15}\b/g
14461
- },
14462
- {
14463
- category: "API_KEY",
14464
- regex: /\b(?:sk|pk|api|key|token|secret|bearer|access)[_-]?[a-zA-Z0-9_-]{20,}\b/gi,
14465
- validate: (match) => {
14466
- const hasDigit = /\d/.test(match);
14467
- const hasMixedCase = /[a-z]/.test(match) && /[A-Z]/.test(match);
14468
- return hasDigit || hasMixedCase;
14469
- }
14470
- },
14471
- {
14472
- category: "US_ADDRESS",
14473
- regex: /\b\d{1,5}[ \t]+(?:[A-Z][a-z]+[ \t]+){1,3}(?:St(?:reet)?|Ave(?:nue)?|Blvd|Boulevard|Dr(?:ive)?|Ln|Lane|Rd|Road|Ct|Court|Pl(?:ace)?|Way|Cir(?:cle)?|Pkwy|Parkway|Hwy|Highway)\.?(?=[\s,.;:!?)]|$)/g,
14474
- validate: (match) => {
14475
- return !/^0/.test(match.trim());
14476
- }
14477
- },
14478
- {
14479
- category: "DATE_OF_BIRTH",
14480
- regex: /\b(?:(?:0?[1-9]|1[0-2])[/-](?:0?[1-9]|[12]\d|3[01])[/-](?:19|20)\d{2})\b/g,
14481
- context: /(?:\bdob\b|\bd\.?o\.?b\.?|\bborn\b|\bbirth\s?date\b|\bbirthdate\b|\bbirthday\b|\bdate\s+of\s+birth\b)[^\d\n]{0,12}$/i,
14482
- validate: (match) => {
14483
- const m = match.match(/(\d{1,2})[/-](\d{1,2})[/-](\d{4})/);
14484
- if (!m) return false;
14485
- const month = parseInt(m[1], 10);
14486
- const day = parseInt(m[2], 10);
14487
- const year = parseInt(m[3], 10);
14488
- const isLeap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
14489
- const daysInMonth = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
14490
- return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth[month - 1];
14491
- }
14445
+ var _interopRequireDefault = require_interopRequireDefault();
14446
+ exports2.__esModule = true;
14447
+ exports2.ensureDecoratorUsedProperly = ensureDecoratorUsedProperly;
14448
+ var _invariant = _interopRequireDefault(require_invariant());
14449
+ function ensureDecoratorUsedProperly(columnName, target, key, descriptor) {
14450
+ (0, _invariant.default)(columnName, "Pass column name (raw field name) to the decorator - error in ".concat(target.constructor.name, ".prototype.").concat(key, " given."));
14451
+ if (descriptor) {
14452
+ (0, _invariant.default)("initializer" in descriptor, "Model field decorators can only be used for simple properties - method, setter or getter ".concat(target.constructor.name, ".prototype.").concat(key, " given."));
14453
+ (0, _invariant.default)("function" !== typeof descriptor.initializer, 'Model field decorators must not be used on properties with a default value - error in "'.concat(target.constructor.name, ".prototype.").concat(key, '".'));
14492
14454
  }
14493
- ];
14455
+ }
14494
14456
  }
14495
14457
  });
14496
14458
 
14497
- // src/lib/pii/redactor.ts
14498
- function escapeRegExp(value) {
14499
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14500
- }
14501
- function isWordChar(ch) {
14502
- return /[A-Za-z0-9_]/.test(ch) || ch.charCodeAt(0) > 127;
14503
- }
14504
- function isPiiRedactor(value) {
14505
- return typeof value === "object" && value !== null && typeof value.redactMessages === "function" && typeof value.deAnonymize === "function";
14506
- }
14507
- function resolvePiiRedactor(piiRedaction) {
14508
- if (piiRedaction === true) return new PiiRedactor();
14509
- if (!piiRedaction) return void 0;
14510
- if (isPiiRedactor(piiRedaction)) return piiRedaction;
14511
- console.warn(
14512
- "[PiiRedactor] `piiRedaction` is neither `true` nor a PiiRedactor instance; PII redaction is DISABLED for this request."
14513
- );
14514
- return void 0;
14515
- }
14516
- function createStreamingDeAnonymizer(redactor, emit) {
14517
- let buffer = "";
14518
- const trailingFragment = /\[[A-Za-z0-9_]*$/;
14519
- return {
14520
- push(chunk) {
14521
- if (!chunk) return;
14522
- buffer += chunk;
14523
- const m = buffer.match(trailingFragment);
14524
- if (!m || m.index === void 0) {
14525
- emit(redactor.deAnonymize(buffer));
14526
- buffer = "";
14527
- } else if (m.index > 0) {
14528
- emit(redactor.deAnonymize(buffer.slice(0, m.index)));
14529
- buffer = buffer.slice(m.index);
14530
- }
14531
- },
14532
- flush() {
14533
- if (buffer) {
14534
- emit(redactor.deAnonymize(buffer));
14535
- buffer = "";
14536
- }
14537
- }
14538
- };
14539
- }
14540
- var PiiRedactor;
14541
- var init_redactor = __esm({
14542
- "src/lib/pii/redactor.ts"() {
14459
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/json/index.js
14460
+ var require_json = __commonJS({
14461
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/json/index.js"(exports2) {
14543
14462
  "use strict";
14544
- init_patterns();
14545
- PiiRedactor = class {
14546
- constructor(options = {}) {
14547
- /** Maps original PII value → placeholder tag. */
14548
- this.valueToPlaceholder = /* @__PURE__ */ new Map();
14549
- /** Maps placeholder tag → original PII value (reverse lookup for de-anonymization). */
14550
- this.placeholderToValue = /* @__PURE__ */ new Map();
14551
- /** Next index per category for placeholder numbering. */
14552
- this.categoryCounters = /* @__PURE__ */ new Map();
14553
- /**
14554
- * Matches any placeholder-shaped token for this redactor's categories —
14555
- * bracketed `[EMAIL_1]` OR bare `EMAIL_1`, case-insensitive — capturing the
14556
- * body. Drives {@link restoreForStorage}: the extraction / consolidation
14557
- * models echo placeholders mangled (brackets dropped, re-cased), and a single
14558
- * pass with this matcher restores the assigned ones and flags the rest in one
14559
- * go. Built lazily from the union of pattern categories AND any categories
14560
- * minted at runtime (e.g. NER `PERSON`/`LOCATION`/`ORG`): `undefined` = stale
14561
- * (rebuild on next use), `null` = built but there are no categories.
14562
- */
14563
- this.storagePattern = void 0;
14564
- /**
14565
- * Cached body→value lookups for {@link restoreForStorage}, built lazily from
14566
- * the assigned placeholders and invalidated on each mint. `exact` is keyed by
14567
- * the literal body (collision-proof); `ci` by upper-cased body for re-cased
14568
- * echoes, minus any `ciCollisions` where two distinct bodies share a form.
14569
- */
14570
- this.storageLookup = null;
14571
- /** Whether the non-text-content bypass warning has already been emitted. */
14572
- this.warnedNonText = false;
14573
- /** Whether the NER-detector-failure warning has already been emitted. */
14574
- this.warnedNerFailure = false;
14575
- this.nerDetector = options.nerDetector;
14576
- if (options.patterns) {
14577
- this.patterns = options.patterns;
14578
- } else {
14579
- let base = PII_PATTERNS;
14580
- if (options.excludeCategories?.length) {
14581
- const excluded = new Set(options.excludeCategories);
14582
- base = base.filter((p) => !excluded.has(p.category));
14583
- }
14584
- this.patterns = options.extraPatterns ? [...base, ...options.extraPatterns] : base;
14585
- }
14586
- this.patternCategories = [...new Set(this.patterns.map((p) => String(p.category)))];
14587
- }
14588
- /**
14589
- * Build the {@link storagePattern} from the union of pattern categories and
14590
- * any categories minted at runtime (NER entities arrive only when redaction
14591
- * runs, so this can't be fixed at construction). Bracketed `[EMAIL_1]` OR
14592
- * bare `EMAIL_1`, capturing the body, case-insensitive, global. `\d+` (not a
14593
- * fixed body) spans any index; `\b` on the bare arm keeps it off prose like
14594
- * "email 1 of 3".
14595
- */
14596
- buildStoragePattern() {
14597
- const categories = [.../* @__PURE__ */ new Set([...this.patternCategories, ...this.categoryCounters.keys()])];
14598
- if (categories.length === 0) return null;
14599
- const categoryAlt = categories.sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
14600
- return new RegExp(`\\[((?:${categoryAlt})_\\d+)\\]|\\b((?:${categoryAlt})_\\d+)\\b`, "gi");
14601
- }
14602
- /**
14603
- * Returns the current number of unique PII values tracked.
14604
- */
14605
- get size() {
14606
- return this.valueToPlaceholder.size;
14607
- }
14608
- /**
14609
- * Returns a snapshot of all placeholder → original value mappings.
14610
- * Useful for debugging or UI display. This is a copy — mutating it (or
14611
- * later redactions) does not affect the returned map and vice versa.
14612
- */
14613
- getMappings() {
14614
- return new Map(this.placeholderToValue);
14463
+ exports2.__esModule = true;
14464
+ exports2.jsonDecorator = exports2.default = void 0;
14465
+ var _common = require_common2();
14466
+ var parseJSON = function(value) {
14467
+ if (null === value || value === void 0 || "" === value) {
14468
+ return void 0;
14615
14469
  }
14616
- /**
14617
- * Get or create a placeholder for a PII value.
14618
- * The same value always gets the same placeholder within a redactor instance.
14619
- */
14620
- getPlaceholder(category, value) {
14621
- const normalizedValue = value.trim();
14622
- const existing = this.valueToPlaceholder.get(normalizedValue);
14623
- if (existing) return existing;
14624
- const existingCount = this.categoryCounters.get(category);
14625
- const count = (existingCount ?? 0) + 1;
14626
- this.categoryCounters.set(category, count);
14627
- if (existingCount === void 0) this.storagePattern = void 0;
14628
- const placeholder = `[${category}_${count}]`;
14629
- this.valueToPlaceholder.set(normalizedValue, placeholder);
14630
- this.placeholderToValue.set(placeholder, normalizedValue);
14631
- this.storageLookup = null;
14632
- return placeholder;
14470
+ try {
14471
+ return JSON.parse(value);
14472
+ } catch (_) {
14473
+ return void 0;
14633
14474
  }
14634
- /**
14635
- * Find all matches of a single pattern in `text`, applying its `validate` and
14636
- * `context` guards. Matches are non-overlapping and ascending (the global
14637
- * regex advances `lastIndex` past each match). Shared by the sync {@link scan}
14638
- * (sequential, per-pattern) and the async {@link collectRegexSpans} (span)
14639
- * paths so the detection rules live in exactly one place.
14640
- */
14641
- findMatches(text12, pattern) {
14642
- const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
14643
- const found = [];
14644
- let m;
14645
- while ((m = regex.exec(text12)) !== null) {
14646
- const value = m[0];
14647
- if (pattern.validate && !pattern.validate(value)) continue;
14648
- if (pattern.context) {
14649
- const before = text12.slice(Math.max(0, m.index - 40), m.index);
14650
- if (!pattern.context.test(before)) continue;
14475
+ };
14476
+ var defaultOptions = {
14477
+ memo: false
14478
+ };
14479
+ var jsonDecorator = exports2.jsonDecorator = function(rawFieldName, sanitizer, options = defaultOptions) {
14480
+ return function(target, key, descriptor) {
14481
+ (0, _common.ensureDecoratorUsedProperly)(rawFieldName, target, key, descriptor);
14482
+ return {
14483
+ configurable: true,
14484
+ enumerable: true,
14485
+ get: function get() {
14486
+ var model = this;
14487
+ var rawValue = model.asModel._getRaw(rawFieldName);
14488
+ if (options.memo) {
14489
+ model._jsonDecoratorCache = model._jsonDecoratorCache || {};
14490
+ var cachedEntry = model._jsonDecoratorCache[rawFieldName];
14491
+ if (cachedEntry && cachedEntry[0] === rawValue) {
14492
+ return cachedEntry[1];
14493
+ }
14494
+ }
14495
+ var parsedValue = parseJSON(rawValue);
14496
+ var sanitized = sanitizer(parsedValue, model);
14497
+ if (options.memo) {
14498
+ model._jsonDecoratorCache[rawFieldName] = [rawValue, sanitized];
14499
+ }
14500
+ return sanitized;
14501
+ },
14502
+ set: function set(json4) {
14503
+ var model = this;
14504
+ var sanitizedValue = sanitizer(json4, model);
14505
+ var stringifiedValue = null != sanitizedValue ? JSON.stringify(sanitizedValue) : null;
14506
+ model.asModel._setRaw(rawFieldName, stringifiedValue);
14651
14507
  }
14652
- found.push({ match: value, index: m.index });
14653
- }
14654
- return found;
14655
- }
14656
- /**
14657
- * Scan text for PII and rebuild it, replacing each match with whatever
14658
- * `makeReplacement(category, value)` returns. Shared by `redactText`
14659
- * (numbered, stateful placeholders) and `maskText` (unnumbered, stateless).
14660
- *
14661
- * Patterns are applied sequentially: each scans the text already rewritten by
14662
- * earlier patterns, so an earlier (more specific) pattern claims a region
14663
- * before a later one can — placeholders are inert and match no pattern.
14664
- */
14665
- scan(text12, makeReplacement) {
14666
- const matches = [];
14667
- let redacted = text12;
14668
- for (const pattern of this.patterns) {
14669
- const found = this.findMatches(redacted, pattern);
14670
- if (found.length === 0) continue;
14671
- let result = "";
14672
- let cursor = 0;
14673
- for (const { match, index } of found) {
14674
- const placeholder = makeReplacement(pattern.category, match);
14675
- matches.push({ category: pattern.category, original: match, placeholder });
14676
- result += redacted.slice(cursor, index) + placeholder;
14677
- cursor = index + match.length;
14678
- }
14679
- result += redacted.slice(cursor);
14680
- redacted = result;
14681
- }
14682
- return { text: redacted, matches };
14683
- }
14684
- /**
14685
- * Scan and redact PII from a single text string, using numbered, reversible
14686
- * placeholders ([EMAIL_1], …) tracked on this instance.
14687
- */
14688
- redactText(text12) {
14689
- return this.scan(text12, (category, value) => this.getPlaceholder(category, value));
14690
- }
14691
- /**
14692
- * Mask PII with unnumbered, NON-reversible tokens ([EMAIL], [SSN], …) without
14693
- * mutating this instance's state. Use for one-way purposes where the value is
14694
- * never restored — notably embedding inputs: it keeps real PII off the
14695
- * embeddings endpoint while staying deterministic, so the same content always
14696
- * masks identically and search stays consistent across conversations.
14697
- */
14698
- maskText(text12) {
14699
- return this.scan(text12, (category) => `[${category}]`).text;
14700
- }
14701
- /** All regex matches as spans on the ORIGINAL text (priority 0). Uses the
14702
- * same {@link findMatches} guards as the sync scan. */
14703
- collectRegexSpans(text12) {
14704
- const spans = [];
14705
- this.patterns.forEach((pattern, patternIndex) => {
14706
- for (const { match, index } of this.findMatches(text12, pattern)) {
14707
- spans.push({
14708
- start: index,
14709
- end: index + match.length,
14710
- category: pattern.category,
14711
- priority: 0,
14712
- order: patternIndex
14713
- });
14508
+ };
14509
+ };
14510
+ };
14511
+ var _default = exports2.default = jsonDecorator;
14512
+ }
14513
+ });
14514
+
14515
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/nochange/index.js
14516
+ var require_nochange = __commonJS({
14517
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/nochange/index.js"(exports2) {
14518
+ "use strict";
14519
+ var _interopRequireDefault = require_interopRequireDefault();
14520
+ exports2.__esModule = true;
14521
+ exports2.default = void 0;
14522
+ var _extends2 = _interopRequireDefault(require_extends());
14523
+ var _makeDecorator = _interopRequireDefault(require_makeDecorator());
14524
+ var _invariant = _interopRequireDefault(require_invariant());
14525
+ var nochange = (0, _makeDecorator.default)(function() {
14526
+ return function(target, key, descriptor) {
14527
+ (0, _invariant.default)(descriptor.set, "@nochange can only be applied to model fields (to properties with a setter)");
14528
+ var errorMessage = "Attempt to set a new value on a @nochange field: ".concat(target.constructor.name, ".prototype.").concat(key);
14529
+ return (0, _extends2.default)({}, descriptor, {
14530
+ set: function(value) {
14531
+ var model = this;
14532
+ (0, _invariant.default)("create" === model.asModel._preparedState, errorMessage);
14533
+ descriptor.set.call(model, value);
14714
14534
  }
14715
14535
  });
14716
- return spans;
14717
- }
14718
- /**
14719
- * Normalize raw NER spans: clamp to the text bounds, drop empty/categoryless
14720
- * spans, and snap each to whole-word boundaries. Snapping prevents a detector
14721
- * that tagged only part of a word (a common WordPiece artifact — e.g. "Strip"
14722
- * of "Stripe") from producing a corrupting partial placeholder like
14723
- * `[ORG_1]e`.
14724
- */
14725
- normalizeNerSpans(text12, raw) {
14726
- if (!Array.isArray(raw)) return [];
14727
- const out = [];
14728
- raw.forEach((s, i) => {
14729
- if (!Number.isFinite(s.start) || !Number.isFinite(s.end)) return;
14730
- let start = Math.max(0, Math.min(Math.trunc(s.start), text12.length));
14731
- let end = Math.max(0, Math.min(Math.trunc(s.end), text12.length));
14732
- if (end <= start) return;
14733
- while (start > 0 && isWordChar(text12[start - 1])) start--;
14734
- while (end < text12.length && isWordChar(text12[end])) end++;
14735
- const category = String(s.category ?? "").trim();
14736
- if (!category) return;
14737
- out.push({ start, end, category, priority: 1, order: i });
14738
- });
14739
- return out;
14740
- }
14741
- /** Run the configured detector defensively: failures degrade to regex-only
14742
- * (fail-open on detection, not on protection) with a one-time warning. */
14743
- async detectNerSpans(text12) {
14744
- if (!this.nerDetector) return [];
14745
- try {
14746
- return this.normalizeNerSpans(text12, await this.nerDetector.detect(text12));
14747
- } catch (err) {
14748
- this.warnNerFailure(err);
14749
- return [];
14750
- }
14751
- }
14752
- /**
14753
- * Resolve overlaps by placing higher-priority spans first and dropping any
14754
- * span that overlaps one already placed. Regex (priority 0) is placed before
14755
- * NER (priority 1), and within regex by ascending pattern index — so regex
14756
- * always wins on overlap and the regex-only result reproduces the sync scan's
14757
- * sequential "earlier pattern wins". O(n²) but n (spans per message) is tiny.
14758
- *
14759
- * The returned spans stay in `(priority, order, start)` order — i.e. mint
14760
- * order — which {@link rebuildSpans} relies on.
14761
- */
14762
- resolveOverlaps(spans) {
14763
- const ordered = [...spans].sort(
14764
- (a, b) => a.priority - b.priority || a.order - b.order || a.start - b.start
14765
- );
14766
- const placed = [];
14767
- for (const s of ordered) {
14768
- if (!placed.some((p) => s.start < p.end && p.start < s.end)) placed.push(s);
14769
- }
14770
- return placed;
14771
- }
14772
- /**
14773
- * Rebuild `text` from resolved (non-overlapping) spans. `spans` must arrive in
14774
- * mint order — `(priority, order, start)`, as {@link resolveOverlaps} returns —
14775
- * so placeholders are MINTED by iterating it directly (regex categories first,
14776
- * in pattern then document order, matching the sync scan's numbering). The
14777
- * string is then EMITTED in document order.
14778
- */
14779
- rebuildSpans(text12, spans, makeReplacement) {
14780
- const placeholderByStart = /* @__PURE__ */ new Map();
14781
- const matches = [];
14782
- for (const s of spans) {
14783
- const value = text12.slice(s.start, s.end);
14784
- const placeholder = makeReplacement(s.category, value);
14785
- placeholderByStart.set(s.start, placeholder);
14786
- matches.push({ category: s.category, original: value, placeholder });
14787
- }
14788
- const emitOrder = [...spans].sort((a, b) => a.start - b.start);
14789
- let result = "";
14790
- let cursor = 0;
14791
- for (const s of emitOrder) {
14792
- result += text12.slice(cursor, s.start) + (placeholderByStart.get(s.start) ?? "");
14793
- cursor = s.end;
14794
- }
14795
- result += text12.slice(cursor);
14796
- return { text: result, matches };
14797
- }
14798
- /** Merge regex + NER spans for `text` into a resolved, non-overlapping set. */
14799
- async detectAllSpans(text12) {
14800
- const nerPromise = this.detectNerSpans(text12);
14801
- const regexSpans = this.collectRegexSpans(text12);
14802
- const ner = await nerPromise;
14803
- return this.resolveOverlaps([...regexSpans, ...ner]);
14804
- }
14805
- /**
14806
- * Async counterpart to {@link redactText}: redacts structured PII (regex) AND
14807
- * unstructured PII (the configured {@link NerDetector}) with numbered,
14808
- * reversible placeholders. With no detector, identical to `redactText`.
14809
- */
14810
- async redactTextAsync(text12) {
14811
- if (!this.nerDetector) return this.redactText(text12);
14812
- const spans = await this.detectAllSpans(text12);
14813
- return this.rebuildSpans(
14814
- text12,
14815
- spans,
14816
- (category, value) => this.getPlaceholder(category, value)
14817
- );
14818
- }
14819
- /**
14820
- * Async counterpart to {@link maskText}: stateless `[CATEGORY]` masking over
14821
- * regex + NER matches. With no detector, identical to `maskText`.
14822
- */
14823
- async maskTextAsync(text12) {
14824
- if (!this.nerDetector) return this.maskText(text12);
14825
- const spans = await this.detectAllSpans(text12);
14826
- return this.rebuildSpans(text12, spans, (category) => `[${category}]`).text;
14827
- }
14828
- /**
14829
- * Async counterpart to {@link redactMessages}. Text parts are redacted
14830
- * sequentially so placeholder numbering stays deterministic across the
14831
- * conversation. With no detector, identical to `redactMessages`.
14832
- */
14833
- async redactMessagesAsync(messages) {
14834
- if (!this.nerDetector) return this.redactMessages(messages);
14835
- const allMatches = [];
14836
- const redactedMessages = [];
14837
- for (const msg of messages) {
14838
- if (!msg.content) {
14839
- redactedMessages.push(msg);
14840
- continue;
14841
- }
14842
- const newContent = [];
14843
- for (const part of msg.content) {
14844
- if (part.type === "text" && part.text) {
14845
- const result = await this.redactTextAsync(part.text);
14846
- allMatches.push(...result.matches);
14847
- newContent.push(result.matches.length > 0 ? { ...part, text: result.text } : part);
14848
- } else {
14849
- if (part.type !== "text") this.warnNonTextBypass();
14850
- newContent.push(part);
14851
- }
14852
- }
14853
- redactedMessages.push({ ...msg, content: newContent });
14854
- }
14855
- return { messages: redactedMessages, matches: allMatches };
14856
- }
14857
- /**
14858
- * Redact PII from an array of LlmapiMessage objects.
14859
- * Returns new message objects — originals are not mutated.
14860
- *
14861
- * All roles' text content is redacted, not just user/system: tool results
14862
- * can return PII fetched from external systems, and assistant history is
14863
- * persisted with original (de-anonymized) values, so both must be re-redacted
14864
- * before they go back over the wire. Placeholders the model already emitted
14865
- * are inert here (they don't match any PII pattern), so re-redaction is safe.
14866
- */
14867
- redactMessages(messages) {
14868
- const allMatches = [];
14869
- const redactedMessages = messages.map((msg) => {
14870
- if (!msg.content) return msg;
14871
- const newContent = msg.content.map((part) => {
14872
- if (part.type === "text" && part.text) {
14873
- const result = this.redactText(part.text);
14874
- allMatches.push(...result.matches);
14875
- if (result.matches.length > 0) {
14876
- return { ...part, text: result.text };
14877
- }
14878
- } else if (part.type !== "text") {
14879
- this.warnNonTextBypass();
14880
- }
14881
- return part;
14882
- });
14883
- return { ...msg, content: newContent };
14884
- });
14885
- return { messages: redactedMessages, matches: allMatches };
14886
- }
14887
- /** Emit the non-text bypass warning at most once per redactor instance. */
14888
- warnNonTextBypass() {
14889
- if (this.warnedNonText) return;
14890
- this.warnedNonText = true;
14891
- console.warn(
14892
- "[PiiRedactor] A message contains non-text content (images, files, or attachments) that is NOT scanned for PII \u2014 only text parts are redacted."
14893
- );
14894
- }
14895
- /** Emit the NER-detector-failure warning at most once per redactor instance. */
14896
- warnNerFailure(err) {
14897
- if (this.warnedNerFailure) return;
14898
- this.warnedNerFailure = true;
14899
- console.warn(
14900
- "[PiiRedactor] The NER detector threw; falling back to regex-only redaction for this call's unstructured PII. The detector is still retried on subsequent calls; this warning is emitted only once.",
14901
- err
14902
- );
14903
- }
14904
- /**
14905
- * Restore original PII values in text that contains placeholders.
14906
- * Used to de-anonymize LLM responses before displaying to the user.
14907
- *
14908
- * Exact: matches the literal `[EMAIL_1]` form only. The storage paths use
14909
- * {@link restoreForStorage} instead, which tolerates the mangled echoes the
14910
- * extraction models produce.
14911
- */
14912
- deAnonymize(text12) {
14913
- if (!text12.includes("[")) return text12;
14914
- let restored = text12;
14915
- for (const [placeholder, original] of this.placeholderToValue) {
14916
- if (restored.includes(placeholder)) {
14917
- restored = restored.split(placeholder).join(original);
14918
- }
14919
- }
14920
- return restored;
14921
- }
14922
- /**
14923
- * De-anonymize for PERSISTENCE (auto-extraction / consolidation), tolerant of
14924
- * the ways the extraction models mangle a placeholder when echoing it back:
14925
- * dropped brackets (`EMAIL_1`) and changed case (`email_1` / `Email_1`).
14926
- *
14927
- * Returns the restored text plus `unresolved` — true when a placeholder-shaped
14928
- * token for one of this redactor's categories was NOT assigned during
14929
- * redaction (a model hallucination, or an ambiguous re-cased collision).
14930
- * Callers drop/degrade such facts so an opaque token never reaches the vault.
14931
- *
14932
- * One left-to-right pass over the ORIGINAL text. Two properties matter:
14933
- * - a restored value is never re-scanned — so a real value that happens to
14934
- * contain a `<CATEGORY>_<n>` substring (e.g. the email `ssn_1@example.com`,
14935
- * or an API key containing `EMAIL_1`) is neither corrupted nor false-flagged;
14936
- * - `unresolved` is derived from the lookup miss during this pass, not from a
14937
- * regex re-scan of the output — so it can't trip over a restored value.
14938
- */
14939
- restoreForStorage(text12) {
14940
- if (this.storagePattern === void 0) this.storagePattern = this.buildStoragePattern();
14941
- if (!this.storagePattern || this.placeholderToValue.size === 0) {
14942
- return { text: text12, unresolved: false };
14943
- }
14944
- this.storageLookup ?? (this.storageLookup = this.buildStorageLookup());
14945
- const { exact, ci, ciCollisions } = this.storageLookup;
14946
- let unresolved = false;
14947
- const restored = text12.replace(
14948
- this.storagePattern,
14949
- (match, bracketed, bare) => {
14950
- const body = bracketed ?? bare ?? "";
14951
- const exactHit = exact.get(body);
14952
- if (exactHit !== void 0) return exactHit;
14953
- const key = body.toUpperCase();
14954
- if (!ciCollisions.has(key)) {
14955
- const ciHit = ci.get(key);
14956
- if (ciHit !== void 0) return ciHit;
14957
- }
14958
- unresolved = true;
14959
- return match;
14960
- }
14961
- );
14962
- return { text: restored, unresolved };
14963
- }
14964
- /**
14965
- * Build the {@link restoreForStorage} body→value lookups from the assigned
14966
- * placeholders. `exact` (literal body) is collision-proof; `ci` (upper-cased
14967
- * body) handles re-cased echoes, except where two distinct bodies share an
14968
- * upper-cased form — those are recorded in `ciCollisions` so a re-cased echo
14969
- * of them is left unresolved rather than resolved to the wrong value.
14970
- */
14971
- buildStorageLookup() {
14972
- const exact = /* @__PURE__ */ new Map();
14973
- const ci = /* @__PURE__ */ new Map();
14974
- const ciCollisions = /* @__PURE__ */ new Set();
14975
- for (const [placeholder, value] of this.placeholderToValue) {
14976
- const body = placeholder.slice(1, -1);
14977
- exact.set(body, value);
14978
- const key = body.toUpperCase();
14979
- if (ci.has(key) && ci.get(key) !== value) ciCollisions.add(key);
14980
- else ci.set(key, value);
14981
- }
14982
- return { exact, ci, ciCollisions };
14983
- }
14984
- /**
14985
- * Reset all state. Useful for testing or when starting a fresh conversation.
14986
- */
14987
- clear() {
14988
- this.valueToPlaceholder.clear();
14989
- this.placeholderToValue.clear();
14990
- this.categoryCounters.clear();
14991
- this.storageLookup = null;
14992
- this.storagePattern = void 0;
14993
- this.warnedNonText = false;
14994
- this.warnedNerFailure = false;
14995
- }
14996
- };
14997
- }
14998
- });
14999
-
15000
- // src/lib/chat/endpointOverride.ts
15001
- function validateEndpointOverride(override) {
15002
- const trimmed = override.trim();
15003
- if (trimmed === "") {
15004
- return {
15005
- valid: false,
15006
- message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
15007
- };
15008
- }
15009
- if (trimmed.startsWith("//") || trimmed.includes("://")) {
15010
- return {
15011
- valid: false,
15012
- message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
15013
- };
15014
- }
15015
- return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
15016
- }
15017
- var init_endpointOverride = __esm({
15018
- "src/lib/chat/endpointOverride.ts"() {
15019
- "use strict";
14536
+ };
14537
+ });
14538
+ var _default = exports2.default = nochange;
15020
14539
  }
15021
14540
  });
15022
14541
 
15023
- // src/lib/internalFlowMarker.ts
15024
- function withInternalFlowMarker(systemPrompt) {
15025
- if (systemPrompt.includes(INTERNAL_FLOW_MARKER)) return systemPrompt;
15026
- return `${INTERNAL_FLOW_MARKER}
15027
- ${systemPrompt}`;
15028
- }
15029
- var INTERNAL_FLOW_MARKER;
15030
- var init_internalFlowMarker = __esm({
15031
- "src/lib/internalFlowMarker.ts"() {
14542
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/field/index.js
14543
+ var require_field = __commonJS({
14544
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/field/index.js"(exports2) {
15032
14545
  "use strict";
15033
- INTERNAL_FLOW_MARKER = "Anuma internal first-party flow (not user chat).";
14546
+ var _interopRequireDefault = require_interopRequireDefault();
14547
+ exports2.__esModule = true;
14548
+ exports2.default = void 0;
14549
+ var _makeDecorator = _interopRequireDefault(require_makeDecorator());
14550
+ var _common = require_common2();
14551
+ var field10 = (0, _makeDecorator.default)(function(columnName) {
14552
+ return function(target, key, descriptor) {
14553
+ (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
14554
+ return {
14555
+ configurable: true,
14556
+ enumerable: true,
14557
+ get: function() {
14558
+ return this.asModel._getRaw(columnName);
14559
+ },
14560
+ set: function(value) {
14561
+ this.asModel._setRaw(columnName, value);
14562
+ }
14563
+ };
14564
+ };
14565
+ });
14566
+ var _default = exports2.default = field10;
15034
14567
  }
15035
14568
  });
15036
14569
 
15037
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js
15038
- var require_action = __commonJS({
15039
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/action/index.js"(exports2) {
14570
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/utils/common/memory/index.js
14571
+ var require_memory = __commonJS({
14572
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/utils/common/memory/index.js"(exports2) {
15040
14573
  "use strict";
15041
- var _interopRequireDefault = require_interopRequireDefault();
15042
14574
  exports2.__esModule = true;
15043
- exports2.reader = reader;
15044
- exports2.writer = writer;
15045
- var _extends2 = _interopRequireDefault(require_extends());
15046
- function writer(target, key, descriptor) {
15047
- var actionName = "".concat(target.table || target.constructor.name, ".").concat(key);
15048
- return (0, _extends2.default)({}, descriptor, {
15049
- value: function(...args) {
15050
- var _this = this;
15051
- return this.database.write(function() {
15052
- return descriptor.value.apply(_this, args);
15053
- }, actionName);
15054
- }
15055
- });
14575
+ exports2._triggerOnLowMemory = _triggerOnLowMemory;
14576
+ exports2.onLowMemory = onLowMemory;
14577
+ var lowMemoryCallbacks = [];
14578
+ function onLowMemory(callback) {
14579
+ lowMemoryCallbacks.push(callback);
15056
14580
  }
15057
- function reader(target, key, descriptor) {
15058
- var actionName = "".concat(target.table || target.constructor.name, ".").concat(key);
15059
- return (0, _extends2.default)({}, descriptor, {
15060
- value: function(...args) {
15061
- var _this2 = this;
15062
- return this.database.read(function() {
15063
- return descriptor.value.apply(_this2, args);
15064
- }, actionName);
15065
- }
14581
+ function _triggerOnLowMemory() {
14582
+ lowMemoryCallbacks.forEach(function(callback) {
14583
+ return callback();
15066
14584
  });
15067
14585
  }
15068
14586
  }
15069
14587
  });
15070
14588
 
15071
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/children/index.js
15072
- var require_children = __commonJS({
15073
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/children/index.js"(exports2) {
14589
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/date/index.js
14590
+ var require_date = __commonJS({
14591
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/date/index.js"(exports2) {
15074
14592
  "use strict";
15075
14593
  var _interopRequireDefault = require_interopRequireDefault();
15076
14594
  exports2.__esModule = true;
15077
14595
  exports2.default = void 0;
15078
14596
  var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15079
- var _logError = _interopRequireDefault(require_logError());
15080
- var _invariant = _interopRequireDefault(require_invariant());
15081
- var Q13 = _interopRequireWildcard(require_QueryDescription());
15082
- function _getRequireWildcardCache(e) {
15083
- if ("function" != typeof WeakMap) return null;
15084
- var r = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap();
15085
- return (_getRequireWildcardCache = function _getRequireWildcardCache2(e2) {
15086
- return e2 ? t : r;
15087
- })(e);
15088
- }
15089
- function _interopRequireWildcard(e, r) {
15090
- if (!r && e && e.__esModule) return e;
15091
- if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };
15092
- var t = _getRequireWildcardCache(r);
15093
- if (t && t.has(e)) return t.get(e);
15094
- var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor;
15095
- for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
15096
- var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;
15097
- i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];
15098
- }
15099
- return n.default = e, t && t.set(e, n), n;
15100
- }
15101
- var children = (0, _makeDecorator.default)(function(childTable) {
15102
- return function() {
14597
+ var _memory = require_memory();
14598
+ var _common = require_common2();
14599
+ var cache = /* @__PURE__ */ new Map();
14600
+ (0, _memory.onLowMemory)(function() {
14601
+ return cache.clear();
14602
+ });
14603
+ var dateDecorator = (0, _makeDecorator.default)(function(columnName) {
14604
+ return function(target, key, descriptor) {
14605
+ (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
15103
14606
  return {
14607
+ configurable: true,
14608
+ enumerable: true,
15104
14609
  get: function() {
15105
- var that = this;
15106
- that._childrenQueryCache = that._childrenQueryCache || {};
15107
- var cachedQuery = that._childrenQueryCache[childTable];
15108
- if (cachedQuery) {
15109
- return cachedQuery;
14610
+ var rawValue = this.asModel._getRaw(columnName);
14611
+ if ("number" === typeof rawValue) {
14612
+ var cached = cache.get(rawValue);
14613
+ if (cached) {
14614
+ return cached;
14615
+ }
14616
+ var date10 = new Date(rawValue);
14617
+ cache.set(rawValue, date10);
14618
+ return date10;
15110
14619
  }
15111
- var model = that.asModel;
15112
- var childCollection = model.collections.get(childTable);
15113
- var association = model.constructor.associations[childTable];
15114
- (0, _invariant.default)(association && "has_many" === association.type, "@children decorator used for a table that's not has_many");
15115
- var query = childCollection.query(Q13.where(association.foreignKey, model.id));
15116
- that._childrenQueryCache[childTable] = query;
15117
- return query;
14620
+ return null;
15118
14621
  },
15119
- set: function() {
15120
- (0, _logError.default)("Setter called on a @children-marked property");
14622
+ set: function(date10) {
14623
+ var rawValue = date10 ? +new Date(date10) : null;
14624
+ if (rawValue && date10) {
14625
+ cache.set(rawValue, new Date(date10));
14626
+ }
14627
+ this.asModel._setRaw(columnName, rawValue);
15121
14628
  }
15122
14629
  };
15123
14630
  };
15124
14631
  });
15125
- var _default = exports2.default = children;
14632
+ var _default = exports2.default = dateDecorator;
15126
14633
  }
15127
14634
  });
15128
14635
 
15129
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/common.js
15130
- var require_common2 = __commonJS({
15131
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/common.js"(exports2) {
14636
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/text/index.js
14637
+ var require_text = __commonJS({
14638
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/text/index.js"(exports2) {
15132
14639
  "use strict";
15133
14640
  var _interopRequireDefault = require_interopRequireDefault();
15134
14641
  exports2.__esModule = true;
15135
- exports2.ensureDecoratorUsedProperly = ensureDecoratorUsedProperly;
14642
+ exports2.default = void 0;
14643
+ var _makeDecorator = _interopRequireDefault(require_makeDecorator());
14644
+ var _common = require_common2();
14645
+ var text12 = (0, _makeDecorator.default)(function(columnName) {
14646
+ return function(target, key, descriptor) {
14647
+ (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
14648
+ return {
14649
+ configurable: true,
14650
+ enumerable: true,
14651
+ get: function() {
14652
+ return this.asModel._getRaw(columnName);
14653
+ },
14654
+ set: function(value) {
14655
+ this.asModel._setRaw(columnName, "string" === typeof value ? value.trim() : null);
14656
+ }
14657
+ };
14658
+ };
14659
+ });
14660
+ var _default = exports2.default = text12;
14661
+ }
14662
+ });
14663
+
14664
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/readonly/index.js
14665
+ var require_readonly = __commonJS({
14666
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/readonly/index.js"(exports2) {
14667
+ "use strict";
14668
+ var _interopRequireDefault = require_interopRequireDefault();
14669
+ exports2.__esModule = true;
14670
+ exports2.default = void 0;
14671
+ var _extends2 = _interopRequireDefault(require_extends());
14672
+ var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15136
14673
  var _invariant = _interopRequireDefault(require_invariant());
15137
- function ensureDecoratorUsedProperly(columnName, target, key, descriptor) {
15138
- (0, _invariant.default)(columnName, "Pass column name (raw field name) to the decorator - error in ".concat(target.constructor.name, ".prototype.").concat(key, " given."));
15139
- if (descriptor) {
15140
- (0, _invariant.default)("initializer" in descriptor, "Model field decorators can only be used for simple properties - method, setter or getter ".concat(target.constructor.name, ".prototype.").concat(key, " given."));
15141
- (0, _invariant.default)("function" !== typeof descriptor.initializer, 'Model field decorators must not be used on properties with a default value - error in "'.concat(target.constructor.name, ".prototype.").concat(key, '".'));
15142
- }
15143
- }
14674
+ var readonly8 = (0, _makeDecorator.default)(function() {
14675
+ return function(target, key, descriptor) {
14676
+ if (descriptor.get || descriptor.set) {
14677
+ return (0, _extends2.default)({}, descriptor, {
14678
+ set: function() {
14679
+ (0, _invariant.default)(false, "Attempt to set new value on a property ".concat(target.constructor.name, ".prototype.").concat(key, " marked as @readonly"));
14680
+ }
14681
+ });
14682
+ }
14683
+ descriptor.writable = false;
14684
+ return descriptor;
14685
+ };
14686
+ });
14687
+ var _default = exports2.default = readonly8;
15144
14688
  }
15145
14689
  });
15146
14690
 
15147
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/json/index.js
15148
- var require_json = __commonJS({
15149
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/json/index.js"(exports2) {
14691
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/relation/index.js
14692
+ var require_relation = __commonJS({
14693
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/relation/index.js"(exports2) {
15150
14694
  "use strict";
14695
+ var _interopRequireDefault = require_interopRequireDefault();
15151
14696
  exports2.__esModule = true;
15152
- exports2.jsonDecorator = exports2.default = void 0;
14697
+ exports2.default = void 0;
15153
14698
  var _common = require_common2();
15154
- var parseJSON = function(value) {
15155
- if (null === value || value === void 0 || "" === value) {
15156
- return void 0;
15157
- }
15158
- try {
15159
- return JSON.parse(value);
15160
- } catch (_) {
15161
- return void 0;
15162
- }
15163
- };
15164
- var defaultOptions = {
15165
- memo: false
15166
- };
15167
- var jsonDecorator = exports2.jsonDecorator = function(rawFieldName, sanitizer, options = defaultOptions) {
14699
+ var _Relation = _interopRequireDefault(require_Relation());
14700
+ var relation = function(relationTable, relationIdColumn, options) {
15168
14701
  return function(target, key, descriptor) {
15169
- (0, _common.ensureDecoratorUsedProperly)(rawFieldName, target, key, descriptor);
14702
+ (0, _common.ensureDecoratorUsedProperly)(relationIdColumn, target, key, descriptor);
15170
14703
  return {
15171
- configurable: true,
15172
- enumerable: true,
15173
- get: function get() {
14704
+ get: function() {
15174
14705
  var model = this;
15175
- var rawValue = model.asModel._getRaw(rawFieldName);
15176
- if (options.memo) {
15177
- model._jsonDecoratorCache = model._jsonDecoratorCache || {};
15178
- var cachedEntry = model._jsonDecoratorCache[rawFieldName];
15179
- if (cachedEntry && cachedEntry[0] === rawValue) {
15180
- return cachedEntry[1];
15181
- }
15182
- }
15183
- var parsedValue = parseJSON(rawValue);
15184
- var sanitized = sanitizer(parsedValue, model);
15185
- if (options.memo) {
15186
- model._jsonDecoratorCache[rawFieldName] = [rawValue, sanitized];
14706
+ model._relationCache = model._relationCache || {};
14707
+ var cachedRelation = model._relationCache[key];
14708
+ if (cachedRelation) {
14709
+ return cachedRelation;
15187
14710
  }
15188
- return sanitized;
14711
+ var newRelation = new _Relation.default(model.asModel, relationTable, relationIdColumn, options || {
14712
+ isImmutable: false
14713
+ });
14714
+ model._relationCache[key] = newRelation;
14715
+ return newRelation;
15189
14716
  },
15190
- set: function set(json4) {
15191
- var model = this;
15192
- var sanitizedValue = sanitizer(json4, model);
15193
- var stringifiedValue = null != sanitizedValue ? JSON.stringify(sanitizedValue) : null;
15194
- model.asModel._setRaw(rawFieldName, stringifiedValue);
14717
+ set: function() {
14718
+ throw new Error("Don't set relation directly. Use relation.set() instead");
15195
14719
  }
15196
14720
  };
15197
14721
  };
15198
14722
  };
15199
- var _default = exports2.default = jsonDecorator;
14723
+ var _default = exports2.default = relation;
14724
+ }
14725
+ });
14726
+
14727
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/immutableRelation/index.js
14728
+ var require_immutableRelation = __commonJS({
14729
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/immutableRelation/index.js"(exports2) {
14730
+ "use strict";
14731
+ var _interopRequireDefault = require_interopRequireDefault();
14732
+ exports2.__esModule = true;
14733
+ exports2.default = void 0;
14734
+ var _relation = _interopRequireDefault(require_relation());
14735
+ var immutableRelation = function(relationTable, relationIdColumn) {
14736
+ return (0, _relation.default)(relationTable, relationIdColumn, {
14737
+ isImmutable: true
14738
+ });
14739
+ };
14740
+ var _default = exports2.default = immutableRelation;
15200
14741
  }
15201
14742
  });
15202
14743
 
15203
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/nochange/index.js
15204
- var require_nochange = __commonJS({
15205
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/nochange/index.js"(exports2) {
14744
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/experimentalFailsafe/index.js
14745
+ var require_experimentalFailsafe = __commonJS({
14746
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/experimentalFailsafe/index.js"(exports2) {
15206
14747
  "use strict";
15207
14748
  var _interopRequireDefault = require_interopRequireDefault();
15208
14749
  exports2.__esModule = true;
15209
14750
  exports2.default = void 0;
15210
14751
  var _extends2 = _interopRequireDefault(require_extends());
15211
- var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15212
- var _invariant = _interopRequireDefault(require_invariant());
15213
- var nochange = (0, _makeDecorator.default)(function() {
14752
+ var _fp = require_fp();
14753
+ var _rx = require_rx();
14754
+ var failsafe = function(fallback = void 0) {
15214
14755
  return function(target, key, descriptor) {
15215
- (0, _invariant.default)(descriptor.set, "@nochange can only be applied to model fields (to properties with a setter)");
15216
- var errorMessage = "Attempt to set a new value on a @nochange field: ".concat(target.constructor.name, ".prototype.").concat(key);
15217
14756
  return (0, _extends2.default)({}, descriptor, {
15218
- set: function(value) {
15219
- var model = this;
15220
- (0, _invariant.default)("create" === model.asModel._preparedState, errorMessage);
15221
- descriptor.set.call(model, value);
14757
+ get: function() {
14758
+ var value;
14759
+ var unsafeThis = this;
14760
+ if ("value" in descriptor) {
14761
+ value = descriptor.value;
14762
+ } else if ("get" in descriptor) {
14763
+ value = descriptor.get.call(unsafeThis);
14764
+ } else if ("initializer" in descriptor) {
14765
+ value = descriptor.initializer.call(unsafeThis);
14766
+ }
14767
+ if (value && (0, _fp.isObj)(value)) {
14768
+ var originalFetch = value.fetch;
14769
+ var originalObserve = value.observe;
14770
+ if ("function" === typeof originalFetch) {
14771
+ value.fetch = function(...args) {
14772
+ var result = originalFetch.apply(value, args);
14773
+ if ((0, _fp.isObj)(result) && "function" === typeof result.catch) {
14774
+ return result.catch(function() {
14775
+ return fallback;
14776
+ });
14777
+ }
14778
+ return result;
14779
+ };
14780
+ }
14781
+ if ("function" === typeof originalObserve) {
14782
+ value.observe = function(...args) {
14783
+ var result = originalObserve.apply(value, args);
14784
+ if ((0, _fp.isObj)(result) && "function" === typeof result.pipe) {
14785
+ return result.pipe((0, _rx.catchError)(function() {
14786
+ return (0, _rx.of)(fallback);
14787
+ }));
14788
+ }
14789
+ return result;
14790
+ };
14791
+ }
14792
+ }
14793
+ Object.defineProperty(unsafeThis, key, {
14794
+ value,
14795
+ enumerable: descriptor.enumerable
14796
+ });
14797
+ return value;
15222
14798
  }
15223
14799
  });
15224
14800
  };
15225
- });
15226
- var _default = exports2.default = nochange;
14801
+ };
14802
+ var _default = exports2.default = failsafe;
15227
14803
  }
15228
14804
  });
15229
14805
 
15230
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/field/index.js
15231
- var require_field = __commonJS({
15232
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/field/index.js"(exports2) {
14806
+ // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/index.js
14807
+ var require_decorators = __commonJS({
14808
+ "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/index.js"(exports2) {
15233
14809
  "use strict";
15234
14810
  var _interopRequireDefault = require_interopRequireDefault();
15235
14811
  exports2.__esModule = true;
15236
- exports2.default = void 0;
15237
- var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15238
- var _common = require_common2();
15239
- var field10 = (0, _makeDecorator.default)(function(columnName) {
15240
- return function(target, key, descriptor) {
15241
- (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
15242
- return {
15243
- configurable: true,
15244
- enumerable: true,
15245
- get: function() {
15246
- return this.asModel._getRaw(columnName);
15247
- },
15248
- set: function(value) {
15249
- this.asModel._setRaw(columnName, value);
15250
- }
15251
- };
15252
- };
14812
+ exports2.writer = exports2.text = exports2.relation = exports2.readonly = exports2.reader = exports2.nochange = exports2.lazy = exports2.json = exports2.immutableRelation = exports2.field = exports2.experimentalFailsafe = exports2.date = exports2.children = void 0;
14813
+ var _action = require_action();
14814
+ exports2.writer = _action.writer;
14815
+ exports2.reader = _action.reader;
14816
+ var _children = _interopRequireDefault(require_children());
14817
+ exports2.children = _children.default;
14818
+ var _json = _interopRequireDefault(require_json());
14819
+ exports2.json = _json.default;
14820
+ var _nochange = _interopRequireDefault(require_nochange());
14821
+ exports2.nochange = _nochange.default;
14822
+ var _field = _interopRequireDefault(require_field());
14823
+ exports2.field = _field.default;
14824
+ var _date = _interopRequireDefault(require_date());
14825
+ exports2.date = _date.default;
14826
+ var _text = _interopRequireDefault(require_text());
14827
+ exports2.text = _text.default;
14828
+ var _readonly = _interopRequireDefault(require_readonly());
14829
+ exports2.readonly = _readonly.default;
14830
+ var _lazy = _interopRequireDefault(require_lazy());
14831
+ exports2.lazy = _lazy.default;
14832
+ var _relation = _interopRequireDefault(require_relation());
14833
+ exports2.relation = _relation.default;
14834
+ var _immutableRelation = _interopRequireDefault(require_immutableRelation());
14835
+ exports2.immutableRelation = _immutableRelation.default;
14836
+ var _experimentalFailsafe = _interopRequireDefault(require_experimentalFailsafe());
14837
+ exports2.experimentalFailsafe = _experimentalFailsafe.default;
14838
+ }
14839
+ });
14840
+
14841
+ // src/clientConfig.ts
14842
+ var clientConfig_exports = {};
14843
+ __export(clientConfig_exports, {
14844
+ BASE_URL: () => BASE_URL,
14845
+ MCP_R2_DOMAIN: () => MCP_R2_DOMAIN,
14846
+ createClientConfig: () => createClientConfig,
14847
+ resolveBaseUrl: () => resolveBaseUrl
14848
+ });
14849
+ function readGlobal(key) {
14850
+ if (typeof globalThis === "undefined") return void 0;
14851
+ const value = globalThis[key];
14852
+ return typeof value === "string" && value.length > 0 ? value : void 0;
14853
+ }
14854
+ function resolveBaseUrl() {
14855
+ const fromProcess = typeof process !== "undefined" ? process.env.API_URL || process.env.NEXT_PUBLIC_API_URL || process.env.EXPO_PUBLIC_API_URL : void 0;
14856
+ const fromGlobal = readGlobal("API_URL") ?? readGlobal("NEXT_PUBLIC_API_URL") ?? readGlobal("EXPO_PUBLIC_API_URL");
14857
+ return (fromProcess || fromGlobal || DEFAULT_BASE_URL).replace(/\/+$/, "");
14858
+ }
14859
+ var DEFAULT_BASE_URL, BASE_URL, MCP_R2_DOMAIN, createClientConfig;
14860
+ var init_clientConfig = __esm({
14861
+ "src/clientConfig.ts"() {
14862
+ "use strict";
14863
+ DEFAULT_BASE_URL = "https://portal.anuma-dev.ai";
14864
+ BASE_URL = resolveBaseUrl();
14865
+ MCP_R2_DOMAIN = "4cf0e0ea50b97e72386fcf2f92a2d4e8.r2.cloudflarestorage.com";
14866
+ createClientConfig = (config) => ({
14867
+ ...config,
14868
+ baseUrl: BASE_URL
15253
14869
  });
15254
- var _default = exports2.default = field10;
15255
14870
  }
15256
14871
  });
15257
14872
 
15258
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/utils/common/memory/index.js
15259
- var require_memory = __commonJS({
15260
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/utils/common/memory/index.js"(exports2) {
15261
- "use strict";
15262
- exports2.__esModule = true;
15263
- exports2._triggerOnLowMemory = _triggerOnLowMemory;
15264
- exports2.onLowMemory = onLowMemory;
15265
- var lowMemoryCallbacks = [];
15266
- function onLowMemory(callback) {
15267
- lowMemoryCallbacks.push(callback);
15268
- }
15269
- function _triggerOnLowMemory() {
15270
- lowMemoryCallbacks.forEach(function(callback) {
15271
- return callback();
15272
- });
14873
+ // src/lib/taskType.ts
14874
+ function taskTypeHeader(taskType) {
14875
+ return taskType ? { [TASK_TYPE_HEADER]: taskType } : {};
14876
+ }
14877
+ var TASK_TYPE_HEADER;
14878
+ var init_taskType = __esm({
14879
+ "src/lib/taskType.ts"() {
14880
+ "use strict";
14881
+ TASK_TYPE_HEADER = "X-Anuma-Task-Type";
14882
+ }
14883
+ });
14884
+
14885
+ // src/lib/pii/patterns.ts
14886
+ function luhnCheck(digits) {
14887
+ let sum = 0;
14888
+ let alternate = false;
14889
+ for (let i = digits.length - 1; i >= 0; i--) {
14890
+ let n = parseInt(digits[i], 10);
14891
+ if (alternate) {
14892
+ n *= 2;
14893
+ if (n > 9) n -= 9;
14894
+ }
14895
+ sum += n;
14896
+ alternate = !alternate;
14897
+ }
14898
+ return sum % 10 === 0;
14899
+ }
14900
+ var PII_PATTERNS;
14901
+ var init_patterns = __esm({
14902
+ "src/lib/pii/patterns.ts"() {
14903
+ "use strict";
14904
+ PII_PATTERNS = [
14905
+ {
14906
+ category: "EMAIL",
14907
+ regex: /\b[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]{1,255}\.[a-zA-Z]{2,24}\b/g
14908
+ },
14909
+ {
14910
+ category: "SSN",
14911
+ regex: /\b\d{3}-\d{2}-\d{4}\b/g,
14912
+ validate: (match) => {
14913
+ const parts = match.split("-");
14914
+ const area = parseInt(parts[0], 10);
14915
+ return area !== 0 && area !== 666 && area < 900;
14916
+ }
14917
+ },
14918
+ {
14919
+ category: "SSN",
14920
+ regex: /\b\d{3}[ ]?\d{2}[ ]?\d{4}\b/g,
14921
+ context: /(?:\bssn\b|\bsocial\s+security(?:\s+(?:number|no\.?|#))?\b)[^\d\n]{0,12}$/i,
14922
+ validate: (match) => {
14923
+ const digits = match.replace(/\D/g, "");
14924
+ if (digits.length !== 9) return false;
14925
+ const area = parseInt(digits.slice(0, 3), 10);
14926
+ return area !== 0 && area !== 666 && area < 900;
14927
+ }
14928
+ },
14929
+ {
14930
+ category: "CREDIT_CARD",
14931
+ regex: /\b\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{1,7}\b|\b\d{13,19}\b/g,
14932
+ validate: (match) => {
14933
+ const digits = match.replace(/\D/g, "");
14934
+ if (digits.length < 13 || digits.length > 19) return false;
14935
+ return luhnCheck(digits);
14936
+ }
14937
+ },
14938
+ {
14939
+ category: "IP_ADDRESS",
14940
+ regex: /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g,
14941
+ validate: (match) => {
14942
+ return match !== "0.0.0.0" && match !== "127.0.0.1" && match !== "255.255.255.255";
14943
+ }
14944
+ },
14945
+ {
14946
+ category: "IP_ADDRESS",
14947
+ regex: /(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:))/g,
14948
+ validate: (match) => match !== "::1" && match !== "::"
14949
+ },
14950
+ {
14951
+ category: "PHONE",
14952
+ regex: /(?:\+\d{1,3}[\s.-])?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\b|\+\d{10,15}\b/g
14953
+ },
14954
+ {
14955
+ category: "API_KEY",
14956
+ regex: /\b(?:sk|pk|api|key|token|secret|bearer|access)[_-]?[a-zA-Z0-9_-]{20,}\b/gi,
14957
+ validate: (match) => {
14958
+ const hasDigit = /\d/.test(match);
14959
+ const hasMixedCase = /[a-z]/.test(match) && /[A-Z]/.test(match);
14960
+ return hasDigit || hasMixedCase;
14961
+ }
14962
+ },
14963
+ {
14964
+ category: "US_ADDRESS",
14965
+ regex: /\b\d{1,5}[ \t]+(?:[A-Z][a-z]+[ \t]+){1,3}(?:St(?:reet)?|Ave(?:nue)?|Blvd|Boulevard|Dr(?:ive)?|Ln|Lane|Rd|Road|Ct|Court|Pl(?:ace)?|Way|Cir(?:cle)?|Pkwy|Parkway|Hwy|Highway)\.?(?=[\s,.;:!?)]|$)/g,
14966
+ validate: (match) => {
14967
+ return !/^0/.test(match.trim());
14968
+ }
14969
+ },
14970
+ {
14971
+ category: "DATE_OF_BIRTH",
14972
+ regex: /\b(?:(?:0?[1-9]|1[0-2])[/-](?:0?[1-9]|[12]\d|3[01])[/-](?:19|20)\d{2})\b/g,
14973
+ context: /(?:\bdob\b|\bd\.?o\.?b\.?|\bborn\b|\bbirth\s?date\b|\bbirthdate\b|\bbirthday\b|\bdate\s+of\s+birth\b)[^\d\n]{0,12}$/i,
14974
+ validate: (match) => {
14975
+ const m = match.match(/(\d{1,2})[/-](\d{1,2})[/-](\d{4})/);
14976
+ if (!m) return false;
14977
+ const month = parseInt(m[1], 10);
14978
+ const day = parseInt(m[2], 10);
14979
+ const year = parseInt(m[3], 10);
14980
+ const isLeap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
14981
+ const daysInMonth = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
14982
+ return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth[month - 1];
14983
+ }
14984
+ }
14985
+ ];
14986
+ }
14987
+ });
14988
+
14989
+ // src/lib/pii/redactor.ts
14990
+ function escapeRegExp(value) {
14991
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14992
+ }
14993
+ function isWordChar(ch) {
14994
+ return /[A-Za-z0-9_]/.test(ch) || ch.charCodeAt(0) > 127;
14995
+ }
14996
+ function isPiiRedactor(value) {
14997
+ return typeof value === "object" && value !== null && typeof value.redactMessages === "function" && typeof value.deAnonymize === "function";
14998
+ }
14999
+ function resolvePiiRedactor(piiRedaction) {
15000
+ if (piiRedaction === true) return new PiiRedactor();
15001
+ if (!piiRedaction) return void 0;
15002
+ if (isPiiRedactor(piiRedaction)) return piiRedaction;
15003
+ console.warn(
15004
+ "[PiiRedactor] `piiRedaction` is neither `true` nor a PiiRedactor instance; PII redaction is DISABLED for this request."
15005
+ );
15006
+ return void 0;
15007
+ }
15008
+ function createStreamingDeAnonymizer(redactor, emit) {
15009
+ let buffer = "";
15010
+ const trailingFragment = /\[[A-Za-z0-9_]*$/;
15011
+ return {
15012
+ push(chunk) {
15013
+ if (!chunk) return;
15014
+ buffer += chunk;
15015
+ const m = buffer.match(trailingFragment);
15016
+ if (!m || m.index === void 0) {
15017
+ emit(redactor.deAnonymize(buffer));
15018
+ buffer = "";
15019
+ } else if (m.index > 0) {
15020
+ emit(redactor.deAnonymize(buffer.slice(0, m.index)));
15021
+ buffer = buffer.slice(m.index);
15022
+ }
15023
+ },
15024
+ flush() {
15025
+ if (buffer) {
15026
+ emit(redactor.deAnonymize(buffer));
15027
+ buffer = "";
15028
+ }
15273
15029
  }
15274
- }
15275
- });
15276
-
15277
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/date/index.js
15278
- var require_date = __commonJS({
15279
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/date/index.js"(exports2) {
15030
+ };
15031
+ }
15032
+ var PiiRedactor;
15033
+ var init_redactor = __esm({
15034
+ "src/lib/pii/redactor.ts"() {
15280
15035
  "use strict";
15281
- var _interopRequireDefault = require_interopRequireDefault();
15282
- exports2.__esModule = true;
15283
- exports2.default = void 0;
15284
- var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15285
- var _memory = require_memory();
15286
- var _common = require_common2();
15287
- var cache = /* @__PURE__ */ new Map();
15288
- (0, _memory.onLowMemory)(function() {
15289
- return cache.clear();
15290
- });
15291
- var dateDecorator = (0, _makeDecorator.default)(function(columnName) {
15292
- return function(target, key, descriptor) {
15293
- (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
15294
- return {
15295
- configurable: true,
15296
- enumerable: true,
15297
- get: function() {
15298
- var rawValue = this.asModel._getRaw(columnName);
15299
- if ("number" === typeof rawValue) {
15300
- var cached = cache.get(rawValue);
15301
- if (cached) {
15302
- return cached;
15303
- }
15304
- var date10 = new Date(rawValue);
15305
- cache.set(rawValue, date10);
15306
- return date10;
15307
- }
15308
- return null;
15309
- },
15310
- set: function(date10) {
15311
- var rawValue = date10 ? +new Date(date10) : null;
15312
- if (rawValue && date10) {
15313
- cache.set(rawValue, new Date(date10));
15314
- }
15315
- this.asModel._setRaw(columnName, rawValue);
15036
+ init_patterns();
15037
+ PiiRedactor = class {
15038
+ constructor(options = {}) {
15039
+ /** Maps original PII value → placeholder tag. */
15040
+ this.valueToPlaceholder = /* @__PURE__ */ new Map();
15041
+ /** Maps placeholder tag → original PII value (reverse lookup for de-anonymization). */
15042
+ this.placeholderToValue = /* @__PURE__ */ new Map();
15043
+ /** Next index per category for placeholder numbering. */
15044
+ this.categoryCounters = /* @__PURE__ */ new Map();
15045
+ /**
15046
+ * Matches any placeholder-shaped token for this redactor's categories —
15047
+ * bracketed `[EMAIL_1]` OR bare `EMAIL_1`, case-insensitive — capturing the
15048
+ * body. Drives {@link restoreForStorage}: the extraction / consolidation
15049
+ * models echo placeholders mangled (brackets dropped, re-cased), and a single
15050
+ * pass with this matcher restores the assigned ones and flags the rest in one
15051
+ * go. Built lazily from the union of pattern categories AND any categories
15052
+ * minted at runtime (e.g. NER `PERSON`/`LOCATION`/`ORG`): `undefined` = stale
15053
+ * (rebuild on next use), `null` = built but there are no categories.
15054
+ */
15055
+ this.storagePattern = void 0;
15056
+ /**
15057
+ * Cached body→value lookups for {@link restoreForStorage}, built lazily from
15058
+ * the assigned placeholders and invalidated on each mint. `exact` is keyed by
15059
+ * the literal body (collision-proof); `ci` by upper-cased body for re-cased
15060
+ * echoes, minus any `ciCollisions` where two distinct bodies share a form.
15061
+ */
15062
+ this.storageLookup = null;
15063
+ /** Whether the non-text-content bypass warning has already been emitted. */
15064
+ this.warnedNonText = false;
15065
+ /** Whether the NER-detector-failure warning has already been emitted. */
15066
+ this.warnedNerFailure = false;
15067
+ this.nerDetector = options.nerDetector;
15068
+ if (options.patterns) {
15069
+ this.patterns = options.patterns;
15070
+ } else {
15071
+ let base = PII_PATTERNS;
15072
+ if (options.excludeCategories?.length) {
15073
+ const excluded = new Set(options.excludeCategories);
15074
+ base = base.filter((p) => !excluded.has(p.category));
15316
15075
  }
15317
- };
15318
- };
15319
- });
15320
- var _default = exports2.default = dateDecorator;
15321
- }
15322
- });
15323
-
15324
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/text/index.js
15325
- var require_text = __commonJS({
15326
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/text/index.js"(exports2) {
15327
- "use strict";
15328
- var _interopRequireDefault = require_interopRequireDefault();
15329
- exports2.__esModule = true;
15330
- exports2.default = void 0;
15331
- var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15332
- var _common = require_common2();
15333
- var text12 = (0, _makeDecorator.default)(function(columnName) {
15334
- return function(target, key, descriptor) {
15335
- (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor);
15336
- return {
15337
- configurable: true,
15338
- enumerable: true,
15339
- get: function() {
15340
- return this.asModel._getRaw(columnName);
15341
- },
15342
- set: function(value) {
15343
- this.asModel._setRaw(columnName, "string" === typeof value ? value.trim() : null);
15076
+ this.patterns = options.extraPatterns ? [...base, ...options.extraPatterns] : base;
15077
+ }
15078
+ this.patternCategories = [...new Set(this.patterns.map((p) => String(p.category)))];
15079
+ }
15080
+ /**
15081
+ * Build the {@link storagePattern} from the union of pattern categories and
15082
+ * any categories minted at runtime (NER entities arrive only when redaction
15083
+ * runs, so this can't be fixed at construction). Bracketed `[EMAIL_1]` OR
15084
+ * bare `EMAIL_1`, capturing the body, case-insensitive, global. `\d+` (not a
15085
+ * fixed body) spans any index; `\b` on the bare arm keeps it off prose like
15086
+ * "email 1 of 3".
15087
+ */
15088
+ buildStoragePattern() {
15089
+ const categories = [.../* @__PURE__ */ new Set([...this.patternCategories, ...this.categoryCounters.keys()])];
15090
+ if (categories.length === 0) return null;
15091
+ const categoryAlt = categories.sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
15092
+ return new RegExp(`\\[((?:${categoryAlt})_\\d+)\\]|\\b((?:${categoryAlt})_\\d+)\\b`, "gi");
15093
+ }
15094
+ /**
15095
+ * Returns the current number of unique PII values tracked.
15096
+ */
15097
+ get size() {
15098
+ return this.valueToPlaceholder.size;
15099
+ }
15100
+ /**
15101
+ * Returns a snapshot of all placeholder → original value mappings.
15102
+ * Useful for debugging or UI display. This is a copy — mutating it (or
15103
+ * later redactions) does not affect the returned map and vice versa.
15104
+ */
15105
+ getMappings() {
15106
+ return new Map(this.placeholderToValue);
15107
+ }
15108
+ /**
15109
+ * Get or create a placeholder for a PII value.
15110
+ * The same value always gets the same placeholder within a redactor instance.
15111
+ */
15112
+ getPlaceholder(category, value) {
15113
+ const normalizedValue = value.trim();
15114
+ const existing = this.valueToPlaceholder.get(normalizedValue);
15115
+ if (existing) return existing;
15116
+ const existingCount = this.categoryCounters.get(category);
15117
+ const count = (existingCount ?? 0) + 1;
15118
+ this.categoryCounters.set(category, count);
15119
+ if (existingCount === void 0) this.storagePattern = void 0;
15120
+ const placeholder = `[${category}_${count}]`;
15121
+ this.valueToPlaceholder.set(normalizedValue, placeholder);
15122
+ this.placeholderToValue.set(placeholder, normalizedValue);
15123
+ this.storageLookup = null;
15124
+ return placeholder;
15125
+ }
15126
+ /**
15127
+ * Find all matches of a single pattern in `text`, applying its `validate` and
15128
+ * `context` guards. Matches are non-overlapping and ascending (the global
15129
+ * regex advances `lastIndex` past each match). Shared by the sync {@link scan}
15130
+ * (sequential, per-pattern) and the async {@link collectRegexSpans} (span)
15131
+ * paths so the detection rules live in exactly one place.
15132
+ */
15133
+ findMatches(text12, pattern) {
15134
+ const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
15135
+ const found = [];
15136
+ let m;
15137
+ while ((m = regex.exec(text12)) !== null) {
15138
+ const value = m[0];
15139
+ if (pattern.validate && !pattern.validate(value)) continue;
15140
+ if (pattern.context) {
15141
+ const before = text12.slice(Math.max(0, m.index - 40), m.index);
15142
+ if (!pattern.context.test(before)) continue;
15143
+ }
15144
+ found.push({ match: value, index: m.index });
15145
+ }
15146
+ return found;
15147
+ }
15148
+ /**
15149
+ * Scan text for PII and rebuild it, replacing each match with whatever
15150
+ * `makeReplacement(category, value)` returns. Shared by `redactText`
15151
+ * (numbered, stateful placeholders) and `maskText` (unnumbered, stateless).
15152
+ *
15153
+ * Patterns are applied sequentially: each scans the text already rewritten by
15154
+ * earlier patterns, so an earlier (more specific) pattern claims a region
15155
+ * before a later one can — placeholders are inert and match no pattern.
15156
+ */
15157
+ scan(text12, makeReplacement) {
15158
+ const matches = [];
15159
+ let redacted = text12;
15160
+ for (const pattern of this.patterns) {
15161
+ const found = this.findMatches(redacted, pattern);
15162
+ if (found.length === 0) continue;
15163
+ let result = "";
15164
+ let cursor = 0;
15165
+ for (const { match, index } of found) {
15166
+ const placeholder = makeReplacement(pattern.category, match);
15167
+ matches.push({ category: pattern.category, original: match, placeholder });
15168
+ result += redacted.slice(cursor, index) + placeholder;
15169
+ cursor = index + match.length;
15344
15170
  }
15345
- };
15346
- };
15347
- });
15348
- var _default = exports2.default = text12;
15349
- }
15350
- });
15351
-
15352
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/readonly/index.js
15353
- var require_readonly = __commonJS({
15354
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/readonly/index.js"(exports2) {
15355
- "use strict";
15356
- var _interopRequireDefault = require_interopRequireDefault();
15357
- exports2.__esModule = true;
15358
- exports2.default = void 0;
15359
- var _extends2 = _interopRequireDefault(require_extends());
15360
- var _makeDecorator = _interopRequireDefault(require_makeDecorator());
15361
- var _invariant = _interopRequireDefault(require_invariant());
15362
- var readonly8 = (0, _makeDecorator.default)(function() {
15363
- return function(target, key, descriptor) {
15364
- if (descriptor.get || descriptor.set) {
15365
- return (0, _extends2.default)({}, descriptor, {
15366
- set: function() {
15367
- (0, _invariant.default)(false, "Attempt to set new value on a property ".concat(target.constructor.name, ".prototype.").concat(key, " marked as @readonly"));
15368
- }
15369
- });
15171
+ result += redacted.slice(cursor);
15172
+ redacted = result;
15370
15173
  }
15371
- descriptor.writable = false;
15372
- return descriptor;
15373
- };
15374
- });
15375
- var _default = exports2.default = readonly8;
15376
- }
15377
- });
15378
-
15379
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/relation/index.js
15380
- var require_relation = __commonJS({
15381
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/relation/index.js"(exports2) {
15382
- "use strict";
15383
- var _interopRequireDefault = require_interopRequireDefault();
15384
- exports2.__esModule = true;
15385
- exports2.default = void 0;
15386
- var _common = require_common2();
15387
- var _Relation = _interopRequireDefault(require_Relation());
15388
- var relation = function(relationTable, relationIdColumn, options) {
15389
- return function(target, key, descriptor) {
15390
- (0, _common.ensureDecoratorUsedProperly)(relationIdColumn, target, key, descriptor);
15391
- return {
15392
- get: function() {
15393
- var model = this;
15394
- model._relationCache = model._relationCache || {};
15395
- var cachedRelation = model._relationCache[key];
15396
- if (cachedRelation) {
15397
- return cachedRelation;
15398
- }
15399
- var newRelation = new _Relation.default(model.asModel, relationTable, relationIdColumn, options || {
15400
- isImmutable: false
15174
+ return { text: redacted, matches };
15175
+ }
15176
+ /**
15177
+ * Scan and redact PII from a single text string, using numbered, reversible
15178
+ * placeholders ([EMAIL_1], …) tracked on this instance.
15179
+ */
15180
+ redactText(text12) {
15181
+ return this.scan(text12, (category, value) => this.getPlaceholder(category, value));
15182
+ }
15183
+ /**
15184
+ * Mask PII with unnumbered, NON-reversible tokens ([EMAIL], [SSN], …) without
15185
+ * mutating this instance's state. Use for one-way purposes where the value is
15186
+ * never restored — notably embedding inputs: it keeps real PII off the
15187
+ * embeddings endpoint while staying deterministic, so the same content always
15188
+ * masks identically and search stays consistent across conversations.
15189
+ */
15190
+ maskText(text12) {
15191
+ return this.scan(text12, (category) => `[${category}]`).text;
15192
+ }
15193
+ /** All regex matches as spans on the ORIGINAL text (priority 0). Uses the
15194
+ * same {@link findMatches} guards as the sync scan. */
15195
+ collectRegexSpans(text12) {
15196
+ const spans = [];
15197
+ this.patterns.forEach((pattern, patternIndex) => {
15198
+ for (const { match, index } of this.findMatches(text12, pattern)) {
15199
+ spans.push({
15200
+ start: index,
15201
+ end: index + match.length,
15202
+ category: pattern.category,
15203
+ priority: 0,
15204
+ order: patternIndex
15401
15205
  });
15402
- model._relationCache[key] = newRelation;
15403
- return newRelation;
15404
- },
15405
- set: function() {
15406
- throw new Error("Don't set relation directly. Use relation.set() instead");
15407
15206
  }
15408
- };
15409
- };
15410
- };
15411
- var _default = exports2.default = relation;
15412
- }
15413
- });
15414
-
15415
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/immutableRelation/index.js
15416
- var require_immutableRelation = __commonJS({
15417
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/immutableRelation/index.js"(exports2) {
15418
- "use strict";
15419
- var _interopRequireDefault = require_interopRequireDefault();
15420
- exports2.__esModule = true;
15421
- exports2.default = void 0;
15422
- var _relation = _interopRequireDefault(require_relation());
15423
- var immutableRelation = function(relationTable, relationIdColumn) {
15424
- return (0, _relation.default)(relationTable, relationIdColumn, {
15425
- isImmutable: true
15426
- });
15427
- };
15428
- var _default = exports2.default = immutableRelation;
15429
- }
15430
- });
15431
-
15432
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/experimentalFailsafe/index.js
15433
- var require_experimentalFailsafe = __commonJS({
15434
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/experimentalFailsafe/index.js"(exports2) {
15435
- "use strict";
15436
- var _interopRequireDefault = require_interopRequireDefault();
15437
- exports2.__esModule = true;
15438
- exports2.default = void 0;
15439
- var _extends2 = _interopRequireDefault(require_extends());
15440
- var _fp = require_fp();
15441
- var _rx = require_rx();
15442
- var failsafe = function(fallback = void 0) {
15443
- return function(target, key, descriptor) {
15444
- return (0, _extends2.default)({}, descriptor, {
15445
- get: function() {
15446
- var value;
15447
- var unsafeThis = this;
15448
- if ("value" in descriptor) {
15449
- value = descriptor.value;
15450
- } else if ("get" in descriptor) {
15451
- value = descriptor.get.call(unsafeThis);
15452
- } else if ("initializer" in descriptor) {
15453
- value = descriptor.initializer.call(unsafeThis);
15207
+ });
15208
+ return spans;
15209
+ }
15210
+ /**
15211
+ * Normalize raw NER spans: clamp to the text bounds, drop empty/categoryless
15212
+ * spans, and snap each to whole-word boundaries. Snapping prevents a detector
15213
+ * that tagged only part of a word (a common WordPiece artifact — e.g. "Strip"
15214
+ * of "Stripe") from producing a corrupting partial placeholder like
15215
+ * `[ORG_1]e`.
15216
+ */
15217
+ normalizeNerSpans(text12, raw) {
15218
+ if (!Array.isArray(raw)) return [];
15219
+ const out = [];
15220
+ raw.forEach((s, i) => {
15221
+ if (!Number.isFinite(s.start) || !Number.isFinite(s.end)) return;
15222
+ let start = Math.max(0, Math.min(Math.trunc(s.start), text12.length));
15223
+ let end = Math.max(0, Math.min(Math.trunc(s.end), text12.length));
15224
+ if (end <= start) return;
15225
+ while (start > 0 && isWordChar(text12[start - 1])) start--;
15226
+ while (end < text12.length && isWordChar(text12[end])) end++;
15227
+ const category = String(s.category ?? "").trim();
15228
+ if (!category) return;
15229
+ out.push({ start, end, category, priority: 1, order: i });
15230
+ });
15231
+ return out;
15232
+ }
15233
+ /** Run the configured detector defensively: failures degrade to regex-only
15234
+ * (fail-open on detection, not on protection) with a one-time warning. */
15235
+ async detectNerSpans(text12) {
15236
+ if (!this.nerDetector) return [];
15237
+ try {
15238
+ return this.normalizeNerSpans(text12, await this.nerDetector.detect(text12));
15239
+ } catch (err) {
15240
+ this.warnNerFailure(err);
15241
+ return [];
15242
+ }
15243
+ }
15244
+ /**
15245
+ * Resolve overlaps by placing higher-priority spans first and dropping any
15246
+ * span that overlaps one already placed. Regex (priority 0) is placed before
15247
+ * NER (priority 1), and within regex by ascending pattern index — so regex
15248
+ * always wins on overlap and the regex-only result reproduces the sync scan's
15249
+ * sequential "earlier pattern wins". O(n²) but n (spans per message) is tiny.
15250
+ *
15251
+ * The returned spans stay in `(priority, order, start)` order — i.e. mint
15252
+ * order — which {@link rebuildSpans} relies on.
15253
+ */
15254
+ resolveOverlaps(spans) {
15255
+ const ordered = [...spans].sort(
15256
+ (a, b) => a.priority - b.priority || a.order - b.order || a.start - b.start
15257
+ );
15258
+ const placed = [];
15259
+ for (const s of ordered) {
15260
+ if (!placed.some((p) => s.start < p.end && p.start < s.end)) placed.push(s);
15261
+ }
15262
+ return placed;
15263
+ }
15264
+ /**
15265
+ * Rebuild `text` from resolved (non-overlapping) spans. `spans` must arrive in
15266
+ * mint order — `(priority, order, start)`, as {@link resolveOverlaps} returns —
15267
+ * so placeholders are MINTED by iterating it directly (regex categories first,
15268
+ * in pattern then document order, matching the sync scan's numbering). The
15269
+ * string is then EMITTED in document order.
15270
+ */
15271
+ rebuildSpans(text12, spans, makeReplacement) {
15272
+ const placeholderByStart = /* @__PURE__ */ new Map();
15273
+ const matches = [];
15274
+ for (const s of spans) {
15275
+ const value = text12.slice(s.start, s.end);
15276
+ const placeholder = makeReplacement(s.category, value);
15277
+ placeholderByStart.set(s.start, placeholder);
15278
+ matches.push({ category: s.category, original: value, placeholder });
15279
+ }
15280
+ const emitOrder = [...spans].sort((a, b) => a.start - b.start);
15281
+ let result = "";
15282
+ let cursor = 0;
15283
+ for (const s of emitOrder) {
15284
+ result += text12.slice(cursor, s.start) + (placeholderByStart.get(s.start) ?? "");
15285
+ cursor = s.end;
15286
+ }
15287
+ result += text12.slice(cursor);
15288
+ return { text: result, matches };
15289
+ }
15290
+ /** Merge regex + NER spans for `text` into a resolved, non-overlapping set. */
15291
+ async detectAllSpans(text12) {
15292
+ const nerPromise = this.detectNerSpans(text12);
15293
+ const regexSpans = this.collectRegexSpans(text12);
15294
+ const ner = await nerPromise;
15295
+ return this.resolveOverlaps([...regexSpans, ...ner]);
15296
+ }
15297
+ /**
15298
+ * Async counterpart to {@link redactText}: redacts structured PII (regex) AND
15299
+ * unstructured PII (the configured {@link NerDetector}) with numbered,
15300
+ * reversible placeholders. With no detector, identical to `redactText`.
15301
+ */
15302
+ async redactTextAsync(text12) {
15303
+ if (!this.nerDetector) return this.redactText(text12);
15304
+ const spans = await this.detectAllSpans(text12);
15305
+ return this.rebuildSpans(
15306
+ text12,
15307
+ spans,
15308
+ (category, value) => this.getPlaceholder(category, value)
15309
+ );
15310
+ }
15311
+ /**
15312
+ * Async counterpart to {@link maskText}: stateless `[CATEGORY]` masking over
15313
+ * regex + NER matches. With no detector, identical to `maskText`.
15314
+ */
15315
+ async maskTextAsync(text12) {
15316
+ if (!this.nerDetector) return this.maskText(text12);
15317
+ const spans = await this.detectAllSpans(text12);
15318
+ return this.rebuildSpans(text12, spans, (category) => `[${category}]`).text;
15319
+ }
15320
+ /**
15321
+ * Async counterpart to {@link redactMessages}. Text parts are redacted
15322
+ * sequentially so placeholder numbering stays deterministic across the
15323
+ * conversation. With no detector, identical to `redactMessages`.
15324
+ */
15325
+ async redactMessagesAsync(messages) {
15326
+ if (!this.nerDetector) return this.redactMessages(messages);
15327
+ const allMatches = [];
15328
+ const redactedMessages = [];
15329
+ for (const msg of messages) {
15330
+ if (!msg.content) {
15331
+ redactedMessages.push(msg);
15332
+ continue;
15333
+ }
15334
+ const newContent = [];
15335
+ for (const part of msg.content) {
15336
+ if (part.type === "text" && part.text) {
15337
+ const result = await this.redactTextAsync(part.text);
15338
+ allMatches.push(...result.matches);
15339
+ newContent.push(result.matches.length > 0 ? { ...part, text: result.text } : part);
15340
+ } else {
15341
+ if (part.type !== "text") this.warnNonTextBypass();
15342
+ newContent.push(part);
15454
15343
  }
15455
- if (value && (0, _fp.isObj)(value)) {
15456
- var originalFetch = value.fetch;
15457
- var originalObserve = value.observe;
15458
- if ("function" === typeof originalFetch) {
15459
- value.fetch = function(...args) {
15460
- var result = originalFetch.apply(value, args);
15461
- if ((0, _fp.isObj)(result) && "function" === typeof result.catch) {
15462
- return result.catch(function() {
15463
- return fallback;
15464
- });
15465
- }
15466
- return result;
15467
- };
15468
- }
15469
- if ("function" === typeof originalObserve) {
15470
- value.observe = function(...args) {
15471
- var result = originalObserve.apply(value, args);
15472
- if ((0, _fp.isObj)(result) && "function" === typeof result.pipe) {
15473
- return result.pipe((0, _rx.catchError)(function() {
15474
- return (0, _rx.of)(fallback);
15475
- }));
15476
- }
15477
- return result;
15478
- };
15344
+ }
15345
+ redactedMessages.push({ ...msg, content: newContent });
15346
+ }
15347
+ return { messages: redactedMessages, matches: allMatches };
15348
+ }
15349
+ /**
15350
+ * Redact PII from an array of LlmapiMessage objects.
15351
+ * Returns new message objects — originals are not mutated.
15352
+ *
15353
+ * All roles' text content is redacted, not just user/system: tool results
15354
+ * can return PII fetched from external systems, and assistant history is
15355
+ * persisted with original (de-anonymized) values, so both must be re-redacted
15356
+ * before they go back over the wire. Placeholders the model already emitted
15357
+ * are inert here (they don't match any PII pattern), so re-redaction is safe.
15358
+ */
15359
+ redactMessages(messages) {
15360
+ const allMatches = [];
15361
+ const redactedMessages = messages.map((msg) => {
15362
+ if (!msg.content) return msg;
15363
+ const newContent = msg.content.map((part) => {
15364
+ if (part.type === "text" && part.text) {
15365
+ const result = this.redactText(part.text);
15366
+ allMatches.push(...result.matches);
15367
+ if (result.matches.length > 0) {
15368
+ return { ...part, text: result.text };
15479
15369
  }
15370
+ } else if (part.type !== "text") {
15371
+ this.warnNonTextBypass();
15480
15372
  }
15481
- Object.defineProperty(unsafeThis, key, {
15482
- value,
15483
- enumerable: descriptor.enumerable
15484
- });
15485
- return value;
15486
- }
15373
+ return part;
15374
+ });
15375
+ return { ...msg, content: newContent };
15487
15376
  });
15488
- };
15377
+ return { messages: redactedMessages, matches: allMatches };
15378
+ }
15379
+ /** Emit the non-text bypass warning at most once per redactor instance. */
15380
+ warnNonTextBypass() {
15381
+ if (this.warnedNonText) return;
15382
+ this.warnedNonText = true;
15383
+ console.warn(
15384
+ "[PiiRedactor] A message contains non-text content (images, files, or attachments) that is NOT scanned for PII \u2014 only text parts are redacted."
15385
+ );
15386
+ }
15387
+ /** Emit the NER-detector-failure warning at most once per redactor instance. */
15388
+ warnNerFailure(err) {
15389
+ if (this.warnedNerFailure) return;
15390
+ this.warnedNerFailure = true;
15391
+ console.warn(
15392
+ "[PiiRedactor] The NER detector threw; falling back to regex-only redaction for this call's unstructured PII. The detector is still retried on subsequent calls; this warning is emitted only once.",
15393
+ err
15394
+ );
15395
+ }
15396
+ /**
15397
+ * Restore original PII values in text that contains placeholders.
15398
+ * Used to de-anonymize LLM responses before displaying to the user.
15399
+ *
15400
+ * Exact: matches the literal `[EMAIL_1]` form only. The storage paths use
15401
+ * {@link restoreForStorage} instead, which tolerates the mangled echoes the
15402
+ * extraction models produce.
15403
+ */
15404
+ deAnonymize(text12) {
15405
+ if (!text12.includes("[")) return text12;
15406
+ let restored = text12;
15407
+ for (const [placeholder, original] of this.placeholderToValue) {
15408
+ if (restored.includes(placeholder)) {
15409
+ restored = restored.split(placeholder).join(original);
15410
+ }
15411
+ }
15412
+ return restored;
15413
+ }
15414
+ /**
15415
+ * De-anonymize for PERSISTENCE (auto-extraction / consolidation), tolerant of
15416
+ * the ways the extraction models mangle a placeholder when echoing it back:
15417
+ * dropped brackets (`EMAIL_1`) and changed case (`email_1` / `Email_1`).
15418
+ *
15419
+ * Returns the restored text plus `unresolved` — true when a placeholder-shaped
15420
+ * token for one of this redactor's categories was NOT assigned during
15421
+ * redaction (a model hallucination, or an ambiguous re-cased collision).
15422
+ * Callers drop/degrade such facts so an opaque token never reaches the vault.
15423
+ *
15424
+ * One left-to-right pass over the ORIGINAL text. Two properties matter:
15425
+ * - a restored value is never re-scanned — so a real value that happens to
15426
+ * contain a `<CATEGORY>_<n>` substring (e.g. the email `ssn_1@example.com`,
15427
+ * or an API key containing `EMAIL_1`) is neither corrupted nor false-flagged;
15428
+ * - `unresolved` is derived from the lookup miss during this pass, not from a
15429
+ * regex re-scan of the output — so it can't trip over a restored value.
15430
+ */
15431
+ restoreForStorage(text12) {
15432
+ if (this.storagePattern === void 0) this.storagePattern = this.buildStoragePattern();
15433
+ if (!this.storagePattern || this.placeholderToValue.size === 0) {
15434
+ return { text: text12, unresolved: false };
15435
+ }
15436
+ this.storageLookup ?? (this.storageLookup = this.buildStorageLookup());
15437
+ const { exact, ci, ciCollisions } = this.storageLookup;
15438
+ let unresolved = false;
15439
+ const restored = text12.replace(
15440
+ this.storagePattern,
15441
+ (match, bracketed, bare) => {
15442
+ const body = bracketed ?? bare ?? "";
15443
+ const exactHit = exact.get(body);
15444
+ if (exactHit !== void 0) return exactHit;
15445
+ const key = body.toUpperCase();
15446
+ if (!ciCollisions.has(key)) {
15447
+ const ciHit = ci.get(key);
15448
+ if (ciHit !== void 0) return ciHit;
15449
+ }
15450
+ unresolved = true;
15451
+ return match;
15452
+ }
15453
+ );
15454
+ return { text: restored, unresolved };
15455
+ }
15456
+ /**
15457
+ * Build the {@link restoreForStorage} body→value lookups from the assigned
15458
+ * placeholders. `exact` (literal body) is collision-proof; `ci` (upper-cased
15459
+ * body) handles re-cased echoes, except where two distinct bodies share an
15460
+ * upper-cased form — those are recorded in `ciCollisions` so a re-cased echo
15461
+ * of them is left unresolved rather than resolved to the wrong value.
15462
+ */
15463
+ buildStorageLookup() {
15464
+ const exact = /* @__PURE__ */ new Map();
15465
+ const ci = /* @__PURE__ */ new Map();
15466
+ const ciCollisions = /* @__PURE__ */ new Set();
15467
+ for (const [placeholder, value] of this.placeholderToValue) {
15468
+ const body = placeholder.slice(1, -1);
15469
+ exact.set(body, value);
15470
+ const key = body.toUpperCase();
15471
+ if (ci.has(key) && ci.get(key) !== value) ciCollisions.add(key);
15472
+ else ci.set(key, value);
15473
+ }
15474
+ return { exact, ci, ciCollisions };
15475
+ }
15476
+ /**
15477
+ * Reset all state. Useful for testing or when starting a fresh conversation.
15478
+ */
15479
+ clear() {
15480
+ this.valueToPlaceholder.clear();
15481
+ this.placeholderToValue.clear();
15482
+ this.categoryCounters.clear();
15483
+ this.storageLookup = null;
15484
+ this.storagePattern = void 0;
15485
+ this.warnedNonText = false;
15486
+ this.warnedNerFailure = false;
15487
+ }
15489
15488
  };
15490
- var _default = exports2.default = failsafe;
15491
15489
  }
15492
15490
  });
15493
15491
 
15494
- // node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/index.js
15495
- var require_decorators = __commonJS({
15496
- "node_modules/.pnpm/@nozbe+watermelondb@0.28.0/node_modules/@nozbe/watermelondb/decorators/index.js"(exports2) {
15497
- "use strict";
15498
- var _interopRequireDefault = require_interopRequireDefault();
15499
- exports2.__esModule = true;
15500
- exports2.writer = exports2.text = exports2.relation = exports2.readonly = exports2.reader = exports2.nochange = exports2.lazy = exports2.json = exports2.immutableRelation = exports2.field = exports2.experimentalFailsafe = exports2.date = exports2.children = void 0;
15501
- var _action = require_action();
15502
- exports2.writer = _action.writer;
15503
- exports2.reader = _action.reader;
15504
- var _children = _interopRequireDefault(require_children());
15505
- exports2.children = _children.default;
15506
- var _json = _interopRequireDefault(require_json());
15507
- exports2.json = _json.default;
15508
- var _nochange = _interopRequireDefault(require_nochange());
15509
- exports2.nochange = _nochange.default;
15510
- var _field = _interopRequireDefault(require_field());
15511
- exports2.field = _field.default;
15512
- var _date = _interopRequireDefault(require_date());
15513
- exports2.date = _date.default;
15514
- var _text = _interopRequireDefault(require_text());
15515
- exports2.text = _text.default;
15516
- var _readonly = _interopRequireDefault(require_readonly());
15517
- exports2.readonly = _readonly.default;
15518
- var _lazy = _interopRequireDefault(require_lazy());
15519
- exports2.lazy = _lazy.default;
15520
- var _relation = _interopRequireDefault(require_relation());
15521
- exports2.relation = _relation.default;
15522
- var _immutableRelation = _interopRequireDefault(require_immutableRelation());
15523
- exports2.immutableRelation = _immutableRelation.default;
15524
- var _experimentalFailsafe = _interopRequireDefault(require_experimentalFailsafe());
15525
- exports2.experimentalFailsafe = _experimentalFailsafe.default;
15492
+ // src/lib/chat/endpointOverride.ts
15493
+ function validateEndpointOverride(override) {
15494
+ const trimmed = override.trim();
15495
+ if (trimmed === "") {
15496
+ return {
15497
+ valid: false,
15498
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
15499
+ };
15500
+ }
15501
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
15502
+ return {
15503
+ valid: false,
15504
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
15505
+ };
15506
+ }
15507
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
15508
+ }
15509
+ var init_endpointOverride = __esm({
15510
+ "src/lib/chat/endpointOverride.ts"() {
15511
+ "use strict";
15512
+ }
15513
+ });
15514
+
15515
+ // src/lib/internalFlowMarker.ts
15516
+ function withInternalFlowMarker(systemPrompt) {
15517
+ if (systemPrompt.includes(INTERNAL_FLOW_MARKER)) return systemPrompt;
15518
+ return `${INTERNAL_FLOW_MARKER}
15519
+ ${systemPrompt}`;
15520
+ }
15521
+ var INTERNAL_FLOW_MARKER;
15522
+ var init_internalFlowMarker = __esm({
15523
+ "src/lib/internalFlowMarker.ts"() {
15524
+ "use strict";
15525
+ INTERNAL_FLOW_MARKER = "Anuma internal first-party flow (not user chat).";
15526
15526
  }
15527
15527
  });
15528
15528
 
@@ -16831,7 +16831,7 @@ ${line}`;
16831
16831
  }
16832
16832
 
16833
16833
  // src/lib/db/chat/operations.ts
16834
- var import_watermelondb2 = __toESM(require_watermelondb());
16834
+ var import_watermelondb3 = __toESM(require_watermelondb());
16835
16835
  var import_uuid2 = require("uuid");
16836
16836
 
16837
16837
  // src/react/useEncryption.ts
@@ -18010,106 +18010,260 @@ function worseDecryptStatus(a, b) {
18010
18010
  };
18011
18011
  return rank(b) >= rank(a) ? b : a;
18012
18012
  }
18013
- async function decryptMessageFields(message, address, signMessage, embeddedWalletSigner) {
18014
- if (!address) {
18015
- return message;
18016
- }
18017
- if (signMessage) {
18018
- try {
18019
- await requestEncryptionKey(address, signMessage, embeddedWalletSigner);
18020
- } catch (error) {
18021
- getLogger().warn("Failed to request encryption key for decryption:", error);
18022
- }
18023
- }
18024
- let assembled = await assembleDecryptedMessage(message, address);
18025
- const needsRefresh = assembled.decryptionStatus === "auth_mismatch" || assembled.decryptionStatus === "key_missing";
18026
- if (needsRefresh && signMessage && assembled.refreshProbe) {
18027
- try {
18028
- const refreshed = await refreshEncryptionKeyIfMatches(
18029
- address,
18030
- assembled.refreshProbe,
18031
- signMessage,
18032
- embeddedWalletSigner
18033
- );
18034
- if (refreshed) {
18035
- assembled = await assembleDecryptedMessage(message, address);
18036
- }
18037
- } catch (error) {
18038
- getLogger().warn("Failed to refresh encryption key after decrypt failure:", error);
18039
- }
18013
+ async function decryptMessageFields(message, address, signMessage, embeddedWalletSigner) {
18014
+ if (!address) {
18015
+ return message;
18016
+ }
18017
+ if (signMessage) {
18018
+ try {
18019
+ await requestEncryptionKey(address, signMessage, embeddedWalletSigner);
18020
+ } catch (error) {
18021
+ getLogger().warn("Failed to request encryption key for decryption:", error);
18022
+ }
18023
+ }
18024
+ let assembled = await assembleDecryptedMessage(message, address);
18025
+ const needsRefresh = assembled.decryptionStatus === "auth_mismatch" || assembled.decryptionStatus === "key_missing";
18026
+ if (needsRefresh && signMessage && assembled.refreshProbe) {
18027
+ try {
18028
+ const refreshed = await refreshEncryptionKeyIfMatches(
18029
+ address,
18030
+ assembled.refreshProbe,
18031
+ signMessage,
18032
+ embeddedWalletSigner
18033
+ );
18034
+ if (refreshed) {
18035
+ assembled = await assembleDecryptedMessage(message, address);
18036
+ }
18037
+ } catch (error) {
18038
+ getLogger().warn("Failed to refresh encryption key after decrypt failure:", error);
18039
+ }
18040
+ }
18041
+ const { refreshProbe: _probe, ...result } = assembled;
18042
+ return result;
18043
+ }
18044
+ async function assembleDecryptedMessage(message, address) {
18045
+ const contentResult = await decryptFieldDetailed(message.content, address);
18046
+ const thinkingResult = message.thinking ? await decryptFieldDetailed(message.thinking, address) : null;
18047
+ const vectorResult = await decryptMaybeJsonFieldDetailed(
18048
+ message.vector,
18049
+ address
18050
+ );
18051
+ const chunksResult = await decryptMaybeJsonFieldDetailed(
18052
+ message.chunks,
18053
+ address
18054
+ );
18055
+ const sourcesResult = await decryptMaybeJsonFieldDetailed(
18056
+ message.sources,
18057
+ address
18058
+ );
18059
+ const thoughtProcessResult = await decryptMaybeJsonFieldDetailed(
18060
+ message.thoughtProcess,
18061
+ address
18062
+ );
18063
+ const toolCallEventsResult = await decryptMaybeJsonFieldDetailed(
18064
+ message.toolCallEvents,
18065
+ address
18066
+ );
18067
+ let decryptionStatus;
18068
+ let refreshProbe;
18069
+ const consider = (status, probe) => {
18070
+ decryptionStatus = worseDecryptStatus(decryptionStatus, status);
18071
+ if (!refreshProbe && probe && (status === "auth_mismatch" || status === "key_missing")) {
18072
+ refreshProbe = probe;
18073
+ }
18074
+ };
18075
+ consider(contentResult.status, isEncrypted(message.content) ? message.content : void 0);
18076
+ if (thinkingResult && message.thinking && typeof message.thinking === "string") {
18077
+ consider(thinkingResult.status, isEncrypted(message.thinking) ? message.thinking : void 0);
18078
+ }
18079
+ if (vectorResult.probe) consider(vectorResult.status, vectorResult.probe);
18080
+ if (chunksResult.probe) consider(chunksResult.status, chunksResult.probe);
18081
+ if (sourcesResult.probe) consider(sourcesResult.status, sourcesResult.probe);
18082
+ if (thoughtProcessResult.probe) {
18083
+ consider(thoughtProcessResult.status, thoughtProcessResult.probe);
18084
+ }
18085
+ if (toolCallEventsResult.probe) {
18086
+ consider(toolCallEventsResult.status, toolCallEventsResult.probe);
18087
+ }
18088
+ const toolCallEvents = Array.isArray(toolCallEventsResult.value) ? await mapToolCallPayloads(toolCallEventsResult.value, async (value) => {
18089
+ const detailed = await decryptFieldDetailed(value, address);
18090
+ consider(detailed.status, isEncrypted(value) ? value : void 0);
18091
+ return detailed.value;
18092
+ }) : void 0;
18093
+ const statusForConsumer = decryptionStatus === "ok" || decryptionStatus === "plaintext" || !decryptionStatus ? void 0 : decryptionStatus;
18094
+ const result = {
18095
+ ...message,
18096
+ content: contentResult.value,
18097
+ thinking: thinkingResult ? thinkingResult.value : message.thinking,
18098
+ vector: vectorResult.value,
18099
+ chunks: chunksResult.value,
18100
+ sources: sourcesResult.value,
18101
+ thoughtProcess: thoughtProcessResult.value,
18102
+ toolCallEvents
18103
+ };
18104
+ if (statusForConsumer) {
18105
+ result.decryptionStatus = statusForConsumer;
18106
+ } else {
18107
+ delete result.decryptionStatus;
18108
+ }
18109
+ if (refreshProbe) {
18110
+ result.refreshProbe = refreshProbe;
18111
+ }
18112
+ return result;
18113
+ }
18114
+
18115
+ // src/lib/db/chat/models.ts
18116
+ var import_watermelondb2 = __toESM(require_watermelondb());
18117
+ var import_decorators = __toESM(require_decorators());
18118
+ var Message = class extends import_watermelondb2.Model {
18119
+ };
18120
+ Message.table = "history";
18121
+ Message.associations = {
18122
+ conversations: { type: "belongs_to", key: "conversation_id" },
18123
+ media: { type: "has_many", foreignKey: "message_id" }
18124
+ };
18125
+ __decorateClass([
18126
+ (0, import_decorators.field)("message_id")
18127
+ ], Message.prototype, "messageId", 2);
18128
+ __decorateClass([
18129
+ (0, import_decorators.text)("conversation_id")
18130
+ ], Message.prototype, "conversationId", 2);
18131
+ __decorateClass([
18132
+ (0, import_decorators.text)("role")
18133
+ ], Message.prototype, "role", 2);
18134
+ __decorateClass([
18135
+ (0, import_decorators.text)("content")
18136
+ ], Message.prototype, "content", 2);
18137
+ __decorateClass([
18138
+ (0, import_decorators.text)("model")
18139
+ ], Message.prototype, "model", 2);
18140
+ __decorateClass([
18141
+ (0, import_decorators.text)("image_model")
18142
+ ], Message.prototype, "imageModel", 2);
18143
+ __decorateClass([
18144
+ (0, import_decorators.json)("files", (raw) => raw)
18145
+ ], Message.prototype, "files", 2);
18146
+ __decorateClass([
18147
+ (0, import_decorators.json)("file_ids", (raw) => raw)
18148
+ ], Message.prototype, "fileIds", 2);
18149
+ __decorateClass([
18150
+ (0, import_decorators.date)("created_at")
18151
+ ], Message.prototype, "createdAt", 2);
18152
+ __decorateClass([
18153
+ (0, import_decorators.date)("updated_at")
18154
+ ], Message.prototype, "updatedAt", 2);
18155
+ __decorateClass([
18156
+ (0, import_decorators.json)("vector", (raw) => raw)
18157
+ ], Message.prototype, "vector", 2);
18158
+ __decorateClass([
18159
+ (0, import_decorators.text)("embedding_model")
18160
+ ], Message.prototype, "embeddingModel", 2);
18161
+ __decorateClass([
18162
+ (0, import_decorators.json)("chunks", (raw) => raw)
18163
+ ], Message.prototype, "chunks", 2);
18164
+ __decorateClass([
18165
+ (0, import_decorators.json)("usage", (raw) => raw)
18166
+ ], Message.prototype, "usage", 2);
18167
+ __decorateClass([
18168
+ (0, import_decorators.json)("sources", (raw) => raw)
18169
+ ], Message.prototype, "sources", 2);
18170
+ __decorateClass([
18171
+ (0, import_decorators.field)("response_duration")
18172
+ ], Message.prototype, "responseDuration", 2);
18173
+ __decorateClass([
18174
+ (0, import_decorators.field)("was_stopped")
18175
+ ], Message.prototype, "wasStopped", 2);
18176
+ __decorateClass([
18177
+ (0, import_decorators.text)("error")
18178
+ ], Message.prototype, "error", 2);
18179
+ __decorateClass([
18180
+ (0, import_decorators.json)("thought_process", (raw) => raw)
18181
+ ], Message.prototype, "thoughtProcess", 2);
18182
+ __decorateClass([
18183
+ (0, import_decorators.text)("thinking")
18184
+ ], Message.prototype, "thinking", 2);
18185
+ __decorateClass([
18186
+ (0, import_decorators.text)("parent_message_id")
18187
+ ], Message.prototype, "parentMessageId", 2);
18188
+ __decorateClass([
18189
+ (0, import_decorators.text)("feedback")
18190
+ ], Message.prototype, "feedback", 2);
18191
+ __decorateClass([
18192
+ (0, import_decorators.json)("tool_call_events", (raw) => raw)
18193
+ ], Message.prototype, "toolCallEvents", 2);
18194
+ __decorateClass([
18195
+ (0, import_decorators.text)("origin")
18196
+ ], Message.prototype, "origin", 2);
18197
+ var Conversation = class extends import_watermelondb2.Model {
18198
+ };
18199
+ Conversation.table = "conversations";
18200
+ Conversation.associations = {
18201
+ history: { type: "has_many", foreignKey: "conversation_id" },
18202
+ projects: { type: "belongs_to", key: "project_id" }
18203
+ };
18204
+ __decorateClass([
18205
+ (0, import_decorators.text)("conversation_id")
18206
+ ], Conversation.prototype, "conversationId", 2);
18207
+ __decorateClass([
18208
+ (0, import_decorators.text)("title")
18209
+ ], Conversation.prototype, "title", 2);
18210
+ __decorateClass([
18211
+ (0, import_decorators.text)("project_id")
18212
+ ], Conversation.prototype, "projectId", 2);
18213
+ __decorateClass([
18214
+ (0, import_decorators.date)("created_at")
18215
+ ], Conversation.prototype, "createdAt", 2);
18216
+ __decorateClass([
18217
+ (0, import_decorators.date)("updated_at")
18218
+ ], Conversation.prototype, "updatedAt", 2);
18219
+ __decorateClass([
18220
+ (0, import_decorators.field)("is_deleted")
18221
+ ], Conversation.prototype, "isDeleted", 2);
18222
+ __decorateClass([
18223
+ (0, import_decorators.date)("pinned_at")
18224
+ ], Conversation.prototype, "pinnedAt", 2);
18225
+ var ConversationSummary = class extends import_watermelondb2.Model {
18226
+ };
18227
+ ConversationSummary.table = "conversation_summaries";
18228
+ ConversationSummary.associations = {
18229
+ conversations: { type: "belongs_to", key: "conversation_id" }
18230
+ };
18231
+ __decorateClass([
18232
+ (0, import_decorators.text)("conversation_id")
18233
+ ], ConversationSummary.prototype, "conversationId", 2);
18234
+ __decorateClass([
18235
+ (0, import_decorators.text)("summary")
18236
+ ], ConversationSummary.prototype, "summary", 2);
18237
+ __decorateClass([
18238
+ (0, import_decorators.text)("summarized_up_to")
18239
+ ], ConversationSummary.prototype, "summarizedUpTo", 2);
18240
+ __decorateClass([
18241
+ (0, import_decorators.field)("token_count")
18242
+ ], ConversationSummary.prototype, "tokenCount", 2);
18243
+ __decorateClass([
18244
+ (0, import_decorators.date)("created_at")
18245
+ ], ConversationSummary.prototype, "createdAt", 2);
18246
+ __decorateClass([
18247
+ (0, import_decorators.date)("updated_at")
18248
+ ], ConversationSummary.prototype, "updatedAt", 2);
18249
+
18250
+ // src/lib/db/chat/summaryRevision.ts
18251
+ var conversationRevisions = /* @__PURE__ */ new WeakMap();
18252
+ function getConversationRevision(database, conversationId) {
18253
+ return conversationRevisions.get(database)?.get(conversationId) ?? 0;
18254
+ }
18255
+ function advanceConversationRevision(database, conversationId) {
18256
+ let revisions = conversationRevisions.get(database);
18257
+ if (!revisions) {
18258
+ revisions = /* @__PURE__ */ new Map();
18259
+ conversationRevisions.set(database, revisions);
18040
18260
  }
18041
- const { refreshProbe: _probe, ...result } = assembled;
18042
- return result;
18261
+ revisions.set(conversationId, getConversationRevision(database, conversationId) + 1);
18043
18262
  }
18044
- async function assembleDecryptedMessage(message, address) {
18045
- const contentResult = await decryptFieldDetailed(message.content, address);
18046
- const thinkingResult = message.thinking ? await decryptFieldDetailed(message.thinking, address) : null;
18047
- const vectorResult = await decryptMaybeJsonFieldDetailed(
18048
- message.vector,
18049
- address
18050
- );
18051
- const chunksResult = await decryptMaybeJsonFieldDetailed(
18052
- message.chunks,
18053
- address
18054
- );
18055
- const sourcesResult = await decryptMaybeJsonFieldDetailed(
18056
- message.sources,
18057
- address
18058
- );
18059
- const thoughtProcessResult = await decryptMaybeJsonFieldDetailed(
18060
- message.thoughtProcess,
18061
- address
18062
- );
18063
- const toolCallEventsResult = await decryptMaybeJsonFieldDetailed(
18064
- message.toolCallEvents,
18065
- address
18066
- );
18067
- let decryptionStatus;
18068
- let refreshProbe;
18069
- const consider = (status, probe) => {
18070
- decryptionStatus = worseDecryptStatus(decryptionStatus, status);
18071
- if (!refreshProbe && probe && (status === "auth_mismatch" || status === "key_missing")) {
18072
- refreshProbe = probe;
18073
- }
18074
- };
18075
- consider(contentResult.status, isEncrypted(message.content) ? message.content : void 0);
18076
- if (thinkingResult && message.thinking && typeof message.thinking === "string") {
18077
- consider(thinkingResult.status, isEncrypted(message.thinking) ? message.thinking : void 0);
18078
- }
18079
- if (vectorResult.probe) consider(vectorResult.status, vectorResult.probe);
18080
- if (chunksResult.probe) consider(chunksResult.status, chunksResult.probe);
18081
- if (sourcesResult.probe) consider(sourcesResult.status, sourcesResult.probe);
18082
- if (thoughtProcessResult.probe) {
18083
- consider(thoughtProcessResult.status, thoughtProcessResult.probe);
18084
- }
18085
- if (toolCallEventsResult.probe) {
18086
- consider(toolCallEventsResult.status, toolCallEventsResult.probe);
18087
- }
18088
- const toolCallEvents = Array.isArray(toolCallEventsResult.value) ? await mapToolCallPayloads(toolCallEventsResult.value, async (value) => {
18089
- const detailed = await decryptFieldDetailed(value, address);
18090
- consider(detailed.status, isEncrypted(value) ? value : void 0);
18091
- return detailed.value;
18092
- }) : void 0;
18093
- const statusForConsumer = decryptionStatus === "ok" || decryptionStatus === "plaintext" || !decryptionStatus ? void 0 : decryptionStatus;
18094
- const result = {
18095
- ...message,
18096
- content: contentResult.value,
18097
- thinking: thinkingResult ? thinkingResult.value : message.thinking,
18098
- vector: vectorResult.value,
18099
- chunks: chunksResult.value,
18100
- sources: sourcesResult.value,
18101
- thoughtProcess: thoughtProcessResult.value,
18102
- toolCallEvents
18103
- };
18104
- if (statusForConsumer) {
18105
- result.decryptionStatus = statusForConsumer;
18106
- } else {
18107
- delete result.decryptionStatus;
18108
- }
18109
- if (refreshProbe) {
18110
- result.refreshProbe = refreshProbe;
18263
+ function assertConversationRevision(database, conversationId, revision) {
18264
+ if (getConversationRevision(database, conversationId) !== revision) {
18265
+ throw new Error("The conversation changed during summarization. Retry the request.");
18111
18266
  }
18112
- return result;
18113
18267
  }
18114
18268
 
18115
18269
  // src/lib/db/chat/types.ts
@@ -18354,7 +18508,7 @@ async function createConversationOp(ctx, opts, defaultTitle = "New Conversation"
18354
18508
  );
18355
18509
  }
18356
18510
  async function getConversationOp(ctx, id) {
18357
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
18511
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("conversation_id", id), import_watermelondb3.Q.where("is_deleted", false)).fetch();
18358
18512
  return results.length > 0 ? await conversationToStored(
18359
18513
  results[0],
18360
18514
  ctx.walletAddress,
@@ -18363,7 +18517,7 @@ async function getConversationOp(ctx, id) {
18363
18517
  ) : null;
18364
18518
  }
18365
18519
  async function getConversationsOp(ctx) {
18366
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false), import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).unsafeFetchRaw();
18520
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("is_deleted", false), import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)).unsafeFetchRaw();
18367
18521
  return Promise.all(
18368
18522
  results.map(
18369
18523
  (raw) => conversationRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
@@ -18384,33 +18538,33 @@ function conversationRawToLazyStored(raw) {
18384
18538
  };
18385
18539
  }
18386
18540
  async function getConversationsLazyOp(ctx) {
18387
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false), import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).unsafeFetchRaw();
18541
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("is_deleted", false), import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)).unsafeFetchRaw();
18388
18542
  return results.map(conversationRawToLazyStored);
18389
18543
  }
18390
18544
  async function getConversationsPageOp(ctx, options) {
18391
18545
  const limit = Math.floor(options?.limit ?? 200);
18392
18546
  if (!Number.isFinite(limit) || limit < 1) return [];
18393
18547
  const exclude = new Set(options?.boundaryExcludeUniqueIds ?? []);
18394
- const clauses = [import_watermelondb2.Q.where("is_deleted", false)];
18548
+ const clauses = [import_watermelondb3.Q.where("is_deleted", false)];
18395
18549
  if (options?.before !== void 0) {
18396
18550
  clauses.push(
18397
- exclude.size > 0 ? import_watermelondb2.Q.where("created_at", import_watermelondb2.Q.lte(options.before)) : import_watermelondb2.Q.where("created_at", import_watermelondb2.Q.lt(options.before))
18551
+ exclude.size > 0 ? import_watermelondb3.Q.where("created_at", import_watermelondb3.Q.lte(options.before)) : import_watermelondb3.Q.where("created_at", import_watermelondb3.Q.lt(options.before))
18398
18552
  );
18399
18553
  }
18400
- const fetched = await ctx.conversationsCollection.query(...clauses, import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc), import_watermelondb2.Q.take(limit + exclude.size)).unsafeFetchRaw();
18554
+ const fetched = await ctx.conversationsCollection.query(...clauses, import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc), import_watermelondb3.Q.take(limit + exclude.size)).unsafeFetchRaw();
18401
18555
  const results = exclude.size > 0 ? fetched.filter((raw) => !exclude.has(raw.id)) : fetched;
18402
18556
  return results.slice(0, limit).map(conversationRawToLazyStored);
18403
18557
  }
18404
18558
  async function getConversationsByProjectLazyOp(ctx, projectId) {
18405
18559
  const results = await ctx.conversationsCollection.query(
18406
- import_watermelondb2.Q.where("project_id", projectId === null ? "" : projectId),
18407
- import_watermelondb2.Q.where("is_deleted", false),
18408
- import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)
18560
+ import_watermelondb3.Q.where("project_id", projectId === null ? "" : projectId),
18561
+ import_watermelondb3.Q.where("is_deleted", false),
18562
+ import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)
18409
18563
  ).unsafeFetchRaw();
18410
18564
  return results.map(conversationRawToLazyStored);
18411
18565
  }
18412
18566
  async function updateConversationTitleOp(ctx, id, title) {
18413
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
18567
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("conversation_id", id), import_watermelondb3.Q.where("is_deleted", false)).fetch();
18414
18568
  if (results.length > 0) {
18415
18569
  const encryptedOpts = ctx.walletAddress && ctx.signMessage ? await encryptConversationFields(
18416
18570
  { title },
@@ -18429,13 +18583,13 @@ async function updateConversationTitleOp(ctx, id, title) {
18429
18583
  return false;
18430
18584
  }
18431
18585
  async function deleteConversationOp(ctx, id) {
18432
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
18586
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("conversation_id", id), import_watermelondb3.Q.where("is_deleted", false)).fetch();
18433
18587
  if (results.length > 0) {
18434
18588
  await ctx.database.write(async () => {
18435
18589
  await results[0].update((conv) => {
18436
18590
  conv._setRaw("is_deleted", true);
18437
18591
  });
18438
- const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", id)).fetch() : [];
18592
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb3.Q.where("conversation_id", id)).fetch() : [];
18439
18593
  for (const job of jobs) await job.destroyPermanently();
18440
18594
  });
18441
18595
  return true;
@@ -18443,7 +18597,7 @@ async function deleteConversationOp(ctx, id) {
18443
18597
  return false;
18444
18598
  }
18445
18599
  async function updateConversationProjectOp(ctx, id, projectId) {
18446
- const results = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", id), import_watermelondb2.Q.where("is_deleted", false)).fetch();
18600
+ const results = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("conversation_id", id), import_watermelondb3.Q.where("is_deleted", false)).fetch();
18447
18601
  if (results.length > 0) {
18448
18602
  await ctx.database.write(async () => {
18449
18603
  await results[0].update((conv) => {
@@ -18456,9 +18610,9 @@ async function updateConversationProjectOp(ctx, id, projectId) {
18456
18610
  }
18457
18611
  async function getConversationsByProjectOp(ctx, projectId) {
18458
18612
  const results = await ctx.conversationsCollection.query(
18459
- import_watermelondb2.Q.where("project_id", projectId === null ? "" : projectId),
18460
- import_watermelondb2.Q.where("is_deleted", false),
18461
- import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)
18613
+ import_watermelondb3.Q.where("project_id", projectId === null ? "" : projectId),
18614
+ import_watermelondb3.Q.where("is_deleted", false),
18615
+ import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)
18462
18616
  ).unsafeFetchRaw();
18463
18617
  return Promise.all(
18464
18618
  results.map(
@@ -18467,7 +18621,7 @@ async function getConversationsByProjectOp(ctx, projectId) {
18467
18621
  );
18468
18622
  }
18469
18623
  async function getMessagesOp(ctx, convId, opts) {
18470
- const fetched = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId), import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.asc)).unsafeFetchRaw();
18624
+ const fetched = await ctx.messagesCollection.query(import_watermelondb3.Q.where("conversation_id", convId), import_watermelondb3.Q.sortBy("message_id", import_watermelondb3.Q.asc)).unsafeFetchRaw();
18471
18625
  const results = opts?.rawFilter ? fetched.filter(opts.rawFilter) : fetched;
18472
18626
  return Promise.all(
18473
18627
  results.map(
@@ -18475,10 +18629,22 @@ async function getMessagesOp(ctx, convId, opts) {
18475
18629
  )
18476
18630
  );
18477
18631
  }
18632
+ async function invalidateConversationSummary(ctx, conversationId, changedMessageId) {
18633
+ if (!ctx.database.schema?.tables[ConversationSummary.table]) return;
18634
+ const summaries = await ctx.database.get(ConversationSummary.table).query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
18635
+ for (const summary of summaries) {
18636
+ if (changedMessageId !== void 0) {
18637
+ const [boundary] = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", summary.summarizedUpTo), import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
18638
+ if (boundary && changedMessageId > boundary.messageId) continue;
18639
+ }
18640
+ await summary.destroyPermanently();
18641
+ }
18642
+ }
18478
18643
  async function clearMessagesOp(ctx, convId) {
18479
18644
  await ctx.database.write(async () => {
18480
- const messages = await ctx.messagesCollection.query(import_watermelondb2.Q.where("conversation_id", convId)).fetch();
18481
- const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", convId)).fetch() : [];
18645
+ await invalidateConversationSummary(ctx, convId);
18646
+ const messages = await ctx.messagesCollection.query(import_watermelondb3.Q.where("conversation_id", convId)).fetch();
18647
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb3.Q.where("conversation_id", convId)).fetch() : [];
18482
18648
  for (const job of jobs) {
18483
18649
  await job.update((row) => {
18484
18650
  row._setRaw("message_ids", "[]");
@@ -18488,11 +18654,12 @@ async function clearMessagesOp(ctx, convId) {
18488
18654
  }
18489
18655
  for (const message of messages) {
18490
18656
  await message.update((msg) => {
18491
- msg._setRaw("file_ids", null);
18657
+ if (ctx.messagesCollection.schema.columns.file_ids) msg._setRaw("file_ids", null);
18492
18658
  msg._setRaw("files", null);
18493
18659
  });
18494
18660
  await message.destroyPermanently();
18495
18661
  }
18662
+ advanceConversationRevision(ctx.database, convId);
18496
18663
  });
18497
18664
  }
18498
18665
  async function deleteMessageOp(ctx, uniqueId) {
@@ -18503,7 +18670,8 @@ async function deleteMessageOp(ctx, uniqueId) {
18503
18670
  return null;
18504
18671
  }
18505
18672
  await ctx.database.write(async () => {
18506
- const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb2.Q.where("conversation_id", message.conversationId)).fetch() : [];
18673
+ await invalidateConversationSummary(ctx, message.conversationId, message.messageId);
18674
+ const jobs = ctx.database.schema?.tables[ExtractionJob.table] ? await ctx.database.get(ExtractionJob.table).query(import_watermelondb3.Q.where("conversation_id", message.conversationId)).fetch() : [];
18507
18675
  for (const job of jobs) {
18508
18676
  const ids = JSON.parse(String(job._getRaw("message_ids")));
18509
18677
  await job.update((row) => {
@@ -18512,10 +18680,11 @@ async function deleteMessageOp(ctx, uniqueId) {
18512
18680
  });
18513
18681
  }
18514
18682
  await message.update((msg) => {
18515
- msg._setRaw("file_ids", null);
18683
+ if (ctx.messagesCollection.schema.columns.file_ids) msg._setRaw("file_ids", null);
18516
18684
  msg._setRaw("files", null);
18517
18685
  });
18518
18686
  await message.destroyPermanently();
18687
+ advanceConversationRevision(ctx.database, message.conversationId);
18519
18688
  });
18520
18689
  return uniqueId;
18521
18690
  }
@@ -18576,9 +18745,9 @@ async function createMessageOp(ctx, opts) {
18576
18745
  }
18577
18746
  }
18578
18747
  const newest = await ctx.messagesCollection.query(
18579
- import_watermelondb2.Q.where("conversation_id", opts.conversationId),
18580
- import_watermelondb2.Q.sortBy("message_id", import_watermelondb2.Q.desc),
18581
- import_watermelondb2.Q.take(1)
18748
+ import_watermelondb3.Q.where("conversation_id", opts.conversationId),
18749
+ import_watermelondb3.Q.sortBy("message_id", import_watermelondb3.Q.desc),
18750
+ import_watermelondb3.Q.take(1)
18582
18751
  ).fetch();
18583
18752
  const messageId = (newest[0]?.messageId ?? 0) + 1;
18584
18753
  const encryptedOpts = await encryptMessageOptsIfNeeded(ctx, opts);
@@ -18592,7 +18761,7 @@ async function createMessageOp(ctx, opts) {
18592
18761
  return messageToStored(created, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
18593
18762
  }
18594
18763
  async function upsertMessageOp(ctx, opts) {
18595
- const results = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", opts.uniqueId)).fetch();
18764
+ const results = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", opts.uniqueId)).fetch();
18596
18765
  const existing = results.length > 0 ? results[0] : null;
18597
18766
  if (!existing) {
18598
18767
  return createMessageOp(ctx, opts);
@@ -18700,13 +18869,13 @@ async function decryptMessageEmbeddingFields(raw, walletAddress) {
18700
18869
  async function sealLegacyMessageEmbeddingsOp(ctx) {
18701
18870
  if (!ctx.walletAddress) return 0;
18702
18871
  const candidateIds = await ctx.messagesCollection.query(
18703
- import_watermelondb2.Q.where("content", import_watermelondb2.Q.like("enc:%")),
18704
- import_watermelondb2.Q.or(
18872
+ import_watermelondb3.Q.where("content", import_watermelondb3.Q.like("enc:%")),
18873
+ import_watermelondb3.Q.or(
18705
18874
  ...["vector", "chunks"].map(
18706
- (column) => import_watermelondb2.Q.and(
18707
- import_watermelondb2.Q.where(column, import_watermelondb2.Q.notEq(null)),
18708
- import_watermelondb2.Q.where(column, import_watermelondb2.Q.notEq("")),
18709
- import_watermelondb2.Q.where(column, import_watermelondb2.Q.notLike("enc:%"))
18875
+ (column) => import_watermelondb3.Q.and(
18876
+ import_watermelondb3.Q.where(column, import_watermelondb3.Q.notEq(null)),
18877
+ import_watermelondb3.Q.where(column, import_watermelondb3.Q.notEq("")),
18878
+ import_watermelondb3.Q.where(column, import_watermelondb3.Q.notLike("enc:%"))
18710
18879
  )
18711
18880
  )
18712
18881
  )
@@ -18716,7 +18885,7 @@ async function sealLegacyMessageEmbeddingsOp(ctx) {
18716
18885
  for (let start = 0; start < candidateIds.length; start += 50) {
18717
18886
  const pageIds = candidateIds.slice(start, start + 50);
18718
18887
  await ctx.database.write(async () => {
18719
- const pageMessages = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(pageIds))).fetch();
18888
+ const pageMessages = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf(pageIds))).fetch();
18720
18889
  for (const message of pageMessages) {
18721
18890
  if (!isEncrypted(message.content)) continue;
18722
18891
  const content = await decryptFieldDetailed(message.content, ctx.walletAddress);
@@ -18797,15 +18966,15 @@ function chunksWithoutText(raw) {
18797
18966
  });
18798
18967
  }
18799
18968
  async function stripLegacyChunkTextOp(ctx) {
18800
- const candidateIds = await ctx.messagesCollection.query(import_watermelondb2.Q.where("chunks", import_watermelondb2.Q.like(`%${import_watermelondb2.Q.sanitizeLikeString('"text":')}%`))).fetchIds();
18969
+ const candidateIds = await ctx.messagesCollection.query(import_watermelondb3.Q.where("chunks", import_watermelondb3.Q.like(`%${import_watermelondb3.Q.sanitizeLikeString('"text":')}%`))).fetchIds();
18801
18970
  let stripped = 0;
18802
18971
  for (let start = 0; start < candidateIds.length; start += RAW_REPAIR_PAGE_SIZE) {
18803
18972
  const pageIds = candidateIds.slice(start, start + RAW_REPAIR_PAGE_SIZE);
18804
- const rows = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(pageIds))).unsafeFetchRaw();
18973
+ const rows = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf(pageIds))).unsafeFetchRaw();
18805
18974
  const changedIds = rows.filter((row) => chunksWithoutText(row.chunks) !== null).map((row) => String(row.id));
18806
18975
  if (changedIds.length === 0) continue;
18807
18976
  await ctx.database.write(async () => {
18808
- const messages = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(changedIds))).fetch();
18977
+ const messages = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf(changedIds))).fetch();
18809
18978
  const updates = messages.flatMap((message) => {
18810
18979
  const raw = message._raw;
18811
18980
  const chunks = chunksWithoutText(raw.chunks);
@@ -18852,13 +19021,13 @@ async function encryptToolCallEventsOp(ctx) {
18852
19021
  return 0;
18853
19022
  }
18854
19023
  const candidateIds = await ctx.messagesCollection.query(
18855
- import_watermelondb2.Q.where("tool_call_events", import_watermelondb2.Q.notEq(null)),
18856
- import_watermelondb2.Q.where("tool_call_events", import_watermelondb2.Q.notIn(["", "[]"]))
19024
+ import_watermelondb3.Q.where("tool_call_events", import_watermelondb3.Q.notEq(null)),
19025
+ import_watermelondb3.Q.where("tool_call_events", import_watermelondb3.Q.notIn(["", "[]"]))
18857
19026
  ).fetchIds();
18858
19027
  let encrypted = 0;
18859
19028
  for (let start = 0; start < candidateIds.length; start += RAW_REPAIR_PAGE_SIZE) {
18860
19029
  const pageIds = candidateIds.slice(start, start + RAW_REPAIR_PAGE_SIZE);
18861
- const rows = await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(pageIds))).unsafeFetchRaw();
19030
+ const rows = await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf(pageIds))).unsafeFetchRaw();
18862
19031
  const repairs = /* @__PURE__ */ new Map();
18863
19032
  for (const row of rows) {
18864
19033
  const column = await encryptedToolCallEventsColumn(
@@ -18871,12 +19040,12 @@ async function encryptToolCallEventsOp(ctx) {
18871
19040
  }
18872
19041
  if (repairs.size === 0) continue;
18873
19042
  await ctx.database.write(async () => {
18874
- const messages = (await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf([...repairs.keys()]))).fetch()).filter(
19043
+ const messages = (await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf([...repairs.keys()]))).fetch()).filter(
18875
19044
  (message) => message._raw.tool_call_events === repairs.get(message.id)?.raw
18876
19045
  );
18877
19046
  if (messages.length === 0) return;
18878
19047
  const conversationIds = [...new Set(messages.map((message) => message.conversationId))];
18879
- const conversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("conversation_id", import_watermelondb2.Q.oneOf(conversationIds))).fetch();
19048
+ const conversations = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("conversation_id", import_watermelondb3.Q.oneOf(conversationIds))).fetch();
18880
19049
  await ctx.database.batch(
18881
19050
  ...messages.map(
18882
19051
  (message) => message.prepareUpdate((msg) => {
@@ -18903,7 +19072,7 @@ async function savedKeyOpensExistingData(ctx, address, signMessage, embeddedWall
18903
19072
  return refreshEncryptionKeyIfMatches(address, v2Probe, signMessage, embeddedWalletSigner);
18904
19073
  }
18905
19074
  async function findEncryptedContent(ctx, pattern) {
18906
- const [row] = await ctx.messagesCollection.query(import_watermelondb2.Q.where("content", import_watermelondb2.Q.like(pattern)), import_watermelondb2.Q.take(1)).unsafeFetchRaw();
19075
+ const [row] = await ctx.messagesCollection.query(import_watermelondb3.Q.where("content", import_watermelondb3.Q.like(pattern)), import_watermelondb3.Q.take(1)).unsafeFetchRaw();
18907
19076
  return row ? String(row.content) : null;
18908
19077
  }
18909
19078
  async function updateMessageErrorOp(ctx, uniqueId, error) {
@@ -18948,6 +19117,14 @@ async function _updateMessageOp(ctx, uniqueId, opts) {
18948
19117
  ctx.embeddedWalletSigner
18949
19118
  ) : opts;
18950
19119
  await ctx.database.write(async () => {
19120
+ let contentChanged = false;
19121
+ if (opts.content !== void 0) {
19122
+ const originalContent = ctx.walletAddress ? await decryptField(message.content, ctx.walletAddress) : message.content;
19123
+ contentChanged = opts.content !== originalContent;
19124
+ if (contentChanged) {
19125
+ await invalidateConversationSummary(ctx, message.conversationId, message.messageId);
19126
+ }
19127
+ }
18951
19128
  await message.update((msg) => {
18952
19129
  if (encryptedOpts.content !== void 0)
18953
19130
  msg._setRaw("content", encryptedOpts.content);
@@ -18992,6 +19169,7 @@ async function _updateMessageOp(ctx, uniqueId, opts) {
18992
19169
  msg._setRaw("tool_call_events", tceValue);
18993
19170
  }
18994
19171
  });
19172
+ if (contentChanged) advanceConversationRevision(ctx.database, message.conversationId);
18995
19173
  });
18996
19174
  return messageToStored(message, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner);
18997
19175
  }
@@ -19019,11 +19197,11 @@ async function readRawJsonField(raw, column, walletAddress) {
19019
19197
  }
19020
19198
  async function searchMessagesOp(ctx, queryVector, options) {
19021
19199
  const { limit = 10, minSimilarity = 0.5, conversationId } = options || {};
19022
- const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
19200
+ const activeConversations = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("is_deleted", false)).fetch();
19023
19201
  const activeConversationIds = new Set(
19024
19202
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
19025
19203
  );
19026
- const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
19204
+ const queryConditions = conversationId ? [import_watermelondb3.Q.where("conversation_id", conversationId)] : [];
19027
19205
  const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
19028
19206
  const candidates = [];
19029
19207
  for (const message of messages) {
@@ -19062,7 +19240,7 @@ function toChunkCandidateKey(raw) {
19062
19240
  }
19063
19241
  async function fetchRowsByIds(ctx, ids) {
19064
19242
  if (ids.length === 0) return [];
19065
- return await ctx.messagesCollection.query(import_watermelondb2.Q.where("id", import_watermelondb2.Q.oneOf(ids))).unsafeFetchRaw();
19243
+ return await ctx.messagesCollection.query(import_watermelondb3.Q.where("id", import_watermelondb3.Q.oneOf(ids))).unsafeFetchRaw();
19066
19244
  }
19067
19245
  async function projectedChunkRowSource(ctx, filter) {
19068
19246
  const clauses = [
@@ -19080,7 +19258,7 @@ async function projectedChunkRowSource(ctx, filter) {
19080
19258
  args.push(filter.excludeConversationId);
19081
19259
  }
19082
19260
  const keyRows = await ctx.messagesCollection.query(
19083
- import_watermelondb2.Q.unsafeSqlQuery(
19261
+ import_watermelondb3.Q.unsafeSqlQuery(
19084
19262
  `select "id", "conversation_id", "updated_at", "embedding_model" from "history" where ${clauses.join(" and ")}`,
19085
19263
  args
19086
19264
  )
@@ -19090,7 +19268,7 @@ async function projectedChunkRowSource(ctx, filter) {
19090
19268
  loadVectorRows: async (ids) => {
19091
19269
  if (ids.length === 0) return [];
19092
19270
  return await ctx.messagesCollection.query(
19093
- import_watermelondb2.Q.unsafeSqlQuery(
19271
+ import_watermelondb3.Q.unsafeSqlQuery(
19094
19272
  `select "id", "updated_at", "chunks", "vector" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
19095
19273
  ids
19096
19274
  )
@@ -19100,11 +19278,11 @@ async function projectedChunkRowSource(ctx, filter) {
19100
19278
  };
19101
19279
  }
19102
19280
  async function fullScanChunkRowSource(ctx, filter) {
19103
- const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
19281
+ const activeConversations = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("is_deleted", false)).fetch();
19104
19282
  const activeConversationIds = new Set(
19105
19283
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
19106
19284
  );
19107
- const queryConditions = filter.conversationId !== void 0 ? [import_watermelondb2.Q.where("conversation_id", filter.conversationId)] : [];
19285
+ const queryConditions = filter.conversationId !== void 0 ? [import_watermelondb3.Q.where("conversation_id", filter.conversationId)] : [];
19108
19286
  const rows = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
19109
19287
  const rowsById = /* @__PURE__ */ new Map();
19110
19288
  for (const row of rows) {
@@ -19311,12 +19489,12 @@ async function searchChunksOp(ctx, queryVector, options) {
19311
19489
  }
19312
19490
  async function getAllFilesOp(ctx, options) {
19313
19491
  const { conversationId, limit } = options || {};
19314
- const activeConversations = await ctx.conversationsCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
19492
+ const activeConversations = await ctx.conversationsCollection.query(import_watermelondb3.Q.where("is_deleted", false)).fetch();
19315
19493
  const activeConversationIds = new Set(
19316
19494
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
19317
19495
  );
19318
- const queryConditions = conversationId ? [import_watermelondb2.Q.where("conversation_id", conversationId)] : [];
19319
- const messages = await ctx.messagesCollection.query(...queryConditions, import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).fetch();
19496
+ const queryConditions = conversationId ? [import_watermelondb3.Q.where("conversation_id", conversationId)] : [];
19497
+ const messages = await ctx.messagesCollection.query(...queryConditions, import_watermelondb3.Q.sortBy("created_at", import_watermelondb3.Q.desc)).fetch();
19320
19498
  const filesWithContext = [];
19321
19499
  for (const message of messages) {
19322
19500
  const msgConvId = String(message._getRaw("conversation_id"));
@@ -25301,7 +25479,7 @@ function createStockPricePreProcessor(options = {}) {
25301
25479
  init_clientConfig();
25302
25480
 
25303
25481
  // src/lib/db/chat/summaryOperations.ts
25304
- var import_watermelondb3 = __toESM(require_watermelondb());
25482
+ var import_watermelondb4 = __toESM(require_watermelondb());
25305
25483
  function createSummaryContext(database) {
25306
25484
  return {
25307
25485
  database,
@@ -25320,14 +25498,14 @@ function modelToStored(record) {
25320
25498
  };
25321
25499
  }
25322
25500
  async function getConversationSummaryOp(ctx, conversationId) {
25323
- const results = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
25501
+ const results = await ctx.summariesCollection.query(import_watermelondb4.Q.where("conversation_id", conversationId)).fetch();
25324
25502
  if (results.length === 0) return null;
25325
25503
  return modelToStored(results[0]);
25326
25504
  }
25327
25505
  async function upsertConversationSummaryOp(ctx, conversationId, summary, summarizedUpTo, tokenCount) {
25328
25506
  let record;
25329
25507
  await ctx.database.write(async () => {
25330
- const existing = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
25508
+ const existing = await ctx.summariesCollection.query(import_watermelondb4.Q.where("conversation_id", conversationId)).fetch();
25331
25509
  if (existing.length > 0) {
25332
25510
  record = await existing[0].update((rec) => {
25333
25511
  rec.summary = summary;
@@ -25347,7 +25525,7 @@ async function upsertConversationSummaryOp(ctx, conversationId, summary, summari
25347
25525
  }
25348
25526
  async function deleteConversationSummaryOp(ctx, conversationId) {
25349
25527
  await ctx.database.write(async () => {
25350
- const existing = await ctx.summariesCollection.query(import_watermelondb3.Q.where("conversation_id", conversationId)).fetch();
25528
+ const existing = await ctx.summariesCollection.query(import_watermelondb4.Q.where("conversation_id", conversationId)).fetch();
25351
25529
  for (const record of existing) {
25352
25530
  await record.destroyPermanently();
25353
25531
  }
@@ -25591,6 +25769,13 @@ var lastCompactionTime = /* @__PURE__ */ new Map();
25591
25769
  var COMPACTION_COOLDOWN_MS = 6e4;
25592
25770
  var MAX_SUMMARY_TOKEN_RATIO = 0.8;
25593
25771
  async function maybeSummarizeHistory(options) {
25772
+ const { database, conversationId } = options;
25773
+ const revision = getConversationRevision(database, conversationId);
25774
+ const result = await summarizeHistoryAtRevision(options, revision);
25775
+ assertConversationRevision(database, conversationId, revision);
25776
+ return result;
25777
+ }
25778
+ async function summarizeHistoryAtRevision(options, revision) {
25594
25779
  const { database, conversationId, messages, summarizeHistory, summaryMinWindowMessages, token } = options;
25595
25780
  if (!summarizeHistory) {
25596
25781
  return { messagesToConvert: messages, summarySystemMessage: null };
@@ -25631,7 +25816,7 @@ async function maybeSummarizeHistory(options) {
25631
25816
  clearTimeout(staleGuardTimerId);
25632
25817
  }
25633
25818
  }
25634
- const promise = doSummarizeHistory(options);
25819
+ const promise = doSummarizeHistory(options, revision);
25635
25820
  summarizationLocks.set(conversationId, promise);
25636
25821
  try {
25637
25822
  return await promise;
@@ -25645,7 +25830,7 @@ Summary to condense:
25645
25830
  {summary}
25646
25831
 
25647
25832
  Condensed summary:`;
25648
- async function doSummarizeHistory(options) {
25833
+ async function doSummarizeHistory(options, revision) {
25649
25834
  const {
25650
25835
  database,
25651
25836
  conversationId,
@@ -25660,7 +25845,20 @@ async function doSummarizeHistory(options) {
25660
25845
  } = options;
25661
25846
  try {
25662
25847
  const summaryCtx = createSummaryContext(database);
25848
+ const persistSummary = (summary, summarizedUpTo, tokenCount) => database.write(async (writer) => {
25849
+ assertConversationRevision(database, conversationId, revision);
25850
+ await writer.callWriter(
25851
+ () => upsertConversationSummaryOp(
25852
+ summaryCtx,
25853
+ conversationId,
25854
+ summary,
25855
+ summarizedUpTo,
25856
+ tokenCount
25857
+ )
25858
+ );
25859
+ });
25663
25860
  let cachedSummary = await getConversationSummaryOp(summaryCtx, conversationId);
25861
+ assertConversationRevision(database, conversationId, revision);
25664
25862
  const lastCompacted = lastCompactionTime.get(conversationId) ?? 0;
25665
25863
  const compactionCooledDown = Date.now() - lastCompacted > COMPACTION_COOLDOWN_MS;
25666
25864
  if (cachedSummary && cachedSummary.tokenCount > summaryTokenThreshold * MAX_SUMMARY_TOKEN_RATIO && compactionCooledDown) {
@@ -25676,13 +25874,7 @@ async function doSummarizeHistory(options) {
25676
25874
  );
25677
25875
  const compactedSummary = redactor ? redactor.deAnonymize(rawCompacted) : rawCompacted;
25678
25876
  const compactedTokens = estimateTokens(compactedSummary);
25679
- await upsertConversationSummaryOp(
25680
- summaryCtx,
25681
- conversationId,
25682
- compactedSummary,
25683
- cachedSummary.summarizedUpTo,
25684
- compactedTokens
25685
- );
25877
+ await persistSummary(compactedSummary, cachedSummary.summarizedUpTo, compactedTokens);
25686
25878
  cachedSummary = {
25687
25879
  ...cachedSummary,
25688
25880
  summary: compactedSummary,
@@ -25690,6 +25882,7 @@ async function doSummarizeHistory(options) {
25690
25882
  };
25691
25883
  lastCompactionTime.set(conversationId, Date.now());
25692
25884
  } catch {
25885
+ assertConversationRevision(database, conversationId, revision);
25693
25886
  lastCompactionTime.set(conversationId, Date.now());
25694
25887
  getLogger().warn(
25695
25888
  "[summarize] Summary compaction failed, proceeding with oversized summary"
@@ -25721,10 +25914,9 @@ async function doSummarizeHistory(options) {
25721
25914
  redactor,
25722
25915
  onPiiRedacted
25723
25916
  });
25917
+ assertConversationRevision(database, conversationId, revision);
25724
25918
  if (summarizeResult.didSummarize && summarizeResult.summary && summarizeResult.summarizedUpTo) {
25725
- await upsertConversationSummaryOp(
25726
- summaryCtx,
25727
- conversationId,
25919
+ await persistSummary(
25728
25920
  summarizeResult.summary,
25729
25921
  summarizeResult.summarizedUpTo,
25730
25922
  summarizeResult.summaryTokenCount
@@ -25743,6 +25935,7 @@ async function doSummarizeHistory(options) {
25743
25935
  summarySystemMessage: summarizeResult.summary ? summaryToSystemMessage(summarizeResult.summary) : null
25744
25936
  };
25745
25937
  } catch (err) {
25938
+ assertConversationRevision(database, conversationId, revision);
25746
25939
  getLogger().warn("[summarize] Summarization failed, falling back to verbatim:", err);
25747
25940
  return { messagesToConvert: messages, summarySystemMessage: null };
25748
25941
  }
@@ -25901,141 +26094,6 @@ async function decryptConversationTitle(encryptedTitle, address) {
25901
26094
  }
25902
26095
  }
25903
26096
 
25904
- // src/lib/db/chat/models.ts
25905
- var import_watermelondb4 = __toESM(require_watermelondb());
25906
- var import_decorators = __toESM(require_decorators());
25907
- var Message = class extends import_watermelondb4.Model {
25908
- };
25909
- Message.table = "history";
25910
- Message.associations = {
25911
- conversations: { type: "belongs_to", key: "conversation_id" },
25912
- media: { type: "has_many", foreignKey: "message_id" }
25913
- };
25914
- __decorateClass([
25915
- (0, import_decorators.field)("message_id")
25916
- ], Message.prototype, "messageId", 2);
25917
- __decorateClass([
25918
- (0, import_decorators.text)("conversation_id")
25919
- ], Message.prototype, "conversationId", 2);
25920
- __decorateClass([
25921
- (0, import_decorators.text)("role")
25922
- ], Message.prototype, "role", 2);
25923
- __decorateClass([
25924
- (0, import_decorators.text)("content")
25925
- ], Message.prototype, "content", 2);
25926
- __decorateClass([
25927
- (0, import_decorators.text)("model")
25928
- ], Message.prototype, "model", 2);
25929
- __decorateClass([
25930
- (0, import_decorators.text)("image_model")
25931
- ], Message.prototype, "imageModel", 2);
25932
- __decorateClass([
25933
- (0, import_decorators.json)("files", (raw) => raw)
25934
- ], Message.prototype, "files", 2);
25935
- __decorateClass([
25936
- (0, import_decorators.json)("file_ids", (raw) => raw)
25937
- ], Message.prototype, "fileIds", 2);
25938
- __decorateClass([
25939
- (0, import_decorators.date)("created_at")
25940
- ], Message.prototype, "createdAt", 2);
25941
- __decorateClass([
25942
- (0, import_decorators.date)("updated_at")
25943
- ], Message.prototype, "updatedAt", 2);
25944
- __decorateClass([
25945
- (0, import_decorators.json)("vector", (raw) => raw)
25946
- ], Message.prototype, "vector", 2);
25947
- __decorateClass([
25948
- (0, import_decorators.text)("embedding_model")
25949
- ], Message.prototype, "embeddingModel", 2);
25950
- __decorateClass([
25951
- (0, import_decorators.json)("chunks", (raw) => raw)
25952
- ], Message.prototype, "chunks", 2);
25953
- __decorateClass([
25954
- (0, import_decorators.json)("usage", (raw) => raw)
25955
- ], Message.prototype, "usage", 2);
25956
- __decorateClass([
25957
- (0, import_decorators.json)("sources", (raw) => raw)
25958
- ], Message.prototype, "sources", 2);
25959
- __decorateClass([
25960
- (0, import_decorators.field)("response_duration")
25961
- ], Message.prototype, "responseDuration", 2);
25962
- __decorateClass([
25963
- (0, import_decorators.field)("was_stopped")
25964
- ], Message.prototype, "wasStopped", 2);
25965
- __decorateClass([
25966
- (0, import_decorators.text)("error")
25967
- ], Message.prototype, "error", 2);
25968
- __decorateClass([
25969
- (0, import_decorators.json)("thought_process", (raw) => raw)
25970
- ], Message.prototype, "thoughtProcess", 2);
25971
- __decorateClass([
25972
- (0, import_decorators.text)("thinking")
25973
- ], Message.prototype, "thinking", 2);
25974
- __decorateClass([
25975
- (0, import_decorators.text)("parent_message_id")
25976
- ], Message.prototype, "parentMessageId", 2);
25977
- __decorateClass([
25978
- (0, import_decorators.text)("feedback")
25979
- ], Message.prototype, "feedback", 2);
25980
- __decorateClass([
25981
- (0, import_decorators.json)("tool_call_events", (raw) => raw)
25982
- ], Message.prototype, "toolCallEvents", 2);
25983
- __decorateClass([
25984
- (0, import_decorators.text)("origin")
25985
- ], Message.prototype, "origin", 2);
25986
- var Conversation = class extends import_watermelondb4.Model {
25987
- };
25988
- Conversation.table = "conversations";
25989
- Conversation.associations = {
25990
- history: { type: "has_many", foreignKey: "conversation_id" },
25991
- projects: { type: "belongs_to", key: "project_id" }
25992
- };
25993
- __decorateClass([
25994
- (0, import_decorators.text)("conversation_id")
25995
- ], Conversation.prototype, "conversationId", 2);
25996
- __decorateClass([
25997
- (0, import_decorators.text)("title")
25998
- ], Conversation.prototype, "title", 2);
25999
- __decorateClass([
26000
- (0, import_decorators.text)("project_id")
26001
- ], Conversation.prototype, "projectId", 2);
26002
- __decorateClass([
26003
- (0, import_decorators.date)("created_at")
26004
- ], Conversation.prototype, "createdAt", 2);
26005
- __decorateClass([
26006
- (0, import_decorators.date)("updated_at")
26007
- ], Conversation.prototype, "updatedAt", 2);
26008
- __decorateClass([
26009
- (0, import_decorators.field)("is_deleted")
26010
- ], Conversation.prototype, "isDeleted", 2);
26011
- __decorateClass([
26012
- (0, import_decorators.date)("pinned_at")
26013
- ], Conversation.prototype, "pinnedAt", 2);
26014
- var ConversationSummary = class extends import_watermelondb4.Model {
26015
- };
26016
- ConversationSummary.table = "conversation_summaries";
26017
- ConversationSummary.associations = {
26018
- conversations: { type: "belongs_to", key: "conversation_id" }
26019
- };
26020
- __decorateClass([
26021
- (0, import_decorators.text)("conversation_id")
26022
- ], ConversationSummary.prototype, "conversationId", 2);
26023
- __decorateClass([
26024
- (0, import_decorators.text)("summary")
26025
- ], ConversationSummary.prototype, "summary", 2);
26026
- __decorateClass([
26027
- (0, import_decorators.text)("summarized_up_to")
26028
- ], ConversationSummary.prototype, "summarizedUpTo", 2);
26029
- __decorateClass([
26030
- (0, import_decorators.field)("token_count")
26031
- ], ConversationSummary.prototype, "tokenCount", 2);
26032
- __decorateClass([
26033
- (0, import_decorators.date)("created_at")
26034
- ], ConversationSummary.prototype, "createdAt", 2);
26035
- __decorateClass([
26036
- (0, import_decorators.date)("updated_at")
26037
- ], ConversationSummary.prototype, "updatedAt", 2);
26038
-
26039
26097
  // src/lib/db/manager.ts
26040
26098
  var import_watermelondb16 = __toESM(require_watermelondb());
26041
26099
 
@@ -36910,10 +36968,7 @@ async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark,
36910
36968
  for (const facet of facets) {
36911
36969
  if (!previous.sections.some((s) => s.key === facet.key)) stale.add(facet.key);
36912
36970
  }
36913
- const citedIds = new Set(previous.sections.flatMap((s) => s.sourceMemoryIds));
36914
- let toAttribute = changed.filter(
36915
- (m) => !m.isDeleted && !m.supersededBy && !citedIds.has(m.uniqueId)
36916
- );
36971
+ let toAttribute = changed.filter((m) => !m.isDeleted && !m.supersededBy);
36917
36972
  if (reviewedMemoryIds !== void 0) {
36918
36973
  const allowed = new Set(reviewedMemoryIds);
36919
36974
  toAttribute = toAttribute.filter((m) => allowed.has(m.uniqueId));