@anuma/sdk 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1596,7 +1596,16 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
1596
1596
  import { Q as Q2 } from "@nozbe/watermelondb";
1597
1597
 
1598
1598
  // src/lib/db/entities/types.ts
1599
- var ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
1599
+ var ENTITY_KINDS = [
1600
+ "person",
1601
+ "organization",
1602
+ "place",
1603
+ "event",
1604
+ "product",
1605
+ "thing",
1606
+ "concept",
1607
+ "other"
1608
+ ];
1600
1609
  function normalizeEntityName(name) {
1601
1610
  return name.trim().toLowerCase();
1602
1611
  }
@@ -4680,7 +4689,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4680
4689
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4681
4690
  );
4682
4691
  }
4683
- const { recall: recall2 } = await import("./recall-4JTNZBAW.mjs");
4692
+ const { recall: recall2 } = await import("./recall-YKNVP2DF.mjs");
4684
4693
  const result = await recall2(
4685
4694
  query,
4686
4695
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -3833,7 +3833,16 @@ var ENTITY_KINDS;
3833
3833
  var init_types3 = __esm({
3834
3834
  "src/lib/db/entities/types.ts"() {
3835
3835
  "use strict";
3836
- ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
3836
+ ENTITY_KINDS = [
3837
+ "person",
3838
+ "organization",
3839
+ "place",
3840
+ "event",
3841
+ "product",
3842
+ "thing",
3843
+ "concept",
3844
+ "other"
3845
+ ];
3837
3846
  }
3838
3847
  });
3839
3848
 
@@ -12809,7 +12818,18 @@ For each durable fact, output:
12809
12818
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
12810
12819
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
12811
12820
  - sourceMessageIds: which message IDs from the conversation contained the evidence
12812
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
12821
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
12822
+ - person: a named individual ("Sara", "Dr. Lee")
12823
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
12824
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
12825
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
12826
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
12827
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
12828
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
12829
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
12830
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
12831
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
12832
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
12813
12833
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
12814
12834
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
12815
12835
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -16768,8 +16788,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
16768
16788
  }
16769
16789
  var DatabaseManager = class {
16770
16790
  constructor(options) {
16771
- this.database = null;
16772
- this.currentWalletAddress = void 0;
16791
+ /**
16792
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
16793
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
16794
+ * that is requested again — including after a transient `undefined`/guest
16795
+ * excursion or an address that briefly arrives in a different form during
16796
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
16797
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
16798
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
16799
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
16800
+ * disposing an instance the app may still hold a reference to.
16801
+ */
16802
+ this.databases = /* @__PURE__ */ new Map();
16773
16803
  this.migrationInProgress = false;
16774
16804
  this.dbNamePrefix = options.dbNamePrefix;
16775
16805
  this.createAdapter = options.createAdapter;
@@ -16786,8 +16816,9 @@ var DatabaseManager = class {
16786
16816
  /**
16787
16817
  * Get or create a WatermelonDB Database instance for the given wallet.
16788
16818
  *
16789
- * If the wallet address has changed since the last call, the previous
16790
- * database instance is discarded and a new one is created.
16819
+ * Instances are cached per derived dbName and reused across calls (including
16820
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
16821
+ * only for a dbName not yet seen. See the `databases` field for why.
16791
16822
  *
16792
16823
  * @param walletAddress - The wallet address to scope the database to.
16793
16824
  * If undefined, uses a "guest" database.
@@ -16795,46 +16826,47 @@ var DatabaseManager = class {
16795
16826
  * @throws If a destructive migration is in progress
16796
16827
  */
16797
16828
  getDatabase(walletAddress) {
16798
- if (this.database && this.currentWalletAddress !== walletAddress) {
16799
- this.logger.debug?.("Wallet changed, switching database", {
16800
- component: "DatabaseManager",
16801
- from: this.currentWalletAddress,
16802
- to: walletAddress
16803
- });
16804
- this.database = null;
16805
- }
16806
- if (this.database) {
16807
- return this.database;
16829
+ const dbName = this.getDbName(walletAddress);
16830
+ const cached = this.databases.get(dbName);
16831
+ if (cached) {
16832
+ return cached;
16808
16833
  }
16809
16834
  const needsMigration = this.handleSchemaMigration(walletAddress);
16810
16835
  if (needsMigration) {
16811
16836
  throw new Error("Database migration in progress - app will restart");
16812
16837
  }
16813
- this.currentWalletAddress = walletAddress;
16814
- const dbName = this.getDbName(walletAddress);
16815
16838
  this.logger.debug?.("Initializing database", {
16816
16839
  component: "DatabaseManager",
16817
16840
  dbName,
16818
16841
  walletAddress
16819
16842
  });
16820
16843
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
16821
- this.database = new import_watermelondb20.Database({
16844
+ const database = new import_watermelondb20.Database({
16822
16845
  adapter,
16823
16846
  modelClasses: sdkModelClasses
16824
16847
  });
16825
- return this.database;
16848
+ this.databases.set(dbName, database);
16849
+ return database;
16826
16850
  }
16827
16851
  /**
16828
- * Reset the current database (useful for logout or testing).
16852
+ * Reset ALL cached databases (useful for logout or testing).
16829
16853
  */
16830
16854
  async resetDatabase() {
16831
- const db = this.database;
16832
- if (db) {
16833
- await db.write(async () => {
16834
- await db.unsafeResetDatabase();
16835
- });
16836
- this.database = null;
16837
- this.currentWalletAddress = void 0;
16855
+ const errors = [];
16856
+ for (const [dbName, db] of [...this.databases.entries()]) {
16857
+ try {
16858
+ await db.write(async () => {
16859
+ await db.unsafeResetDatabase();
16860
+ });
16861
+ this.databases.delete(dbName);
16862
+ } catch (error) {
16863
+ errors.push({ dbName, error });
16864
+ }
16865
+ }
16866
+ if (errors.length > 0) {
16867
+ throw new Error(
16868
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
16869
+ );
16838
16870
  }
16839
16871
  }
16840
16872
  /**
@@ -4329,7 +4329,7 @@ declare class MemoryEntity extends Model {
4329
4329
  * can't drift apart.
4330
4330
  * @public
4331
4331
  */
4332
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
4332
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
4333
4333
  /** @public */
4334
4334
  type EntityKind = (typeof ENTITY_KINDS)[number];
4335
4335
 
@@ -6542,8 +6542,18 @@ interface DatabaseManagerOptions {
6542
6542
  * ```
6543
6543
  */
6544
6544
  declare class DatabaseManager {
6545
- private database;
6546
- private currentWalletAddress;
6545
+ /**
6546
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
6547
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
6548
+ * that is requested again — including after a transient `undefined`/guest
6549
+ * excursion or an address that briefly arrives in a different form during
6550
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
6551
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
6552
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
6553
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
6554
+ * disposing an instance the app may still hold a reference to.
6555
+ */
6556
+ private readonly databases;
6547
6557
  private migrationInProgress;
6548
6558
  private readonly dbNamePrefix;
6549
6559
  private readonly createAdapter;
@@ -6558,8 +6568,9 @@ declare class DatabaseManager {
6558
6568
  /**
6559
6569
  * Get or create a WatermelonDB Database instance for the given wallet.
6560
6570
  *
6561
- * If the wallet address has changed since the last call, the previous
6562
- * database instance is discarded and a new one is created.
6571
+ * Instances are cached per derived dbName and reused across calls (including
6572
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
6573
+ * only for a dbName not yet seen. See the `databases` field for why.
6563
6574
  *
6564
6575
  * @param walletAddress - The wallet address to scope the database to.
6565
6576
  * If undefined, uses a "guest" database.
@@ -6568,7 +6579,7 @@ declare class DatabaseManager {
6568
6579
  */
6569
6580
  getDatabase(walletAddress?: string): Database;
6570
6581
  /**
6571
- * Reset the current database (useful for logout or testing).
6582
+ * Reset ALL cached databases (useful for logout or testing).
6572
6583
  */
6573
6584
  resetDatabase(): Promise<void>;
6574
6585
  /**
@@ -4329,7 +4329,7 @@ declare class MemoryEntity extends Model {
4329
4329
  * can't drift apart.
4330
4330
  * @public
4331
4331
  */
4332
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
4332
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
4333
4333
  /** @public */
4334
4334
  type EntityKind = (typeof ENTITY_KINDS)[number];
4335
4335
 
@@ -6542,8 +6542,18 @@ interface DatabaseManagerOptions {
6542
6542
  * ```
6543
6543
  */
6544
6544
  declare class DatabaseManager {
6545
- private database;
6546
- private currentWalletAddress;
6545
+ /**
6546
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
6547
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
6548
+ * that is requested again — including after a transient `undefined`/guest
6549
+ * excursion or an address that briefly arrives in a different form during
6550
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
6551
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
6552
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
6553
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
6554
+ * disposing an instance the app may still hold a reference to.
6555
+ */
6556
+ private readonly databases;
6547
6557
  private migrationInProgress;
6548
6558
  private readonly dbNamePrefix;
6549
6559
  private readonly createAdapter;
@@ -6558,8 +6568,9 @@ declare class DatabaseManager {
6558
6568
  /**
6559
6569
  * Get or create a WatermelonDB Database instance for the given wallet.
6560
6570
  *
6561
- * If the wallet address has changed since the last call, the previous
6562
- * database instance is discarded and a new one is created.
6571
+ * Instances are cached per derived dbName and reused across calls (including
6572
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
6573
+ * only for a dbName not yet seen. See the `databases` field for why.
6563
6574
  *
6564
6575
  * @param walletAddress - The wallet address to scope the database to.
6565
6576
  * If undefined, uses a "guest" database.
@@ -6568,7 +6579,7 @@ declare class DatabaseManager {
6568
6579
  */
6569
6580
  getDatabase(walletAddress?: string): Database;
6570
6581
  /**
6571
- * Reset the current database (useful for logout or testing).
6582
+ * Reset ALL cached databases (useful for logout or testing).
6572
6583
  */
6573
6584
  resetDatabase(): Promise<void>;
6574
6585
  /**
@@ -79,7 +79,7 @@ import {
79
79
  updateVaultMemoryOp,
80
80
  upsertMessageOp,
81
81
  useEncryption
82
- } from "./chunk-IKPPX3GY.mjs";
82
+ } from "./chunk-QKF7PRL5.mjs";
83
83
  import {
84
84
  PII_PATTERNS,
85
85
  PiiRedactor,
@@ -6094,7 +6094,18 @@ For each durable fact, output:
6094
6094
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
6095
6095
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
6096
6096
  - sourceMessageIds: which message IDs from the conversation contained the evidence
6097
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
6097
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
6098
+ - person: a named individual ("Sara", "Dr. Lee")
6099
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
6100
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
6101
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
6102
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
6103
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
6104
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
6105
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
6106
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
6107
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
6108
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
6098
6109
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
6099
6110
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
6100
6111
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -10020,8 +10031,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
10020
10031
  }
10021
10032
  var DatabaseManager = class {
10022
10033
  constructor(options) {
10023
- this.database = null;
10024
- this.currentWalletAddress = void 0;
10034
+ /**
10035
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
10036
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
10037
+ * that is requested again — including after a transient `undefined`/guest
10038
+ * excursion or an address that briefly arrives in a different form during
10039
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
10040
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
10041
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
10042
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
10043
+ * disposing an instance the app may still hold a reference to.
10044
+ */
10045
+ this.databases = /* @__PURE__ */ new Map();
10025
10046
  this.migrationInProgress = false;
10026
10047
  this.dbNamePrefix = options.dbNamePrefix;
10027
10048
  this.createAdapter = options.createAdapter;
@@ -10038,8 +10059,9 @@ var DatabaseManager = class {
10038
10059
  /**
10039
10060
  * Get or create a WatermelonDB Database instance for the given wallet.
10040
10061
  *
10041
- * If the wallet address has changed since the last call, the previous
10042
- * database instance is discarded and a new one is created.
10062
+ * Instances are cached per derived dbName and reused across calls (including
10063
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
10064
+ * only for a dbName not yet seen. See the `databases` field for why.
10043
10065
  *
10044
10066
  * @param walletAddress - The wallet address to scope the database to.
10045
10067
  * If undefined, uses a "guest" database.
@@ -10047,46 +10069,47 @@ var DatabaseManager = class {
10047
10069
  * @throws If a destructive migration is in progress
10048
10070
  */
10049
10071
  getDatabase(walletAddress) {
10050
- if (this.database && this.currentWalletAddress !== walletAddress) {
10051
- this.logger.debug?.("Wallet changed, switching database", {
10052
- component: "DatabaseManager",
10053
- from: this.currentWalletAddress,
10054
- to: walletAddress
10055
- });
10056
- this.database = null;
10057
- }
10058
- if (this.database) {
10059
- return this.database;
10072
+ const dbName = this.getDbName(walletAddress);
10073
+ const cached = this.databases.get(dbName);
10074
+ if (cached) {
10075
+ return cached;
10060
10076
  }
10061
10077
  const needsMigration = this.handleSchemaMigration(walletAddress);
10062
10078
  if (needsMigration) {
10063
10079
  throw new Error("Database migration in progress - app will restart");
10064
10080
  }
10065
- this.currentWalletAddress = walletAddress;
10066
- const dbName = this.getDbName(walletAddress);
10067
10081
  this.logger.debug?.("Initializing database", {
10068
10082
  component: "DatabaseManager",
10069
10083
  dbName,
10070
10084
  walletAddress
10071
10085
  });
10072
10086
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
10073
- this.database = new Database({
10087
+ const database = new Database({
10074
10088
  adapter,
10075
10089
  modelClasses: sdkModelClasses
10076
10090
  });
10077
- return this.database;
10091
+ this.databases.set(dbName, database);
10092
+ return database;
10078
10093
  }
10079
10094
  /**
10080
- * Reset the current database (useful for logout or testing).
10095
+ * Reset ALL cached databases (useful for logout or testing).
10081
10096
  */
10082
10097
  async resetDatabase() {
10083
- const db = this.database;
10084
- if (db) {
10085
- await db.write(async () => {
10086
- await db.unsafeResetDatabase();
10087
- });
10088
- this.database = null;
10089
- this.currentWalletAddress = void 0;
10098
+ const errors = [];
10099
+ for (const [dbName, db] of [...this.databases.entries()]) {
10100
+ try {
10101
+ await db.write(async () => {
10102
+ await db.unsafeResetDatabase();
10103
+ });
10104
+ this.databases.delete(dbName);
10105
+ } catch (error) {
10106
+ errors.push({ dbName, error });
10107
+ }
10108
+ }
10109
+ if (errors.length > 0) {
10110
+ throw new Error(
10111
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
10112
+ );
10090
10113
  }
10091
10114
  }
10092
10115
  /**
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-IKPPX3GY.mjs";
3
+ } from "./chunk-QKF7PRL5.mjs";
4
4
  import "./chunk-DFVCS3QQ.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -1730,7 +1730,16 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
1730
1730
  import { Q as Q2 } from "@nozbe/watermelondb";
1731
1731
 
1732
1732
  // src/lib/db/entities/types.ts
1733
- var ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
1733
+ var ENTITY_KINDS = [
1734
+ "person",
1735
+ "organization",
1736
+ "place",
1737
+ "event",
1738
+ "product",
1739
+ "thing",
1740
+ "concept",
1741
+ "other"
1742
+ ];
1734
1743
  function normalizeEntityName(name) {
1735
1744
  return name.trim().toLowerCase();
1736
1745
  }
@@ -5011,7 +5020,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
5011
5020
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
5012
5021
  );
5013
5022
  }
5014
- const { recall: recall2 } = await import("./recall-Z2HUBS2Y.mjs");
5023
+ const { recall: recall2 } = await import("./recall-UWH5HU6V.mjs");
5015
5024
  const result = await recall2(
5016
5025
  query,
5017
5026
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -4089,7 +4089,16 @@ var ENTITY_KINDS;
4089
4089
  var init_types3 = __esm({
4090
4090
  "src/lib/db/entities/types.ts"() {
4091
4091
  "use strict";
4092
- ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
4092
+ ENTITY_KINDS = [
4093
+ "person",
4094
+ "organization",
4095
+ "place",
4096
+ "event",
4097
+ "product",
4098
+ "thing",
4099
+ "concept",
4100
+ "other"
4101
+ ];
4093
4102
  }
4094
4103
  });
4095
4104
 
@@ -14158,7 +14167,18 @@ For each durable fact, output:
14158
14167
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
14159
14168
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
14160
14169
  - sourceMessageIds: which message IDs from the conversation contained the evidence
14161
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
14170
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
14171
+ - person: a named individual ("Sara", "Dr. Lee")
14172
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
14173
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
14174
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
14175
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
14176
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
14177
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
14178
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
14179
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
14180
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
14181
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
14162
14182
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
14163
14183
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
14164
14184
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -19962,8 +19982,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
19962
19982
  }
19963
19983
  var DatabaseManager = class {
19964
19984
  constructor(options) {
19965
- this.database = null;
19966
- this.currentWalletAddress = void 0;
19985
+ /**
19986
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
19987
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
19988
+ * that is requested again — including after a transient `undefined`/guest
19989
+ * excursion or an address that briefly arrives in a different form during
19990
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
19991
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
19992
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
19993
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
19994
+ * disposing an instance the app may still hold a reference to.
19995
+ */
19996
+ this.databases = /* @__PURE__ */ new Map();
19967
19997
  this.migrationInProgress = false;
19968
19998
  this.dbNamePrefix = options.dbNamePrefix;
19969
19999
  this.createAdapter = options.createAdapter;
@@ -19980,8 +20010,9 @@ var DatabaseManager = class {
19980
20010
  /**
19981
20011
  * Get or create a WatermelonDB Database instance for the given wallet.
19982
20012
  *
19983
- * If the wallet address has changed since the last call, the previous
19984
- * database instance is discarded and a new one is created.
20013
+ * Instances are cached per derived dbName and reused across calls (including
20014
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
20015
+ * only for a dbName not yet seen. See the `databases` field for why.
19985
20016
  *
19986
20017
  * @param walletAddress - The wallet address to scope the database to.
19987
20018
  * If undefined, uses a "guest" database.
@@ -19989,46 +20020,47 @@ var DatabaseManager = class {
19989
20020
  * @throws If a destructive migration is in progress
19990
20021
  */
19991
20022
  getDatabase(walletAddress) {
19992
- if (this.database && this.currentWalletAddress !== walletAddress) {
19993
- this.logger.debug?.("Wallet changed, switching database", {
19994
- component: "DatabaseManager",
19995
- from: this.currentWalletAddress,
19996
- to: walletAddress
19997
- });
19998
- this.database = null;
19999
- }
20000
- if (this.database) {
20001
- return this.database;
20023
+ const dbName = this.getDbName(walletAddress);
20024
+ const cached = this.databases.get(dbName);
20025
+ if (cached) {
20026
+ return cached;
20002
20027
  }
20003
20028
  const needsMigration = this.handleSchemaMigration(walletAddress);
20004
20029
  if (needsMigration) {
20005
20030
  throw new Error("Database migration in progress - app will restart");
20006
20031
  }
20007
- this.currentWalletAddress = walletAddress;
20008
- const dbName = this.getDbName(walletAddress);
20009
20032
  this.logger.debug?.("Initializing database", {
20010
20033
  component: "DatabaseManager",
20011
20034
  dbName,
20012
20035
  walletAddress
20013
20036
  });
20014
20037
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
20015
- this.database = new import_watermelondb21.Database({
20038
+ const database = new import_watermelondb21.Database({
20016
20039
  adapter,
20017
20040
  modelClasses: sdkModelClasses
20018
20041
  });
20019
- return this.database;
20042
+ this.databases.set(dbName, database);
20043
+ return database;
20020
20044
  }
20021
20045
  /**
20022
- * Reset the current database (useful for logout or testing).
20046
+ * Reset ALL cached databases (useful for logout or testing).
20023
20047
  */
20024
20048
  async resetDatabase() {
20025
- const db = this.database;
20026
- if (db) {
20027
- await db.write(async () => {
20028
- await db.unsafeResetDatabase();
20029
- });
20030
- this.database = null;
20031
- this.currentWalletAddress = void 0;
20049
+ const errors = [];
20050
+ for (const [dbName, db] of [...this.databases.entries()]) {
20051
+ try {
20052
+ await db.write(async () => {
20053
+ await db.unsafeResetDatabase();
20054
+ });
20055
+ this.databases.delete(dbName);
20056
+ } catch (error) {
20057
+ errors.push({ dbName, error });
20058
+ }
20059
+ }
20060
+ if (errors.length > 0) {
20061
+ throw new Error(
20062
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
20063
+ );
20032
20064
  }
20033
20065
  }
20034
20066
  /**
@@ -5552,7 +5552,7 @@ declare class MemoryEntity extends Model {
5552
5552
  * can't drift apart.
5553
5553
  * @public
5554
5554
  */
5555
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
5555
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
5556
5556
  /** @public */
5557
5557
  type EntityKind = (typeof ENTITY_KINDS)[number];
5558
5558
  interface StoredEntity {
@@ -8077,8 +8077,18 @@ declare const webPlatformStorage: PlatformStorage;
8077
8077
  * ```
8078
8078
  */
8079
8079
  declare class DatabaseManager {
8080
- private database;
8081
- private currentWalletAddress;
8080
+ /**
8081
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
8082
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
8083
+ * that is requested again — including after a transient `undefined`/guest
8084
+ * excursion or an address that briefly arrives in a different form during
8085
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
8086
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
8087
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
8088
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
8089
+ * disposing an instance the app may still hold a reference to.
8090
+ */
8091
+ private readonly databases;
8082
8092
  private migrationInProgress;
8083
8093
  private readonly dbNamePrefix;
8084
8094
  private readonly createAdapter;
@@ -8093,8 +8103,9 @@ declare class DatabaseManager {
8093
8103
  /**
8094
8104
  * Get or create a WatermelonDB Database instance for the given wallet.
8095
8105
  *
8096
- * If the wallet address has changed since the last call, the previous
8097
- * database instance is discarded and a new one is created.
8106
+ * Instances are cached per derived dbName and reused across calls (including
8107
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
8108
+ * only for a dbName not yet seen. See the `databases` field for why.
8098
8109
  *
8099
8110
  * @param walletAddress - The wallet address to scope the database to.
8100
8111
  * If undefined, uses a "guest" database.
@@ -8103,7 +8114,7 @@ declare class DatabaseManager {
8103
8114
  */
8104
8115
  getDatabase(walletAddress?: string): Database;
8105
8116
  /**
8106
- * Reset the current database (useful for logout or testing).
8117
+ * Reset ALL cached databases (useful for logout or testing).
8107
8118
  */
8108
8119
  resetDatabase(): Promise<void>;
8109
8120
  /**
@@ -5552,7 +5552,7 @@ declare class MemoryEntity extends Model {
5552
5552
  * can't drift apart.
5553
5553
  * @public
5554
5554
  */
5555
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
5555
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
5556
5556
  /** @public */
5557
5557
  type EntityKind = (typeof ENTITY_KINDS)[number];
5558
5558
  interface StoredEntity {
@@ -8077,8 +8077,18 @@ declare const webPlatformStorage: PlatformStorage;
8077
8077
  * ```
8078
8078
  */
8079
8079
  declare class DatabaseManager {
8080
- private database;
8081
- private currentWalletAddress;
8080
+ /**
8081
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
8082
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
8083
+ * that is requested again — including after a transient `undefined`/guest
8084
+ * excursion or an address that briefly arrives in a different form during
8085
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
8086
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
8087
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
8088
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
8089
+ * disposing an instance the app may still hold a reference to.
8090
+ */
8091
+ private readonly databases;
8082
8092
  private migrationInProgress;
8083
8093
  private readonly dbNamePrefix;
8084
8094
  private readonly createAdapter;
@@ -8093,8 +8103,9 @@ declare class DatabaseManager {
8093
8103
  /**
8094
8104
  * Get or create a WatermelonDB Database instance for the given wallet.
8095
8105
  *
8096
- * If the wallet address has changed since the last call, the previous
8097
- * database instance is discarded and a new one is created.
8106
+ * Instances are cached per derived dbName and reused across calls (including
8107
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
8108
+ * only for a dbName not yet seen. See the `databases` field for why.
8098
8109
  *
8099
8110
  * @param walletAddress - The wallet address to scope the database to.
8100
8111
  * If undefined, uses a "guest" database.
@@ -8103,7 +8114,7 @@ declare class DatabaseManager {
8103
8114
  */
8104
8115
  getDatabase(walletAddress?: string): Database;
8105
8116
  /**
8106
- * Reset the current database (useful for logout or testing).
8117
+ * Reset ALL cached databases (useful for logout or testing).
8107
8118
  */
8108
8119
  resetDatabase(): Promise<void>;
8109
8120
  /**
@@ -121,7 +121,7 @@ import {
121
121
  updateVaultMemoryEmbeddingOp,
122
122
  updateVaultMemoryOp,
123
123
  useEncryption
124
- } from "./chunk-KQZ4KVYY.mjs";
124
+ } from "./chunk-SEZCFG24.mjs";
125
125
  import {
126
126
  PiiRedactor,
127
127
  createStreamingDeAnonymizer,
@@ -6916,7 +6916,18 @@ For each durable fact, output:
6916
6916
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
6917
6917
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
6918
6918
  - sourceMessageIds: which message IDs from the conversation contained the evidence
6919
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
6919
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
6920
+ - person: a named individual ("Sara", "Dr. Lee")
6921
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
6922
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
6923
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
6924
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
6925
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
6926
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
6927
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
6928
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
6929
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
6930
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
6920
6931
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
6921
6932
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
6922
6933
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -12680,8 +12691,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
12680
12691
  }
12681
12692
  var DatabaseManager = class {
12682
12693
  constructor(options) {
12683
- this.database = null;
12684
- this.currentWalletAddress = void 0;
12694
+ /**
12695
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
12696
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
12697
+ * that is requested again — including after a transient `undefined`/guest
12698
+ * excursion or an address that briefly arrives in a different form during
12699
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
12700
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
12701
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
12702
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
12703
+ * disposing an instance the app may still hold a reference to.
12704
+ */
12705
+ this.databases = /* @__PURE__ */ new Map();
12685
12706
  this.migrationInProgress = false;
12686
12707
  this.dbNamePrefix = options.dbNamePrefix;
12687
12708
  this.createAdapter = options.createAdapter;
@@ -12698,8 +12719,9 @@ var DatabaseManager = class {
12698
12719
  /**
12699
12720
  * Get or create a WatermelonDB Database instance for the given wallet.
12700
12721
  *
12701
- * If the wallet address has changed since the last call, the previous
12702
- * database instance is discarded and a new one is created.
12722
+ * Instances are cached per derived dbName and reused across calls (including
12723
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
12724
+ * only for a dbName not yet seen. See the `databases` field for why.
12703
12725
  *
12704
12726
  * @param walletAddress - The wallet address to scope the database to.
12705
12727
  * If undefined, uses a "guest" database.
@@ -12707,46 +12729,47 @@ var DatabaseManager = class {
12707
12729
  * @throws If a destructive migration is in progress
12708
12730
  */
12709
12731
  getDatabase(walletAddress) {
12710
- if (this.database && this.currentWalletAddress !== walletAddress) {
12711
- this.logger.debug?.("Wallet changed, switching database", {
12712
- component: "DatabaseManager",
12713
- from: this.currentWalletAddress,
12714
- to: walletAddress
12715
- });
12716
- this.database = null;
12717
- }
12718
- if (this.database) {
12719
- return this.database;
12732
+ const dbName = this.getDbName(walletAddress);
12733
+ const cached = this.databases.get(dbName);
12734
+ if (cached) {
12735
+ return cached;
12720
12736
  }
12721
12737
  const needsMigration = this.handleSchemaMigration(walletAddress);
12722
12738
  if (needsMigration) {
12723
12739
  throw new Error("Database migration in progress - app will restart");
12724
12740
  }
12725
- this.currentWalletAddress = walletAddress;
12726
- const dbName = this.getDbName(walletAddress);
12727
12741
  this.logger.debug?.("Initializing database", {
12728
12742
  component: "DatabaseManager",
12729
12743
  dbName,
12730
12744
  walletAddress
12731
12745
  });
12732
12746
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
12733
- this.database = new Database({
12747
+ const database = new Database({
12734
12748
  adapter,
12735
12749
  modelClasses: sdkModelClasses
12736
12750
  });
12737
- return this.database;
12751
+ this.databases.set(dbName, database);
12752
+ return database;
12738
12753
  }
12739
12754
  /**
12740
- * Reset the current database (useful for logout or testing).
12755
+ * Reset ALL cached databases (useful for logout or testing).
12741
12756
  */
12742
12757
  async resetDatabase() {
12743
- const db = this.database;
12744
- if (db) {
12745
- await db.write(async () => {
12746
- await db.unsafeResetDatabase();
12747
- });
12748
- this.database = null;
12749
- this.currentWalletAddress = void 0;
12758
+ const errors = [];
12759
+ for (const [dbName, db] of [...this.databases.entries()]) {
12760
+ try {
12761
+ await db.write(async () => {
12762
+ await db.unsafeResetDatabase();
12763
+ });
12764
+ this.databases.delete(dbName);
12765
+ } catch (error) {
12766
+ errors.push({ dbName, error });
12767
+ }
12768
+ }
12769
+ if (errors.length > 0) {
12770
+ throw new Error(
12771
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
12772
+ );
12750
12773
  }
12751
12774
  }
12752
12775
  /**
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-KQZ4KVYY.mjs";
3
+ } from "./chunk-SEZCFG24.mjs";
4
4
  import "./chunk-DFVCS3QQ.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -15782,7 +15782,16 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
15782
15782
  var import_watermelondb2 = __toESM(require_watermelondb());
15783
15783
 
15784
15784
  // src/lib/db/entities/types.ts
15785
- var ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
15785
+ var ENTITY_KINDS = [
15786
+ "person",
15787
+ "organization",
15788
+ "place",
15789
+ "event",
15790
+ "product",
15791
+ "thing",
15792
+ "concept",
15793
+ "other"
15794
+ ];
15786
15795
  function normalizeEntityName(name) {
15787
15796
  return name.trim().toLowerCase();
15788
15797
  }
@@ -18838,7 +18847,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
18838
18847
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
18839
18848
  );
18840
18849
  }
18841
- const { recall: recall2 } = await import("./recall-IF2HX7ZA.mjs");
18850
+ const { recall: recall2 } = await import("./recall-24ZIA2VL.mjs");
18842
18851
  const result = await recall2(
18843
18852
  query,
18844
18853
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -16568,7 +16568,16 @@ var ENTITY_KINDS;
16568
16568
  var init_types3 = __esm({
16569
16569
  "src/lib/db/entities/types.ts"() {
16570
16570
  "use strict";
16571
- ENTITY_KINDS = ["person", "place", "thing", "concept", "other"];
16571
+ ENTITY_KINDS = [
16572
+ "person",
16573
+ "organization",
16574
+ "place",
16575
+ "event",
16576
+ "product",
16577
+ "thing",
16578
+ "concept",
16579
+ "other"
16580
+ ];
16572
16581
  }
16573
16582
  });
16574
16583
 
@@ -22893,8 +22902,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
22893
22902
  }
22894
22903
  var DatabaseManager = class {
22895
22904
  constructor(options) {
22896
- this.database = null;
22897
- this.currentWalletAddress = void 0;
22905
+ /**
22906
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
22907
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
22908
+ * that is requested again — including after a transient `undefined`/guest
22909
+ * excursion or an address that briefly arrives in a different form during
22910
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
22911
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
22912
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
22913
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
22914
+ * disposing an instance the app may still hold a reference to.
22915
+ */
22916
+ this.databases = /* @__PURE__ */ new Map();
22898
22917
  this.migrationInProgress = false;
22899
22918
  this.dbNamePrefix = options.dbNamePrefix;
22900
22919
  this.createAdapter = options.createAdapter;
@@ -22911,8 +22930,9 @@ var DatabaseManager = class {
22911
22930
  /**
22912
22931
  * Get or create a WatermelonDB Database instance for the given wallet.
22913
22932
  *
22914
- * If the wallet address has changed since the last call, the previous
22915
- * database instance is discarded and a new one is created.
22933
+ * Instances are cached per derived dbName and reused across calls (including
22934
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
22935
+ * only for a dbName not yet seen. See the `databases` field for why.
22916
22936
  *
22917
22937
  * @param walletAddress - The wallet address to scope the database to.
22918
22938
  * If undefined, uses a "guest" database.
@@ -22920,46 +22940,47 @@ var DatabaseManager = class {
22920
22940
  * @throws If a destructive migration is in progress
22921
22941
  */
22922
22942
  getDatabase(walletAddress) {
22923
- if (this.database && this.currentWalletAddress !== walletAddress) {
22924
- this.logger.debug?.("Wallet changed, switching database", {
22925
- component: "DatabaseManager",
22926
- from: this.currentWalletAddress,
22927
- to: walletAddress
22928
- });
22929
- this.database = null;
22930
- }
22931
- if (this.database) {
22932
- return this.database;
22943
+ const dbName = this.getDbName(walletAddress);
22944
+ const cached = this.databases.get(dbName);
22945
+ if (cached) {
22946
+ return cached;
22933
22947
  }
22934
22948
  const needsMigration = this.handleSchemaMigration(walletAddress);
22935
22949
  if (needsMigration) {
22936
22950
  throw new Error("Database migration in progress - app will restart");
22937
22951
  }
22938
- this.currentWalletAddress = walletAddress;
22939
- const dbName = this.getDbName(walletAddress);
22940
22952
  this.logger.debug?.("Initializing database", {
22941
22953
  component: "DatabaseManager",
22942
22954
  dbName,
22943
22955
  walletAddress
22944
22956
  });
22945
22957
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
22946
- this.database = new import_watermelondb13.Database({
22958
+ const database = new import_watermelondb13.Database({
22947
22959
  adapter,
22948
22960
  modelClasses: sdkModelClasses
22949
22961
  });
22950
- return this.database;
22962
+ this.databases.set(dbName, database);
22963
+ return database;
22951
22964
  }
22952
22965
  /**
22953
- * Reset the current database (useful for logout or testing).
22966
+ * Reset ALL cached databases (useful for logout or testing).
22954
22967
  */
22955
22968
  async resetDatabase() {
22956
- const db = this.database;
22957
- if (db) {
22958
- await db.write(async () => {
22959
- await db.unsafeResetDatabase();
22960
- });
22961
- this.database = null;
22962
- this.currentWalletAddress = void 0;
22969
+ const errors = [];
22970
+ for (const [dbName, db] of [...this.databases.entries()]) {
22971
+ try {
22972
+ await db.write(async () => {
22973
+ await db.unsafeResetDatabase();
22974
+ });
22975
+ this.databases.delete(dbName);
22976
+ } catch (error) {
22977
+ errors.push({ dbName, error });
22978
+ }
22979
+ }
22980
+ if (errors.length > 0) {
22981
+ throw new Error(
22982
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
22983
+ );
22963
22984
  }
22964
22985
  }
22965
22986
  /**
@@ -25802,7 +25823,18 @@ For each durable fact, output:
25802
25823
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
25803
25824
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
25804
25825
  - sourceMessageIds: which message IDs from the conversation contained the evidence
25805
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
25826
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
25827
+ - person: a named individual ("Sara", "Dr. Lee")
25828
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
25829
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
25830
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
25831
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
25832
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
25833
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
25834
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
25835
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
25836
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
25837
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
25806
25838
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
25807
25839
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
25808
25840
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -248,8 +248,18 @@ interface DatabaseManagerOptions {
248
248
  * ```
249
249
  */
250
250
  declare class DatabaseManager {
251
- private database;
252
- private currentWalletAddress;
251
+ /**
252
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
253
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
254
+ * that is requested again — including after a transient `undefined`/guest
255
+ * excursion or an address that briefly arrives in a different form during
256
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
257
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
258
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
259
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
260
+ * disposing an instance the app may still hold a reference to.
261
+ */
262
+ private readonly databases;
253
263
  private migrationInProgress;
254
264
  private readonly dbNamePrefix;
255
265
  private readonly createAdapter;
@@ -264,8 +274,9 @@ declare class DatabaseManager {
264
274
  /**
265
275
  * Get or create a WatermelonDB Database instance for the given wallet.
266
276
  *
267
- * If the wallet address has changed since the last call, the previous
268
- * database instance is discarded and a new one is created.
277
+ * Instances are cached per derived dbName and reused across calls (including
278
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
279
+ * only for a dbName not yet seen. See the `databases` field for why.
269
280
  *
270
281
  * @param walletAddress - The wallet address to scope the database to.
271
282
  * If undefined, uses a "guest" database.
@@ -274,7 +285,7 @@ declare class DatabaseManager {
274
285
  */
275
286
  getDatabase(walletAddress?: string): Database;
276
287
  /**
277
- * Reset the current database (useful for logout or testing).
288
+ * Reset ALL cached databases (useful for logout or testing).
278
289
  */
279
290
  resetDatabase(): Promise<void>;
280
291
  /**
@@ -4132,7 +4143,7 @@ declare class MemoryEntity extends Model$1 {
4132
4143
  * can't drift apart.
4133
4144
  * @public
4134
4145
  */
4135
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
4146
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
4136
4147
  /** @public */
4137
4148
  type EntityKind = (typeof ENTITY_KINDS)[number];
4138
4149
 
@@ -248,8 +248,18 @@ interface DatabaseManagerOptions {
248
248
  * ```
249
249
  */
250
250
  declare class DatabaseManager {
251
- private database;
252
- private currentWalletAddress;
251
+ /**
252
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
253
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
254
+ * that is requested again — including after a transient `undefined`/guest
255
+ * excursion or an address that briefly arrives in a different form during
256
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
257
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
258
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
259
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
260
+ * disposing an instance the app may still hold a reference to.
261
+ */
262
+ private readonly databases;
253
263
  private migrationInProgress;
254
264
  private readonly dbNamePrefix;
255
265
  private readonly createAdapter;
@@ -264,8 +274,9 @@ declare class DatabaseManager {
264
274
  /**
265
275
  * Get or create a WatermelonDB Database instance for the given wallet.
266
276
  *
267
- * If the wallet address has changed since the last call, the previous
268
- * database instance is discarded and a new one is created.
277
+ * Instances are cached per derived dbName and reused across calls (including
278
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
279
+ * only for a dbName not yet seen. See the `databases` field for why.
269
280
  *
270
281
  * @param walletAddress - The wallet address to scope the database to.
271
282
  * If undefined, uses a "guest" database.
@@ -274,7 +285,7 @@ declare class DatabaseManager {
274
285
  */
275
286
  getDatabase(walletAddress?: string): Database;
276
287
  /**
277
- * Reset the current database (useful for logout or testing).
288
+ * Reset ALL cached databases (useful for logout or testing).
278
289
  */
279
290
  resetDatabase(): Promise<void>;
280
291
  /**
@@ -4132,7 +4143,7 @@ declare class MemoryEntity extends Model$1 {
4132
4143
  * can't drift apart.
4133
4144
  * @public
4134
4145
  */
4135
- declare const ENTITY_KINDS: readonly ["person", "place", "thing", "concept", "other"];
4146
+ declare const ENTITY_KINDS: readonly ["person", "organization", "place", "event", "product", "thing", "concept", "other"];
4136
4147
  /** @public */
4137
4148
  type EntityKind = (typeof ENTITY_KINDS)[number];
4138
4149
 
@@ -95,7 +95,7 @@ import {
95
95
  updateVaultMemoryEmbeddingOp,
96
96
  updateVaultMemoryOp,
97
97
  upsertMessageOp
98
- } from "./chunk-KLJBJWWI.mjs";
98
+ } from "./chunk-Z7CJGOTG.mjs";
99
99
  import {
100
100
  createStreamingDeAnonymizer,
101
101
  resolvePiiRedactor
@@ -2000,8 +2000,18 @@ function getMigrationReloadKey(prefix, walletAddress) {
2000
2000
  }
2001
2001
  var DatabaseManager = class {
2002
2002
  constructor(options) {
2003
- this.database = null;
2004
- this.currentWalletAddress = void 0;
2003
+ /**
2004
+ * WatermelonDB instances cached by their derived `dbName` (not by raw wallet
2005
+ * address). Caching per dbName makes {@link getDatabase} idempotent: a wallet
2006
+ * that is requested again — including after a transient `undefined`/guest
2007
+ * excursion or an address that briefly arrives in a different form during
2008
+ * sign-up — reuses the SAME instance instead of being torn down and rebuilt.
2009
+ * The rebuild was what left the web OPFS adapter disposed/orphaned mid-session
2010
+ * (client#4821). Instances are kept for the lifetime of the manager (typically
2011
+ * guest + one wallet); switching wallets keeps prior ones cached rather than
2012
+ * disposing an instance the app may still hold a reference to.
2013
+ */
2014
+ this.databases = /* @__PURE__ */ new Map();
2005
2015
  this.migrationInProgress = false;
2006
2016
  this.dbNamePrefix = options.dbNamePrefix;
2007
2017
  this.createAdapter = options.createAdapter;
@@ -2018,8 +2028,9 @@ var DatabaseManager = class {
2018
2028
  /**
2019
2029
  * Get or create a WatermelonDB Database instance for the given wallet.
2020
2030
  *
2021
- * If the wallet address has changed since the last call, the previous
2022
- * database instance is discarded and a new one is created.
2031
+ * Instances are cached per derived dbName and reused across calls (including
2032
+ * repeat/guest-interleaved requests for the same wallet); a new one is built
2033
+ * only for a dbName not yet seen. See the `databases` field for why.
2023
2034
  *
2024
2035
  * @param walletAddress - The wallet address to scope the database to.
2025
2036
  * If undefined, uses a "guest" database.
@@ -2027,46 +2038,47 @@ var DatabaseManager = class {
2027
2038
  * @throws If a destructive migration is in progress
2028
2039
  */
2029
2040
  getDatabase(walletAddress) {
2030
- if (this.database && this.currentWalletAddress !== walletAddress) {
2031
- this.logger.debug?.("Wallet changed, switching database", {
2032
- component: "DatabaseManager",
2033
- from: this.currentWalletAddress,
2034
- to: walletAddress
2035
- });
2036
- this.database = null;
2037
- }
2038
- if (this.database) {
2039
- return this.database;
2041
+ const dbName = this.getDbName(walletAddress);
2042
+ const cached = this.databases.get(dbName);
2043
+ if (cached) {
2044
+ return cached;
2040
2045
  }
2041
2046
  const needsMigration = this.handleSchemaMigration(walletAddress);
2042
2047
  if (needsMigration) {
2043
2048
  throw new Error("Database migration in progress - app will restart");
2044
2049
  }
2045
- this.currentWalletAddress = walletAddress;
2046
- const dbName = this.getDbName(walletAddress);
2047
2050
  this.logger.debug?.("Initializing database", {
2048
2051
  component: "DatabaseManager",
2049
2052
  dbName,
2050
2053
  walletAddress
2051
2054
  });
2052
2055
  const adapter = this.createAdapter(dbName, sdkSchema, sdkMigrations);
2053
- this.database = new import_watermelondb13.Database({
2056
+ const database = new import_watermelondb13.Database({
2054
2057
  adapter,
2055
2058
  modelClasses: sdkModelClasses
2056
2059
  });
2057
- return this.database;
2060
+ this.databases.set(dbName, database);
2061
+ return database;
2058
2062
  }
2059
2063
  /**
2060
- * Reset the current database (useful for logout or testing).
2064
+ * Reset ALL cached databases (useful for logout or testing).
2061
2065
  */
2062
2066
  async resetDatabase() {
2063
- const db = this.database;
2064
- if (db) {
2065
- await db.write(async () => {
2066
- await db.unsafeResetDatabase();
2067
- });
2068
- this.database = null;
2069
- this.currentWalletAddress = void 0;
2067
+ const errors = [];
2068
+ for (const [dbName, db] of [...this.databases.entries()]) {
2069
+ try {
2070
+ await db.write(async () => {
2071
+ await db.unsafeResetDatabase();
2072
+ });
2073
+ this.databases.delete(dbName);
2074
+ } catch (error) {
2075
+ errors.push({ dbName, error });
2076
+ }
2077
+ }
2078
+ if (errors.length > 0) {
2079
+ throw new Error(
2080
+ `Failed to reset ${errors.length} database(s): ${errors.map((e) => e.dbName).join(", ")}`
2081
+ );
2070
2082
  }
2071
2083
  }
2072
2084
  /**
@@ -4870,7 +4882,18 @@ For each durable fact, output:
4870
4882
  - type: one of identity | preference | relationship | plan | ongoing_context | constraint | other
4871
4883
  - confidence: 0.0-1.0; how sure you are this is durable AND true. Only include facts >= 0.7.
4872
4884
  - sourceMessageIds: which message IDs from the conversation contained the evidence
4873
- - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "place" | "thing" | "concept" | "other" }. Optional. Skip generic nouns. Pick the best-fitting kind: a company/product/physical object is "thing"; a topic, skill, or idea is "concept"; use "other" only when none fit.
4885
+ - entities: named entities mentioned, each as an object { "name": string, "kind": one of "person" | "organization" | "place" | "event" | "product" | "thing" | "concept" | "other" }. Optional \u2014 include only NAMED entities, skip generic/common nouns. Choose the MOST SPECIFIC kind:
4886
+ - person: a named individual ("Sara", "Dr. Lee")
4887
+ - organization: a company, team, school, or group ("Google", "the Warriors", "Stanford")
4888
+ - place: a geographic location or venue ("San Francisco", "Blue Bottle on Valencia")
4889
+ - event: a dated or nameable occurrence ("Chicago Marathon", "the Japan trip", "their wedding")
4890
+ - product: a named app, tool, brand, device, book, film, or other media title ("Linear", "iPhone", "Dune", "Ableton")
4891
+ - thing: a concrete physical object that isn't a branded product ("road bike", "sourdough starter")
4892
+ - concept: a topic, skill, field, language, or idea ("machine learning", "Spanish", "async communication")
4893
+ - other: a named entity that genuinely fits none of the above \u2014 use sparingly
4894
+ Prefer organization / product / event / place over the generic "thing"; fall back to "thing" or "other" only when no specific kind fits. Examples:
4895
+ - "started a new job at Hollowpoint Labs and I'm learning Haskell for it" \u2192 entities: [{ "name": "Hollowpoint Labs", "kind": "organization" }, { "name": "Haskell", "kind": "concept" }]
4896
+ - "running the Berlin Half in May and I track workouts in Whoop" \u2192 entities: [{ "name": "Berlin Half", "kind": "event" }, { "name": "Whoop", "kind": "product" }]
4874
4897
  - eventTime: when the event in this fact occurs/occurred. Resolve relative phrases ("yesterday", "next week") against the current date given in the user message. Output one of:
4875
4898
  - { "kind": "point", "start": "YYYY-MM-DD" } for a single date ("met Sara on March 14, 2026")
4876
4899
  - { "kind": "range", "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } for a span ("Japan trip May 4\u201315, 2026")
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-KLJBJWWI.mjs";
3
+ } from "./chunk-Z7CJGOTG.mjs";
4
4
  import "./chunk-RCCKRTBI.mjs";
5
5
  import "./chunk-FMGZTVXQ.mjs";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",