@ponraaj/dimple-sdk 0.0.0-alpha.7 → 0.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ var __export = (target, all) => {
16
16
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
17
17
 
18
18
  // src/make.ts
19
- import { Effect as Effect50, Exit as Exit7, Layer as Layer26, ManagedRuntime, Scope as Scope5 } from "effect";
19
+ import { Effect as Effect51, Exit as Exit8, Layer as Layer26, ManagedRuntime, Scope as Scope5 } from "effect";
20
20
 
21
21
  // ../core/src/core.ts
22
22
  import { Context, Schema } from "effect";
@@ -29,10 +29,10 @@ class CoreConfigError extends Schema.TaggedErrorClass()("core.ConfigError", {
29
29
  class Service extends Context.Service()("@dimple/core/Core") {
30
30
  }
31
31
  // ../core/src/layer.ts
32
- import { Effect as Effect39, Layer as Layer15, Schema as Schema24 } from "effect";
32
+ import { Effect as Effect40, Layer as Layer15, Schema as Schema24 } from "effect";
33
33
 
34
34
  // ../store/src/store.ts
35
- import { Config as Config4, Effect as Effect26, Layer as Layer10, Schema as Schema19 } from "effect";
35
+ import { Config as Config4, Effect as Effect27, Layer as Layer10, Schema as Schema19 } from "effect";
36
36
  import { eq as eq8, and as and8, desc as desc2, asc as asc5, inArray as inArray4, ne as ne4, or, isNull as isNull2, like, sql as sql7 } from "drizzle-orm";
37
37
 
38
38
  // ../store/src/db-backend.ts
@@ -113,6 +113,11 @@ var EMBEDDED_MIGRATIONS = [
113
113
  name: "20260807074034_ttl_secs_nullable",
114
114
  hash: "be3a8d0e64b0224021d869c1c7e217e40b6eb868615cc4c86a6717f5b7211721",
115
115
  sql: "PRAGMA foreign_keys=OFF;--> statement-breakpoint\nCREATE TABLE `__new_memories` (\n\t`id` text PRIMARY KEY,\n\t`logical_key` text NOT NULL,\n\t`content` text NOT NULL,\n\t`content_type` text DEFAULT 'text/plain' NOT NULL,\n\t`state` text DEFAULT 'active' NOT NULL,\n\t`created_at` integer NOT NULL,\n\t`ttl_secs` integer DEFAULT 604800,\n\t`namespace` text DEFAULT 'default' NOT NULL,\n\t`metadata` text,\n\t`embedding` F32_BLOB(384),\n\t`embedding_model` text\n);\n--> statement-breakpoint\nINSERT INTO `__new_memories`(`id`, `logical_key`, `content`, `content_type`, `state`, `created_at`, `ttl_secs`, `namespace`, `metadata`, `embedding`, `embedding_model`) SELECT `id`, `logical_key`, `content`, `content_type`, `state`, `created_at`, `ttl_secs`, `namespace`, `metadata`, `embedding`, `embedding_model` FROM `memories`;--> statement-breakpoint\nDROP TABLE `memories`;--> statement-breakpoint\nALTER TABLE `__new_memories` RENAME TO `memories`;--> statement-breakpoint\nPRAGMA foreign_keys=ON;--> statement-breakpoint\nCREATE INDEX `idx_memories_ns_created` ON `memories` (`namespace`,`created_at`);--> statement-breakpoint\nCREATE INDEX `idx_memories_state_created` ON `memories` (`state`,`created_at`);--> statement-breakpoint\nCREATE UNIQUE INDEX `idx_memories_active_key` ON `memories` (`namespace`,`logical_key`) WHERE state = 'active';"
116
+ },
117
+ {
118
+ name: "20260812122840_perf_indexes",
119
+ hash: "cc45ad830f7101fae454e9d5b43cfdfb922dbee7a5e5b7d9b9b81ec610d21d8e",
120
+ sql: "CREATE INDEX `idx_memories_created` ON `memories` (`created_at`);--> statement-breakpoint\nCREATE INDEX `ae_status_source` ON `artifact_edges` (`status`,`source`,`expired_at`,`created_at`);"
116
121
  }
117
122
  ];
118
123
 
@@ -160,6 +165,7 @@ var memories = sqliteTable("memories", {
160
165
  }, (table) => [
161
166
  index("idx_memories_ns_created").on(table.namespace, table.createdAt),
162
167
  index("idx_memories_state_created").on(table.state, table.createdAt),
168
+ index("idx_memories_created").on(table.createdAt),
163
169
  uniqueIndex("idx_memories_active_key").on(table.namespace, table.logicalKey).where(sql`state = 'active'`)
164
170
  ]);
165
171
  var memoryEvents = sqliteTable("memory_events", {
@@ -343,7 +349,8 @@ var artifactEdges = sqliteTable3("artifact_edges", {
343
349
  }, (t) => [
344
350
  primaryKey3({ columns: [t.fromMemoryId, t.toMemoryId, t.edgeKind] }),
345
351
  index3("ae_from").on(t.fromMemoryId, t.status),
346
- index3("ae_to").on(t.toMemoryId, t.status)
352
+ index3("ae_to").on(t.toMemoryId, t.status),
353
+ index3("ae_status_source").on(t.status, t.source, t.expiredAt, t.createdAt)
347
354
  ]);
348
355
  var ArtifactEdgeInsert = createInsertSchema3(artifactEdges);
349
356
  var ArtifactEdgeSelect = createSelectSchema3(artifactEdges);
@@ -766,6 +773,7 @@ import { Effect as Effect7, Schema as Schema7 } from "effect";
766
773
  import { parse, parseTree, printParseErrorCode } from "jsonc-parser";
767
774
  var CONFIG_FILE_NAMES = ["dimple.jsonc", "dimple.json"];
768
775
  var GLOBAL_DIR = ".config/dimple";
776
+ var resolveCwd = () => process.env["DIMPLE_CWD"] ?? process.cwd();
769
777
  var defaults = {
770
778
  storeUrl: "file:./.dimple/dimple.db",
771
779
  defaultK: 60,
@@ -871,7 +879,7 @@ var loadFs = () => Effect7.tryPromise(() => import("node:fs"));
871
879
  var findProjectConfig = () => Effect7.gen(function* () {
872
880
  const fs = yield* loadFs();
873
881
  const path = yield* Effect7.tryPromise(() => import("node:path"));
874
- let dir = process.cwd();
882
+ let dir = resolveCwd();
875
883
  for (let i = 0;i < 20; i++) {
876
884
  for (const name of CONFIG_FILE_NAMES) {
877
885
  const p = path.join(dir, name);
@@ -940,6 +948,23 @@ var resolveEnvVars = (value) => {
940
948
  }
941
949
  return value;
942
950
  };
951
+ var resolveCwdUrls = (value) => {
952
+ const cwd = process.env["DIMPLE_CWD"];
953
+ if (cwd === undefined)
954
+ return value;
955
+ if (typeof value === "string" && value.startsWith("file:./")) {
956
+ return `file:${cwd}${cwd.endsWith("/") ? "" : "/"}${value.slice("file:./".length)}`;
957
+ }
958
+ if (Array.isArray(value))
959
+ return value.map(resolveCwdUrls);
960
+ if (value !== null && typeof value === "object") {
961
+ const result = {};
962
+ for (const [k, v] of Object.entries(value))
963
+ result[k] = resolveCwdUrls(v);
964
+ return result;
965
+ }
966
+ return value;
967
+ };
943
968
  var deepMerge = (base, over) => {
944
969
  const out = { ...base };
945
970
  for (const [key, value] of Object.entries(over)) {
@@ -957,7 +982,7 @@ var loadConfigFromFile = (path) => Effect7.gen(function* () {
957
982
  const parsed = yield* parseJsoncEffect(content, (cause) => new Error(`config parse ${path}: ${cause.message}`));
958
983
  const merged = deepMerge(defaults, parsed);
959
984
  const resolved = resolveEnvVars(merged);
960
- return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolved).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
985
+ return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolveCwdUrls(resolved)).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
961
986
  });
962
987
  var loadConfig = (overrides) => Effect7.gen(function* () {
963
988
  const fileParts = yield* loadFromFiles();
@@ -970,7 +995,7 @@ var loadConfig = (overrides) => Effect7.gen(function* () {
970
995
  if (overrides)
971
996
  merged = deepMerge(merged, overrides);
972
997
  const resolved = resolveEnvVars(merged);
973
- return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolved).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
998
+ return yield* Schema7.decodeUnknownEffect(DimpleConfig, { errors: "all" })(resolveCwdUrls(resolved)).pipe(Effect7.mapError((e) => new Error(`config validation: ${e.message}`)));
974
999
  });
975
1000
 
976
1001
  // ../config/src/service.ts
@@ -1065,7 +1090,14 @@ var runEmbeddedMigrations = async (client) => {
1065
1090
  var makeClientDb = (url) => {
1066
1091
  const client = createClient({ url });
1067
1092
  const db = drizzle({ client });
1093
+ const rawExecute = client.execute.bind(client);
1094
+ const rawExecuteMultiple = client.executeMultiple.bind(client);
1095
+ const rawBatch = client.batch.bind(client);
1068
1096
  const rawTransaction = client.transaction.bind(client);
1097
+ const local = url.startsWith("file:");
1098
+ if (local) {
1099
+ rawExecute("PRAGMA busy_timeout=5000");
1100
+ }
1069
1101
  let txTail = Promise.resolve();
1070
1102
  let releaseSlot;
1071
1103
  const acquire = () => {
@@ -1082,14 +1114,18 @@ var makeClientDb = (url) => {
1082
1114
  releaseSlot?.();
1083
1115
  releaseSlot = undefined;
1084
1116
  };
1085
- const rawExecute = client.execute.bind(client);
1086
- const rawExecuteMultiple = client.executeMultiple.bind(client);
1087
- const rawBatch = client.batch.bind(client);
1088
1117
  const chained = (run) => {
1089
1118
  const prev = acquire();
1090
1119
  return prev.then(run, run).finally(release);
1091
1120
  };
1092
- client.execute = (...args) => chained(() => rawExecute(...args));
1121
+ const WRITE_RE = /^\s*(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|REPLACE|PRAGMA|BEGIN|COMMIT|ROLLBACK|VACUUM|REINDEX|ANALYZE|ATTACH|DETACH)\b/i;
1122
+ const isWrite = (sql3) => WRITE_RE.test(sql3);
1123
+ client.execute = (...args) => {
1124
+ const statement = args[0];
1125
+ const sql3 = typeof statement === "string" ? statement : statement.sql ?? "";
1126
+ const run = () => rawExecute(...args);
1127
+ return local && !isWrite(sql3) ? run() : chained(run);
1128
+ };
1093
1129
  client.executeMultiple = (sql3) => chained(() => rawExecuteMultiple(sql3));
1094
1130
  client.batch = (statements, mode) => chained(() => rawBatch(statements, mode));
1095
1131
  client.transaction = async (mode) => {
@@ -1227,16 +1263,16 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1227
1263
  topics,
1228
1264
  artifactEdges
1229
1265
  },
1230
- ftsSearch: (query, namespace, limit, topicKeys, modelKey, contentType) => Effect9.tryPromise({
1266
+ ftsSearch: (query, namespace, limit, topicKeys, modelKey, contentType, includeEmbedding) => Effect9.tryPromise({
1231
1267
  try: () => client.execute({
1232
- sql: `SELECT m.*, f.rank
1268
+ sql: `SELECT m.id, m.logical_key, m.content, m.content_type, m.state, m.created_at, m.ttl_secs, m.namespace, m.metadata, m.embedding_model${includeEmbedding ? ", m.embedding" : ""}, f.rank
1233
1269
  FROM memories_fts f
1234
1270
  JOIN memories m ON m.rowid = f.rowid
1235
1271
  WHERE f.content MATCH ? AND m.namespace = ? AND m.state = 'active'${topicFilter(topicKeys)}${contentTypeFilter(contentType)}
1236
1272
  ORDER BY f.rank
1237
1273
  LIMIT ?`,
1238
1274
  args: [
1239
- `"${query.replaceAll('"', '""')}"`,
1275
+ query.split(/\s+/).filter((token) => token.length > 0).map((token) => `"${token.replaceAll('"', '""')}"`).join(" "),
1240
1276
  namespace,
1241
1277
  ...topicKeys && topicKeys.length > 0 ? [...topicKeys, modelKey ?? ""] : [],
1242
1278
  ...typeof contentType === "string" ? [contentType] : contentType ?? [],
@@ -1245,7 +1281,7 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1245
1281
  }).then((rs) => rs.rows),
1246
1282
  catch: (cause) => new StoreError({ message: `DbBackend.ftsSearch: ${cause}` })
1247
1283
  }),
1248
- vectorSearch: (query, namespace, limit, model, topicKeys, contentType) => Effect9.gen(function* () {
1284
+ vectorSearch: (query, namespace, limit, model, topicKeys, contentType, includeEmbedding) => Effect9.gen(function* () {
1249
1285
  for (const n of query) {
1250
1286
  if (!Number.isFinite(n)) {
1251
1287
  yield* new StoreError({
@@ -1256,26 +1292,50 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1256
1292
  const tableName = vectorTableName(model);
1257
1293
  const vecJson = JSON.stringify([...query]);
1258
1294
  const convertFn = VECTOR_CONVERT_FNS[model.vectorType ?? "float32"] ?? "vector32";
1259
- const args = [
1260
- vecJson,
1295
+ const whereClause = `WHERE m.namespace = ? AND m.state = 'active'${topicFilter(topicKeys)}${contentTypeFilter(contentType)}`;
1296
+ const selectColumns = `m.id, m.logical_key, m.content, m.content_type, m.state, m.created_at, m.ttl_secs, m.namespace, m.metadata, m.embedding_model${includeEmbedding ? ", m.embedding" : ""}`;
1297
+ const baseArgs = [
1261
1298
  namespace,
1262
1299
  ...topicKeys && topicKeys.length > 0 ? [...topicKeys, model.id] : [],
1263
- ...typeof contentType === "string" ? [contentType] : contentType ?? [],
1264
- limit
1300
+ ...typeof contentType === "string" ? [contentType] : contentType ?? []
1265
1301
  ];
1302
+ const indexName = `idx_${tableName}_vec`;
1303
+ const annSql = `
1304
+ SELECT ${selectColumns},
1305
+ vector_distance_cos(v.embedding, ${convertFn}(?)) AS distance
1306
+ FROM vector_top_k(?, ${convertFn}(?), ?) AS k
1307
+ JOIN ${tableName} v ON v.rowid = k.id
1308
+ JOIN memories m ON m.id = v.memory_id
1309
+ ${whereClause}
1310
+ ORDER BY distance, m.id
1311
+ LIMIT ?
1312
+ `;
1313
+ const scanSql = `
1314
+ SELECT ${selectColumns},
1315
+ vector_distance_cos(v.embedding, ${convertFn}(?)) AS distance
1316
+ FROM memories m
1317
+ JOIN ${tableName} v ON v.memory_id = m.id
1318
+ ${whereClause}
1319
+ ORDER BY distance, m.id
1320
+ LIMIT ?
1321
+ `;
1322
+ const annK = Math.max(limit * 16, 64);
1323
+ const runAnn = () => client.execute({
1324
+ sql: annSql,
1325
+ args: [vecJson, indexName, vecJson, annK, ...baseArgs, limit]
1326
+ }).then((rs) => rs.rows);
1327
+ const runScan = () => client.execute({
1328
+ sql: scanSql,
1329
+ args: [vecJson, ...baseArgs, limit]
1330
+ }).then((rs) => rs.rows);
1266
1331
  const rows = yield* Effect9.tryPromise({
1267
- try: () => client.execute({
1268
- sql: `
1269
- SELECT m.id, m.logical_key, m.content, m.content_type, m.state, m.created_at, m.ttl_secs, m.namespace, m.metadata, m.embedding_model, m.embedding,
1270
- vector_distance_cos(v.embedding, ${convertFn}(?)) AS distance
1271
- FROM memories m
1272
- JOIN ${tableName} v ON v.memory_id = m.id
1273
- WHERE m.namespace = ? AND m.state = 'active'${topicFilter(topicKeys)}${contentTypeFilter(contentType)}
1274
- ORDER BY distance
1275
- LIMIT ?
1276
- `,
1277
- args
1278
- }).then((rs) => rs.rows),
1332
+ try: () => runAnn().catch((cause) => {
1333
+ const message = String(cause.message ?? cause);
1334
+ const absent = message.includes("no such table") || message.includes("no such index") || message.includes("vector_top_k");
1335
+ if (absent)
1336
+ return runScan();
1337
+ throw cause;
1338
+ }),
1279
1339
  catch: (cause) => new StoreError({ message: `DbBackend.vectorSearch: ${cause}` })
1280
1340
  }).pipe(Effect9.catchTag("Store.StoreError", (error) => error.message.includes("no such table") ? Effect9.succeed([]) : Effect9.fail(error)));
1281
1341
  return rows;
@@ -1455,7 +1515,7 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1455
1515
  return results;
1456
1516
  }),
1457
1517
  ensureVectorTable: (model) => Effect9.tryPromise({
1458
- try: () => {
1518
+ try: async () => {
1459
1519
  const tableName = vectorTableName(model);
1460
1520
  const vecType = model.vectorType ?? "float32";
1461
1521
  const sqlType = VECTOR_SQL_TYPES[vecType] ?? "F32_BLOB";
@@ -1467,7 +1527,12 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1467
1527
  model_id TEXT NOT NULL
1468
1528
  );
1469
1529
  `;
1470
- return client.executeMultiple(sql3);
1530
+ await client.executeMultiple(sql3);
1531
+ if (vecType === "float32") {
1532
+ try {
1533
+ await client.execute(`CREATE INDEX IF NOT EXISTS idx_${tableName}_vec ON ${tableName}(libsql_vector_idx(embedding))`);
1534
+ } catch {}
1535
+ }
1471
1536
  },
1472
1537
  catch: (cause) => new StoreError({ message: `DbBackend.ensureVectorTable: ${cause}` })
1473
1538
  }),
@@ -1483,6 +1548,24 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1483
1548
  },
1484
1549
  catch: (cause) => new StoreError({ message: `DbBackend.insertVector: ${cause}` })
1485
1550
  }),
1551
+ insertVectors: (entries, model) => Effect9.tryPromise({
1552
+ try: async () => {
1553
+ if (entries.length === 0)
1554
+ return;
1555
+ const tableName = vectorTableName(model);
1556
+ const convertFn = VECTOR_CONVERT_FNS[model.vectorType ?? "float32"] ?? "vector32";
1557
+ const values = entries.map(() => `(?, ${convertFn}(?), ?)`).join(", ");
1558
+ const args = [];
1559
+ for (const entry of entries) {
1560
+ args.push(entry.memoryId, JSON.stringify([...entry.vector]), model.id);
1561
+ }
1562
+ return client.execute({
1563
+ sql: `INSERT INTO ${tableName} (memory_id, embedding, model_id) VALUES ${values} ON CONFLICT(memory_id) DO UPDATE SET embedding = excluded.embedding, model_id = excluded.model_id`,
1564
+ args
1565
+ });
1566
+ },
1567
+ catch: (cause) => new StoreError({ message: `DbBackend.insertVectors: ${cause}` })
1568
+ }),
1486
1569
  deleteVector: () => Effect9.succeed(undefined),
1487
1570
  ensureTopicVectorTable: (model) => Effect9.tryPromise({
1488
1571
  try: () => {
@@ -1510,6 +1593,23 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1510
1593
  },
1511
1594
  catch: (cause) => new StoreError({ message: `DbBackend.upsertTopicVector: ${cause}` })
1512
1595
  }),
1596
+ upsertTopicVectors: (entries, model) => Effect9.tryPromise({
1597
+ try: async () => {
1598
+ if (entries.length === 0)
1599
+ return;
1600
+ const tableName = topicVectorTableName(model);
1601
+ const values = entries.map((e) => "(?, vector32(?), ?)").join(", ");
1602
+ const args = [];
1603
+ for (const entry of entries) {
1604
+ args.push(entry.topicKey, JSON.stringify([...entry.sumVector]), model.id);
1605
+ }
1606
+ return client.execute({
1607
+ sql: `INSERT OR REPLACE INTO ${tableName} (topic_key, sum_vector, model_key) VALUES ${values}`,
1608
+ args
1609
+ });
1610
+ },
1611
+ catch: (cause) => new StoreError({ message: `DbBackend.upsertTopicVectors: ${cause}` })
1612
+ }),
1513
1613
  deleteTopicVectors: (model) => Effect9.tryPromise({
1514
1614
  try: () => {
1515
1615
  const tableName = topicVectorTableName(model);
@@ -1531,6 +1631,20 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1531
1631
  sumVector: decodeVectorBlob(row.sum_vector)
1532
1632
  }));
1533
1633
  }),
1634
+ fetchTopicVector: (topicKey, model) => Effect9.gen(function* () {
1635
+ const tableName = topicVectorTableName(model);
1636
+ const rows = yield* Effect9.tryPromise({
1637
+ try: () => client.execute({
1638
+ sql: `SELECT topic_key, sum_vector FROM ${tableName} WHERE topic_key = ?`,
1639
+ args: [topicKey]
1640
+ }).then((rs) => rs.rows),
1641
+ catch: (cause) => new StoreError({ message: `DbBackend.fetchTopicVector: ${cause}` })
1642
+ });
1643
+ const row = rows[0];
1644
+ if (!row)
1645
+ return;
1646
+ return { topicKey: row.topic_key, sumVector: decodeVectorBlob(row.sum_vector) };
1647
+ }),
1534
1648
  fetchMemoryVectors: (memoryIds, model) => Effect9.gen(function* () {
1535
1649
  if (memoryIds.length === 0)
1536
1650
  return [];
@@ -2124,6 +2238,131 @@ var routeInsert = Effect11.fn("topics.routeInsert")(function* (memoryId, vector,
2124
2238
  }
2125
2239
  return { topicKey, overflow: leafCf.count > (config.maxLeafItems ?? 128) };
2126
2240
  });
2241
+ var routeInsertMany = Effect11.fn("topics.routeInsertMany")(function* (entries, model, config) {
2242
+ const violations = validateConfig(config);
2243
+ if (violations.length > 0) {
2244
+ return yield* new InvariantViolation({ topicKey: ROOT_TOPIC_KEY, violations });
2245
+ }
2246
+ for (const entry of entries) {
2247
+ if (entry.vector.length !== model.dimensions) {
2248
+ return yield* new ModelMismatch({
2249
+ expected: `${model.id} (${model.dimensions} dims)`,
2250
+ actual: `${entry.vector.length} dims`
2251
+ });
2252
+ }
2253
+ }
2254
+ if (entries.length === 0)
2255
+ return [];
2256
+ const backend = yield* Service2;
2257
+ const db = backend.db;
2258
+ const now = yield* Clock.currentTimeMillis;
2259
+ const maxLeafItems = config.maxLeafItems ?? 128;
2260
+ const rootRows = yield* Effect11.tryPromise({
2261
+ try: () => db.select({ topicKey: topics.topicKey }).from(topics).where(and(eq(topics.topicKey, ROOT_TOPIC_KEY), eq(topics.modelKey, model.id))).limit(1),
2262
+ catch: (cause) => cause
2263
+ }).pipe(routingFailure("topics.routeInsertMany"));
2264
+ if (rootRows.length === 0) {
2265
+ return yield* new TopicNotFound({ topicKey: ROOT_TOPIC_KEY });
2266
+ }
2267
+ const vectorRows = yield* backend.fetchTopicVectors(model).pipe(routingFailure("topics.routeInsertMany"));
2268
+ const sumByTopic = new Map(vectorRows.map((r) => [r.topicKey, new Float32Array(r.sumVector)]));
2269
+ const zeros = new Float32Array(model.dimensions);
2270
+ const sumOf = (topicKey) => sumByTopic.get(topicKey) ?? zeros;
2271
+ const result = yield* Effect11.tryPromise({
2272
+ try: () => db.transaction(async (tx) => {
2273
+ const topicRows = await tx.select({
2274
+ topicKey: topics.topicKey,
2275
+ kind: topics.kind,
2276
+ status: topics.status,
2277
+ depth: topics.depth,
2278
+ memberCount: topics.memberCount,
2279
+ sumSquares: topics.sumSquares
2280
+ }).from(topics).where(eq(topics.modelKey, model.id)).orderBy(asc(topics.topicKey));
2281
+ const relations = await tx.select({
2282
+ parentKey: topicRelations.parentKey,
2283
+ childKey: topicRelations.childKey
2284
+ }).from(topicRelations).where(and(eq(topicRelations.modelKey, model.id), eq(topicRelations.relationKind, "parent_child")));
2285
+ const topicByKey = new Map(topicRows.map((r) => [r.topicKey, r]));
2286
+ const childrenByParent = new Map;
2287
+ const parentOf = new Map;
2288
+ for (const rel of relations) {
2289
+ const list = childrenByParent.get(rel.parentKey);
2290
+ if (list)
2291
+ list.push(rel.childKey);
2292
+ else
2293
+ childrenByParent.set(rel.parentKey, [rel.childKey]);
2294
+ parentOf.set(rel.childKey, rel.parentKey);
2295
+ }
2296
+ for (const children of childrenByParent.values()) {
2297
+ children.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
2298
+ }
2299
+ const membershipRows = [];
2300
+ const topicDelta = new Map;
2301
+ const outcomes = [];
2302
+ for (const entry of entries) {
2303
+ const x = new Float32Array(entry.vector);
2304
+ const descent = descendTree(topicByKey, childrenByParent, sumOf, x, config);
2305
+ if (!descent.ok)
2306
+ return Promise.reject(descent.error);
2307
+ const topicKey = descent.topicKey;
2308
+ const leafRow = topicByKey.get(topicKey);
2309
+ if (!leafRow) {
2310
+ return Promise.reject(new Error(`topics.routeInsertMany: topic row missing for ${topicKey}`));
2311
+ }
2312
+ const currentSum = sumOf(topicKey);
2313
+ const leafCf = cfInsert({ count: leafRow.memberCount, ls: currentSum, ss: leafRow.sumSquares }, x);
2314
+ leafRow.memberCount = leafCf.count;
2315
+ leafRow.sumSquares = leafCf.ss;
2316
+ sumByTopic.set(topicKey, leafCf.ls);
2317
+ membershipRows.push({
2318
+ topicKey,
2319
+ memoryId: entry.memoryId,
2320
+ modelKey: model.id,
2321
+ updatedAt: now
2322
+ });
2323
+ const acc = topicDelta.get(topicKey);
2324
+ if (acc) {
2325
+ acc.count += 1;
2326
+ acc.ss = leafCf.ss;
2327
+ } else {
2328
+ topicDelta.set(topicKey, { count: 1, ss: leafCf.ss });
2329
+ }
2330
+ let ancestorKey = parentOf.get(topicKey);
2331
+ while (ancestorKey !== undefined) {
2332
+ const row = topicByKey.get(ancestorKey);
2333
+ if (!row) {
2334
+ return Promise.reject(new Error(`topics.routeInsertMany: ancestor row missing for ${ancestorKey}`));
2335
+ }
2336
+ row.memberCount += 1;
2337
+ const ancestorSum = addToSum(sumOf(ancestorKey), x);
2338
+ sumByTopic.set(ancestorKey, ancestorSum);
2339
+ const accA = topicDelta.get(ancestorKey);
2340
+ if (accA)
2341
+ accA.count += 1;
2342
+ else
2343
+ topicDelta.set(ancestorKey, { count: 1, ss: row.sumSquares });
2344
+ ancestorKey = parentOf.get(ancestorKey);
2345
+ }
2346
+ outcomes.push({
2347
+ memoryId: entry.memoryId,
2348
+ topicKey,
2349
+ overflow: leafCf.count > maxLeafItems
2350
+ });
2351
+ }
2352
+ await tx.insert(topicMemberships).values(membershipRows);
2353
+ for (const [key, delta] of topicDelta) {
2354
+ await tx.update(topics).set({ memberCount: delta.count, sumSquares: delta.ss, updatedAt: now }).where(and(eq(topics.topicKey, key), eq(topics.modelKey, model.id)));
2355
+ }
2356
+ return { outcomes, topicDelta };
2357
+ }),
2358
+ catch: (cause) => cause
2359
+ }).pipe(routingFailure("topics.routeInsertMany"));
2360
+ yield* backend.upsertTopicVectors(Array.from(result.topicDelta, ([topicKey]) => ({
2361
+ topicKey,
2362
+ sumVector: Array.from(sumByTopic.get(topicKey) ?? zeros)
2363
+ })), model).pipe(routingFailure("topics.routeInsertMany"));
2364
+ return result.outcomes;
2365
+ });
2127
2366
 
2128
2367
  // ../store/src/topics/query.ts
2129
2368
  var DEFAULT_BEAM_WIDTH = 3;
@@ -2219,7 +2458,7 @@ var resolveTopics = Effect12.fn("topics.resolveTopics")(function* (queryVector,
2219
2458
  });
2220
2459
 
2221
2460
  // ../store/src/topics/lifecycle.ts
2222
- import { Clock as Clock3, Effect as Effect21 } from "effect";
2461
+ import { Clock as Clock3, Effect as Effect22 } from "effect";
2223
2462
  import { and as and3, eq as eq3, inArray, ne } from "drizzle-orm";
2224
2463
 
2225
2464
  // ../workflow/src/queue.ts
@@ -2368,7 +2607,18 @@ var queues = [
2368
2607
  AuditWrite
2369
2608
  ];
2370
2609
  // ../workflow/src/runtime.ts
2371
- import { Cause as Cause2, Clock as Clock2, Context as Context7, Duration as Duration2, Effect as Effect18, Exit as Exit5, Layer as Layer7, Option as Option5, Schema as Schema14 } from "effect";
2610
+ import {
2611
+ Cause as Cause2,
2612
+ Clock as Clock2,
2613
+ Context as Context7,
2614
+ Deferred as Deferred2,
2615
+ Duration as Duration2,
2616
+ Effect as Effect19,
2617
+ Exit as Exit6,
2618
+ Layer as Layer7,
2619
+ Option as Option5,
2620
+ Schema as Schema14
2621
+ } from "effect";
2372
2622
  import { WorkflowEngine as WorkflowEngine3 } from "effect/unstable/workflow";
2373
2623
  import { PersistedQueue as PersistedQueue3, Persistence as PersistenceNamespace3 } from "effect/unstable/persistence";
2374
2624
 
@@ -2458,8 +2708,37 @@ var readWorkflowResult = (store, workflow, executionId) => Effect14.gen(function
2458
2708
  return Option2.none();
2459
2709
  });
2460
2710
 
2711
+ // ../workflow/src/signals.ts
2712
+ import { Deferred, Effect as Effect15, Exit as Exit2 } from "effect";
2713
+ var listeners = new Map;
2714
+ var onExecutionDone = (executionId, deferred) => {
2715
+ let set = listeners.get(executionId);
2716
+ if (set === undefined) {
2717
+ set = new Set;
2718
+ listeners.set(executionId, set);
2719
+ }
2720
+ set.add(deferred);
2721
+ };
2722
+ var offExecutionDone = (executionId, deferred) => {
2723
+ const set = listeners.get(executionId);
2724
+ if (set === undefined)
2725
+ return;
2726
+ set.delete(deferred);
2727
+ if (set.size === 0)
2728
+ listeners.delete(executionId);
2729
+ };
2730
+ var notifyExecutionDone = (executionId) => {
2731
+ const set = listeners.get(executionId);
2732
+ if (set === undefined)
2733
+ return;
2734
+ listeners.delete(executionId);
2735
+ for (const deferred of set) {
2736
+ Effect15.runFork(Deferred.done(deferred, Exit2.void));
2737
+ }
2738
+ };
2739
+
2461
2740
  // ../workflow/src/engine.ts
2462
- import { Effect as Effect17, Exit as Exit4, Fiber, FiberMap, Layer as Layer6, Option as Option4, Schema as Schema13 } from "effect";
2741
+ import { Effect as Effect18, Exit as Exit5, Fiber, FiberMap, Layer as Layer6, Option as Option4, Schema as Schema13 } from "effect";
2463
2742
  import { Workflow as Workflow3, WorkflowEngine as WorkflowEngine2 } from "effect/unstable/workflow";
2464
2743
  import { Persistence as PersistenceNamespace2, PersistedQueue as PersistedQueue2 } from "effect/unstable/persistence";
2465
2744
 
@@ -2468,8 +2747,8 @@ import {
2468
2747
  Cause,
2469
2748
  Data,
2470
2749
  Duration,
2471
- Effect as Effect15,
2472
- Exit as Exit2,
2750
+ Effect as Effect16,
2751
+ Exit as Exit3,
2473
2752
  Latch,
2474
2753
  Layer as Layer4,
2475
2754
  MutableRef,
@@ -2482,7 +2761,7 @@ import { SqlClient } from "effect/unstable/sql";
2482
2761
 
2483
2762
  class QueueKey extends Data.Class {
2484
2763
  }
2485
- var makeStore = Effect15.fnUntraced(function* (options) {
2764
+ var makeStore = Effect16.fnUntraced(function* (options) {
2486
2765
  const sql3 = (yield* SqlClient.SqlClient).withoutTransforms();
2487
2766
  const tableName = options?.tableName ?? "effect_queue";
2488
2767
  const tableNameSql = sql3(tableName);
@@ -2515,16 +2794,16 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2515
2794
  VALUES (${id}, ${name}, ${element}, FALSE, 0, ${sqlNow}, ${sqlNow})
2516
2795
  `;
2517
2796
  const elementIds = new Set;
2518
- const refreshLocks = Effect15.suspend(() => {
2797
+ const refreshLocks = Effect16.suspend(() => {
2519
2798
  if (elementIds.size === 0)
2520
- return Effect15.void;
2799
+ return Effect16.void;
2521
2800
  const ids = Array.from(elementIds);
2522
2801
  return sql3`
2523
2802
  UPDATE ${tableNameSql}
2524
2803
  SET acquired_at = ${sqlNow}
2525
2804
  WHERE sequence IN (${sql3.literal(ids.join(","))})
2526
2805
  AND acquired_by = ${workerIdSql}
2527
- `.pipe(Effect15.asVoid);
2806
+ `.pipe(Effect16.asVoid);
2528
2807
  });
2529
2808
  const complete = (sequence, attempts) => {
2530
2809
  elementIds.delete(sequence);
@@ -2533,7 +2812,7 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2533
2812
  SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, completed = ${sqlTrue}, attempts = ${attempts}
2534
2813
  WHERE sequence = ${sequence}
2535
2814
  AND acquired_by = ${workerIdSql}
2536
- `.pipe(Effect15.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect15.orDie);
2815
+ `.pipe(Effect16.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect16.orDie);
2537
2816
  };
2538
2817
  const retry = (sequence, attempts, cause) => {
2539
2818
  elementIds.delete(sequence);
@@ -2542,33 +2821,33 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2542
2821
  SET acquired_at = NULL, acquired_by = NULL, updated_at = ${sqlNow}, attempts = ${attempts}, last_failure = ${Cause.pretty(cause)}
2543
2822
  WHERE sequence = ${sequence}
2544
2823
  AND acquired_by = ${workerIdSql}
2545
- `.pipe(Effect15.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect15.orDie);
2824
+ `.pipe(Effect16.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect16.orDie);
2546
2825
  };
2547
2826
  const interrupt = (ids) => {
2548
2827
  for (const id of ids)
2549
2828
  elementIds.delete(id);
2550
2829
  if (ids.length === 0)
2551
- return Effect15.void;
2830
+ return Effect16.void;
2552
2831
  return sql3`
2553
2832
  UPDATE ${tableNameSql}
2554
2833
  SET acquired_at = NULL, acquired_by = NULL
2555
2834
  WHERE sequence IN (${sql3.literal(ids.join(","))})
2556
2835
  AND acquired_by = ${workerIdSql}
2557
- `.pipe(Effect15.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect15.orDie);
2836
+ `.pipe(Effect16.retry({ times: 5, schedule: Schedule.exponential(100, 1.5) }), Effect16.orDie);
2558
2837
  };
2559
- yield* refreshLocks.pipe(Effect15.tapCause(Effect15.logWarning), Effect15.retry(Schedule.spaced(500)), Effect15.schedule(Schedule.fixed(lockRefreshInterval)), Effect15.annotateLogs({
2838
+ yield* refreshLocks.pipe(Effect16.tapCause(Effect16.logWarning), Effect16.retry(Schedule.spaced(500)), Effect16.schedule(Schedule.fixed(lockRefreshInterval)), Effect16.annotateLogs({
2560
2839
  package: "@dimple/workflow",
2561
2840
  module: "QueueStore",
2562
2841
  fiber: "refreshLocks"
2563
- }), Effect15.forkScoped);
2842
+ }), Effect16.forkScoped);
2564
2843
  const mailboxes = yield* RcMap.make({
2565
- lookup: Effect15.fnUntraced(function* ({ maxAttempts, name }) {
2844
+ lookup: Effect16.fnUntraced(function* ({ maxAttempts, name }) {
2566
2845
  const queue = yield* Queue.make();
2567
2846
  const takers = MutableRef.make(0);
2568
2847
  const pollLatch = Latch.makeUnsafe();
2569
2848
  const takenLatch = Latch.makeUnsafe();
2570
- yield* Effect15.addFinalizer(() => Effect15.flatMap(Queue.clear(queue), (elements) => elements.length === 0 ? Effect15.void : interrupt(Array.from(elements, (e) => e.sequence))));
2571
- const poll = (size) => Effect15.suspend(() => sql3`
2849
+ yield* Effect16.addFinalizer(() => Effect16.flatMap(Queue.clear(queue), (elements) => elements.length === 0 ? Effect16.void : interrupt(Array.from(elements, (e) => e.sequence))));
2850
+ const poll = (size) => Effect16.suspend(() => sql3`
2572
2851
  UPDATE ${tableNameSql}
2573
2852
  SET acquired_at = ${sqlNow}, acquired_by = ${workerIdSql}
2574
2853
  WHERE queue_name = ${name}
@@ -2576,14 +2855,14 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2576
2855
  AND attempts < ${maxAttempts}
2577
2856
  AND (acquired_at IS NULL OR acquired_at < ${expiresAt})
2578
2857
  RETURNING sequence, id, queue_name, element, attempts
2579
- `.pipe(Effect15.map((rows) => rows)));
2580
- yield* Effect15.gen(function* () {
2858
+ `.pipe(Effect16.map((rows) => rows)));
2859
+ yield* Effect16.gen(function* () {
2581
2860
  while (true) {
2582
2861
  yield* pollLatch.await;
2583
- yield* Effect15.yieldNow;
2862
+ yield* Effect16.yieldNow;
2584
2863
  const results = takers.current === 0 ? [] : yield* poll(takers.current);
2585
2864
  if (results.length === 0) {
2586
- yield* Effect15.sleep(pollInterval);
2865
+ yield* Effect16.sleep(pollInterval);
2587
2866
  continue;
2588
2867
  }
2589
2868
  takenLatch.closeUnsafe();
@@ -2592,24 +2871,24 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2592
2871
  }
2593
2872
  yield* Queue.offerAll(queue, results);
2594
2873
  yield* takenLatch.await;
2595
- yield* Effect15.yieldNow;
2874
+ yield* Effect16.yieldNow;
2596
2875
  }
2597
- }).pipe(Effect15.sandbox, Effect15.retry(Schedule.spaced(500)), Effect15.forkScoped, Effect15.interruptible);
2876
+ }).pipe(Effect16.sandbox, Effect16.retry(Schedule.spaced(500)), Effect16.forkScoped, Effect16.interruptible);
2598
2877
  return { queue, takers, pollLatch, takenLatch };
2599
2878
  }),
2600
2879
  idleTimeToLive: Duration.seconds(30)
2601
2880
  });
2602
2881
  return PersistedQueue.PersistedQueueStore.of({
2603
- offer: ({ element, id, name }) => Effect15.catchCause(Effect15.suspend(() => offer(id, name, JSON.stringify(element))), (cause) => Effect15.fail(new PersistedQueue.PersistedQueueError({
2882
+ offer: ({ element, id, name }) => Effect16.catchCause(Effect16.suspend(() => offer(id, name, JSON.stringify(element))), (cause) => Effect16.fail(new PersistedQueue.PersistedQueueError({
2604
2883
  message: "Failed to offer element to persisted queue",
2605
2884
  cause
2606
2885
  }))),
2607
- take: ({ maxAttempts, name }) => Effect15.uninterruptibleMask((restore) => RcMap.get(mailboxes, new QueueKey({ name, maxAttempts })).pipe(Effect15.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
2886
+ take: ({ maxAttempts, name }) => Effect16.uninterruptibleMask((restore) => RcMap.get(mailboxes, new QueueKey({ name, maxAttempts })).pipe(Effect16.flatMap(({ pollLatch, queue, takenLatch, takers }) => {
2608
2887
  takers.current++;
2609
2888
  if (takers.current === 1) {
2610
2889
  pollLatch.openUnsafe();
2611
2890
  }
2612
- return Effect15.tap(restore(Queue.take(queue)), () => Effect15.sync(() => {
2891
+ return Effect16.tap(restore(Queue.take(queue)), () => Effect16.sync(() => {
2613
2892
  takers.current--;
2614
2893
  if (takers.current === 0) {
2615
2894
  pollLatch.closeUnsafe();
@@ -2618,10 +2897,10 @@ var makeStore = Effect15.fnUntraced(function* (options) {
2618
2897
  takenLatch.openUnsafe();
2619
2898
  }
2620
2899
  }));
2621
- }), Effect15.scoped, restore, Effect15.tap((element) => Effect15.addFinalizer(Exit2.match({
2900
+ }), Effect16.scoped, restore, Effect16.tap((element) => Effect16.addFinalizer(Exit3.match({
2622
2901
  onFailure: (cause) => Cause.hasInterruptsOnly(cause) ? interrupt([element.sequence]) : retry(element.sequence, element.attempts + 1, cause),
2623
2902
  onSuccess: () => complete(element.sequence, element.attempts + 1)
2624
- }))), Effect15.map((element) => ({ ...element, element: JSON.parse(element.element) }))))
2903
+ }))), Effect16.map((element) => ({ ...element, element: JSON.parse(element.element) }))))
2625
2904
  });
2626
2905
  });
2627
2906
  var storeSqliteLayer = Layer4.effect(PersistedQueue.PersistedQueueStore, makeStore());
@@ -2638,8 +2917,8 @@ __export(exports_LibsqlClient, {
2638
2917
  import * as Libsql from "@libsql/client";
2639
2918
  import * as Config3 from "effect/Config";
2640
2919
  import * as Context6 from "effect/Context";
2641
- import * as Effect16 from "effect/Effect";
2642
- import * as Exit3 from "effect/Exit";
2920
+ import * as Effect17 from "effect/Effect";
2921
+ import * as Exit4 from "effect/Exit";
2643
2922
  import * as Layer5 from "effect/Layer";
2644
2923
  import * as Option3 from "effect/Option";
2645
2924
  import * as Redacted from "effect/Redacted";
@@ -2658,7 +2937,7 @@ var classifyError = (cause, message, operation) => classifySqliteError(cause, {
2658
2937
  var TypeId = "~@effect/sql-libsql/LibsqlClient";
2659
2938
  var LibsqlClient = /* @__PURE__ */ Context6.Service("@effect/sql-libsql/LibsqlClient");
2660
2939
  var LibsqlTransaction = /* @__PURE__ */ Context6.Service("@effect/sql-libsql/LibsqlClient/LibsqlTransaction");
2661
- var make4 = (options) => Effect16.gen(function* () {
2940
+ var make4 = (options) => Effect17.gen(function* () {
2662
2941
  const compiler = Statement.makeCompilerSqlite(options.transformQueryNames);
2663
2942
  const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames).array : undefined;
2664
2943
  const spanAttributes = [...options.spanAttributes ? Object.entries(options.spanAttributes) : [], [ATTR_DB_SYSTEM_NAME, "sqlite"]];
@@ -2669,7 +2948,7 @@ var make4 = (options) => Effect16.gen(function* () {
2669
2948
  this.sdk = sdk;
2670
2949
  }
2671
2950
  run(sql3, params = []) {
2672
- return Effect16.map(Effect16.tryPromise({
2951
+ return Effect17.map(Effect17.tryPromise({
2673
2952
  try: () => this.sdk.execute({
2674
2953
  sql: sql3,
2675
2954
  args: params
@@ -2680,7 +2959,7 @@ var make4 = (options) => Effect16.gen(function* () {
2680
2959
  }), (results) => results.rows);
2681
2960
  }
2682
2961
  runRaw(sql3, params = []) {
2683
- return Effect16.tryPromise({
2962
+ return Effect17.tryPromise({
2684
2963
  try: () => this.sdk.execute({
2685
2964
  sql: sql3,
2686
2965
  args: params
@@ -2691,13 +2970,13 @@ var make4 = (options) => Effect16.gen(function* () {
2691
2970
  });
2692
2971
  }
2693
2972
  execute(sql3, params, transformRows2) {
2694
- return transformRows2 ? Effect16.map(this.run(sql3, params), transformRows2) : this.run(sql3, params);
2973
+ return transformRows2 ? Effect17.map(this.run(sql3, params), transformRows2) : this.run(sql3, params);
2695
2974
  }
2696
2975
  executeRaw(sql3, params) {
2697
2976
  return this.runRaw(sql3, params);
2698
2977
  }
2699
2978
  executeValues(sql3, params) {
2700
- return Effect16.map(this.run(sql3, params), (rows) => rows.map((row) => Array.from(row)));
2979
+ return Effect17.map(this.run(sql3, params), (rows) => rows.map((row) => Array.from(row)));
2701
2980
  }
2702
2981
  executeValuesUnprepared(sql3, params) {
2703
2982
  return this.executeValues(sql3, params);
@@ -2709,7 +2988,7 @@ var make4 = (options) => Effect16.gen(function* () {
2709
2988
  return Stream.die("executeStream not implemented");
2710
2989
  }
2711
2990
  get beginTransaction() {
2712
- return Effect16.map(Effect16.tryPromise({
2991
+ return Effect17.map(Effect17.tryPromise({
2713
2992
  try: () => this.sdk.transaction("write"),
2714
2993
  catch: (cause) => new SqlError2({
2715
2994
  reason: classifyError(cause, "Failed to begin transaction", "beginTransaction")
@@ -2717,7 +2996,7 @@ var make4 = (options) => Effect16.gen(function* () {
2717
2996
  }), (tx) => new LibsqlConnectionImpl(tx));
2718
2997
  }
2719
2998
  get commit() {
2720
- return Effect16.tryPromise({
2999
+ return Effect17.tryPromise({
2721
3000
  try: () => this.sdk.commit(),
2722
3001
  catch: (cause) => new SqlError2({
2723
3002
  reason: classifyError(cause, "Failed to commit transaction", "commitTransaction")
@@ -2725,7 +3004,7 @@ var make4 = (options) => Effect16.gen(function* () {
2725
3004
  });
2726
3005
  }
2727
3006
  get rollback() {
2728
- return Effect16.tryPromise({
3007
+ return Effect17.tryPromise({
2729
3008
  try: () => this.sdk.rollback(),
2730
3009
  catch: (cause) => new SqlError2({
2731
3010
  reason: classifyError(cause, "Failed to rollback transaction", "rollbackTransaction")
@@ -2733,33 +3012,33 @@ var make4 = (options) => Effect16.gen(function* () {
2733
3012
  });
2734
3013
  }
2735
3014
  }
2736
- const connection = "liveClient" in options ? new LibsqlConnectionImpl(options.liveClient) : yield* Effect16.map(Effect16.acquireRelease(Effect16.sync(() => Libsql.createClient({
3015
+ const connection = "liveClient" in options ? new LibsqlConnectionImpl(options.liveClient) : yield* Effect17.map(Effect17.acquireRelease(Effect17.sync(() => Libsql.createClient({
2737
3016
  ...options,
2738
3017
  authToken: Redacted.isRedacted(options.authToken) ? Redacted.value(options.authToken) : options.authToken,
2739
3018
  encryptionKey: Redacted.isRedacted(options.encryptionKey) ? Redacted.value(options.encryptionKey) : options.encryptionKey,
2740
3019
  url: options.url.toString(),
2741
3020
  syncUrl: options.syncUrl?.toString()
2742
- })), (sdk) => Effect16.sync(() => sdk.close())), (sdk) => new LibsqlConnectionImpl(sdk));
3021
+ })), (sdk) => Effect17.sync(() => sdk.close())), (sdk) => new LibsqlConnectionImpl(sdk));
2743
3022
  const semaphore = yield* Semaphore.make(1);
2744
3023
  const withTransaction = Client.makeWithTransaction({
2745
3024
  transactionService: LibsqlTransaction,
2746
3025
  spanAttributes,
2747
- acquireConnection: Effect16.uninterruptibleMask(Effect16.fnUntraced(function* (restore) {
3026
+ acquireConnection: Effect17.uninterruptibleMask(Effect17.fnUntraced(function* (restore) {
2748
3027
  const scope = Scope3.makeUnsafe();
2749
3028
  yield* restore(semaphore.take(1));
2750
3029
  yield* Scope3.addFinalizer(scope, semaphore.release(1));
2751
- const conn = yield* connection.beginTransaction.pipe(Effect16.tapCause((cause) => Scope3.close(scope, Exit3.failCause(cause))));
3030
+ const conn = yield* connection.beginTransaction.pipe(Effect17.tapCause((cause) => Scope3.close(scope, Exit4.failCause(cause))));
2752
3031
  return [scope, conn];
2753
3032
  })),
2754
- begin: () => Effect16.void,
3033
+ begin: () => Effect17.void,
2755
3034
  savepoint: (conn, id) => conn.executeRaw(`SAVEPOINT effect_sql_${id};`, []),
2756
3035
  commit: (conn) => conn.commit,
2757
3036
  rollback: (conn) => conn.rollback,
2758
3037
  rollbackSavepoint: (conn, id) => conn.executeRaw(`ROLLBACK TO SAVEPOINT effect_sql_${id};`, [])
2759
3038
  });
2760
- const acquirer = Effect16.flatMap(Effect16.serviceOption(LibsqlTransaction), Option3.match({
2761
- onNone: () => semaphore.withPermits(1)(Effect16.succeed(connection)),
2762
- onSome: ([conn]) => Effect16.succeed(conn)
3039
+ const acquirer = Effect17.flatMap(Effect17.serviceOption(LibsqlTransaction), Option3.match({
3040
+ onNone: () => semaphore.withPermits(1)(Effect17.succeed(connection)),
3041
+ onSome: ([conn]) => Effect17.succeed(conn)
2763
3042
  }));
2764
3043
  return Object.assign(yield* Client.make({
2765
3044
  acquirer,
@@ -2773,21 +3052,21 @@ var make4 = (options) => Effect16.gen(function* () {
2773
3052
  sdk: connection.sdk
2774
3053
  });
2775
3054
  });
2776
- var layerConfig2 = (config) => Layer5.effectContext(Config3.unwrap(config).pipe(Effect16.flatMap(make4), Effect16.map((client) => Context6.make(LibsqlClient, client).pipe(Context6.add(Client.SqlClient, client))))).pipe(Layer5.provide(Reactivity.layer));
2777
- var layer5 = (config) => Layer5.effectContext(Effect16.map(make4(config), (client) => Context6.make(LibsqlClient, client).pipe(Context6.add(Client.SqlClient, client)))).pipe(Layer5.provide(Reactivity.layer));
3055
+ var layerConfig2 = (config) => Layer5.effectContext(Config3.unwrap(config).pipe(Effect17.flatMap(make4), Effect17.map((client) => Context6.make(LibsqlClient, client).pipe(Context6.add(Client.SqlClient, client))))).pipe(Layer5.provide(Reactivity.layer));
3056
+ var layer5 = (config) => Layer5.effectContext(Effect17.map(make4(config), (client) => Context6.make(LibsqlClient, client).pipe(Context6.add(Client.SqlClient, client)))).pipe(Layer5.provide(Reactivity.layer));
2778
3057
  // ../workflow/src/engine.ts
2779
3058
  var Persistence = PersistenceNamespace2.Persistence;
2780
3059
  var PersistedQueueFactory = PersistedQueue2.PersistedQueueFactory;
2781
- var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(function* () {
3060
+ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect18.gen(function* () {
2782
3061
  const persistence = yield* Persistence;
2783
3062
  const queueFactory = yield* PersistedQueueFactory;
2784
- const scope = yield* Effect17.scope;
3063
+ const scope = yield* Effect18.scope;
2785
3064
  const store = yield* persistence.make({ storeId: STORE_ID });
2786
3065
  const resumeQueue = yield* queueFactory.make({ name: RESUME_QUEUE, schema: ResumeSignal });
2787
3066
  const workflows = new Map;
2788
3067
  const executions = new Map;
2789
3068
  const clocks = yield* FiberMap.make();
2790
- const resume = (state) => Effect17.gen(function* () {
3069
+ const resume = (state) => Effect18.gen(function* () {
2791
3070
  const exit = state.fiber?.pollUnsafe();
2792
3071
  if (exit && exit._tag === "Success" && exit.value._tag === "Complete")
2793
3072
  return;
@@ -2799,31 +3078,32 @@ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(fu
2799
3078
  const instance = WorkflowEngine2.WorkflowInstance.initial(state.instance.workflow, state.instance.executionId);
2800
3079
  instance.interrupted = state.instance.interrupted;
2801
3080
  state.instance = instance;
2802
- state.fiber = yield* state.execute(state.payload, state.instance.executionId).pipe(Effect17.onExit(() => {
3081
+ state.fiber = yield* state.execute(state.payload, state.instance.executionId).pipe(Effect18.onExit(() => {
2803
3082
  if (!instance.interrupted)
2804
- return Effect17.void;
3083
+ return Effect18.void;
2805
3084
  instance.suspended = false;
2806
- return Effect17.withFiber((fiber) => Effect17.interruptible(Fiber.interrupt(fiber)));
2807
- }), Workflow3.intoResult, Effect17.provideService(WorkflowEngine2.WorkflowInstance, instance), Effect17.provideService(WorkflowEngine2.WorkflowEngine, engine), Effect17.tap((result) => {
3085
+ return Effect18.withFiber((fiber) => Effect18.interruptible(Fiber.interrupt(fiber)));
3086
+ }), Workflow3.intoResult, Effect18.provideService(WorkflowEngine2.WorkflowInstance, instance), Effect18.provideService(WorkflowEngine2.WorkflowEngine, engine), Effect18.tap((result) => {
2808
3087
  if (result._tag !== "Complete")
2809
- return Effect17.void;
2810
- return Effect17.gen(function* () {
3088
+ return Effect18.void;
3089
+ return Effect18.gen(function* () {
2811
3090
  const entry2 = workflows.get(state.instance.workflow._tag);
2812
3091
  if (!entry2)
2813
3092
  return;
2814
- const encoded = yield* Effect17.orDie(Schema13.encodeEffect(resultCodecFor(entry2.workflow))(result));
2815
- yield* setEncodedExit(store, resultKey(state.instance.executionId), Exit4.succeed(encoded));
3093
+ const encoded = yield* Effect18.orDie(Schema13.encodeEffect(resultCodecFor(entry2.workflow))(result));
3094
+ yield* setEncodedExit(store, resultKey(state.instance.executionId), Exit5.succeed(encoded));
3095
+ notifyExecutionDone(state.instance.executionId);
2816
3096
  });
2817
- }), Effect17.tap((result) => {
3097
+ }), Effect18.tap((result) => {
2818
3098
  if (!state.parent || result._tag !== "Complete")
2819
- return Effect17.void;
3099
+ return Effect18.void;
2820
3100
  const parent = executions.get(state.parent);
2821
3101
  if (parent)
2822
- return Effect17.forkIn(resume(parent), scope);
2823
- return Effect17.void;
2824
- }), Effect17.forkIn(entry.scope));
3102
+ return Effect18.forkIn(resume(parent), scope);
3103
+ return Effect18.void;
3104
+ }), Effect18.forkIn(entry.scope));
2825
3105
  });
2826
- const resumeExecution = (executionId) => Effect17.gen(function* () {
3106
+ const resumeExecution = (executionId) => Effect18.gen(function* () {
2827
3107
  const state = executions.get(executionId);
2828
3108
  if (state) {
2829
3109
  yield* resume(state);
@@ -2846,22 +3126,22 @@ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(fu
2846
3126
  }
2847
3127
  });
2848
3128
  const engine = WorkflowEngine2.makeUnsafe({
2849
- register: Effect17.fnUntraced(function* (workflow, execute) {
3129
+ register: Effect18.fnUntraced(function* (workflow, execute) {
2850
3130
  workflows.set(workflow._tag, {
2851
3131
  workflow,
2852
3132
  execute,
2853
- scope: yield* Effect17.scope
3133
+ scope: yield* Effect18.scope
2854
3134
  });
2855
3135
  }),
2856
3136
  execute: executeEncoded(workflows, executions, store, resume),
2857
- interrupt: Effect17.fnUntraced(function* (_workflow, executionId) {
3137
+ interrupt: Effect18.fnUntraced(function* (_workflow, executionId) {
2858
3138
  const state = executions.get(executionId);
2859
3139
  if (!state)
2860
3140
  return;
2861
3141
  state.instance.interrupted = true;
2862
3142
  yield* resume(state);
2863
3143
  }),
2864
- interruptUnsafe: Effect17.fnUntraced(function* (_workflow, executionId) {
3144
+ interruptUnsafe: Effect18.fnUntraced(function* (_workflow, executionId) {
2865
3145
  const state = executions.get(executionId);
2866
3146
  if (!state)
2867
3147
  return;
@@ -2871,36 +3151,36 @@ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(fu
2871
3151
  }
2872
3152
  }),
2873
3153
  resume: (_workflow, executionId) => resumeExecution(executionId),
2874
- activityExecute: Effect17.fnUntraced(function* (activity, attempt) {
3154
+ activityExecute: Effect18.fnUntraced(function* (activity, attempt) {
2875
3155
  const instance = yield* WorkflowEngine2.WorkflowInstance;
2876
3156
  const key = activityKey(instance.executionId, activity.name, attempt);
2877
3157
  const persisted = yield* getEncodedExit(store, key);
2878
- if (persisted && Exit4.isSuccess(persisted)) {
3158
+ if (persisted && Exit5.isSuccess(persisted)) {
2879
3159
  const entry = workflows.get(instance.workflow._tag);
2880
3160
  if (!entry)
2881
- return yield* Effect17.die(`activity replay: unknown workflow ${instance.workflow._tag}`);
2882
- return yield* Effect17.orDie(Schema13.decodeUnknownEffect(resultCodecFor(entry.workflow))(persisted.value));
3161
+ return yield* Effect18.die(`activity replay: unknown workflow ${instance.workflow._tag}`);
3162
+ return yield* Effect18.orDie(Schema13.decodeUnknownEffect(resultCodecFor(entry.workflow))(persisted.value));
2883
3163
  }
2884
3164
  const activityInstance = WorkflowEngine2.WorkflowInstance.initial(instance.workflow, instance.executionId);
2885
3165
  activityInstance.interrupted = instance.interrupted;
2886
- const result = yield* activity.executeEncoded.pipe(Workflow3.intoResult, Effect17.provideService(WorkflowEngine2.WorkflowInstance, activityInstance), Effect17.exit);
3166
+ const result = yield* activity.executeEncoded.pipe(Workflow3.intoResult, Effect18.provideService(WorkflowEngine2.WorkflowInstance, activityInstance), Effect18.exit);
2887
3167
  if (result._tag === "Success" && result.value._tag !== "Suspended") {
2888
3168
  const entry = workflows.get(instance.workflow._tag);
2889
3169
  if (entry) {
2890
- const encoded = yield* Effect17.orDie(Schema13.encodeEffect(resultCodecFor(entry.workflow))(result.value));
2891
- yield* setEncodedExit(store, key, Exit4.succeed(encoded));
3170
+ const encoded = yield* Effect18.orDie(Schema13.encodeEffect(resultCodecFor(entry.workflow))(result.value));
3171
+ yield* setEncodedExit(store, key, Exit5.succeed(encoded));
2892
3172
  }
2893
3173
  }
2894
3174
  return yield* result;
2895
3175
  }),
2896
- poll: (_workflow, executionId) => Effect17.gen(function* () {
3176
+ poll: (_workflow, executionId) => Effect18.gen(function* () {
2897
3177
  const state = executions.get(executionId);
2898
3178
  if (!state) {
2899
3179
  const persisted = yield* getEncodedExit(store, resultKey(executionId));
2900
- if (persisted && Exit4.isSuccess(persisted)) {
3180
+ if (persisted && Exit5.isSuccess(persisted)) {
2901
3181
  const workflow = yield* workflowTagFor(workflows, store, executionId);
2902
3182
  if (workflow) {
2903
- return Option4.some(yield* Effect17.orDie(Schema13.decodeUnknownEffect(resultCodecFor(workflow))(persisted.value)));
3183
+ return Option4.some(yield* Effect18.orDie(Schema13.decodeUnknownEffect(resultCodecFor(workflow))(persisted.value)));
2904
3184
  }
2905
3185
  }
2906
3186
  return Option4.none();
@@ -2910,9 +3190,9 @@ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(fu
2910
3190
  return Option4.none();
2911
3191
  if (exit._tag === "Success")
2912
3192
  return Option4.some(exit.value);
2913
- return yield* Effect17.die(exit.cause);
3193
+ return yield* Effect18.die(exit.cause);
2914
3194
  }),
2915
- deferredResult: Effect17.fnUntraced(function* (deferred) {
3195
+ deferredResult: Effect18.fnUntraced(function* (deferred) {
2916
3196
  const instance = yield* WorkflowEngine2.WorkflowInstance;
2917
3197
  const persisted = yield* getEncodedExit(store, deferredKey(instance.executionId, deferred.name));
2918
3198
  if (persisted) {
@@ -2920,26 +3200,26 @@ var layerDurable = Layer6.effect(WorkflowEngine2.WorkflowEngine)(Effect17.gen(fu
2920
3200
  }
2921
3201
  return Option4.none();
2922
3202
  }),
2923
- deferredDone: (options) => Effect17.gen(function* () {
3203
+ deferredDone: (options) => Effect18.gen(function* () {
2924
3204
  yield* setEncodedExit(store, deferredKey(options.executionId, options.deferredName), options.exit);
2925
- yield* resumeQueue.offer({ executionId: options.executionId }, { id: options.executionId }).pipe(Effect17.orDie);
3205
+ yield* resumeQueue.offer({ executionId: options.executionId }, { id: options.executionId }).pipe(Effect18.orDie);
2926
3206
  yield* resumeExecution(options.executionId);
2927
3207
  }),
2928
3208
  scheduleClock: (workflow, options) => engine.deferredDone(options.clock.deferred, {
2929
3209
  workflowName: workflow._tag,
2930
3210
  executionId: options.executionId,
2931
3211
  deferredName: options.clock.deferred.name,
2932
- exit: Exit4.void
2933
- }).pipe(Effect17.delay(options.clock.duration), FiberMap.run(clocks, `${options.executionId}/${options.clock.name}`, {
3212
+ exit: Exit5.void
3213
+ }).pipe(Effect18.delay(options.clock.duration), FiberMap.run(clocks, `${options.executionId}/${options.clock.name}`, {
2934
3214
  onlyIfMissing: true
2935
- }), Effect17.asVoid)
3215
+ }), Effect18.asVoid)
2936
3216
  });
2937
3217
  return engine;
2938
3218
  }));
2939
- var executeEncoded = (workflows, executions, store, resume) => Effect17.fnUntraced(function* (workflow, options) {
3219
+ var executeEncoded = (workflows, executions, store, resume) => Effect18.fnUntraced(function* (workflow, options) {
2940
3220
  const entry = workflows.get(workflow._tag);
2941
3221
  if (!entry) {
2942
- return yield* Effect17.orDie(Effect17.fail(`Workflow ${workflow._tag} is not registered`));
3222
+ return yield* Effect18.orDie(Effect18.fail(`Workflow ${workflow._tag} is not registered`));
2943
3223
  }
2944
3224
  let state = executions.get(options.executionId);
2945
3225
  if (!state) {
@@ -2951,24 +3231,24 @@ var executeEncoded = (workflows, executions, store, resume) => Effect17.fnUntrac
2951
3231
  parent: options.parent?.executionId
2952
3232
  };
2953
3233
  executions.set(options.executionId, state);
2954
- yield* setEncodedExit(store, recordKey(options.executionId), Exit4.succeed({ workflowTag: workflow._tag, payload: options.payload }));
3234
+ yield* setEncodedExit(store, recordKey(options.executionId), Exit5.succeed({ workflowTag: workflow._tag, payload: options.payload }));
2955
3235
  yield* resume(state);
2956
3236
  }
2957
3237
  if (options.discard)
2958
- return yield* Effect17.void;
3238
+ return yield* Effect18.void;
2959
3239
  if (!state.fiber) {
2960
- return yield* Effect17.orDie(Effect17.fail(`Workflow ${workflow._tag}: execution ${options.executionId} has no fiber`));
3240
+ return yield* Effect18.orDie(Effect18.fail(`Workflow ${workflow._tag}: execution ${options.executionId} has no fiber`));
2961
3241
  }
2962
3242
  return yield* Fiber.join(state.fiber);
2963
3243
  });
2964
- var loadRecord = (store, executionId) => Effect17.gen(function* () {
3244
+ var loadRecord = (store, executionId) => Effect18.gen(function* () {
2965
3245
  const persisted = yield* getEncodedExit(store, recordKey(executionId));
2966
- if (persisted && Exit4.isSuccess(persisted)) {
2967
- return yield* Effect17.orDie(Schema13.decodeUnknownEffect(ExecutionRecord)(persisted.value));
3246
+ if (persisted && Exit5.isSuccess(persisted)) {
3247
+ return yield* Effect18.orDie(Schema13.decodeUnknownEffect(ExecutionRecord)(persisted.value));
2968
3248
  }
2969
3249
  return;
2970
3250
  });
2971
- var workflowTagFor = (workflows, store, executionId) => Effect17.gen(function* () {
3251
+ var workflowTagFor = (workflows, store, executionId) => Effect18.gen(function* () {
2972
3252
  const record = yield* loadRecord(store, executionId);
2973
3253
  if (!record)
2974
3254
  return;
@@ -2977,10 +3257,10 @@ var workflowTagFor = (workflows, store, executionId) => Effect17.gen(function* (
2977
3257
  var ensureDbDir = (url) => url.startsWith("file:") ? import("node:fs").then((fs) => import("node:path").then(({ dirname }) => {
2978
3258
  fs.mkdirSync(dirname(url.slice("file:".length)), { recursive: true });
2979
3259
  })) : Promise.resolve();
2980
- var libsqlStack = (options) => Layer6.unwrap(Effect17.tryPromise({
3260
+ var libsqlStack = (options) => Layer6.unwrap(Effect18.tryPromise({
2981
3261
  try: () => ensureDbDir(options.url),
2982
3262
  catch: (cause) => new Error(String(cause))
2983
- }).pipe(Effect17.catch(() => Effect17.void), Effect17.map(() => Layer6.mergeAll(PersistenceNamespace2.layerSql, PersistedQueue2.layer.pipe(Layer6.provide(storeSqliteLayer))).pipe(Layer6.provide(exports_LibsqlClient.layer(options))))));
3263
+ }).pipe(Effect18.catch(() => Effect18.void), Effect18.map(() => Layer6.mergeAll(PersistenceNamespace2.layerSql, PersistedQueue2.layer.pipe(Layer6.provide(storeSqliteLayer))).pipe(Layer6.provide(exports_LibsqlClient.layer(options))))));
2984
3264
  var layerDurableLibsql = (options) => layerDurable.pipe(Layer6.provide(libsqlStack(options)));
2985
3265
 
2986
3266
  // ../workflow/src/runtime.ts
@@ -2999,7 +3279,7 @@ var jobsError = (jobId, e) => {
2999
3279
  return e;
3000
3280
  return new QueueError({ message: e instanceof Error ? e.message : String(e) });
3001
3281
  };
3002
- var layer6 = (defs) => Layer7.effect(Service7, Effect18.gen(function* () {
3282
+ var layer6 = (defs) => Layer7.effect(Service7, Effect19.gen(function* () {
3003
3283
  const engine = yield* WorkflowEngine3.WorkflowEngine;
3004
3284
  const persistence = yield* Persistence2;
3005
3285
  const queueFactory = yield* PersistedQueueFactory2;
@@ -3007,51 +3287,57 @@ var layer6 = (defs) => Layer7.effect(Service7, Effect18.gen(function* () {
3007
3287
  for (const def of defs) {
3008
3288
  yield* def.register(engine);
3009
3289
  }
3010
- const enqueue = Effect18.fn("JobRuntime.enqueue")(function* (def, payload) {
3011
- const decoded = yield* Schema14.decodeUnknownEffect(def.payloadSchema)(payload).pipe(Effect18.mapError((e) => new QueueError({ message: `invalid payload: ${e.message}` })));
3290
+ const enqueue = Effect19.fn("JobRuntime.enqueue")(function* (def, payload) {
3291
+ const decoded = yield* Schema14.decodeUnknownEffect(def.payloadSchema)(payload).pipe(Effect19.mapError((e) => new QueueError({ message: `invalid payload: ${e.message}` })));
3012
3292
  const token = def.idempotencyKey(decoded);
3013
3293
  const envelope = { token, payload: decoded, traceId: "" };
3014
- const jobId = yield* def.workflow.executionId(envelope).pipe(Effect18.provideService(WorkflowEngine3.WorkflowEngine, engine), Effect18.mapError((e) => new QueueError({ message: e instanceof Error ? e.message : String(e) })));
3015
- yield* def.workflow.execute(envelope, { discard: true }).pipe(Effect18.provideService(WorkflowEngine3.WorkflowEngine, engine), Effect18.mapError((e) => jobsError(jobId, e)));
3294
+ const jobId = yield* def.workflow.executionId(envelope).pipe(Effect19.provideService(WorkflowEngine3.WorkflowEngine, engine), Effect19.mapError((e) => new QueueError({ message: e instanceof Error ? e.message : String(e) })));
3295
+ yield* def.workflow.execute(envelope, { discard: true }).pipe(Effect19.provideService(WorkflowEngine3.WorkflowEngine, engine), Effect19.mapError((e) => jobsError(jobId, e)));
3016
3296
  return jobId;
3017
3297
  });
3018
- const wait = Effect18.fn("JobRuntime.wait")(function* (jobId, options) {
3298
+ const wait = Effect19.fn("JobRuntime.wait")(function* (jobId, options) {
3019
3299
  const timeout = Duration2.fromInput(options?.timeout ?? DEFAULT_TIMEOUT).pipe(Option5.getOrElse(() => Duration2.minutes(5)));
3020
3300
  const pollInterval = Duration2.fromInput(options?.pollInterval ?? DEFAULT_POLL_INTERVAL).pipe(Option5.getOrElse(() => Duration2.millis(250)));
3021
- const deadline = yield* Clock2.currentTimeMillis.pipe(Effect18.map((now) => now + Duration2.toMillis(timeout)));
3022
- while (true) {
3023
- if (yield* readMarker(store, jobId, CANCELLED)) {
3024
- return yield* Effect18.fail(new JobCancelled({ jobId }));
3025
- }
3026
- const def = yield* findDef(defs, jobId, store);
3027
- if (def) {
3028
- const result = yield* readWorkflowResult(store, def.workflow, jobId);
3029
- if (Option5.isSome(result)) {
3030
- const r = result.value;
3031
- if (r._tag === "Complete") {
3032
- if (Exit5.isSuccess(r.exit))
3033
- return r.exit.value;
3034
- const failure = Exit5.getCause(r.exit).pipe(Option5.flatMap(Cause2.findErrorOption), Option5.map((e) => typeof e === "string" ? e : String(e)));
3035
- return yield* Effect18.fail(new JobFailed({
3036
- jobId,
3037
- message: Option5.getOrElse(failure, () => "workflow failed")
3038
- }));
3301
+ const deadline = yield* Clock2.currentTimeMillis.pipe(Effect19.map((now) => now + Duration2.toMillis(timeout)));
3302
+ const completionSignal = yield* Deferred2.make();
3303
+ onExecutionDone(jobId, completionSignal);
3304
+ try {
3305
+ while (true) {
3306
+ if (yield* readMarker(store, jobId, CANCELLED)) {
3307
+ return yield* Effect19.fail(new JobCancelled({ jobId }));
3308
+ }
3309
+ const def = yield* findDef(defs, jobId, store);
3310
+ if (def) {
3311
+ const result = yield* readWorkflowResult(store, def.workflow, jobId);
3312
+ if (Option5.isSome(result)) {
3313
+ const r = result.value;
3314
+ if (r._tag === "Complete") {
3315
+ if (Exit6.isSuccess(r.exit))
3316
+ return r.exit.value;
3317
+ const failure = Exit6.getCause(r.exit).pipe(Option5.flatMap(Cause2.findErrorOption), Option5.map((e) => typeof e === "string" ? e : String(e)));
3318
+ return yield* Effect19.fail(new JobFailed({
3319
+ jobId,
3320
+ message: Option5.getOrElse(failure, () => "workflow failed")
3321
+ }));
3322
+ }
3039
3323
  }
3040
3324
  }
3325
+ const now = yield* Clock2.currentTimeMillis;
3326
+ if (now >= deadline) {
3327
+ yield* writeMarker(store, jobId, TIMED_OUT);
3328
+ return yield* Effect19.fail(new JobTimeout({ jobId, timeout }));
3329
+ }
3330
+ yield* Effect19.race(Deferred2.await(completionSignal).pipe(Effect19.asVoid), Effect19.sleep(pollInterval));
3041
3331
  }
3042
- const now = yield* Clock2.currentTimeMillis;
3043
- if (now >= deadline) {
3044
- yield* writeMarker(store, jobId, TIMED_OUT);
3045
- return yield* Effect18.fail(new JobTimeout({ jobId, timeout }));
3046
- }
3047
- yield* Effect18.sleep(pollInterval);
3332
+ } finally {
3333
+ offExecutionDone(jobId, completionSignal);
3048
3334
  }
3049
3335
  });
3050
- const submit = Effect18.fn("JobRuntime.submit")(function* (def, payload) {
3336
+ const submit = Effect19.fn("JobRuntime.submit")(function* (def, payload) {
3051
3337
  const jobId = yield* enqueue(def, payload);
3052
3338
  return yield* wait(jobId);
3053
3339
  });
3054
- const status = Effect18.fn("JobRuntime.status")(function* (jobId) {
3340
+ const status = Effect19.fn("JobRuntime.status")(function* (jobId) {
3055
3341
  if (yield* readMarker(store, jobId, CANCELLED))
3056
3342
  return "cancelled";
3057
3343
  if (yield* readMarker(store, jobId, TIMED_OUT))
@@ -3062,35 +3348,35 @@ var layer6 = (defs) => Layer7.effect(Service7, Effect18.gen(function* () {
3062
3348
  if (Option5.isSome(result)) {
3063
3349
  const r = result.value;
3064
3350
  if (r._tag === "Complete") {
3065
- return Exit5.isSuccess(r.exit) ? "succeeded" : "failed";
3351
+ return Exit6.isSuccess(r.exit) ? "succeeded" : "failed";
3066
3352
  }
3067
3353
  }
3068
3354
  }
3069
3355
  return "pending";
3070
3356
  });
3071
- const cancel = Effect18.fn("JobRuntime.cancel")(function* (jobId) {
3357
+ const cancel = Effect19.fn("JobRuntime.cancel")(function* (jobId) {
3072
3358
  yield* writeMarker(store, jobId, CANCELLED);
3073
3359
  return true;
3074
3360
  });
3075
- const processPending = Effect18.fn("JobRuntime.processPending")(function* (options) {
3361
+ const processPending = Effect19.fn("JobRuntime.processPending")(function* (options) {
3076
3362
  const resumeQueue = yield* queueFactory.make({ name: RESUME_QUEUE, schema: ResumeSignal });
3077
3363
  const window = Duration2.toMillis(Duration2.fromInput(options?.window ?? PROCESS_WINDOW).pipe(Option5.getOrElse(() => Duration2.seconds(30))));
3078
- const deadline = yield* Clock2.currentTimeMillis.pipe(Effect18.map((now) => now + window));
3364
+ const deadline = yield* Clock2.currentTimeMillis.pipe(Effect19.map((now) => now + window));
3079
3365
  let processed = 0;
3080
3366
  while (true) {
3081
3367
  const now = yield* Clock2.currentTimeMillis;
3082
3368
  if (now >= deadline)
3083
3369
  break;
3084
- const signal = yield* resumeQueue.take((value2, _meta) => Effect18.succeed(Option5.some(value2)), { maxAttempts: 1 }).pipe(Effect18.mapError((e) => new QueueError({ message: e instanceof Error ? e.message : String(e) })), Effect18.timeoutOrElse({
3370
+ const signal = yield* resumeQueue.take((value2, _meta) => Effect19.succeed(Option5.some(value2)), { maxAttempts: 1 }).pipe(Effect19.mapError((e) => new QueueError({ message: e instanceof Error ? e.message : String(e) })), Effect19.timeoutOrElse({
3085
3371
  duration: Duration2.millis(100),
3086
- orElse: () => Effect18.succeed(Option5.none())
3372
+ orElse: () => Effect19.succeed(Option5.none())
3087
3373
  }));
3088
3374
  if (Option5.isNone(signal))
3089
3375
  continue;
3090
3376
  const executionId = signal.value.executionId;
3091
3377
  const def = yield* findDef(defs, executionId, store);
3092
3378
  if (def) {
3093
- yield* engine.resume(def.workflow, executionId).pipe(Effect18.orDie);
3379
+ yield* engine.resume(def.workflow, executionId).pipe(Effect19.orDie);
3094
3380
  processed++;
3095
3381
  }
3096
3382
  }
@@ -3098,16 +3384,16 @@ var layer6 = (defs) => Layer7.effect(Service7, Effect18.gen(function* () {
3098
3384
  });
3099
3385
  return Service7.of({ enqueue, wait, submit, status, cancel, processPending });
3100
3386
  }));
3101
- var findDef = (defs, executionId, store) => Effect18.gen(function* () {
3387
+ var findDef = (defs, executionId, store) => Effect19.gen(function* () {
3102
3388
  const persisted = yield* getEncodedExit(store, `${executionId}/record`);
3103
- if (!persisted || !Exit5.isSuccess(persisted))
3389
+ if (!persisted || !Exit6.isSuccess(persisted))
3104
3390
  return;
3105
- const record = yield* Effect18.orDie(Schema14.decodeUnknownEffect(ExecutionRecord)(persisted.value));
3391
+ const record = yield* Effect19.orDie(Schema14.decodeUnknownEffect(ExecutionRecord)(persisted.value));
3106
3392
  return defs.find((d) => d.workflow._tag === record.workflowTag);
3107
3393
  });
3108
3394
  var layerLibsql = (defs, options) => layer6(defs).pipe(Layer7.provideMerge(Layer7.mergeAll(layerDurableLibsql(options), libsqlStack(options))));
3109
3395
  // ../workflow/src/registry.ts
3110
- import { Context as Context8, Effect as Effect19, Layer as Layer8, Option as Option6, Ref, Schema as Schema15 } from "effect";
3396
+ import { Context as Context8, Effect as Effect20, Layer as Layer8, Option as Option6, Ref, Schema as Schema15 } from "effect";
3111
3397
  class JobFailure extends Schema15.TaggedErrorClass()("JobFailure", {
3112
3398
  jobId: Schema15.String,
3113
3399
  message: Schema15.String
@@ -3116,9 +3402,9 @@ class JobFailure extends Schema15.TaggedErrorClass()("JobFailure", {
3116
3402
 
3117
3403
  class Service8 extends Context8.Service()("@dimple/workflow/JobRegistry") {
3118
3404
  }
3119
- var layer7 = Layer8.effect(Service8, Effect19.gen(function* () {
3405
+ var layer7 = Layer8.effect(Service8, Effect20.gen(function* () {
3120
3406
  const handlers = yield* Ref.make(new Map);
3121
- const register = Effect19.fn("JobRegistry.register")(function* (type, handler) {
3407
+ const register = Effect20.fn("JobRegistry.register")(function* (type, handler) {
3122
3408
  const existed = yield* Ref.modify(handlers, (map2) => {
3123
3409
  if (map2.has(type)) {
3124
3410
  return [true, map2];
@@ -3126,46 +3412,46 @@ var layer7 = Layer8.effect(Service8, Effect19.gen(function* () {
3126
3412
  return [false, new Map(map2).set(type, handler)];
3127
3413
  });
3128
3414
  if (existed) {
3129
- return yield* Effect19.fail(new RegistryError({ type, message: `Job type already registered: ${type}` }));
3415
+ return yield* Effect20.fail(new RegistryError({ type, message: `Job type already registered: ${type}` }));
3130
3416
  }
3131
3417
  });
3132
- const resolve = Effect19.fn("JobRegistry.resolve")(function* (type) {
3133
- const handler = yield* Ref.get(handlers).pipe(Effect19.map((map2) => Option6.fromNullishOr(map2.get(type))));
3418
+ const resolve = Effect20.fn("JobRegistry.resolve")(function* (type) {
3419
+ const handler = yield* Ref.get(handlers).pipe(Effect20.map((map2) => Option6.fromNullishOr(map2.get(type))));
3134
3420
  if (Option6.isNone(handler)) {
3135
- return yield* Effect19.fail(new UnknownJobType({ type }));
3421
+ return yield* Effect20.fail(new UnknownJobType({ type }));
3136
3422
  }
3137
3423
  return handler.value;
3138
3424
  });
3139
3425
  return Service8.of({ register, resolve });
3140
3426
  }));
3141
3427
  // ../workflow/src/executor.ts
3142
- import { Cause as Cause3, Duration as Duration3, Effect as Effect20, Layer as Layer9, Schedule as Schedule2 } from "effect";
3428
+ import { Cause as Cause3, Duration as Duration3, Effect as Effect21, Layer as Layer9, Schedule as Schedule2 } from "effect";
3143
3429
  import { DurableQueue as DurableQueue2, WorkflowEngine as WorkflowEngine4 } from "effect/unstable/workflow";
3144
- var runHandler = (def, payload, jobId, maxAttempts) => Effect20.gen(function* () {
3430
+ var runHandler = (def, payload, jobId, maxAttempts) => Effect21.gen(function* () {
3145
3431
  const registry = yield* Service8;
3146
3432
  const handler = yield* registry.resolve(def.name);
3147
- yield* Effect20.logInfo("job.started").pipe(Effect20.annotateLogs({
3433
+ yield* Effect21.logInfo("job.started").pipe(Effect21.annotateLogs({
3148
3434
  scope: "@dimple/workflow",
3149
3435
  event: "job.started",
3150
3436
  job: def.name,
3151
3437
  idempotencyKey: jobId
3152
3438
  }));
3153
3439
  const run = () => handler(payload, { jobId });
3154
- const outcome = yield* (maxAttempts > 1 ? run().pipe(Effect20.retry({
3440
+ const outcome = yield* (maxAttempts > 1 ? run().pipe(Effect21.retry({
3155
3441
  times: maxAttempts - 1,
3156
3442
  schedule: Schedule2.spaced(Duration3.millis(250)).pipe(Schedule2.jittered)
3157
- })) : run()).pipe(Effect20.exit);
3443
+ })) : run()).pipe(Effect21.exit);
3158
3444
  if (outcome._tag === "Failure") {
3159
- yield* Effect20.logError("job.failed").pipe(Effect20.annotateLogs({
3445
+ yield* Effect21.logError("job.failed").pipe(Effect21.annotateLogs({
3160
3446
  scope: "@dimple/workflow",
3161
3447
  event: "job.failed",
3162
3448
  job: def.name,
3163
3449
  idempotencyKey: jobId,
3164
3450
  error: Cause3.pretty(outcome.cause)
3165
3451
  }));
3166
- return yield* Effect20.failCause(outcome.cause).pipe(Effect20.mapError((e) => e.message));
3452
+ return yield* Effect21.failCause(outcome.cause).pipe(Effect21.mapError((e) => e.message));
3167
3453
  }
3168
- yield* Effect20.logInfo("job.completed").pipe(Effect20.annotateLogs({
3454
+ yield* Effect21.logInfo("job.completed").pipe(Effect21.annotateLogs({
3169
3455
  scope: "@dimple/workflow",
3170
3456
  event: "job.completed",
3171
3457
  job: def.name,
@@ -3198,17 +3484,17 @@ var JobsConfig2 = Schema16.Struct({
3198
3484
  pollIntervalMs: Schema16.optional(Schema16.Number)
3199
3485
  });
3200
3486
  // ../store/src/topics/lifecycle.ts
3201
- var routingFailure3 = (tag) => (effect) => effect.pipe(Effect21.mapError((cause) => new RoutingFailure({ cause: { operation: tag, cause } })));
3202
- var isActive = Effect21.fn("topics.isActive")(function* (modelKey) {
3487
+ var routingFailure3 = (tag) => (effect) => effect.pipe(Effect22.mapError((cause) => new RoutingFailure({ cause: { operation: tag, cause } })));
3488
+ var isActive = Effect22.fn("topics.isActive")(function* (modelKey) {
3203
3489
  const backend = yield* Service2;
3204
3490
  const db = backend.db;
3205
- const rows = yield* Effect21.tryPromise({
3491
+ const rows = yield* Effect22.tryPromise({
3206
3492
  try: () => db.select({ topicKey: topics.topicKey }).from(topics).where(and3(eq3(topics.modelKey, modelKey), eq3(topics.status, "active"))).limit(1),
3207
3493
  catch: (cause) => cause
3208
3494
  }).pipe(routingFailure3("topics.isActive"));
3209
3495
  return rows.length > 0;
3210
3496
  });
3211
- var setActiveModel = Effect21.fn("topics.setActiveModel")(function* (model, config) {
3497
+ var setActiveModel = Effect22.fn("topics.setActiveModel")(function* (model, config) {
3212
3498
  const backend = yield* Service2;
3213
3499
  const db = backend.db;
3214
3500
  yield* ensureTopicTree(model, config).pipe(routingFailure3("topics.setActiveModel.seed"));
@@ -3216,14 +3502,14 @@ var setActiveModel = Effect21.fn("topics.setActiveModel")(function* (model, conf
3216
3502
  let routed = 0;
3217
3503
  let alreadyInTree = 0;
3218
3504
  if (memoryIds.length > 0) {
3219
- const topicRows = yield* Effect21.tryPromise({
3505
+ const topicRows = yield* Effect22.tryPromise({
3220
3506
  try: () => db.select({ topicKey: topics.topicKey }).from(topics).where(eq3(topics.modelKey, model.id)),
3221
3507
  catch: (cause) => cause
3222
3508
  }).pipe(routingFailure3("topics.setActiveModel.topics"));
3223
3509
  const modelTopicKeys = topicRows.map((r) => r.topicKey);
3224
3510
  if (modelTopicKeys.length > 0) {
3225
3511
  for (const memoryId of memoryIds) {
3226
- const existing = yield* Effect21.tryPromise({
3512
+ const existing = yield* Effect22.tryPromise({
3227
3513
  try: () => db.select({ topicKey: topicMemberships.topicKey }).from(topicMemberships).where(and3(eq3(topicMemberships.memoryId, memoryId), eq3(topicMemberships.modelKey, model.id), inArray(topicMemberships.topicKey, modelTopicKeys))).limit(1),
3228
3514
  catch: (cause) => cause
3229
3515
  }).pipe(routingFailure3("topics.setActiveModel.membership"));
@@ -3241,7 +3527,7 @@ var setActiveModel = Effect21.fn("topics.setActiveModel")(function* (model, conf
3241
3527
  }
3242
3528
  }
3243
3529
  const auditTimestamp = yield* Clock3.currentTimeMillis;
3244
- const deactivated = yield* Effect21.tryPromise({
3530
+ const deactivated = yield* Effect22.tryPromise({
3245
3531
  try: () => db.transaction(async (tx) => {
3246
3532
  const others = await tx.selectDistinct({ modelKey: topics.modelKey }).from(topics).where(and3(ne(topics.modelKey, model.id), ne(topics.status, "inactive")));
3247
3533
  const affected = others.map((r) => r.modelKey).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
@@ -3265,18 +3551,18 @@ var setActiveModel = Effect21.fn("topics.setActiveModel")(function* (model, conf
3265
3551
  return { routed, alreadyInTree, deactivated };
3266
3552
  });
3267
3553
  function memoryEmbedHandler(deps) {
3268
- const run = (payload, ctx) => Effect21.gen(function* () {
3554
+ const run = (payload, ctx) => Effect22.gen(function* () {
3269
3555
  const envelope = payload;
3270
3556
  const model = deps.getModelConfig(envelope.modelKey);
3271
3557
  if (!model) {
3272
- return yield* Effect21.fail(new JobFailure({
3558
+ return yield* Effect22.fail(new JobFailure({
3273
3559
  jobId: ctx.jobId,
3274
3560
  message: `memory.embed: unknown model "${envelope.modelKey}"`
3275
3561
  }));
3276
3562
  }
3277
- const content = yield* deps.getMemoryContent(envelope.memoryId).pipe(Effect21.catchTag("Store.NotFound", () => Effect21.succeed(undefined)), Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: load content: ${e}` })));
3563
+ const content = yield* deps.getMemoryContent(envelope.memoryId).pipe(Effect22.catchTag("Store.NotFound", () => Effect22.succeed(undefined)), Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: load content: ${e}` })));
3278
3564
  if (content === undefined) {
3279
- yield* Effect21.logDebug("embed.stale-reoffer").pipe(Effect21.annotateLogs({
3565
+ yield* Effect22.logDebug("embed.stale-reoffer").pipe(Effect22.annotateLogs({
3280
3566
  scope: "@dimple/store",
3281
3567
  event: "embed.stale-reoffer",
3282
3568
  memoryId: envelope.memoryId,
@@ -3284,20 +3570,20 @@ function memoryEmbedHandler(deps) {
3284
3570
  }));
3285
3571
  return;
3286
3572
  }
3287
- const vector = yield* deps.embedOne(content, model).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: embed: ${e}` })));
3573
+ const vector = yield* deps.embedOne(content, model).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: embed: ${e}` })));
3288
3574
  const backend = yield* Service2;
3289
3575
  const db = backend.db;
3290
- const existing = yield* Effect21.tryPromise({
3576
+ const existing = yield* Effect22.tryPromise({
3291
3577
  try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and3(eq3(topicMemberships.memoryId, envelope.memoryId), eq3(topicMemberships.modelKey, model.id))),
3292
3578
  catch: (cause) => new StoreError({ message: `memory.embed: membership check: ${cause}` })
3293
- }).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: membership check: ${e}` })));
3294
- yield* ensureTopicTree(model, deps.config).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: seed tree: ${e}` })));
3295
- yield* backend.ensureVectorTable(model).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: vector table: ${e}` })));
3296
- yield* backend.insertVector(envelope.memoryId, [...vector], model).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: insert vector: ${e}` })));
3579
+ }).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: membership check: ${e}` })));
3580
+ yield* ensureTopicTree(model, deps.config).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: seed tree: ${e}` })));
3581
+ yield* backend.ensureVectorTable(model).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: vector table: ${e}` })));
3582
+ yield* backend.insertVector(envelope.memoryId, [...vector], model).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: insert vector: ${e}` })));
3297
3583
  if (existing.length === 0) {
3298
- yield* routeInsert(envelope.memoryId, [...vector], model, deps.config).pipe(Effect21.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: route: ${e}` })));
3584
+ yield* routeInsert(envelope.memoryId, [...vector], model, deps.config).pipe(Effect22.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: route: ${e}` })));
3299
3585
  }
3300
- yield* Effect21.logInfo("embed.completed").pipe(Effect21.annotateLogs({
3586
+ yield* Effect22.logInfo("embed.completed").pipe(Effect22.annotateLogs({
3301
3587
  scope: "@dimple/store",
3302
3588
  event: "embed.completed",
3303
3589
  memoryId: envelope.memoryId,
@@ -3306,11 +3592,11 @@ function memoryEmbedHandler(deps) {
3306
3592
  }));
3307
3593
  return;
3308
3594
  });
3309
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect21.provideService(Service2, deps.backend)) : run;
3595
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect22.provideService(Service2, deps.backend)) : run;
3310
3596
  }
3311
3597
 
3312
3598
  // ../store/src/topics/jobs.ts
3313
- import { Clock as Clock4, Effect as Effect22 } from "effect";
3599
+ import { Clock as Clock4, Effect as Effect23 } from "effect";
3314
3600
  import { and as and4, asc as asc3, eq as eq4, inArray as inArray2, sql as sql4 } from "drizzle-orm";
3315
3601
 
3316
3602
  // ../store/src/topics/topic-vectors.ts
@@ -3334,23 +3620,28 @@ var DEFAULT_SPLIT_DEFERRED_HARD_LIMIT = 512;
3334
3620
  var shouldSplit = (leafMemberCount, config) => leafMemberCount > (config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS);
3335
3621
  var childBaseKey = (parent, vectors) => {
3336
3622
  const dims = vectors.length > 0 ? vectors[0].length : 0;
3337
- const parts = [];
3623
+ let hash = 2166136261;
3624
+ const step = (byte) => {
3625
+ hash ^= byte;
3626
+ hash = Math.imul(hash, 16777619);
3627
+ };
3628
+ for (let i = 0;i < parent.length; i++)
3629
+ step(parent.charCodeAt(i) & 255);
3630
+ step(255);
3338
3631
  for (const v of vectors) {
3339
- const bytes = [];
3340
- for (let i = 0;i < dims; i++) {
3341
- bytes.push(String(v[i]));
3342
- }
3343
- parts.push(bytes.join(","));
3632
+ const bytes = new Uint8Array(v.buffer, v.byteOffset, dims * 4);
3633
+ for (let i = 0;i < bytes.length; i++)
3634
+ step(bytes[i]);
3344
3635
  }
3345
- return `t_${fnv1a32(JSON.stringify({ parent, members: parts })).toString(16).padStart(8, "0")}`;
3636
+ return `t_${(hash >>> 0).toString(16).padStart(8, "0")}`;
3346
3637
  };
3347
- var splitTopic = Effect22.fn("TopicSplit.splitTopic")(function* (topicKey, model, config) {
3638
+ var splitTopic = Effect23.fn("TopicSplit.splitTopic")(function* (topicKey, model, config) {
3348
3639
  const backend = yield* Service2;
3349
3640
  const db = backend.db;
3350
3641
  yield* backend.ensureTopicVectorTable(model);
3351
3642
  const maxLeafItems = config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS;
3352
3643
  const hardLimit = config.splitDeferredHardLimit ?? DEFAULT_SPLIT_DEFERRED_HARD_LIMIT;
3353
- const leafRows = yield* Effect22.tryPromise({
3644
+ const leafRows = yield* Effect23.tryPromise({
3354
3645
  try: () => db.select().from(topics).where(and4(eq4(topics.topicKey, topicKey), eq4(topics.modelKey, model.id))),
3355
3646
  catch: (cause) => new StoreError({ message: `splitTopic: load topic: ${cause}` })
3356
3647
  });
@@ -3361,13 +3652,13 @@ var splitTopic = Effect22.fn("TopicSplit.splitTopic")(function* (topicKey, model
3361
3652
  if (leaf.modelKey !== model.id) {
3362
3653
  return yield* new ModelMismatch({ expected: model.id, actual: leaf.modelKey });
3363
3654
  }
3364
- const memberRows = yield* Effect22.tryPromise({
3655
+ const memberRows = yield* Effect23.tryPromise({
3365
3656
  try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and4(eq4(topicMemberships.topicKey, topicKey), eq4(topicMemberships.modelKey, model.id))).orderBy(asc3(topicMemberships.memoryId)),
3366
3657
  catch: (cause) => new StoreError({ message: `splitTopic: load members: ${cause}` })
3367
3658
  });
3368
3659
  const memberIds = memberRows.map((r) => r.memoryId);
3369
3660
  const memberCount = memberIds.length;
3370
- const growthRows = yield* Effect22.tryPromise({
3661
+ const growthRows = yield* Effect23.tryPromise({
3371
3662
  try: () => db.select().from(topicGrowthState).where(and4(eq4(topicGrowthState.topicKey, topicKey), eq4(topicGrowthState.modelKey, model.id))),
3372
3663
  catch: (cause) => new StoreError({ message: `splitTopic: load growth state: ${cause}` })
3373
3664
  });
@@ -3399,7 +3690,7 @@ var splitTopic = Effect22.fn("TopicSplit.splitTopic")(function* (topicKey, model
3399
3690
  const forced = memberCount > hardLimit;
3400
3691
  if (!plan.accepted && !forced) {
3401
3692
  const now2 = yield* Clock4.currentTimeMillis;
3402
- yield* Effect22.tryPromise({
3693
+ yield* Effect23.tryPromise({
3403
3694
  try: () => db.transaction(async (tx) => {
3404
3695
  await tx.insert(topicGrowthState).values({
3405
3696
  topicKey,
@@ -3432,7 +3723,7 @@ var splitTopic = Effect22.fn("TopicSplit.splitTopic")(function* (topicKey, model
3432
3723
  };
3433
3724
  }
3434
3725
  const now = yield* Clock4.currentTimeMillis;
3435
- const children = yield* Effect22.tryPromise({
3726
+ const children = yield* Effect23.tryPromise({
3436
3727
  try: () => db.transaction(async (tx) => {
3437
3728
  const child0Cf = cfFromVectors(groupVectors(0));
3438
3729
  const child1Cf = cfFromVectors(groupVectors(1));
@@ -3553,34 +3844,34 @@ var splitTopic = Effect22.fn("TopicSplit.splitTopic")(function* (topicKey, model
3553
3844
  });
3554
3845
  return { accepted: true, children: children.keys, childCounts: children.counts };
3555
3846
  });
3556
- var staleSplitNoOp = (notFound, model) => Effect22.gen(function* () {
3847
+ var staleSplitNoOp = (notFound, model) => Effect23.gen(function* () {
3557
3848
  const backend = yield* Service2;
3558
3849
  const db = backend.db;
3559
- const rows = yield* Effect22.tryPromise({
3850
+ const rows = yield* Effect23.tryPromise({
3560
3851
  try: () => db.select({ topicKey: topics.topicKey }).from(topics).where(and4(eq4(topics.topicKey, notFound.topicKey), eq4(topics.modelKey, model.id))),
3561
3852
  catch: (cause) => new StoreError({ message: `topic.split: stale check: ${cause}` })
3562
3853
  });
3563
3854
  if (rows.length > 0) {
3564
3855
  return { accepted: false, reason: "no-improvement" };
3565
3856
  }
3566
- return yield* Effect22.fail(notFound);
3857
+ return yield* Effect23.fail(notFound);
3567
3858
  });
3568
3859
  function topicSplitHandler(deps) {
3569
- const run = (payload, ctx) => Effect22.gen(function* () {
3860
+ const run = (payload, ctx) => Effect23.gen(function* () {
3570
3861
  const envelope = payload;
3571
3862
  const model = deps.getModelConfig(envelope.modelKey);
3572
3863
  if (!model) {
3573
- return yield* Effect22.fail(new JobFailure({
3864
+ return yield* Effect23.fail(new JobFailure({
3574
3865
  jobId: ctx.jobId,
3575
3866
  message: `topic.split: unknown model "${envelope.modelKey}"`
3576
3867
  }));
3577
3868
  }
3578
- const outcome = yield* splitTopic(envelope.topicId, model, deps.config).pipe(Effect22.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleSplitNoOp(notFound, model)), Effect22.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
3869
+ const outcome = yield* splitTopic(envelope.topicId, model, deps.config).pipe(Effect23.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleSplitNoOp(notFound, model)), Effect23.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
3579
3870
  if (outcome.accepted && deps.onUnevenSplit) {
3580
3871
  const [n0, n1] = outcome.childCounts;
3581
3872
  const [k0] = outcome.children;
3582
3873
  if (Math.max(n0, n1) > 2 * Math.min(n0, n1)) {
3583
- yield* deps.onUnevenSplit(k0, envelope.modelKey).pipe(Effect22.mapError((error) => new JobFailure({
3874
+ yield* deps.onUnevenSplit(k0, envelope.modelKey).pipe(Effect23.mapError((error) => new JobFailure({
3584
3875
  jobId: ctx.jobId,
3585
3876
  message: `topic.split: enqueue rebalance: ${error}`
3586
3877
  })));
@@ -3590,14 +3881,14 @@ function topicSplitHandler(deps) {
3590
3881
  const maxLeafItems = deps.config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS;
3591
3882
  for (let i = 0;i < outcome.children.length; i++) {
3592
3883
  if (outcome.childCounts[i] > maxLeafItems) {
3593
- yield* deps.onChildOverflow(outcome.children[i], envelope.modelKey).pipe(Effect22.mapError((error) => new JobFailure({
3884
+ yield* deps.onChildOverflow(outcome.children[i], envelope.modelKey).pipe(Effect23.mapError((error) => new JobFailure({
3594
3885
  jobId: ctx.jobId,
3595
3886
  message: `topic.split: enqueue child split: ${error}`
3596
3887
  })));
3597
3888
  }
3598
3889
  }
3599
3890
  }
3600
- yield* Effect22.logInfo("split.completed").pipe(Effect22.annotateLogs({
3891
+ yield* Effect23.logInfo("split.completed").pipe(Effect23.annotateLogs({
3601
3892
  scope: "@dimple/store",
3602
3893
  event: "split.completed",
3603
3894
  topicId: envelope.topicId,
@@ -3607,16 +3898,16 @@ function topicSplitHandler(deps) {
3607
3898
  }));
3608
3899
  return;
3609
3900
  });
3610
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect22.provideService(Service2, deps.backend)) : run;
3901
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect23.provideService(Service2, deps.backend)) : run;
3611
3902
  }
3612
3903
 
3613
3904
  // ../store/src/topics/maintenance.ts
3614
- import { Clock as Clock5, Effect as Effect23 } from "effect";
3905
+ import { Clock as Clock5, Effect as Effect24 } from "effect";
3615
3906
  import { and as and5, asc as asc4, eq as eq5, ne as ne2 } from "drizzle-orm";
3616
3907
  var DEFAULT_MIN_LEAF_ITEMS = 32;
3617
3908
  var DEFAULT_MAX_LEAF_ITEMS2 = 128;
3618
3909
  var DEFAULT_REBALANCE_COOLDOWN_MS = 60000;
3619
- var maintenanceError = (tag) => (effect) => effect.pipe(Effect23.mapError((cause) => new StoreError({ message: `${tag}: ${cause}` })));
3910
+ var maintenanceError = (tag) => (effect) => effect.pipe(Effect24.mapError((cause) => new StoreError({ message: `${tag}: ${cause}` })));
3620
3911
  var dot2 = (a, b) => {
3621
3912
  let sum = 0;
3622
3913
  for (let i = 0;i < a.length; i++)
@@ -3634,12 +3925,12 @@ var resolvedConfig2 = (config) => ({
3634
3925
  maxLeafItems: config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS2,
3635
3926
  rebalanceCooldownMs: config.rebalanceCooldownMs ?? DEFAULT_REBALANCE_COOLDOWN_MS
3636
3927
  });
3637
- var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, model, config) {
3928
+ var mergeOrBorrow = Effect24.fn("topics.mergeOrBorrow")(function* (topicKey, model, config) {
3638
3929
  const backend = yield* Service2;
3639
3930
  const db = backend.db;
3640
3931
  const { minLeafItems, maxLeafItems } = resolvedConfig2(config);
3641
3932
  const now = yield* Clock5.currentTimeMillis;
3642
- const leafRows = yield* Effect23.tryPromise({
3933
+ const leafRows = yield* Effect24.tryPromise({
3643
3934
  try: () => db.select().from(topics).where(and5(eq5(topics.topicKey, topicKey), eq5(topics.modelKey, model.id))),
3644
3935
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: load topic: ${cause}` })
3645
3936
  });
@@ -3652,7 +3943,7 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3652
3943
  }
3653
3944
  if (leaf.memberCount >= minLeafItems)
3654
3945
  return { action: "none" };
3655
- const parentRows = yield* Effect23.tryPromise({
3946
+ const parentRows = yield* Effect24.tryPromise({
3656
3947
  try: () => db.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and5(eq5(topicRelations.childKey, topicKey), eq5(topicRelations.modelKey, model.id), eq5(topicRelations.relationKind, "parent_child"))).orderBy(asc4(topicRelations.parentKey)),
3657
3948
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: load parent: ${cause}` })
3658
3949
  });
@@ -3660,16 +3951,16 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3660
3951
  return yield* logUnderfilled(db, topicKey, model, now, "no-borrowable");
3661
3952
  }
3662
3953
  const parentKey = parentRows[0].parentKey;
3663
- const parentRowRows = yield* Effect23.tryPromise({
3954
+ const parentRowRows = yield* Effect24.tryPromise({
3664
3955
  try: () => db.select({ childCount: topics.childCount }).from(topics).where(and5(eq5(topics.topicKey, parentKey), eq5(topics.modelKey, model.id))),
3665
3956
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: load parent row: ${cause}` })
3666
3957
  });
3667
3958
  const parentRow = parentRowRows[0];
3668
- const siblingRows = yield* Effect23.tryPromise({
3959
+ const siblingRows = yield* Effect24.tryPromise({
3669
3960
  try: () => db.select().from(topics).where(and5(eq5(topics.modelKey, model.id), eq5(topics.kind, "leaf"), ne2(topics.status, "inactive"))).orderBy(asc4(topics.topicKey)),
3670
3961
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: load siblings: ${cause}` })
3671
3962
  });
3672
- const relationRows = yield* Effect23.tryPromise({
3963
+ const relationRows = yield* Effect24.tryPromise({
3673
3964
  try: () => db.select({ childKey: topicRelations.childKey }).from(topicRelations).where(and5(eq5(topicRelations.parentKey, parentKey), eq5(topicRelations.modelKey, model.id), eq5(topicRelations.relationKind, "parent_child"))).orderBy(asc4(topicRelations.childKey)),
3674
3965
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: load relations: ${cause}` })
3675
3966
  });
@@ -3691,7 +3982,7 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3691
3982
  const membersByTopic = new Map;
3692
3983
  const vecById = new Map;
3693
3984
  for (const sibling of siblingsOf) {
3694
- const members = yield* Effect23.tryPromise({
3985
+ const members = yield* Effect24.tryPromise({
3695
3986
  try: () => db.select({
3696
3987
  memoryId: topicMemberships.memoryId,
3697
3988
  manualLock: topicMemberships.manualLock
@@ -3704,7 +3995,7 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3704
3995
  vecById.set(row.memoryId, new Float32Array(row.vector));
3705
3996
  }
3706
3997
  const movedIds = new Set;
3707
- const outcome = yield* Effect23.tryPromise({
3998
+ const outcome = yield* Effect24.tryPromise({
3708
3999
  try: () => db.transaction(async (tx) => {
3709
4000
  const leafCf0 = cfOf(leaf);
3710
4001
  let leafCf = leafCf0;
@@ -3820,7 +4111,36 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3820
4111
  await tx.delete(topicRelations).where(and5(eq5(topicRelations.parentKey, parentKey), eq5(topicRelations.childKey, sibling.topicKey), eq5(topicRelations.modelKey, model.id)));
3821
4112
  await deleteTopicVectorRow(tx, model, sibling.topicKey);
3822
4113
  await writeTopicVector(tx, model, topicKey, mergedCf.ls);
3823
- await tx.update(topics).set({ childCount: Math.max(0, (parentRow?.childCount ?? 1) - 1), updatedAt: now }).where(and5(eq5(topics.topicKey, parentKey), eq5(topics.modelKey, model.id)));
4114
+ const remainingChildren = Math.max(0, (parentRow?.childCount ?? 1) - 1);
4115
+ await tx.update(topics).set({ childCount: remainingChildren, updatedAt: now }).where(and5(eq5(topics.topicKey, parentKey), eq5(topics.modelKey, model.id)));
4116
+ if (remainingChildren === 1) {
4117
+ const grandparentRows = await tx.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and5(eq5(topicRelations.childKey, parentKey), eq5(topicRelations.modelKey, model.id), eq5(topicRelations.relationKind, "parent_child"))).limit(1);
4118
+ const grandparentKey = grandparentRows[0]?.parentKey;
4119
+ if (grandparentKey !== undefined) {
4120
+ await tx.delete(topicRelations).where(and5(eq5(topicRelations.parentKey, parentKey), eq5(topicRelations.childKey, topicKey), eq5(topicRelations.modelKey, model.id)));
4121
+ await tx.delete(topicRelations).where(and5(eq5(topicRelations.parentKey, grandparentKey), eq5(topicRelations.childKey, parentKey), eq5(topicRelations.modelKey, model.id)));
4122
+ await tx.insert(topicRelations).values({
4123
+ parentKey: grandparentKey,
4124
+ childKey: topicKey,
4125
+ modelKey: model.id,
4126
+ relationKind: "parent_child",
4127
+ updatedAt: now
4128
+ });
4129
+ await tx.update(topics).set({
4130
+ status: "inactive",
4131
+ memberCount: 0,
4132
+ sumSquares: 0,
4133
+ childCount: 0,
4134
+ updatedAt: now
4135
+ }).where(and5(eq5(topics.topicKey, parentKey), eq5(topics.modelKey, model.id)));
4136
+ await deleteTopicVectorRow(tx, model, parentKey);
4137
+ const grandparentDepthRows = await tx.select({ depth: topics.depth }).from(topics).where(and5(eq5(topics.topicKey, grandparentKey), eq5(topics.modelKey, model.id))).limit(1);
4138
+ await tx.update(topics).set({
4139
+ depth: (grandparentDepthRows[0]?.depth ?? 0) + 1,
4140
+ updatedAt: now
4141
+ }).where(and5(eq5(topics.topicKey, topicKey), eq5(topics.modelKey, model.id)));
4142
+ }
4143
+ }
3824
4144
  await tx.insert(topicGrowthState).values({
3825
4145
  topicKey,
3826
4146
  modelKey: model.id,
@@ -3844,12 +4164,12 @@ var mergeOrBorrow = Effect23.fn("topics.mergeOrBorrow")(function* (topicKey, mod
3844
4164
  });
3845
4165
  return outcome;
3846
4166
  });
3847
- var rebalanceSiblings = Effect23.fn("topics.rebalanceSiblings")(function* (topicKey, model, config) {
4167
+ var rebalanceSiblings = Effect24.fn("topics.rebalanceSiblings")(function* (topicKey, model, config) {
3848
4168
  const backend = yield* Service2;
3849
4169
  const db = backend.db;
3850
4170
  const { rebalanceCooldownMs } = resolvedConfig2(config);
3851
4171
  const now = yield* Clock5.currentTimeMillis;
3852
- const rows = yield* Effect23.tryPromise({
4172
+ const rows = yield* Effect24.tryPromise({
3853
4173
  try: () => db.select().from(topics).where(and5(eq5(topics.topicKey, topicKey), eq5(topics.modelKey, model.id))),
3854
4174
  catch: (cause) => new StoreError({ message: `rebalanceSiblings: load topic: ${cause}` })
3855
4175
  });
@@ -3858,19 +4178,19 @@ var rebalanceSiblings = Effect23.fn("topics.rebalanceSiblings")(function* (topic
3858
4178
  return yield* new TopicNotFound({ topicKey });
3859
4179
  if (row.modelKey !== model.id)
3860
4180
  return yield* new ModelMismatch({ expected: model.id, actual: row.modelKey });
3861
- const parentRows = yield* Effect23.tryPromise({
4181
+ const parentRows = yield* Effect24.tryPromise({
3862
4182
  try: () => db.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and5(eq5(topicRelations.childKey, topicKey), eq5(topicRelations.modelKey, model.id), eq5(topicRelations.relationKind, "parent_child"))).orderBy(asc4(topicRelations.parentKey)),
3863
4183
  catch: (cause) => new StoreError({ message: `rebalanceSiblings: load parent: ${cause}` })
3864
4184
  });
3865
4185
  if (parentRows.length === 0)
3866
4186
  return { action: "none", reason: "balanced" };
3867
4187
  const parentKey = parentRows[0].parentKey;
3868
- const relationRows = yield* Effect23.tryPromise({
4188
+ const relationRows = yield* Effect24.tryPromise({
3869
4189
  try: () => db.select({ childKey: topicRelations.childKey }).from(topicRelations).where(and5(eq5(topicRelations.parentKey, parentKey), eq5(topicRelations.modelKey, model.id), eq5(topicRelations.relationKind, "parent_child"))).orderBy(asc4(topicRelations.childKey)),
3870
4190
  catch: (cause) => new StoreError({ message: `rebalanceSiblings: load relations: ${cause}` })
3871
4191
  });
3872
4192
  const siblingKeys = relationRows.map((r) => r.childKey);
3873
- const topicRows = yield* Effect23.tryPromise({
4193
+ const topicRows = yield* Effect24.tryPromise({
3874
4194
  try: () => db.select().from(topics).where(and5(eq5(topics.modelKey, model.id), eq5(topics.kind, "leaf"), ne2(topics.status, "inactive"))),
3875
4195
  catch: (cause) => new StoreError({ message: `rebalanceSiblings: load siblings: ${cause}` })
3876
4196
  });
@@ -3878,7 +4198,7 @@ var rebalanceSiblings = Effect23.fn("topics.rebalanceSiblings")(function* (topic
3878
4198
  const siblings = siblingKeys.map((k) => map2.get(k)).filter(Boolean);
3879
4199
  if (siblings.length < 2)
3880
4200
  return { action: "none", reason: "balanced" };
3881
- const growthRows = yield* Effect23.tryPromise({
4201
+ const growthRows = yield* Effect24.tryPromise({
3882
4202
  try: () => db.select().from(topicGrowthState).where(and5(eq5(topicGrowthState.topicKey, topicKey), eq5(topicGrowthState.modelKey, model.id))),
3883
4203
  catch: (cause) => new StoreError({ message: `rebalanceSiblings: load growth: ${cause}` })
3884
4204
  });
@@ -3903,7 +4223,7 @@ var rebalanceSiblings = Effect23.fn("topics.rebalanceSiblings")(function* (topic
3903
4223
  const membersByTopic = new Map;
3904
4224
  const vecById = new Map;
3905
4225
  for (const sibling of siblings) {
3906
- const members = yield* Effect23.tryPromise({
4226
+ const members = yield* Effect24.tryPromise({
3907
4227
  try: () => db.select({
3908
4228
  memoryId: topicMemberships.memoryId,
3909
4229
  manualLock: topicMemberships.manualLock
@@ -3916,7 +4236,7 @@ var rebalanceSiblings = Effect23.fn("topics.rebalanceSiblings")(function* (topic
3916
4236
  vecById.set(row2.memoryId, new Float32Array(row2.vector));
3917
4237
  }
3918
4238
  const movedIds = new Set;
3919
- const outcome = yield* Effect23.tryPromise({
4239
+ const outcome = yield* Effect24.tryPromise({
3920
4240
  try: () => db.transaction(async (tx) => {
3921
4241
  const liveCfs = new Map(siblings.map((s) => [s.topicKey, cfOf(s)]));
3922
4242
  const liveList = () => [...liveCfs.entries()].map(([topicKey2, cf]) => ({ topicKey: topicKey2, cf })).sort((a, b) => b.cf.count - a.cf.count || (a.topicKey < b.topicKey ? -1 : 1));
@@ -4003,7 +4323,7 @@ var loadMembers = async (tx, topicKey, modelKey) => {
4003
4323
  const rows = await tx.select({ memoryId: topicMemberships.memoryId, manualLock: topicMemberships.manualLock }).from(topicMemberships).where(and5(eq5(topicMemberships.topicKey, topicKey), eq5(topicMemberships.modelKey, modelKey))).orderBy(asc4(topicMemberships.memoryId));
4004
4324
  return rows;
4005
4325
  };
4006
- var logUnderfilled = (db, topicKey, model, now, reason) => Effect23.tryPromise({
4326
+ var logUnderfilled = (db, topicKey, model, now, reason) => Effect24.tryPromise({
4007
4327
  try: () => db.insert(topicChangeLog).values({
4008
4328
  topicKey,
4009
4329
  operation: "merge",
@@ -4013,7 +4333,7 @@ var logUnderfilled = (db, topicKey, model, now, reason) => Effect23.tryPromise({
4013
4333
  createdAt: now
4014
4334
  }),
4015
4335
  catch: (cause) => new StoreError({ message: `mergeOrBorrow: log underfilled: ${cause}` })
4016
- }).pipe(Effect23.map(() => ({ action: "underfilled", reason })));
4336
+ }).pipe(Effect24.map(() => ({ action: "underfilled", reason })));
4017
4337
  var logUnderfilledTx = async (tx, topicKey, model, now, reason) => {
4018
4338
  await tx.insert(topicChangeLog).values({
4019
4339
  topicKey,
@@ -4026,61 +4346,61 @@ var logUnderfilledTx = async (tx, topicKey, model, now, reason) => {
4026
4346
  return { action: "underfilled", reason };
4027
4347
  };
4028
4348
  function topicMergeHandler(deps) {
4029
- const run = (payload, ctx) => Effect23.gen(function* () {
4349
+ const run = (payload, ctx) => Effect24.gen(function* () {
4030
4350
  const envelope = payload;
4031
4351
  const model = deps.getModelConfig(envelope.modelKey);
4032
4352
  if (!model) {
4033
- return yield* Effect23.fail(new JobFailure({
4353
+ return yield* Effect24.fail(new JobFailure({
4034
4354
  jobId: ctx.jobId,
4035
4355
  message: `topic.merge: unknown model "${envelope.modelKey}"`
4036
4356
  }));
4037
4357
  }
4038
- yield* mergeOrBorrow(envelope.topicId, model, deps.config).pipe(Effect23.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleNoOp(notFound, model)), Effect23.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
4358
+ yield* mergeOrBorrow(envelope.topicId, model, deps.config).pipe(Effect24.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleNoOp(notFound, model)), Effect24.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
4039
4359
  return;
4040
4360
  });
4041
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect23.provideService(Service2, deps.backend)) : run;
4361
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect24.provideService(Service2, deps.backend)) : run;
4042
4362
  }
4043
4363
  function topicRebalanceHandler(deps) {
4044
- const run = (payload, ctx) => Effect23.gen(function* () {
4364
+ const run = (payload, ctx) => Effect24.gen(function* () {
4045
4365
  const envelope = payload;
4046
4366
  const model = deps.getModelConfig(envelope.modelKey);
4047
4367
  if (!model) {
4048
- return yield* Effect23.fail(new JobFailure({
4368
+ return yield* Effect24.fail(new JobFailure({
4049
4369
  jobId: ctx.jobId,
4050
4370
  message: `topic.rebalance: unknown model "${envelope.modelKey}"`
4051
4371
  }));
4052
4372
  }
4053
- yield* rebalanceSiblings(envelope.topicId, model, deps.config).pipe(Effect23.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleNoOp(notFound, model)), Effect23.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
4373
+ yield* rebalanceSiblings(envelope.topicId, model, deps.config).pipe(Effect24.catchIf((error) => error._tag === "topics.TopicNotFound", (notFound) => staleNoOp(notFound, model)), Effect24.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `${error}` })));
4054
4374
  return;
4055
4375
  });
4056
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect23.provideService(Service2, deps.backend)) : run;
4376
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect24.provideService(Service2, deps.backend)) : run;
4057
4377
  }
4058
- var staleNoOp = (notFound, model) => Effect23.gen(function* () {
4378
+ var staleNoOp = (notFound, model) => Effect24.gen(function* () {
4059
4379
  const backend = yield* Service2;
4060
4380
  const db = backend.db;
4061
- const rows = yield* Effect23.tryPromise({
4381
+ const rows = yield* Effect24.tryPromise({
4062
4382
  try: () => db.select({ topicKey: topics.topicKey }).from(topics).where(and5(eq5(topics.topicKey, notFound.topicKey), eq5(topics.modelKey, model.id))),
4063
4383
  catch: (cause) => new StoreError({ message: `maintenance: stale check: ${cause}` })
4064
4384
  });
4065
4385
  if (rows.length > 0) {
4066
4386
  return;
4067
4387
  }
4068
- return yield* Effect23.fail(new JobFailure({ jobId: notFound.topicKey, message: `topic missing: ${notFound.topicKey}` }));
4069
- }).pipe(Effect23.mapError((error) => error instanceof JobFailure ? error : new JobFailure({ jobId: notFound.topicKey, message: `${error}` })));
4388
+ return yield* Effect24.fail(new JobFailure({ jobId: notFound.topicKey, message: `topic missing: ${notFound.topicKey}` }));
4389
+ }).pipe(Effect24.mapError((error) => error instanceof JobFailure ? error : new JobFailure({ jobId: notFound.topicKey, message: `${error}` })));
4070
4390
 
4071
4391
  // ../store/src/topics/observability.ts
4072
- import { Effect as Effect24 } from "effect";
4392
+ import { Effect as Effect25 } from "effect";
4073
4393
  import { and as and6, desc, eq as eq6, inArray as inArray3, ne as ne3, sql as sql5 } from "drizzle-orm";
4074
4394
  var DEFAULT_MAX_LEAF_ITEMS3 = 128;
4075
4395
  var DEFAULT_SPLIT_DEFERRED_HARD_LIMIT2 = 512;
4076
4396
  var DEFAULT_CHANGE_LOG_LIMIT = 50;
4077
- var routingFailure4 = (tag) => (effect) => effect.pipe(Effect24.mapError((cause) => new RoutingFailure({ cause: { operation: tag, cause } })));
4397
+ var routingFailure4 = (tag) => (effect) => effect.pipe(Effect25.mapError((cause) => new RoutingFailure({ cause: { operation: tag, cause } })));
4078
4398
  var modelTopicKeys = (db, modelKey) => db.select({ topicKey: topics.topicKey }).from(topics).where(eq6(topics.modelKey, modelKey));
4079
- var topicMetrics = Effect24.fn("topics.topicMetrics")(function* (modelKey, config) {
4399
+ var topicMetrics = Effect25.fn("topics.topicMetrics")(function* (modelKey, config) {
4080
4400
  const backend = yield* Service2;
4081
4401
  const db = backend.db;
4082
4402
  const maxLeafItems = config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS3;
4083
- const rows = yield* Effect24.tryPromise({
4403
+ const rows = yield* Effect25.tryPromise({
4084
4404
  try: () => db.select({
4085
4405
  topicKey: topics.topicKey,
4086
4406
  kind: topics.kind,
@@ -4113,12 +4433,12 @@ var topicMetrics = Effect24.fn("topics.topicMetrics")(function* (modelKey, confi
4113
4433
  deferredLeaves++;
4114
4434
  }
4115
4435
  leafMemberCounts.sort((a, b) => a - b);
4116
- const growth = yield* Effect24.tryPromise({
4436
+ const growth = yield* Effect25.tryPromise({
4117
4437
  try: () => db.select({ total: sql5`coalesce(sum(${topicGrowthState.splitRejectedCount}), 0)` }).from(topicGrowthState).where(eq6(topicGrowthState.modelKey, modelKey)),
4118
4438
  catch: (cause) => cause
4119
4439
  }).pipe(routingFailure4("topics.topicMetrics.growth"));
4120
4440
  const splitRejectedTotal = Number(growth[0]?.total ?? 0);
4121
- const lastLog = yield* Effect24.tryPromise({
4441
+ const lastLog = yield* Effect25.tryPromise({
4122
4442
  try: () => db.select({
4123
4443
  operation: topicChangeLog.operation,
4124
4444
  createdAt: topicChangeLog.createdAt
@@ -4138,11 +4458,11 @@ var topicMetrics = Effect24.fn("topics.topicMetrics")(function* (modelKey, confi
4138
4458
  lastOperation
4139
4459
  };
4140
4460
  });
4141
- var topicChangeLog2 = Effect24.fn("topics.topicChangeLog")(function* (modelKey, opts) {
4461
+ var topicChangeLog2 = Effect25.fn("topics.topicChangeLog")(function* (modelKey, opts) {
4142
4462
  const backend = yield* Service2;
4143
4463
  const db = backend.db;
4144
4464
  const limit = opts?.limit ?? DEFAULT_CHANGE_LOG_LIMIT;
4145
- const rows = yield* Effect24.tryPromise({
4465
+ const rows = yield* Effect25.tryPromise({
4146
4466
  try: () => {
4147
4467
  const scoped = inArray3(topicChangeLog.topicKey, modelTopicKeys(db, modelKey));
4148
4468
  const filtered = opts?.operation ? and6(scoped, eq6(topicChangeLog.operation, opts.operation)) : scoped;
@@ -4152,14 +4472,14 @@ var topicChangeLog2 = Effect24.fn("topics.topicChangeLog")(function* (modelKey,
4152
4472
  }).pipe(routingFailure4("topics.topicChangeLog"));
4153
4473
  return rows;
4154
4474
  });
4155
- var topicHealth = Effect24.fn("topics.topicHealth")(function* (modelKey, config) {
4475
+ var topicHealth = Effect25.fn("topics.topicHealth")(function* (modelKey, config) {
4156
4476
  const backend = yield* Service2;
4157
4477
  const db = backend.db;
4158
4478
  const maxLeafItems = config.maxLeafItems ?? DEFAULT_MAX_LEAF_ITEMS3;
4159
4479
  const hardLimit = config.splitDeferredHardLimit ?? DEFAULT_SPLIT_DEFERRED_HARD_LIMIT2;
4160
4480
  const slack = Math.max(2, Math.ceil(hardLimit / 2) - maxLeafItems);
4161
4481
  const leafLimit = maxLeafItems + slack;
4162
- const rows = yield* Effect24.tryPromise({
4482
+ const rows = yield* Effect25.tryPromise({
4163
4483
  try: () => db.select({
4164
4484
  topicKey: topics.topicKey,
4165
4485
  kind: topics.kind,
@@ -4169,7 +4489,7 @@ var topicHealth = Effect24.fn("topics.topicHealth")(function* (modelKey, config)
4169
4489
  }).from(topics).where(and6(eq6(topics.modelKey, modelKey), ne3(topics.status, "inactive"))),
4170
4490
  catch: (cause) => cause
4171
4491
  }).pipe(routingFailure4("topics.topicHealth.topics"));
4172
- const countRows = yield* Effect24.tryPromise({
4492
+ const countRows = yield* Effect25.tryPromise({
4173
4493
  try: () => db.select({ topicKey: topicMemberships.topicKey, count: sql5`count(*)` }).from(topicMemberships).where(eq6(topicMemberships.modelKey, modelKey)).groupBy(topicMemberships.topicKey),
4174
4494
  catch: (cause) => cause
4175
4495
  }).pipe(routingFailure4("topics.topicHealth.memberships"));
@@ -4194,30 +4514,30 @@ var topicHealth = Effect24.fn("topics.topicHealth")(function* (modelKey, config)
4194
4514
  });
4195
4515
 
4196
4516
  // ../store/src/graph/observability.ts
4197
- import { Effect as Effect25 } from "effect";
4517
+ import { Effect as Effect26 } from "effect";
4198
4518
  import { and as and7, eq as eq7, isNull, sql as sql6 } from "drizzle-orm";
4199
- var graphCoverage = Effect25.fn("GraphCoverage.run")(function* (modelKey) {
4519
+ var graphCoverage = Effect26.fn("GraphCoverage.run")(function* (modelKey) {
4200
4520
  const backend = yield* Service2;
4201
4521
  const db = backend.db;
4202
- const totalRows = yield* Effect25.tryPromise({
4522
+ const totalRows = yield* Effect26.tryPromise({
4203
4523
  try: () => db.select({ id: memories.id }).from(memories).where(and7(eq7(memories.state, "active"), eq7(memories.embeddingModel, modelKey))),
4204
4524
  catch: (cause) => new StoreError({ message: `graphCoverage: total: ${cause}` })
4205
4525
  });
4206
4526
  const totalMemories = totalRows.length;
4207
- const isolatedRows = yield* backend.listIsolatedMemories(0, modelKey).pipe(Effect25.mapError((cause) => new StoreError({ message: `graphCoverage: isolated: ${cause}` })));
4527
+ const isolatedRows = yield* backend.listIsolatedMemories(0, modelKey).pipe(Effect26.mapError((cause) => new StoreError({ message: `graphCoverage: isolated: ${cause}` })));
4208
4528
  const isolatedMemories = isolatedRows.length;
4209
4529
  const coveredMemories = totalMemories - isolatedMemories;
4210
4530
  const coveragePercent = totalMemories === 0 ? 0 : Math.round(coveredMemories / totalMemories * 1000) / 10;
4211
- const pendingRows = yield* Effect25.tryPromise({
4531
+ const pendingRows = yield* Effect26.tryPromise({
4212
4532
  try: () => db.select({ count: sql6`count(*)` }).from(artifactEdges).where(and7(eq7(artifactEdges.status, "pending"), isNull(artifactEdges.expiredAt))),
4213
4533
  catch: (cause) => new StoreError({ message: `graphCoverage: pending: ${cause}` })
4214
4534
  });
4215
4535
  const pendingCandidates = totalMemories === 0 ? 0 : Number(pendingRows[0]?.count ?? 0);
4216
- const conceptCount = totalMemories === 0 ? 0 : (yield* Effect25.tryPromise({
4536
+ const conceptCount = totalMemories === 0 ? 0 : (yield* Effect26.tryPromise({
4217
4537
  try: () => db.select({ id: concepts.id }).from(concepts),
4218
4538
  catch: (cause) => new StoreError({ message: `graphCoverage: concepts: ${cause}` })
4219
4539
  })).length;
4220
- const annotatedMemories = totalMemories === 0 ? 0 : new Set((yield* Effect25.tryPromise({
4540
+ const annotatedMemories = totalMemories === 0 ? 0 : new Set((yield* Effect26.tryPromise({
4221
4541
  try: () => db.select({ memoryId: memoryConcepts.memoryId }).from(memoryConcepts),
4222
4542
  catch: (cause) => new StoreError({ message: `graphCoverage: annotations: ${cause}` })
4223
4543
  })).map((row) => row.memoryId)).size;
@@ -4447,27 +4767,25 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4447
4767
  const db = backend.db;
4448
4768
  const topicsConfig = topicsOptions?.config ?? Schema19.decodeUnknownSync(TopicConfig)({});
4449
4769
  const topicsEnabled = topicsOptions?.enabled ?? false;
4450
- const updateTopicVectorForget = (backendImpl, model, topicKey, x) => Effect26.gen(function* () {
4451
- const rows = yield* backendImpl.fetchTopicVectors(model).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.forget: load vectors: ${cause}` })));
4452
- const current = rows.find((r) => r.topicKey === topicKey);
4770
+ const updateTopicVectorForget = (backendImpl, model, topicKey, x) => Effect27.gen(function* () {
4771
+ const current = yield* backendImpl.fetchTopicVector(topicKey, model).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.forget: load vector: ${cause}` })));
4453
4772
  const ls = current ? new Float32Array(current.sumVector) : new Float32Array(model.dimensions);
4454
4773
  for (let i = 0;i < ls.length; i++)
4455
4774
  ls[i] = ls[i] - x[i];
4456
- yield* backendImpl.upsertTopicVector(topicKey, Array.from(ls), model).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.forget: write vector: ${cause}` })));
4457
- const relRows = yield* Effect26.tryPromise({
4775
+ yield* backendImpl.upsertTopicVector(topicKey, Array.from(ls), model).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.forget: write vector: ${cause}` })));
4776
+ const relRows = yield* Effect27.tryPromise({
4458
4777
  try: () => db.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and8(eq8(topicRelations.childKey, topicKey), eq8(topicRelations.modelKey, model.id), eq8(topicRelations.relationKind, "parent_child"))),
4459
4778
  catch: (cause) => new StoreError({ message: `Store.forget: load ancestor: ${cause}` })
4460
4779
  });
4461
4780
  let ancestorKey = relRows[0]?.parentKey;
4462
4781
  while (ancestorKey !== undefined) {
4463
4782
  const key = ancestorKey;
4464
- const aRows = yield* backendImpl.fetchTopicVectors(model).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.forget: load vectors: ${cause}` })));
4465
- const a = aRows.find((r) => r.topicKey === key);
4783
+ const a = yield* backendImpl.fetchTopicVector(key, model).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.forget: load vector: ${cause}` })));
4466
4784
  const als = a ? new Float32Array(a.sumVector) : new Float32Array(model.dimensions);
4467
4785
  for (let i = 0;i < als.length; i++)
4468
4786
  als[i] = als[i] - x[i];
4469
- yield* backendImpl.upsertTopicVector(key, Array.from(als), model).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.forget: write vector: ${cause}` })));
4470
- const up = yield* Effect26.tryPromise({
4787
+ yield* backendImpl.upsertTopicVector(key, Array.from(als), model).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.forget: write vector: ${cause}` })));
4788
+ const up = yield* Effect27.tryPromise({
4471
4789
  try: () => db.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and8(eq8(topicRelations.childKey, key), eq8(topicRelations.modelKey, model.id), eq8(topicRelations.relationKind, "parent_child"))),
4472
4790
  catch: (cause) => new StoreError({ message: `Store.forget: load ancestor: ${cause}` })
4473
4791
  });
@@ -4517,18 +4835,18 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4517
4835
  };
4518
4836
  const seam = auditOptions?.enqueueAudit;
4519
4837
  if (seam !== undefined) {
4520
- return propagateSeam ? seam(row).pipe(Effect26.mapError((cause) => new StoreError({ message: `audit.enqueue-failed: ${cause}` }))) : seam(row).pipe(Effect26.catch((cause) => Effect26.logError("audit.enqueue-failed").pipe(Effect26.annotateLogs({
4838
+ return propagateSeam ? seam(row).pipe(Effect27.mapError((cause) => new StoreError({ message: `audit.enqueue-failed: ${cause}` }))) : seam(row).pipe(Effect27.catch((cause) => Effect27.logError("audit.enqueue-failed").pipe(Effect27.annotateLogs({
4521
4839
  scope: "@dimple/store",
4522
4840
  event: "audit.enqueue-failed",
4523
4841
  mode,
4524
4842
  cause: String(cause)
4525
4843
  }))));
4526
4844
  }
4527
- return Effect26.sync(() => {
4845
+ return Effect27.sync(() => {
4528
4846
  db.insert(retrievalLog2).values(row).catch(() => {});
4529
4847
  });
4530
4848
  };
4531
- const logAudit = Effect26.fn("Store.logAudit")(function* (input) {
4849
+ const logAudit = Effect27.fn("Store.logAudit")(function* (input) {
4532
4850
  return yield* logRetrieval(input.query, input.namespace, input.mode, input.resultCount, input.startTime, input.queryProfile, input.sources, input.hashSeed, true);
4533
4851
  });
4534
4852
  const GRAPH_SUPERSEDE_DEMOTION = graphConfig?.supersedeDemotion ?? 0.5;
@@ -4536,9 +4854,10 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4536
4854
  nearDuplicate: 0.9,
4537
4855
  conflictLow: 0.6
4538
4856
  };
4857
+ const graphEnabled = graphConfig?.enabled !== false;
4539
4858
  const GRAPH_CANDIDATE_LIMIT_MAX = 200;
4540
4859
  const GRAPH_ISOLATED_EXAMPLES = 10;
4541
- const fetchResultEdges = (ids) => Effect26.tryPromise({
4860
+ const fetchResultEdges = (ids) => Effect27.tryPromise({
4542
4861
  try: () => db.select({
4543
4862
  fromMemoryId: artifactEdges.fromMemoryId,
4544
4863
  toMemoryId: artifactEdges.toMemoryId,
@@ -4547,11 +4866,11 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4547
4866
  }).from(artifactEdges).where(and8(or(inArray4(artifactEdges.fromMemoryId, ids), inArray4(artifactEdges.toMemoryId, ids)), ne4(artifactEdges.status, "rejected"), isNull2(artifactEdges.expiredAt))),
4548
4867
  catch: (cause) => new StoreError({ message: `Store.retrieval post-pass: edges: ${cause}` })
4549
4868
  });
4550
- const fetchResultConcepts = (ids) => Effect26.tryPromise({
4869
+ const fetchResultConcepts = (ids) => Effect27.tryPromise({
4551
4870
  try: () => db.select({ memoryId: memoryConcepts2.memoryId, conceptId: memoryConcepts2.conceptId }).from(memoryConcepts2).where(inArray4(memoryConcepts2.memoryId, ids)),
4552
4871
  catch: (cause) => new StoreError({ message: `Store.retrieval post-pass: concepts: ${cause}` })
4553
4872
  });
4554
- const augmentRetrieval = (results) => Effect26.gen(function* () {
4873
+ const augmentRetrieval = (results) => Effect27.gen(function* () {
4555
4874
  if (results.length === 0)
4556
4875
  return results;
4557
4876
  const ids = results.map((r) => r.memory.id);
@@ -4561,6 +4880,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4561
4880
  supersedeDemotion: GRAPH_SUPERSEDE_DEMOTION
4562
4881
  });
4563
4882
  });
4883
+ const augmentSearchResults = Effect27.fn("Store.augmentSearchResults")(function* (results) {
4884
+ return yield* augmentRetrieval(results);
4885
+ });
4564
4886
  const EDGE_KINDS = [
4565
4887
  "memory_supports",
4566
4888
  "memory_supersedes",
@@ -4568,7 +4890,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4568
4890
  "code_calls"
4569
4891
  ];
4570
4892
  const EDGE_SOURCES = ["rule", "llm", "agent"];
4571
- const write = Effect26.fn("Store.write")(function* (input) {
4893
+ const write = Effect27.fn("Store.write")(function* (input) {
4572
4894
  if (input.content.includes("\x00")) {
4573
4895
  return yield* new StoreError({
4574
4896
  message: "Store.write: content contains a NUL byte (libSQL would silently truncate it)"
@@ -4577,7 +4899,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4577
4899
  const id = crypto.randomUUID();
4578
4900
  const now = Date.now();
4579
4901
  const metadataBlob = input.typedMetadata !== undefined ? mergeMetadataBlob(input.metadata, input.typedMetadata) : input.metadata ?? null;
4580
- yield* Effect26.tryPromise({
4902
+ yield* Effect27.tryPromise({
4581
4903
  try: () => db.transaction(async (tx) => {
4582
4904
  await tx.insert(memories2).values({
4583
4905
  id,
@@ -4610,8 +4932,60 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4610
4932
  embeddingModel: undefined
4611
4933
  };
4612
4934
  });
4613
- const get = Effect26.fn("Store.get")(function* (id) {
4614
- const rows = yield* Effect26.tryPromise({
4935
+ const writeMany = Effect27.fn("Store.writeMany")(function* (inputs) {
4936
+ if (inputs.length === 0)
4937
+ return [];
4938
+ for (const input of inputs) {
4939
+ if (input.content.includes("\x00")) {
4940
+ return yield* new StoreError({
4941
+ message: "Store.writeMany: content contains a NUL byte (libSQL would silently truncate it)"
4942
+ });
4943
+ }
4944
+ }
4945
+ const now = Date.now();
4946
+ const rows = inputs.map((input) => {
4947
+ const metadataBlob = input.typedMetadata !== undefined ? mergeMetadataBlob(input.metadata, input.typedMetadata) : input.metadata ?? null;
4948
+ return {
4949
+ id: crypto.randomUUID(),
4950
+ logicalKey: input.logicalKey,
4951
+ content: input.content,
4952
+ contentType: input.contentType ?? "text/plain",
4953
+ createdAt: now,
4954
+ ttlSecs: input.ttlSecs === null ? null : input.ttlSecs ?? defaultTtlSecs,
4955
+ namespace: input.namespace ?? "default",
4956
+ metadata: metadataBlob
4957
+ };
4958
+ });
4959
+ yield* Effect27.tryPromise({
4960
+ try: () => db.transaction(async (tx) => {
4961
+ await tx.insert(memories2).values(rows);
4962
+ await tx.insert(memoryEvents2).values(rows.map((row) => ({
4963
+ memoryId: row.id,
4964
+ eventType: "created",
4965
+ createdAt: now
4966
+ })));
4967
+ }),
4968
+ catch: (cause) => backend.isUniqueViolation(cause) ? new DuplicateActiveKey({
4969
+ logicalKey: inputs[0].logicalKey,
4970
+ namespace: inputs[0].namespace ?? "default"
4971
+ }) : new StoreError({ message: `Store.writeMany: ${cause}` })
4972
+ });
4973
+ return rows.map((row) => ({
4974
+ id: row.id,
4975
+ logicalKey: row.logicalKey,
4976
+ content: row.content,
4977
+ contentType: row.contentType,
4978
+ state: "active",
4979
+ createdAt: row.createdAt,
4980
+ ttlSecs: row.ttlSecs,
4981
+ namespace: row.namespace,
4982
+ metadata: row.metadata ?? undefined,
4983
+ embedding: undefined,
4984
+ embeddingModel: undefined
4985
+ }));
4986
+ });
4987
+ const get = Effect27.fn("Store.get")(function* (id) {
4988
+ const rows = yield* Effect27.tryPromise({
4615
4989
  try: () => db.select(memoryFields).from(memories2).where(eq8(memories2.id, id)).limit(1),
4616
4990
  catch: (cause) => new StoreError({ message: `Store.get: ${cause}` })
4617
4991
  });
@@ -4620,8 +4994,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4620
4994
  return yield* new NotFound({ id });
4621
4995
  return fromMemoryRow(row);
4622
4996
  });
4623
- const getByKey = Effect26.fn("Store.getByKey")(function* (key, namespace) {
4624
- const rows = yield* Effect26.tryPromise({
4997
+ const getByKey = Effect27.fn("Store.getByKey")(function* (key, namespace) {
4998
+ const rows = yield* Effect27.tryPromise({
4625
4999
  try: () => db.select(memoryFields).from(memories2).where(and8(eq8(memories2.logicalKey, key), eq8(memories2.namespace, namespace), eq8(memories2.state, "active"))).orderBy(desc2(memories2.createdAt)).limit(1),
4626
5000
  catch: (cause) => new StoreError({ message: `Store.getByKey: ${cause}` })
4627
5001
  });
@@ -4630,9 +5004,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4630
5004
  return yield* new NotFound({ id: key });
4631
5005
  return fromMemoryRow(row);
4632
5006
  });
4633
- const recency = Effect26.fn("Store.recency")(function* (namespace, limit) {
5007
+ const recency = Effect27.fn("Store.recency")(function* (namespace, limit) {
4634
5008
  const start = Date.now();
4635
- const rows = yield* Effect26.tryPromise({
5009
+ const rows = yield* Effect27.tryPromise({
4636
5010
  try: () => db.select(memoryFields).from(memories2).where(and8(eq8(memories2.namespace, namespace), eq8(memories2.state, "active"))).orderBy(desc2(memories2.createdAt)).limit(limit),
4637
5011
  catch: (cause) => new StoreError({ message: `Store.recency: ${cause}` })
4638
5012
  });
@@ -4645,9 +5019,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4645
5019
  yield* logRetrieval(namespace, namespace, "recency", results.length, start);
4646
5020
  return yield* augmentRetrieval(results);
4647
5021
  });
4648
- const searchFts5 = Effect26.fn("Store.searchFts5")(function* (query, namespace, limit, opts) {
5022
+ const searchFts5 = Effect27.fn("Store.searchFts5")(function* (query, namespace, limit, opts) {
4649
5023
  const start = Date.now();
4650
- const rows = yield* backend.ftsSearch(query, namespace, limit, opts?.topicKeys, modelConfig?.id, opts?.contentType);
5024
+ const rows = yield* backend.ftsSearch(query, namespace, limit, opts?.topicKeys, modelConfig?.id, opts?.contentType, opts?.includeEmbedding === true);
4651
5025
  if (opts?.audit !== false) {
4652
5026
  yield* logRetrieval(query, namespace, "fts", rows.length, start);
4653
5027
  }
@@ -4664,7 +5038,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4664
5038
  ttlSecs: r.ttl_secs,
4665
5039
  namespace: r.namespace,
4666
5040
  metadata: r.metadata ?? undefined,
4667
- embedding: decodeBlobVector(r.embedding),
5041
+ embedding: opts?.includeEmbedding === true ? decodeBlobVector(r.embedding) : undefined,
4668
5042
  embeddingModel: r.embedding_model ?? undefined
4669
5043
  },
4670
5044
  score: -r.rank,
@@ -4672,9 +5046,11 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4672
5046
  source: "fts5"
4673
5047
  };
4674
5048
  });
5049
+ if (opts?.augment === false)
5050
+ return results;
4675
5051
  return yield* augmentRetrieval(results);
4676
5052
  });
4677
- const searchVector = Effect26.fn("Store.searchVector")(function* (query, namespace, limit, opts) {
5053
+ const searchVector = Effect27.fn("Store.searchVector")(function* (query, namespace, limit, opts) {
4678
5054
  if (!modelConfig) {
4679
5055
  return yield* new StoreError({
4680
5056
  message: "Store.searchVector: no embedding model configured"
@@ -4684,13 +5060,13 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4684
5060
  let allowedMemoryIds;
4685
5061
  const conceptIds = opts?.conceptIds;
4686
5062
  if (conceptIds !== undefined && conceptIds.length > 0) {
4687
- const conceptRows = yield* Effect26.tryPromise({
5063
+ const conceptRows = yield* Effect27.tryPromise({
4688
5064
  try: () => db.select({ memoryId: memoryConcepts2.memoryId }).from(memoryConcepts2).where(inArray4(memoryConcepts2.conceptId, conceptIds)),
4689
5065
  catch: (cause) => new StoreError({ message: `Store.searchVector: concepts: ${cause}` })
4690
5066
  });
4691
5067
  allowedMemoryIds = new Set(conceptRows.map((r) => r.memoryId));
4692
5068
  }
4693
- const rows = yield* backend.vectorSearch(query, namespace, limit, modelConfig, opts?.topicKeys, opts?.contentType);
5069
+ const rows = yield* backend.vectorSearch(query, namespace, limit, modelConfig, opts?.topicKeys, opts?.contentType, opts?.includeEmbedding === true);
4694
5070
  const allowed = allowedMemoryIds;
4695
5071
  const scopedRows = allowed !== undefined ? rows.filter((row) => allowed.has(row.id)) : rows;
4696
5072
  if (opts?.audit !== false) {
@@ -4709,7 +5085,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4709
5085
  ttlSecs: r.ttl_secs,
4710
5086
  namespace: r.namespace,
4711
5087
  metadata: r.metadata ?? undefined,
4712
- embedding: decodeBlobVector(r.embedding),
5088
+ embedding: opts?.includeEmbedding === true ? decodeBlobVector(r.embedding) : undefined,
4713
5089
  embeddingModel: r.embedding_model ?? undefined
4714
5090
  },
4715
5091
  score: 1 - r.distance,
@@ -4717,11 +5093,13 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4717
5093
  source: "vector"
4718
5094
  };
4719
5095
  });
5096
+ if (opts?.augment === false)
5097
+ return results;
4720
5098
  return yield* augmentRetrieval(results);
4721
5099
  });
4722
- const supersede = Effect26.fn("Store.supersede")(function* (key, namespace) {
5100
+ const supersede = Effect27.fn("Store.supersede")(function* (key, namespace) {
4723
5101
  const now = Date.now();
4724
- const result = yield* Effect26.tryPromise({
5102
+ const result = yield* Effect27.tryPromise({
4725
5103
  try: () => db.transaction(async (tx) => {
4726
5104
  const active = await tx.select({ id: memories2.id }).from(memories2).where(and8(eq8(memories2.logicalKey, key), eq8(memories2.namespace, namespace), eq8(memories2.state, "active"))).limit(1);
4727
5105
  if (active.length === 0)
@@ -4733,7 +5111,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4733
5111
  if (!result)
4734
5112
  return yield* new NotFound({ id: key });
4735
5113
  });
4736
- const decrementTreeCf = (model, membership, x) => Effect26.tryPromise({
5114
+ const decrementTreeCf = (model, membership, x) => Effect27.tryPromise({
4737
5115
  try: () => db.transaction(async (tx) => {
4738
5116
  const leafRows = await tx.select({
4739
5117
  topicKey: topics.topicKey,
@@ -4763,10 +5141,10 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4763
5141
  }),
4764
5142
  catch: (cause) => new StoreError({ message: `Store: tree CF decrement: ${cause}` })
4765
5143
  });
4766
- const forget = Effect26.fn("Store.forget")(function* (id) {
5144
+ const forget = Effect27.fn("Store.forget")(function* (id) {
4767
5145
  let membership;
4768
5146
  if (topicsEnabled) {
4769
- const memberRows = yield* Effect26.tryPromise({
5147
+ const memberRows = yield* Effect27.tryPromise({
4770
5148
  try: () => db.select({ topicKey: topicMemberships.topicKey, modelKey: topicMemberships.modelKey }).from(topicMemberships).where(eq8(topicMemberships.memoryId, id)),
4771
5149
  catch: (cause) => new StoreError({ message: `Store.forget: load membership: ${cause}` })
4772
5150
  });
@@ -4781,14 +5159,14 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4781
5159
  }
4782
5160
  }
4783
5161
  }
4784
- const rs = yield* Effect26.tryPromise({
5162
+ const rs = yield* Effect27.tryPromise({
4785
5163
  try: () => db.delete(memories2).where(eq8(memories2.id, id)),
4786
5164
  catch: (cause) => new StoreError({ message: `Store.forget: ${cause}` })
4787
5165
  });
4788
5166
  if (rs.rowsAffected === 0)
4789
5167
  return yield* new NotFound({ id });
4790
5168
  if (topicsEnabled && membership && modelConfig && modelConfig.id === membership.modelKey) {
4791
- const leafRows = yield* Effect26.tryPromise({
5169
+ const leafRows = yield* Effect27.tryPromise({
4792
5170
  try: () => db.select({ memberCount: topics.memberCount }).from(topics).where(and8(eq8(topics.topicKey, membership.topicKey), eq8(topics.modelKey, membership.modelKey))),
4793
5171
  catch: (cause) => new StoreError({ message: `Store.forget: load leaf: ${cause}` })
4794
5172
  });
@@ -4796,18 +5174,18 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4796
5174
  if (liveCount < (topicsConfig.minLeafItems ?? 32)) {
4797
5175
  const enqueueMerge = topicsOptions?.enqueueMerge;
4798
5176
  if (enqueueMerge) {
4799
- yield* enqueueMerge(membership.topicKey, membership.modelKey).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.forget enqueueMerge: ${cause}` })));
5177
+ yield* enqueueMerge(membership.topicKey, membership.modelKey).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.forget enqueueMerge: ${cause}` })));
4800
5178
  } else {
4801
- yield* mergeOrBorrow(membership.topicKey, modelConfig, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.forget mergeOrBorrow: ${cause}` })));
5179
+ yield* mergeOrBorrow(membership.topicKey, modelConfig, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.forget mergeOrBorrow: ${cause}` })));
4802
5180
  }
4803
5181
  }
4804
5182
  }
4805
5183
  });
4806
- const sweep = Effect26.fn("Store.sweep")(function* (namespace) {
5184
+ const sweep = Effect27.fn("Store.sweep")(function* (namespace) {
4807
5185
  return yield* backend.sweep(namespace);
4808
5186
  });
4809
- const events = Effect26.fn("Store.events")(function* (memoryId) {
4810
- const rows = yield* Effect26.tryPromise({
5187
+ const events = Effect27.fn("Store.events")(function* (memoryId) {
5188
+ const rows = yield* Effect27.tryPromise({
4811
5189
  try: () => db.select().from(memoryEvents2).where(eq8(memoryEvents2.memoryId, memoryId)).orderBy(asc5(memoryEvents2.createdAt)),
4812
5190
  catch: (cause) => new StoreError({ message: `Store.events: ${cause}` })
4813
5191
  });
@@ -4822,7 +5200,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4822
5200
  }));
4823
5201
  });
4824
5202
  const resolveModel = (modelKey) => modelConfig?.id === modelKey ? modelConfig : undefined;
4825
- const ensureTopicTree2 = Effect26.fn("Store.ensureTopicTree")(function* (modelKey) {
5203
+ const ensureTopicTree2 = Effect27.fn("Store.ensureTopicTree")(function* (modelKey) {
4826
5204
  const model = resolveModel(modelKey);
4827
5205
  if (!model) {
4828
5206
  return yield* new ModelMismatch({
@@ -4830,33 +5208,33 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4830
5208
  actual: modelKey
4831
5209
  });
4832
5210
  }
4833
- return yield* ensureTopicTree(model, topicsConfig).pipe(Effect26.provideService(Service2, backend));
5211
+ return yield* ensureTopicTree(model, topicsConfig).pipe(Effect27.provideService(Service2, backend));
4834
5212
  });
4835
- const resolveTopics2 = Effect26.fn("Store.resolveTopics")(function* (queryVector, opts) {
5213
+ const resolveTopics2 = Effect27.fn("Store.resolveTopics")(function* (queryVector, opts) {
4836
5214
  if (!modelConfig) {
4837
5215
  return yield* new ModelMismatch({
4838
5216
  expected: "a configured embedding model",
4839
5217
  actual: "(none)"
4840
5218
  });
4841
5219
  }
4842
- return yield* resolveTopics(queryVector, modelConfig, topicsConfig, opts).pipe(Effect26.provideService(Service2, backend));
5220
+ return yield* resolveTopics(queryVector, modelConfig, topicsConfig, opts).pipe(Effect27.provideService(Service2, backend));
4843
5221
  });
4844
- const topicMembers = Effect26.fn("Store.topicMembers")(function* (topicKey) {
4845
- const rows = yield* Effect26.tryPromise({
5222
+ const topicMembers = Effect27.fn("Store.topicMembers")(function* (topicKey) {
5223
+ const rows = yield* Effect27.tryPromise({
4846
5224
  try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(eq8(topicMemberships.topicKey, topicKey)).orderBy(asc5(topicMemberships.memoryId)),
4847
5225
  catch: (cause) => new StoreError({ message: `Store.topicMembers: ${cause}` })
4848
5226
  });
4849
5227
  return rows;
4850
5228
  });
4851
- const listTopics = Effect26.fn("Store.listTopics")(function* (modelKey) {
4852
- const rows = yield* Effect26.tryPromise({
5229
+ const listTopics = Effect27.fn("Store.listTopics")(function* (modelKey) {
5230
+ const rows = yield* Effect27.tryPromise({
4853
5231
  try: () => db.select().from(topics).where(eq8(topics.modelKey, modelKey)).orderBy(asc5(topics.topicKey)),
4854
5232
  catch: (cause) => new StoreError({ message: `Store.listTopics: ${cause}` })
4855
5233
  });
4856
5234
  return rows;
4857
5235
  });
4858
- const topicStats = Effect26.fn("Store.topicStats")(function* (modelKey) {
4859
- const rows = yield* Effect26.tryPromise({
5236
+ const topicStats = Effect27.fn("Store.topicStats")(function* (modelKey) {
5237
+ const rows = yield* Effect27.tryPromise({
4860
5238
  try: () => db.select({ kind: topics.kind, depth: topics.depth }).from(topics).where(eq8(topics.modelKey, modelKey)),
4861
5239
  catch: (cause) => new StoreError({ message: `Store.topicStats: ${cause}` })
4862
5240
  });
@@ -4873,7 +5251,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4873
5251
  }
4874
5252
  return { depth, leafCount, internalCount, totalTopics: rows.length };
4875
5253
  });
4876
- const deleteTree = Effect26.fn("Store.deleteTree")(function* (modelKey) {
5254
+ const deleteTree = Effect27.fn("Store.deleteTree")(function* (modelKey) {
4877
5255
  const model = resolveModel(modelKey);
4878
5256
  if (!model) {
4879
5257
  return yield* new ModelMismatch({
@@ -4882,7 +5260,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4882
5260
  });
4883
5261
  }
4884
5262
  yield* backend.deleteTopicVectors(model);
4885
- yield* Effect26.tryPromise({
5263
+ yield* Effect27.tryPromise({
4886
5264
  try: () => db.transaction(async (tx) => {
4887
5265
  const modelTopicKeys2 = tx.select({ topicKey: topics.topicKey }).from(topics).where(eq8(topics.modelKey, modelKey));
4888
5266
  await tx.delete(topicChangeLog).where(inArray4(topicChangeLog.topicKey, modelTopicKeys2));
@@ -4894,7 +5272,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4894
5272
  catch: (cause) => new StoreError({ message: `Store.deleteTree: ${cause}` })
4895
5273
  });
4896
5274
  });
4897
- const processSplits = Effect26.fn("Store.processSplits")(function* (modelKey) {
5275
+ const processSplits = Effect27.fn("Store.processSplits")(function* (modelKey) {
4898
5276
  const model = resolveModel(modelKey);
4899
5277
  if (!model) {
4900
5278
  return yield* new ModelMismatch({
@@ -4904,19 +5282,19 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4904
5282
  }
4905
5283
  let processed = 0;
4906
5284
  for (let iteration = 0;iteration < 100; iteration++) {
4907
- const leaves = yield* Effect26.tryPromise({
5285
+ const leaves = yield* Effect27.tryPromise({
4908
5286
  try: () => db.select({ topicKey: topics.topicKey, memberCount: topics.memberCount }).from(topics).where(and8(eq8(topics.modelKey, modelKey), eq8(topics.kind, "leaf"), ne4(topics.status, "inactive"))).orderBy(asc5(topics.topicKey)),
4909
5287
  catch: (cause) => new StoreError({ message: `Store.processSplits: ${cause}` })
4910
5288
  });
4911
5289
  const candidate = leaves.find((leaf) => shouldSplit(leaf.memberCount, topicsConfig));
4912
5290
  if (!candidate)
4913
5291
  break;
4914
- yield* splitTopic(candidate.topicKey, model, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.processSplits: ${cause}` })));
5292
+ yield* splitTopic(candidate.topicKey, model, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.processSplits: ${cause}` })));
4915
5293
  processed++;
4916
5294
  }
4917
5295
  return processed;
4918
5296
  });
4919
- const mergeOrBorrow2 = Effect26.fn("Store.mergeOrBorrow")(function* (modelKey, topicKey) {
5297
+ const mergeOrBorrow2 = Effect27.fn("Store.mergeOrBorrow")(function* (modelKey, topicKey) {
4920
5298
  const model = resolveModel(modelKey);
4921
5299
  if (!model) {
4922
5300
  return yield* new ModelMismatch({
@@ -4924,9 +5302,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4924
5302
  actual: modelKey
4925
5303
  });
4926
5304
  }
4927
- return yield* mergeOrBorrow(topicKey, model, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.mergeOrBorrow: ${cause}` })));
5305
+ return yield* mergeOrBorrow(topicKey, model, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.mergeOrBorrow: ${cause}` })));
4928
5306
  });
4929
- const rebalanceSiblings2 = Effect26.fn("Store.rebalanceSiblings")(function* (modelKey, topicKey) {
5307
+ const rebalanceSiblings2 = Effect27.fn("Store.rebalanceSiblings")(function* (modelKey, topicKey) {
4930
5308
  const model = resolveModel(modelKey);
4931
5309
  if (!model) {
4932
5310
  return yield* new ModelMismatch({
@@ -4934,15 +5312,15 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4934
5312
  actual: modelKey
4935
5313
  });
4936
5314
  }
4937
- return yield* rebalanceSiblings(topicKey, model, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.rebalanceSiblings: ${cause}` })));
5315
+ return yield* rebalanceSiblings(topicKey, model, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.rebalanceSiblings: ${cause}` })));
4938
5316
  });
4939
- const setActiveModel2 = Effect26.fn("Store.setActiveModel")(function* (model) {
4940
- return yield* setActiveModel(model, topicsConfig).pipe(Effect26.provideService(Service2, backend));
5317
+ const setActiveModel2 = Effect27.fn("Store.setActiveModel")(function* (model) {
5318
+ return yield* setActiveModel(model, topicsConfig).pipe(Effect27.provideService(Service2, backend));
4941
5319
  });
4942
- const isActive2 = Effect26.fn("Store.isActive")(function* (modelKey) {
4943
- return yield* isActive(modelKey).pipe(Effect26.provideService(Service2, backend));
5320
+ const isActive2 = Effect27.fn("Store.isActive")(function* (modelKey) {
5321
+ return yield* isActive(modelKey).pipe(Effect27.provideService(Service2, backend));
4944
5322
  });
4945
- const topicMetrics2 = Effect26.fn("Store.topicMetrics")(function* (modelKey) {
5323
+ const topicMetrics2 = Effect27.fn("Store.topicMetrics")(function* (modelKey) {
4946
5324
  const model = resolveModel(modelKey);
4947
5325
  if (!model) {
4948
5326
  return yield* new ModelMismatch({
@@ -4950,9 +5328,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4950
5328
  actual: modelKey
4951
5329
  });
4952
5330
  }
4953
- return yield* topicMetrics(modelKey, topicsConfig).pipe(Effect26.provideService(Service2, backend));
5331
+ return yield* topicMetrics(modelKey, topicsConfig).pipe(Effect27.provideService(Service2, backend));
4954
5332
  });
4955
- const topicChangeLog3 = Effect26.fn("Store.topicChangeLog")(function* (modelKey, opts) {
5333
+ const topicChangeLog3 = Effect27.fn("Store.topicChangeLog")(function* (modelKey, opts) {
4956
5334
  const model = resolveModel(modelKey);
4957
5335
  if (!model) {
4958
5336
  return yield* new ModelMismatch({
@@ -4960,9 +5338,9 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4960
5338
  actual: modelKey
4961
5339
  });
4962
5340
  }
4963
- return yield* topicChangeLog2(modelKey, opts).pipe(Effect26.provideService(Service2, backend));
5341
+ return yield* topicChangeLog2(modelKey, opts).pipe(Effect27.provideService(Service2, backend));
4964
5342
  });
4965
- const topicHealth2 = Effect26.fn("Store.topicHealth")(function* (modelKey) {
5343
+ const topicHealth2 = Effect27.fn("Store.topicHealth")(function* (modelKey) {
4966
5344
  const model = resolveModel(modelKey);
4967
5345
  if (!model) {
4968
5346
  return yield* new ModelMismatch({
@@ -4970,11 +5348,11 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
4970
5348
  actual: modelKey
4971
5349
  });
4972
5350
  }
4973
- return yield* topicHealth(modelKey, topicsConfig).pipe(Effect26.provideService(Service2, backend));
5351
+ return yield* topicHealth(modelKey, topicsConfig).pipe(Effect27.provideService(Service2, backend));
4974
5352
  });
4975
- const updateEmbedding = Effect26.fn("Store.updateEmbedding")(function* (id, embedding, modelName) {
5353
+ const updateEmbedding = Effect27.fn("Store.updateEmbedding")(function* (id, embedding, modelName, options) {
4976
5354
  const now = Date.now();
4977
- const affected = yield* Effect26.tryPromise({
5355
+ const affected = yield* Effect27.tryPromise({
4978
5356
  try: () => db.transaction(async (tx) => {
4979
5357
  const rs = await tx.update(memories2).set({ embedding: [...embedding], embeddingModel: modelName }).where(eq8(memories2.id, id));
4980
5358
  if (rs.rowsAffected === 0)
@@ -5004,7 +5382,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5004
5382
  if (modelConfig && modelConfig.id === modelName) {
5005
5383
  yield* backend.ensureVectorTable(modelConfig);
5006
5384
  if (topicsEnabled) {
5007
- const memberRows = yield* Effect26.tryPromise({
5385
+ const memberRows = yield* Effect27.tryPromise({
5008
5386
  try: () => db.select({
5009
5387
  topicKey: topicMemberships.topicKey,
5010
5388
  modelKey: topicMemberships.modelKey
@@ -5019,7 +5397,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5019
5397
  yield* decrementTreeCf(modelConfig, mem, x);
5020
5398
  yield* updateTopicVectorForget(backend, modelConfig, mem.topicKey, x);
5021
5399
  }
5022
- yield* Effect26.tryPromise({
5400
+ yield* Effect27.tryPromise({
5023
5401
  try: () => db.delete(topicMemberships).where(and8(eq8(topicMemberships.memoryId, id), eq8(topicMemberships.modelKey, modelName))),
5024
5402
  catch: (cause) => new StoreError({ message: `Store.updateEmbedding: delete membership: ${cause}` })
5025
5403
  });
@@ -5027,21 +5405,76 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5027
5405
  }
5028
5406
  yield* backend.insertVector(id, embedding, modelConfig);
5029
5407
  if (topicsEnabled) {
5030
- const routed = yield* routeInsert(id, embedding, modelConfig, topicsConfig).pipe(Effect26.provideService(Service2, backend), Effect26.mapError((cause) => new StoreError({ message: `Store.updateEmbedding topic route: ${cause}` })));
5031
- yield* scanLeafCandidates(id, embedding, routed.topicKey, modelConfig);
5408
+ const routed = yield* routeInsert(id, embedding, modelConfig, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbedding topic route: ${cause}` })));
5409
+ if (options?.scanCandidates !== false && graphEnabled) {
5410
+ yield* scanLeafCandidates(id, embedding, routed.topicKey, modelConfig);
5411
+ }
5032
5412
  if (routed.overflow) {
5033
5413
  const enqueueSplit = topicsOptions?.enqueueSplit;
5034
5414
  if (enqueueSplit) {
5035
- yield* enqueueSplit(routed.topicKey, modelConfig.id).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.updateEmbedding enqueueSplit: ${cause}` })));
5415
+ yield* enqueueSplit(routed.topicKey, modelConfig.id).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbedding enqueueSplit: ${cause}` })));
5036
5416
  } else {
5037
- yield* processSplits(modelConfig.id).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.updateEmbedding processSplits: ${cause}` })));
5417
+ yield* processSplits(modelConfig.id).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbedding processSplits: ${cause}` })));
5038
5418
  }
5039
5419
  }
5040
5420
  }
5041
5421
  }
5042
5422
  });
5043
- const findStale = Effect26.fn("Store.findStale")(function* (model) {
5044
- const rows = yield* Effect26.tryPromise({
5423
+ const updateEmbeddingMany = Effect27.fn("Store.updateEmbeddingMany")(function* (entries, modelName, options) {
5424
+ if (entries.length === 0)
5425
+ return;
5426
+ const now = Date.now();
5427
+ yield* Effect27.tryPromise({
5428
+ try: () => db.transaction(async (tx) => {
5429
+ await tx.run(sql7`UPDATE ${memories2} SET embedding = CASE id ${sql7.join(entries.map((entry) => sql7`WHEN ${entry.id} THEN ${new Float32Array(entry.embedding).buffer}`), sql7` `)} END, embedding_model = ${modelName} WHERE id IN (${sql7.join(entries.map((entry) => entry.id), sql7`, `)})`);
5430
+ await tx.insert(memoryEvents2).values(entries.map((entry) => ({
5431
+ memoryId: entry.id,
5432
+ eventType: "embedding_updated",
5433
+ reason: modelName,
5434
+ createdAt: now
5435
+ })));
5436
+ }),
5437
+ catch: (cause) => {
5438
+ let chain = "";
5439
+ let c = cause;
5440
+ let i = 0;
5441
+ while (c && i < 6) {
5442
+ chain += " | " + String(c.message ?? c);
5443
+ c = c.cause;
5444
+ i++;
5445
+ }
5446
+ return new StoreError({ message: `Store.updateEmbeddingMany: ${cause}` + chain });
5447
+ }
5448
+ });
5449
+ if (modelConfig && modelConfig.id === modelName) {
5450
+ yield* backend.ensureVectorTable(modelConfig);
5451
+ yield* backend.insertVectors(entries.map((entry) => ({ memoryId: entry.id, vector: entry.embedding })), modelConfig);
5452
+ if (topicsEnabled) {
5453
+ const routed = yield* routeInsertMany(entries.map((entry) => ({ memoryId: entry.id, vector: entry.embedding })), modelConfig, topicsConfig).pipe(Effect27.provideService(Service2, backend), Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbeddingMany route: ${cause}` })));
5454
+ if (options?.scanCandidates === true && graphEnabled) {
5455
+ for (const routedEntry of routed) {
5456
+ const entry = entries.find((e) => e.id === routedEntry.memoryId);
5457
+ if (entry) {
5458
+ yield* scanLeafCandidates(routedEntry.memoryId, entry.embedding, routedEntry.topicKey, modelConfig);
5459
+ }
5460
+ }
5461
+ }
5462
+ const overflowed = [...new Set(routed.filter((r) => r.overflow).map((r) => r.topicKey))];
5463
+ if (overflowed.length > 0) {
5464
+ const enqueueSplit = topicsOptions?.enqueueSplit;
5465
+ if (enqueueSplit) {
5466
+ for (const topicKey of overflowed) {
5467
+ yield* enqueueSplit(topicKey, modelConfig.id).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbeddingMany enqueueSplit: ${cause}` })));
5468
+ }
5469
+ } else {
5470
+ yield* processSplits(modelConfig.id).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbeddingMany processSplits: ${cause}` })));
5471
+ }
5472
+ }
5473
+ }
5474
+ }
5475
+ });
5476
+ const findStale = Effect27.fn("Store.findStale")(function* (model) {
5477
+ const rows = yield* Effect27.tryPromise({
5045
5478
  try: () => db.select({ id: memories2.id }).from(memories2).where(and8(eq8(memories2.state, "active"), sql7`(embedding IS NULL OR embedding_model IS NULL OR embedding_model != ${model})`)),
5046
5479
  catch: (cause) => new StoreError({ message: `Store.findStale: ${cause}` })
5047
5480
  });
@@ -5061,14 +5494,14 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5061
5494
  });
5062
5495
  return memoryId;
5063
5496
  };
5064
- const reconcile = Effect26.fn("Store.reconcile")(function* (namespace, keyPrefix, keyRoot, walkedRoots, keptKeys) {
5497
+ const reconcile = Effect27.fn("Store.reconcile")(function* (namespace, keyPrefix, keyRoot, walkedRoots, keptKeys) {
5065
5498
  const inDomain = (key) => {
5066
5499
  const rel = key.slice(keyPrefix.length).split("#")[0];
5067
5500
  const filePath = keyRoot === "/" ? `/${rel}` : `${keyRoot}/${rel}`;
5068
5501
  return walkedRoots.some((root) => filePath === root || filePath.startsWith(`${root}/`));
5069
5502
  };
5070
5503
  const owned = (key) => keptKeys.includes(key);
5071
- const rows = yield* Effect26.tryPromise({
5504
+ const rows = yield* Effect27.tryPromise({
5072
5505
  try: () => db.select({
5073
5506
  id: memories2.id,
5074
5507
  logicalKey: memories2.logicalKey,
@@ -5079,7 +5512,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5079
5512
  const vanished = rows.filter((r) => inDomain(r.logicalKey) && !owned(r.logicalKey));
5080
5513
  if (vanished.length > 0) {
5081
5514
  const now = Date.now();
5082
- yield* Effect26.tryPromise({
5515
+ yield* Effect27.tryPromise({
5083
5516
  try: () => db.transaction(async (tx) => {
5084
5517
  for (const row of vanished) {
5085
5518
  await flipToSupersededById(tx, row.id, now, "source_removed");
@@ -5090,7 +5523,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5090
5523
  }
5091
5524
  return vanished;
5092
5525
  });
5093
- const writeSuperseding = Effect26.fn("Store.writeSuperseding")(function* (input) {
5526
+ const writeSuperseding = Effect27.fn("Store.writeSuperseding")(function* (input) {
5094
5527
  if (input.content.includes("\x00")) {
5095
5528
  return yield* new StoreError({
5096
5529
  message: "Store.writeSuperseding: content contains a NUL byte (libSQL would silently truncate it)"
@@ -5101,7 +5534,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5101
5534
  const metadataBlob = input.typedMetadata !== undefined ? mergeMetadataBlob(input.metadata, input.typedMetadata) : input.metadata ?? null;
5102
5535
  const namespace = input.namespace ?? "default";
5103
5536
  let supersededId;
5104
- yield* Effect26.tryPromise({
5537
+ yield* Effect27.tryPromise({
5105
5538
  try: () => db.transaction(async (tx) => {
5106
5539
  const existing = await tx.select({ id: memories2.id }).from(memories2).where(and8(eq8(memories2.logicalKey, input.logicalKey), eq8(memories2.namespace, namespace), eq8(memories2.state, "active"))).limit(1);
5107
5540
  const old = existing[0];
@@ -5156,7 +5589,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5156
5589
  ...supersededId !== undefined ? { supersededId } : {}
5157
5590
  };
5158
5591
  });
5159
- const addEdge = Effect26.fn("Store.addEdge")(function* (input) {
5592
+ const addEdge = Effect27.fn("Store.addEdge")(function* (input) {
5160
5593
  if (input.fromMemoryId === input.toMemoryId) {
5161
5594
  return yield* new StoreError({ message: "Store.addEdge: self-loops are not allowed" });
5162
5595
  }
@@ -5182,7 +5615,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5182
5615
  message: `Store.addEdge: confidence must be a finite number in [0,1] (got ${String(confidence)})`
5183
5616
  });
5184
5617
  }
5185
- const endpoints = yield* Effect26.tryPromise({
5618
+ const endpoints = yield* Effect27.tryPromise({
5186
5619
  try: () => db.select({ id: memories2.id }).from(memories2).where(inArray4(memories2.id, [input.fromMemoryId, input.toMemoryId])),
5187
5620
  catch: (cause) => new StoreError({ message: `Store.addEdge: endpoints: ${cause}` })
5188
5621
  });
@@ -5207,7 +5640,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5207
5640
  createdAt: now,
5208
5641
  updatedAt: now
5209
5642
  };
5210
- yield* Effect26.tryPromise({
5643
+ yield* Effect27.tryPromise({
5211
5644
  try: () => db.transaction(async (tx) => {
5212
5645
  await tx.insert(artifactEdges).values(row);
5213
5646
  if (input.confirmed && input.edgeKind === "memory_supersedes") {
@@ -5227,17 +5660,17 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5227
5660
  });
5228
5661
  return row;
5229
5662
  });
5230
- const expireEdge = Effect26.fn("Store.expireEdge")(function* (id) {
5231
- const rs = yield* Effect26.tryPromise({
5663
+ const expireEdge = Effect27.fn("Store.expireEdge")(function* (id) {
5664
+ const rs = yield* Effect27.tryPromise({
5232
5665
  try: () => db.update(artifactEdges).set({ expiredAt: Date.now(), updatedAt: Date.now() }).where(eq8(artifactEdges.id, id)),
5233
5666
  catch: (cause) => new StoreError({ message: `Store.expireEdge: ${cause}` })
5234
5667
  });
5235
5668
  if (rs.rowsAffected === 0)
5236
5669
  return yield* new EdgeNotFound({ id });
5237
5670
  });
5238
- const confirmEdge = Effect26.fn("Store.confirmEdge")(function* (id) {
5671
+ const confirmEdge = Effect27.fn("Store.confirmEdge")(function* (id) {
5239
5672
  const now = Date.now();
5240
- const found = yield* Effect26.tryPromise({
5673
+ const found = yield* Effect27.tryPromise({
5241
5674
  try: () => db.transaction(async (tx) => {
5242
5675
  const rows = await tx.select().from(artifactEdges).where(eq8(artifactEdges.id, id)).limit(1);
5243
5676
  const edge = rows[0];
@@ -5254,15 +5687,15 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5254
5687
  if (!found)
5255
5688
  return yield* new EdgeNotFound({ id });
5256
5689
  });
5257
- const rejectEdge = Effect26.fn("Store.rejectEdge")(function* (id) {
5258
- const rs = yield* Effect26.tryPromise({
5690
+ const rejectEdge = Effect27.fn("Store.rejectEdge")(function* (id) {
5691
+ const rs = yield* Effect27.tryPromise({
5259
5692
  try: () => db.update(artifactEdges).set({ status: "rejected", updatedAt: Date.now() }).where(eq8(artifactEdges.id, id)),
5260
5693
  catch: (cause) => new StoreError({ message: `Store.rejectEdge: ${cause}` })
5261
5694
  });
5262
5695
  if (rs.rowsAffected === 0)
5263
5696
  return yield* new EdgeNotFound({ id });
5264
5697
  });
5265
- const listEdges = Effect26.fn("Store.listEdges")(function* (opts) {
5698
+ const listEdges = Effect27.fn("Store.listEdges")(function* (opts) {
5266
5699
  const conditions = [];
5267
5700
  if (!opts?.includeExpired)
5268
5701
  conditions.push(isNull2(artifactEdges.expiredAt));
@@ -5273,7 +5706,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5273
5706
  conditions.push(eq8(artifactEdges.edgeKind, opts.edgeKind));
5274
5707
  if (opts?.status)
5275
5708
  conditions.push(eq8(artifactEdges.status, opts.status));
5276
- const rows = yield* Effect26.tryPromise({
5709
+ const rows = yield* Effect27.tryPromise({
5277
5710
  try: () => {
5278
5711
  const base = db.select().from(artifactEdges);
5279
5712
  return (conditions.length === 0 ? base : base.where(and8(...conditions))).orderBy(desc2(artifactEdges.createdAt));
@@ -5282,7 +5715,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5282
5715
  });
5283
5716
  return rows;
5284
5717
  });
5285
- const explain = Effect26.fn("Store.explain")(function* (node, opts) {
5718
+ const explain = Effect27.fn("Store.explain")(function* (node, opts) {
5286
5719
  if (node.kind !== "memory" && node.kind !== "topic" && node.kind !== "concept") {
5287
5720
  return yield* new StoreError({
5288
5721
  message: `Store.explain: unknown kind "${String(node.kind)}"`
@@ -5326,37 +5759,41 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5326
5759
  pendingConflicts: pending
5327
5760
  };
5328
5761
  });
5329
- const writeCandidateEdge = (candidate, now) => Effect26.tryPromise({
5762
+ const writeCandidateEdges = (candidates, now) => Effect27.tryPromise({
5330
5763
  try: () => db.transaction(async (tx) => {
5331
- const existing = await tx.select({ id: artifactEdges.id }).from(artifactEdges).where(and8(eq8(artifactEdges.fromMemoryId, candidate.fromMemoryId), eq8(artifactEdges.toMemoryId, candidate.toMemoryId), eq8(artifactEdges.edgeKind, candidate.edgeKind))).limit(1);
5332
- if (existing.length > 0)
5333
- return false;
5334
- await tx.insert(artifactEdges).values({
5335
- id: crypto.randomUUID(),
5336
- fromMemoryId: candidate.fromMemoryId,
5337
- toMemoryId: candidate.toMemoryId,
5338
- edgeKind: candidate.edgeKind,
5339
- weight: 1,
5340
- status: candidate.status,
5341
- source: candidate.source,
5342
- confidence: candidate.confidence,
5343
- validAt: candidate.status === "confirmed" ? now : null,
5344
- expiredAt: null,
5345
- createdAt: now,
5346
- updatedAt: now
5347
- });
5348
- if (candidate.status === "confirmed") {
5349
- await flipToSupersededById(tx, candidate.fromMemoryId, now);
5764
+ let inserted = 0;
5765
+ for (const candidate of candidates) {
5766
+ const existing = await tx.select({ id: artifactEdges.id }).from(artifactEdges).where(and8(eq8(artifactEdges.fromMemoryId, candidate.fromMemoryId), eq8(artifactEdges.toMemoryId, candidate.toMemoryId), eq8(artifactEdges.edgeKind, candidate.edgeKind))).limit(1);
5767
+ if (existing.length > 0)
5768
+ continue;
5769
+ await tx.insert(artifactEdges).values({
5770
+ id: crypto.randomUUID(),
5771
+ fromMemoryId: candidate.fromMemoryId,
5772
+ toMemoryId: candidate.toMemoryId,
5773
+ edgeKind: candidate.edgeKind,
5774
+ weight: 1,
5775
+ status: candidate.status,
5776
+ source: candidate.source,
5777
+ confidence: candidate.confidence,
5778
+ validAt: candidate.status === "confirmed" ? now : null,
5779
+ expiredAt: null,
5780
+ createdAt: now,
5781
+ updatedAt: now
5782
+ });
5783
+ if (candidate.status === "confirmed") {
5784
+ await flipToSupersededById(tx, candidate.fromMemoryId, now);
5785
+ }
5786
+ inserted += 1;
5350
5787
  }
5351
- return true;
5788
+ return inserted;
5352
5789
  }),
5353
- catch: (cause) => new StoreError({ message: `Store.writeCandidateEdge: ${cause}` })
5790
+ catch: (cause) => new StoreError({ message: `Store.writeCandidateEdges: ${cause}` })
5354
5791
  });
5355
- const reconcileRenameCandidates = Effect26.fn("Store.reconcileRenameCandidates")(function* (vanished, newUnitIds) {
5792
+ const reconcileRenameCandidates = Effect27.fn("Store.reconcileRenameCandidates")(function* (vanished, newUnitIds) {
5356
5793
  const withVectors = vanished.filter((v) => v.embedding !== null);
5357
5794
  if (withVectors.length === 0 || newUnitIds.length === 0)
5358
5795
  return 0;
5359
- const newRows = yield* Effect26.tryPromise({
5796
+ const newRows = yield* Effect27.tryPromise({
5360
5797
  try: () => db.select({ id: memories2.id, embedding: memories2.embedding }).from(memories2).where(and8(inArray4(memories2.id, newUnitIds))),
5361
5798
  catch: (cause) => new StoreError({ message: `Store.reconcileRenameCandidates: ${cause}` })
5362
5799
  });
@@ -5365,6 +5802,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5365
5802
  return 0;
5366
5803
  const now = Date.now();
5367
5804
  let inserted = 0;
5805
+ const batch = [];
5368
5806
  for (const v of withVectors) {
5369
5807
  const vVec = Float32Array.from(v.embedding);
5370
5808
  for (const n of newWithVectors) {
@@ -5372,7 +5810,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5372
5810
  continue;
5373
5811
  const cosine = cosineSimilarity(vVec, Float32Array.from(n.embedding));
5374
5812
  if (cosine >= GRAPH_CONFLICT_BANDS.nearDuplicate) {
5375
- const ok = yield* writeCandidateEdge({
5813
+ batch.push({
5376
5814
  fromMemoryId: v.id,
5377
5815
  toMemoryId: n.id,
5378
5816
  edgeKind: "memory_supersedes",
@@ -5380,24 +5818,23 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5380
5818
  status: "pending",
5381
5819
  confidence: cosine,
5382
5820
  reason: "rename"
5383
- }, now);
5384
- if (ok)
5385
- inserted += 1;
5821
+ });
5386
5822
  }
5387
5823
  }
5388
5824
  }
5825
+ inserted = yield* writeCandidateEdges(batch, now);
5389
5826
  return inserted;
5390
5827
  });
5391
- const scanLeafCandidates = (memoryId, vector, topicKey, model) => Effect26.gen(function* () {
5392
- const memberRows = yield* Effect26.tryPromise({
5828
+ const scanLeafCandidates = (memoryId, vector, topicKey, model) => Effect27.gen(function* () {
5829
+ const memberRows = yield* Effect27.tryPromise({
5393
5830
  try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and8(eq8(topicMemberships.topicKey, topicKey), eq8(topicMemberships.modelKey, model.id))).orderBy(asc5(topicMemberships.memoryId)),
5394
5831
  catch: (cause) => new StoreError({ message: `Store.updateEmbedding candidates: members: ${cause}` })
5395
5832
  });
5396
5833
  const memberIds = memberRows.map((r) => r.memoryId);
5397
5834
  if (memberIds.length === 0)
5398
5835
  return;
5399
- const vectorRows = yield* backend.fetchMemoryVectors(memberIds, model).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.updateEmbedding candidates: vectors: ${cause}` })));
5400
- const memoryRows = yield* Effect26.tryPromise({
5836
+ const vectorRows = yield* backend.fetchMemoryVectors(memberIds, model).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.updateEmbedding candidates: vectors: ${cause}` })));
5837
+ const memoryRows = yield* Effect27.tryPromise({
5401
5838
  try: () => db.select({
5402
5839
  id: memories2.id,
5403
5840
  logicalKey: memories2.logicalKey,
@@ -5406,7 +5843,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5406
5843
  }).from(memories2).where(and8(inArray4(memories2.id, memberIds), eq8(memories2.state, "active"))),
5407
5844
  catch: (cause) => new StoreError({ message: `Store.updateEmbedding candidates: memories: ${cause}` })
5408
5845
  });
5409
- const selfRows = yield* Effect26.tryPromise({
5846
+ const selfRows = yield* Effect27.tryPromise({
5410
5847
  try: () => db.select({
5411
5848
  logicalKey: memories2.logicalKey,
5412
5849
  createdAt: memories2.createdAt,
@@ -5432,13 +5869,11 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5432
5869
  });
5433
5870
  const candidates = leafLocalCandidates(memoryId, new Float32Array(vector), self.createdAt, self.logicalKey, self.metadata, members, GRAPH_CONFLICT_BANDS);
5434
5871
  const now = Date.now();
5435
- for (const candidate of candidates) {
5436
- yield* writeCandidateEdge(candidate, now);
5437
- }
5872
+ yield* writeCandidateEdges(candidates, now);
5438
5873
  });
5439
- const graphCandidates = Effect26.fn("Store.graphCandidates")(function* (opts) {
5874
+ const graphCandidates = Effect27.fn("Store.graphCandidates")(function* (opts) {
5440
5875
  const limit = Math.max(1, Math.min(opts?.limit ?? 50, GRAPH_CANDIDATE_LIMIT_MAX));
5441
- const rows = yield* Effect26.tryPromise({
5876
+ const rows = yield* Effect27.tryPromise({
5442
5877
  try: () => db.select({
5443
5878
  id: artifactEdges.id,
5444
5879
  fromMemoryId: artifactEdges.fromMemoryId,
@@ -5450,7 +5885,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5450
5885
  }).from(artifactEdges).where(and8(eq8(artifactEdges.status, "pending"), isNull2(artifactEdges.expiredAt))).orderBy(desc2(artifactEdges.createdAt), asc5(artifactEdges.id)).limit(limit),
5451
5886
  catch: (cause) => new StoreError({ message: `Store.graphCandidates: pending: ${cause}` })
5452
5887
  });
5453
- const isolatedRows = yield* backend.listIsolatedMemories(0).pipe(Effect26.mapError((cause) => new StoreError({ message: `Store.graphCandidates: isolated: ${cause}` })));
5888
+ const isolatedRows = yield* backend.listIsolatedMemories(0).pipe(Effect27.mapError((cause) => new StoreError({ message: `Store.graphCandidates: isolated: ${cause}` })));
5454
5889
  const isolated = isolatedRows.map((row) => ({
5455
5890
  id: String(row.id),
5456
5891
  createdAt: Number(row.createdAt)
@@ -5469,13 +5904,13 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5469
5904
  oldestIsolated: isolated.slice(0, GRAPH_ISOLATED_EXAMPLES)
5470
5905
  };
5471
5906
  });
5472
- const graphCoverage2 = Effect26.fn("Store.graphCoverage")(function* (modelKey) {
5473
- return yield* graphCoverage(modelKey).pipe(Effect26.provideService(Service2, backend));
5907
+ const graphCoverage2 = Effect27.fn("Store.graphCoverage")(function* (modelKey) {
5908
+ return yield* graphCoverage(modelKey).pipe(Effect27.provideService(Service2, backend));
5474
5909
  });
5475
- const createConcept = Effect26.fn("Store.createConcept")(function* (name, embedding) {
5910
+ const createConcept = Effect27.fn("Store.createConcept")(function* (name, embedding) {
5476
5911
  const id = crypto.randomUUID();
5477
5912
  const now = Date.now();
5478
- yield* Effect26.tryPromise({
5913
+ yield* Effect27.tryPromise({
5479
5914
  try: () => db.insert(concepts2).values({
5480
5915
  id,
5481
5916
  name,
@@ -5495,8 +5930,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5495
5930
  lastSeen: now
5496
5931
  };
5497
5932
  });
5498
- const getConcept = Effect26.fn("Store.getConcept")(function* (id) {
5499
- const rows = yield* Effect26.tryPromise({
5933
+ const getConcept = Effect27.fn("Store.getConcept")(function* (id) {
5934
+ const rows = yield* Effect27.tryPromise({
5500
5935
  try: () => db.select().from(concepts2).where(eq8(concepts2.id, id)).limit(1),
5501
5936
  catch: (cause) => new StoreError({ message: `Store.getConcept: ${cause}` })
5502
5937
  });
@@ -5512,8 +5947,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5512
5947
  lastSeen: row.lastSeen
5513
5948
  };
5514
5949
  });
5515
- const listConcepts = Effect26.fn("Store.listConcepts")(function* () {
5516
- const rows = yield* Effect26.tryPromise({
5950
+ const listConcepts = Effect27.fn("Store.listConcepts")(function* () {
5951
+ const rows = yield* Effect27.tryPromise({
5517
5952
  try: () => db.select().from(concepts2),
5518
5953
  catch: (cause) => new StoreError({ message: `Store.listConcepts: ${cause}` })
5519
5954
  });
@@ -5526,8 +5961,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5526
5961
  lastSeen: row.lastSeen
5527
5962
  }));
5528
5963
  });
5529
- const annotateMemory = Effect26.fn("Store.annotateMemory")(function* (memoryId, conceptId, confidence) {
5530
- yield* Effect26.tryPromise({
5964
+ const annotateMemory = Effect27.fn("Store.annotateMemory")(function* (memoryId, conceptId, confidence) {
5965
+ yield* Effect27.tryPromise({
5531
5966
  try: () => db.insert(memoryConcepts2).values({
5532
5967
  conceptId,
5533
5968
  memoryId,
@@ -5537,14 +5972,14 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5537
5972
  catch: (cause) => new StoreError({ message: `Store.annotateMemory: ${cause}` })
5538
5973
  });
5539
5974
  });
5540
- const getMemoryAnnotations = Effect26.fn("Store.getMemoryAnnotations")(function* (memoryId) {
5541
- const rows = yield* Effect26.tryPromise({
5975
+ const getMemoryAnnotations = Effect27.fn("Store.getMemoryAnnotations")(function* (memoryId) {
5976
+ const rows = yield* Effect27.tryPromise({
5542
5977
  try: () => db.select({ conceptId: memoryConcepts2.conceptId }).from(memoryConcepts2).where(eq8(memoryConcepts2.memoryId, memoryId)),
5543
5978
  catch: (cause) => new StoreError({ message: `Store.getMemoryAnnotations: ${cause}` })
5544
5979
  });
5545
5980
  return rows.map((r) => r.conceptId);
5546
5981
  });
5547
- const queryByMetadata = Effect26.fn("Store.queryByMetadata")(function* (filter) {
5982
+ const queryByMetadata = Effect27.fn("Store.queryByMetadata")(function* (filter) {
5548
5983
  const rows = yield* backend.queryByMetadata(filter);
5549
5984
  const typed = rows;
5550
5985
  return typed.map((row) => fromMemoryRow({
@@ -5561,8 +5996,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5561
5996
  embeddingModel: row.embedding_model
5562
5997
  }));
5563
5998
  });
5564
- const getMemoryMetadata = Effect26.fn("Store.getMemoryMetadata")(function* (id) {
5565
- const rows = yield* Effect26.tryPromise({
5999
+ const getMemoryMetadata = Effect27.fn("Store.getMemoryMetadata")(function* (id) {
6000
+ const rows = yield* Effect27.tryPromise({
5566
6001
  try: () => db.select({ metadata: memories2.metadata }).from(memories2).where(eq8(memories2.id, id)).limit(1),
5567
6002
  catch: (cause) => new StoreError({ message: `Store.getMemoryMetadata: ${cause}` })
5568
6003
  });
@@ -5571,8 +6006,8 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5571
6006
  return yield* new NotFound({ id });
5572
6007
  return parseMetadata(row.metadata);
5573
6008
  });
5574
- const setMemoryMetadata = Effect26.fn("Store.setMemoryMetadata")(function* (id, metadata) {
5575
- const updated = yield* Effect26.tryPromise({
6009
+ const setMemoryMetadata = Effect27.fn("Store.setMemoryMetadata")(function* (id, metadata) {
6010
+ const updated = yield* Effect27.tryPromise({
5576
6011
  try: () => db.transaction(async (tx) => {
5577
6012
  const rows = await tx.select({ metadata: memories2.metadata }).from(memories2).where(eq8(memories2.id, id)).limit(1);
5578
6013
  const row = rows[0];
@@ -5588,6 +6023,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5588
6023
  });
5589
6024
  return {
5590
6025
  write,
6026
+ writeMany,
5591
6027
  writeSuperseding,
5592
6028
  reconcile,
5593
6029
  reconcileRenameCandidates,
@@ -5596,6 +6032,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5596
6032
  recency,
5597
6033
  searchFts5,
5598
6034
  searchVector,
6035
+ augmentSearchResults,
5599
6036
  logAudit,
5600
6037
  listSchema: () => backend.listSchema(),
5601
6038
  supersede,
@@ -5603,6 +6040,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5603
6040
  sweep,
5604
6041
  events,
5605
6042
  updateEmbedding,
6043
+ updateEmbeddingMany,
5606
6044
  findStale,
5607
6045
  createConcept,
5608
6046
  getConcept,
@@ -5637,7 +6075,7 @@ var makeStore2 = (backend, modelConfig, topicsOptions, graphConfig, auditOptions
5637
6075
  };
5638
6076
  };
5639
6077
  var layer8 = (url, options) => {
5640
- const base = Layer10.effect(Service5, Effect26.gen(function* () {
6078
+ const base = Layer10.effect(Service5, Effect27.gen(function* () {
5641
6079
  const backend = yield* Service2;
5642
6080
  return Service5.of(makeStore2(backend, options?.embeddingModelConfig, options?.topics, options?.graphConfig, options?.enqueueAudit !== undefined ? { enqueueAudit: options.enqueueAudit } : undefined, options?.defaultTtlSecs));
5643
6081
  })).pipe(Layer10.provideMerge(layer3(url, options)));
@@ -5650,7 +6088,7 @@ var DISABLED_LOGGING = {
5650
6088
  format: "jsonl",
5651
6089
  ringSize: 4096
5652
6090
  };
5653
- var loggingFromConfig = Layer10.unwrap(Effect26.map(Effect26.service(Service4), (cfg) => layer(cfg.config.logging ?? DISABLED_LOGGING)));
6091
+ var loggingFromConfig = Layer10.unwrap(Effect27.map(Effect27.service(Service4), (cfg) => layer(cfg.config.logging ?? DISABLED_LOGGING)));
5654
6092
  // ../store/src/topics/index.ts
5655
6093
  var exports_topics = {};
5656
6094
  __export(exports_topics, {
@@ -5671,6 +6109,7 @@ __export(exports_topics, {
5671
6109
  shouldSplit: () => shouldSplit,
5672
6110
  setActiveModel: () => setActiveModel,
5673
6111
  scoreChildren: () => scoreChildren,
6112
+ routeInsertMany: () => routeInsertMany,
5674
6113
  routeInsert: () => routeInsert,
5675
6114
  resolveTopics: () => resolveTopics,
5676
6115
  rebalanceSiblings: () => rebalanceSiblings,
@@ -5708,15 +6147,15 @@ __export(exports_topics, {
5708
6147
  });
5709
6148
 
5710
6149
  // ../store/src/audit.ts
5711
- import { Effect as Effect27 } from "effect";
6150
+ import { Effect as Effect28 } from "effect";
5712
6151
  function auditWriteHandler(deps = {}) {
5713
- const run = (payload, ctx) => Effect27.gen(function* () {
6152
+ const run = (payload, ctx) => Effect28.gen(function* () {
5714
6153
  const envelope = payload;
5715
6154
  const backend = yield* Service2;
5716
- yield* backend.writeAuditRows(envelope.rows).pipe(Effect27.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `audit.write: ${error}` })));
6155
+ yield* backend.writeAuditRows(envelope.rows).pipe(Effect28.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `audit.write: ${error}` })));
5717
6156
  return;
5718
6157
  });
5719
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect27.provideService(Service2, deps.backend)) : run;
6158
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect28.provideService(Service2, deps.backend)) : run;
5720
6159
  }
5721
6160
  // ../store/src/graph/index.ts
5722
6161
  var exports_graph = {};
@@ -5751,7 +6190,7 @@ __export(exports_graph, {
5751
6190
  });
5752
6191
 
5753
6192
  // ../store/src/graph/concepts.ts
5754
- import { Clock as Clock6, Effect as Effect28 } from "effect";
6193
+ import { Clock as Clock6, Effect as Effect29 } from "effect";
5755
6194
  import { and as and9, asc as asc6, eq as eq9, isNotNull, sql as sql8 } from "drizzle-orm";
5756
6195
  var DEFAULT_CLUSTER_K = 8;
5757
6196
  var DEFAULT_CLUSTER_MIN_SIZE = 3;
@@ -5874,11 +6313,11 @@ var conceptConfidence = (vector, centroid) => {
5874
6313
  return 0;
5875
6314
  return Math.min(1, Math.max(0, dot3(vector, centroid) / Math.sqrt(norm)));
5876
6315
  };
5877
- var clusterLeaf = Effect28.fn("ConceptCluster.clusterLeaf")(function* (topicKey, model, config) {
6316
+ var clusterLeaf = Effect29.fn("ConceptCluster.clusterLeaf")(function* (topicKey, model, config) {
5878
6317
  const backend = yield* Service2;
5879
6318
  const db = backend.db;
5880
6319
  const now = yield* Clock6.currentTimeMillis;
5881
- const memberRows = yield* Effect28.tryPromise({
6320
+ const memberRows = yield* Effect29.tryPromise({
5882
6321
  try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and9(eq9(topicMemberships.topicKey, topicKey), eq9(topicMemberships.modelKey, model.id))).orderBy(asc6(topicMemberships.memoryId)),
5883
6322
  catch: (cause) => new StoreError({ message: `clusterLeaf: load members: ${cause}` })
5884
6323
  });
@@ -5897,11 +6336,11 @@ var clusterLeaf = Effect28.fn("ConceptCluster.clusterLeaf")(function* (topicKey,
5897
6336
  return { concepts: 0, annotated: 0 };
5898
6337
  return yield* persistClusters(db, members, config, now);
5899
6338
  });
5900
- var clusterCorpus = Effect28.fn("ConceptCluster.clusterCorpus")(function* (model, config) {
6339
+ var clusterCorpus = Effect29.fn("ConceptCluster.clusterCorpus")(function* (model, config) {
5901
6340
  const backend = yield* Service2;
5902
6341
  const db = backend.db;
5903
6342
  const now = yield* Clock6.currentTimeMillis;
5904
- const rows = yield* Effect28.tryPromise({
6343
+ const rows = yield* Effect29.tryPromise({
5905
6344
  try: () => db.select({ id: memories.id, embedding: memories.embedding }).from(memories).where(and9(eq9(memories.state, "active"), isNotNull(memories.embedding))).orderBy(asc6(memories.id)),
5906
6345
  catch: (cause) => new StoreError({ message: `clusterCorpus: load memories: ${cause}` })
5907
6346
  });
@@ -5918,7 +6357,7 @@ var clusterCorpus = Effect28.fn("ConceptCluster.clusterCorpus")(function* (model
5918
6357
  return { concepts: 0, annotated: 0 };
5919
6358
  return yield* persistClusters(db, members, config, now);
5920
6359
  });
5921
- var persistClusters = (db, members, config, now) => Effect28.tryPromise({
6360
+ var persistClusters = (db, members, config, now) => Effect29.tryPromise({
5922
6361
  try: () => db.transaction(async (tx) => {
5923
6362
  const { k, minClusterSize } = resolvedClusterConfig(config);
5924
6363
  const vectors = members.map((m) => m.vector);
@@ -5949,18 +6388,18 @@ var persistClusters = (db, members, config, now) => Effect28.tryPromise({
5949
6388
  lastSeen: now
5950
6389
  }
5951
6390
  });
5952
- for (const i of memberIndices) {
6391
+ await tx.insert(memoryConcepts).values(memberIndices.map((i) => {
5953
6392
  const member = members[i];
5954
- await tx.insert(memoryConcepts).values({
6393
+ return {
5955
6394
  conceptId: id,
5956
6395
  memoryId: member.id,
5957
6396
  confidence: conceptConfidence(member.vector, centroid),
5958
6397
  assignedAt: now
5959
- }).onConflictDoUpdate({
5960
- target: [memoryConcepts.conceptId, memoryConcepts.memoryId],
5961
- set: { confidence: conceptConfidence(member.vector, centroid), assignedAt: now }
5962
- });
5963
- }
6398
+ };
6399
+ })).onConflictDoUpdate({
6400
+ target: [memoryConcepts.conceptId, memoryConcepts.memoryId],
6401
+ set: { confidence: sql8`excluded.confidence`, assignedAt: now }
6402
+ });
5964
6403
  conceptCount++;
5965
6404
  annotated += memberIndices.length;
5966
6405
  }
@@ -5969,21 +6408,21 @@ var persistClusters = (db, members, config, now) => Effect28.tryPromise({
5969
6408
  catch: (cause) => new StoreError({ message: `concept cluster: persist: ${cause}` })
5970
6409
  });
5971
6410
  function conceptClusterHandler(deps) {
5972
- const run = (payload, ctx) => Effect28.gen(function* () {
6411
+ const run = (payload, ctx) => Effect29.gen(function* () {
5973
6412
  const envelope = payload;
5974
6413
  const model = deps.getModelConfig(envelope.modelKey);
5975
6414
  if (!model) {
5976
- return yield* Effect28.fail(new JobFailure({
6415
+ return yield* Effect29.fail(new JobFailure({
5977
6416
  jobId: ctx.jobId,
5978
6417
  message: `concept.cluster: unknown model "${envelope.modelKey}"`
5979
6418
  }));
5980
6419
  }
5981
6420
  if (envelope.topicKey !== undefined) {
5982
- yield* clusterLeaf(envelope.topicKey, model, deps.config).pipe(Effect28.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `concept.cluster: ${error}` })));
6421
+ yield* clusterLeaf(envelope.topicKey, model, deps.config).pipe(Effect29.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `concept.cluster: ${error}` })));
5983
6422
  } else {
5984
- yield* clusterCorpus(model, deps.config).pipe(Effect28.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `concept.cluster: ${error}` })));
6423
+ yield* clusterCorpus(model, deps.config).pipe(Effect29.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `concept.cluster: ${error}` })));
5985
6424
  }
5986
- yield* Effect28.logInfo("concepts.clustered").pipe(Effect28.annotateLogs({
6425
+ yield* Effect29.logInfo("concepts.clustered").pipe(Effect29.annotateLogs({
5987
6426
  scope: "@dimple/store",
5988
6427
  event: "concepts.clustered",
5989
6428
  modelKey: envelope.modelKey,
@@ -5991,10 +6430,10 @@ function conceptClusterHandler(deps) {
5991
6430
  }));
5992
6431
  return;
5993
6432
  });
5994
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect28.provideService(Service2, deps.backend)) : run;
6433
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect29.provideService(Service2, deps.backend)) : run;
5995
6434
  }
5996
6435
  // ../store/src/graph/dream.ts
5997
- import { Clock as Clock7, Effect as Effect29 } from "effect";
6436
+ import { Clock as Clock7, Effect as Effect30 } from "effect";
5998
6437
  import { and as and10, asc as asc7, eq as eq10, gt, inArray as inArray5, isNull as isNull3, sql as sql9 } from "drizzle-orm";
5999
6438
  var dreamStateDefaults = {
6000
6439
  minHours: 24,
@@ -6024,110 +6463,99 @@ var flipToSuperseded = async (tx, memoryId, now) => {
6024
6463
  });
6025
6464
  return true;
6026
6465
  };
6027
- var writeRelationEdge = (db, relation, now) => Effect29.tryPromise({
6028
- try: () => db.transaction(async (tx) => {
6029
- const existing = await tx.select({ id: artifactEdges.id, status: artifactEdges.status }).from(artifactEdges).where(and10(eq10(artifactEdges.fromMemoryId, relation.fromMemoryId), eq10(artifactEdges.toMemoryId, relation.toMemoryId), eq10(artifactEdges.edgeKind, relation.edgeKind))).limit(1);
6030
- const confirmed = relation.edgeKind === "memory_supersedes" && relation.confidence >= SUPERSEDE_CONFIRM_BAR;
6031
- if (existing.length > 0) {
6032
- const row = existing[0];
6033
- if (row.status !== "pending")
6034
- return { inserted: false, flipped: false };
6035
- if (confirmed) {
6036
- await tx.update(artifactEdges).set({
6037
- status: "confirmed",
6038
- validAt: now,
6039
- confidence: relation.confidence,
6040
- updatedAt: now
6041
- }).where(eq10(artifactEdges.id, row.id));
6042
- const flipped2 = await flipToSuperseded(tx, relation.fromMemoryId, now);
6043
- return { inserted: false, flipped: flipped2 };
6044
- }
6466
+ var writeRelationEdgeTx = async (tx, relation, now) => {
6467
+ const existing = await tx.select({ id: artifactEdges.id, status: artifactEdges.status }).from(artifactEdges).where(and10(eq10(artifactEdges.fromMemoryId, relation.fromMemoryId), eq10(artifactEdges.toMemoryId, relation.toMemoryId), eq10(artifactEdges.edgeKind, relation.edgeKind))).limit(1);
6468
+ const confirmed = relation.edgeKind === "memory_supersedes" && relation.confidence >= SUPERSEDE_CONFIRM_BAR;
6469
+ if (existing.length > 0) {
6470
+ const row = existing[0];
6471
+ if (row.status !== "pending")
6045
6472
  return { inserted: false, flipped: false };
6046
- }
6473
+ if (confirmed) {
6474
+ await tx.update(artifactEdges).set({
6475
+ status: "confirmed",
6476
+ validAt: now,
6477
+ confidence: relation.confidence,
6478
+ updatedAt: now
6479
+ }).where(eq10(artifactEdges.id, row.id));
6480
+ const flipped2 = await flipToSuperseded(tx, relation.fromMemoryId, now);
6481
+ return { inserted: false, flipped: flipped2 };
6482
+ }
6483
+ return { inserted: false, flipped: false };
6484
+ }
6485
+ await tx.insert(artifactEdges).values({
6486
+ id: crypto.randomUUID(),
6487
+ fromMemoryId: relation.fromMemoryId,
6488
+ toMemoryId: relation.toMemoryId,
6489
+ edgeKind: relation.edgeKind,
6490
+ weight: 1,
6491
+ status: confirmed ? "confirmed" : "pending",
6492
+ source: "llm",
6493
+ confidence: relation.confidence,
6494
+ validAt: confirmed ? now : null,
6495
+ expiredAt: null,
6496
+ createdAt: now,
6497
+ updatedAt: now
6498
+ });
6499
+ const flipped = confirmed ? await flipToSuperseded(tx, relation.fromMemoryId, now) : false;
6500
+ return { inserted: true, flipped };
6501
+ };
6502
+ var writeDerivedMemoryTx = async (tx, content, sources, now) => {
6503
+ const logicalKey = derivedKey(content);
6504
+ const existing = await tx.select({ id: memories.id }).from(memories).where(and10(eq10(memories.logicalKey, logicalKey), eq10(memories.namespace, DERIVED_NAMESPACE), eq10(memories.state, "active"))).limit(1);
6505
+ const memoryId = existing[0]?.id ?? crypto.randomUUID();
6506
+ let inserted = false;
6507
+ if (existing.length === 0) {
6508
+ await tx.insert(memories).values({
6509
+ id: memoryId,
6510
+ logicalKey,
6511
+ content,
6512
+ contentType: "text/plain",
6513
+ state: "active",
6514
+ createdAt: now,
6515
+ ttlSecs: DERIVED_TTL_SECS,
6516
+ namespace: DERIVED_NAMESPACE,
6517
+ metadata: JSON.stringify({ sources })
6518
+ });
6519
+ await tx.insert(memoryEvents).values({
6520
+ memoryId,
6521
+ eventType: "created",
6522
+ createdAt: now
6523
+ });
6524
+ inserted = true;
6525
+ }
6526
+ for (const sourceId of sources) {
6527
+ const edge = await tx.select({ id: artifactEdges.id }).from(artifactEdges).where(and10(eq10(artifactEdges.fromMemoryId, sourceId), eq10(artifactEdges.toMemoryId, memoryId), eq10(artifactEdges.edgeKind, "memory_supports"))).limit(1);
6528
+ if (edge.length > 0)
6529
+ continue;
6047
6530
  await tx.insert(artifactEdges).values({
6048
6531
  id: crypto.randomUUID(),
6049
- fromMemoryId: relation.fromMemoryId,
6050
- toMemoryId: relation.toMemoryId,
6051
- edgeKind: relation.edgeKind,
6532
+ fromMemoryId: sourceId,
6533
+ toMemoryId: memoryId,
6534
+ edgeKind: "memory_supports",
6052
6535
  weight: 1,
6053
- status: confirmed ? "confirmed" : "pending",
6536
+ status: "pending",
6054
6537
  source: "llm",
6055
- confidence: relation.confidence,
6056
- validAt: confirmed ? now : null,
6538
+ confidence: null,
6539
+ validAt: null,
6057
6540
  expiredAt: null,
6058
6541
  createdAt: now,
6059
6542
  updatedAt: now
6060
6543
  });
6061
- const flipped = confirmed ? await flipToSuperseded(tx, relation.fromMemoryId, now) : false;
6062
- return { inserted: true, flipped };
6063
- }),
6064
- catch: (cause) => new StoreError({ message: `memory.dream: write relation: ${cause}` })
6065
- });
6066
- var writeDerivedMemory = (db, content, sources, now) => Effect29.tryPromise({
6067
- try: () => db.transaction(async (tx) => {
6068
- const logicalKey = derivedKey(content);
6069
- const existing = await tx.select({ id: memories.id }).from(memories).where(and10(eq10(memories.logicalKey, logicalKey), eq10(memories.namespace, DERIVED_NAMESPACE), eq10(memories.state, "active"))).limit(1);
6070
- const memoryId = existing[0]?.id ?? crypto.randomUUID();
6071
- let inserted = false;
6072
- if (existing.length === 0) {
6073
- await tx.insert(memories).values({
6074
- id: memoryId,
6075
- logicalKey,
6076
- content,
6077
- contentType: "text/plain",
6078
- state: "active",
6079
- createdAt: now,
6080
- ttlSecs: DERIVED_TTL_SECS,
6081
- namespace: DERIVED_NAMESPACE,
6082
- metadata: JSON.stringify({ sources })
6083
- });
6084
- await tx.insert(memoryEvents).values({
6085
- memoryId,
6086
- eventType: "created",
6087
- createdAt: now
6088
- });
6089
- inserted = true;
6090
- }
6091
- for (const sourceId of sources) {
6092
- const edge = await tx.select({ id: artifactEdges.id }).from(artifactEdges).where(and10(eq10(artifactEdges.fromMemoryId, sourceId), eq10(artifactEdges.toMemoryId, memoryId), eq10(artifactEdges.edgeKind, "memory_supports"))).limit(1);
6093
- if (edge.length > 0)
6094
- continue;
6095
- await tx.insert(artifactEdges).values({
6096
- id: crypto.randomUUID(),
6097
- fromMemoryId: sourceId,
6098
- toMemoryId: memoryId,
6099
- edgeKind: "memory_supports",
6100
- weight: 1,
6101
- status: "pending",
6102
- source: "llm",
6103
- confidence: null,
6104
- validAt: null,
6105
- expiredAt: null,
6106
- createdAt: now,
6107
- updatedAt: now
6108
- });
6109
- }
6110
- return { inserted };
6111
- }),
6112
- catch: (cause) => new StoreError({ message: `memory.dream: write derived: ${cause}` })
6113
- });
6114
- var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6544
+ }
6545
+ return { inserted };
6546
+ };
6547
+ var runDream = Effect30.fn("MemoryDream.run")(function* (model, classify) {
6115
6548
  const backend = yield* Service2;
6116
6549
  const db = backend.db;
6117
6550
  const now = yield* Clock7.currentTimeMillis;
6118
6551
  const { minHours, minNewMemories, maxPairs, maxDerived } = dreamStateDefaults;
6119
- const stateRows = yield* Effect29.tryPromise({
6552
+ const stateRows = yield* Effect30.tryPromise({
6120
6553
  try: () => db.select().from(dreamState).where(eq10(dreamState.modelKey, model.id)),
6121
6554
  catch: (cause) => new StoreError({ message: `memory.dream: load state: ${cause}` })
6122
6555
  });
6123
6556
  const lastDreamAt = stateRows[0]?.lastDreamAt ?? 0;
6124
- const sinceCountRows = yield* Effect29.tryPromise({
6125
- try: () => db.select({ count: sql9`count(*)` }).from(memories).where(gt(memories.createdAt, lastDreamAt)),
6126
- catch: (cause) => new StoreError({ message: `memory.dream: count new: ${cause}` })
6127
- });
6128
- const memoriesSinceWatermark = Number(sinceCountRows[0]?.count ?? 0);
6129
6557
  if (now - lastDreamAt < minHours * 3600000) {
6130
- yield* Effect29.logDebug("dream.skipped").pipe(Effect29.annotateLogs({ scope: "@dimple/store", event: "dream.skipped", reason: "cooldown" }));
6558
+ yield* Effect30.logDebug("dream.skipped").pipe(Effect30.annotateLogs({ scope: "@dimple/store", event: "dream.skipped", reason: "cooldown" }));
6131
6559
  return {
6132
6560
  skipped: true,
6133
6561
  reason: "cooldown",
@@ -6136,8 +6564,13 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6136
6564
  superseded: 0
6137
6565
  };
6138
6566
  }
6567
+ const sinceCountRows = yield* Effect30.tryPromise({
6568
+ try: () => db.select({ count: sql9`count(*)` }).from(memories).where(gt(memories.createdAt, lastDreamAt)),
6569
+ catch: (cause) => new StoreError({ message: `memory.dream: count new: ${cause}` })
6570
+ });
6571
+ const memoriesSinceWatermark = Number(sinceCountRows[0]?.count ?? 0);
6139
6572
  if (memoriesSinceWatermark < minNewMemories) {
6140
- yield* Effect29.logDebug("dream.skipped").pipe(Effect29.annotateLogs({
6573
+ yield* Effect30.logDebug("dream.skipped").pipe(Effect30.annotateLogs({
6141
6574
  scope: "@dimple/store",
6142
6575
  event: "dream.skipped",
6143
6576
  reason: "insufficient-new-memories",
@@ -6151,7 +6584,7 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6151
6584
  superseded: 0
6152
6585
  };
6153
6586
  }
6154
- const pendingRows = yield* Effect29.tryPromise({
6587
+ const pendingRows = yield* Effect30.tryPromise({
6155
6588
  try: () => db.select({
6156
6589
  fromMemoryId: artifactEdges.fromMemoryId,
6157
6590
  toMemoryId: artifactEdges.toMemoryId
@@ -6164,7 +6597,7 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6164
6597
  };
6165
6598
  for (const row of pendingRows)
6166
6599
  addPair(row.fromMemoryId, row.toMemoryId);
6167
- const sinceRows = yield* Effect29.tryPromise({
6600
+ const sinceRows = yield* Effect30.tryPromise({
6168
6601
  try: () => db.select({
6169
6602
  id: memories.id,
6170
6603
  logicalKey: memories.logicalKey,
@@ -6175,19 +6608,19 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6175
6608
  });
6176
6609
  if (sinceRows.length > 0) {
6177
6610
  const sinceIds = sinceRows.map((r) => r.id);
6178
- const membershipRows = yield* Effect29.tryPromise({
6611
+ const membershipRows = yield* Effect30.tryPromise({
6179
6612
  try: () => db.select({ memoryId: topicMemberships.memoryId, topicKey: topicMemberships.topicKey }).from(topicMemberships).where(and10(eq10(topicMemberships.modelKey, model.id), inArray5(topicMemberships.memoryId, sinceIds))),
6180
6613
  catch: (cause) => new StoreError({ message: `memory.dream: scan memberships: ${cause}` })
6181
6614
  });
6182
6615
  const leafKeys = [...new Set(membershipRows.map((m) => m.topicKey))];
6183
6616
  if (leafKeys.length > 0) {
6184
- const mateRows = yield* Effect29.tryPromise({
6617
+ const mateRows = yield* Effect30.tryPromise({
6185
6618
  try: () => db.select({ memoryId: topicMemberships.memoryId, topicKey: topicMemberships.topicKey }).from(topicMemberships).where(and10(eq10(topicMemberships.modelKey, model.id), inArray5(topicMemberships.topicKey, leafKeys))),
6186
6619
  catch: (cause) => new StoreError({ message: `memory.dream: scan mates: ${cause}` })
6187
6620
  });
6188
6621
  const memberIds = [...new Set([...sinceIds, ...mateRows.map((m) => m.memoryId)])];
6189
- const vectorRows = yield* backend.fetchMemoryVectors(memberIds, model).pipe(Effect29.mapError((cause) => new StoreError({ message: `memory.dream: scan vectors: ${cause}` })));
6190
- const memoryRows = yield* Effect29.tryPromise({
6622
+ const vectorRows = yield* backend.fetchMemoryVectors(memberIds, model).pipe(Effect30.mapError((cause) => new StoreError({ message: `memory.dream: scan vectors: ${cause}` })));
6623
+ const memoryRows = yield* Effect30.tryPromise({
6191
6624
  try: () => db.select({
6192
6625
  id: memories.id,
6193
6626
  logicalKey: memories.logicalKey,
@@ -6244,7 +6677,7 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6244
6677
  }
6245
6678
  const orderedPairs = [...pairSet.values()].sort((a, b) => cmp3(a.toMemoryId, b.toMemoryId) || cmp3(a.fromMemoryId, b.fromMemoryId)).slice(0, maxPairs);
6246
6679
  const endpointIds = [...new Set(orderedPairs.flatMap((p) => [p.fromMemoryId, p.toMemoryId]))];
6247
- const contentRows = endpointIds.length > 0 ? yield* Effect29.tryPromise({
6680
+ const contentRows = endpointIds.length > 0 ? yield* Effect30.tryPromise({
6248
6681
  try: () => db.select({ id: memories.id, content: memories.content }).from(memories).where(inArray5(memories.id, endpointIds)),
6249
6682
  catch: (cause) => new StoreError({ message: `memory.dream: pair contents: ${cause}` })
6250
6683
  }) : [];
@@ -6264,17 +6697,17 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6264
6697
  }
6265
6698
  let classification = { relations: [], derived: [] };
6266
6699
  if (dreamPairs.length > 0) {
6267
- classification = yield* classify.classify(dreamPairs, { maxDerived }).pipe(Effect29.mapError((error) => new StoreError({ message: `memory.dream: classify: ${error}` })));
6700
+ classification = yield* classify.classify(dreamPairs, { maxDerived }).pipe(Effect30.mapError((error) => new StoreError({ message: `memory.dream: classify: ${error}` })));
6268
6701
  }
6269
6702
  const endpointIdsOf = [
6270
6703
  ...new Set(classification.relations.flatMap((r) => [r.fromMemoryId, r.toMemoryId]))
6271
6704
  ];
6272
- const endpointRows = endpointIdsOf.length > 0 ? yield* Effect29.tryPromise({
6705
+ const endpointRows = endpointIdsOf.length > 0 ? yield* Effect30.tryPromise({
6273
6706
  try: () => db.select({ id: memories.id, metadata: memories.metadata }).from(memories).where(inArray5(memories.id, endpointIdsOf)),
6274
6707
  catch: (cause) => new StoreError({ message: `memory.dream: endpoints: ${cause}` })
6275
6708
  }) : [];
6276
6709
  const metadataById = new Map(endpointRows.map((r) => [r.id, r.metadata]));
6277
- let superseded = 0;
6710
+ const acceptedRelations = [];
6278
6711
  for (const relation of classification.relations) {
6279
6712
  const fromMetadata = metadataById.get(relation.fromMemoryId);
6280
6713
  const toMetadata = metadataById.get(relation.toMemoryId);
@@ -6283,13 +6716,11 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6283
6716
  if (parseMetadata(fromMetadata)?.pinned === true || parseMetadata(toMetadata)?.pinned === true) {
6284
6717
  continue;
6285
6718
  }
6286
- const result = yield* writeRelationEdge(db, relation, now);
6287
- if (result.flipped)
6288
- superseded++;
6719
+ acceptedRelations.push(relation);
6289
6720
  }
6290
- let created = 0;
6721
+ const acceptedDerived = [];
6291
6722
  for (const derived of classification.derived) {
6292
- const sourceRows = derived.sources.length > 0 ? yield* Effect29.tryPromise({
6723
+ const sourceRows = derived.sources.length > 0 ? yield* Effect30.tryPromise({
6293
6724
  try: () => db.select({ id: memories.id }).from(memories).where(inArray5(memories.id, derived.sources)),
6294
6725
  catch: (cause) => new StoreError({ message: `memory.dream: derived sources: ${cause}` })
6295
6726
  }) : [];
@@ -6297,27 +6728,36 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6297
6728
  const survivingSources = derived.sources.filter((id) => survivingIds.has(id));
6298
6729
  if (survivingSources.length === 0)
6299
6730
  continue;
6300
- const outcome = yield* writeDerivedMemory(db, derived.content, survivingSources, now);
6301
- if (outcome.inserted)
6302
- created++;
6303
- }
6304
- const countRows = yield* Effect29.tryPromise({
6305
- try: () => db.select({ count: sql9`count(*)` }).from(memories),
6306
- catch: (cause) => new StoreError({ message: `memory.dream: total count: ${cause}` })
6307
- });
6308
- const totalMemories = Number(countRows[0]?.count ?? 0);
6309
- yield* Effect29.tryPromise({
6310
- try: () => db.insert(dreamState).values({
6311
- modelKey: model.id,
6312
- lastDreamAt: now,
6313
- lastDreamMemoryCount: totalMemories
6314
- }).onConflictDoUpdate({
6315
- target: dreamState.modelKey,
6316
- set: { lastDreamAt: now, lastDreamMemoryCount: totalMemories }
6731
+ acceptedDerived.push({ content: derived.content, sources: survivingSources });
6732
+ }
6733
+ let superseded = 0;
6734
+ let created = 0;
6735
+ yield* Effect30.tryPromise({
6736
+ try: () => db.transaction(async (tx) => {
6737
+ for (const relation of acceptedRelations) {
6738
+ const result = await writeRelationEdgeTx(tx, relation, now);
6739
+ if (result.flipped)
6740
+ superseded++;
6741
+ }
6742
+ for (const derived of acceptedDerived) {
6743
+ const outcome = await writeDerivedMemoryTx(tx, derived.content, derived.sources, now);
6744
+ if (outcome.inserted)
6745
+ created++;
6746
+ }
6747
+ const countRows = await tx.select({ count: sql9`count(*)` }).from(memories);
6748
+ const totalMemories = Number(countRows[0]?.count ?? 0);
6749
+ await tx.insert(dreamState).values({
6750
+ modelKey: model.id,
6751
+ lastDreamAt: now,
6752
+ lastDreamMemoryCount: totalMemories
6753
+ }).onConflictDoUpdate({
6754
+ target: dreamState.modelKey,
6755
+ set: { lastDreamAt: now, lastDreamMemoryCount: totalMemories }
6756
+ });
6317
6757
  }),
6318
- catch: (cause) => new StoreError({ message: `memory.dream: advance watermark: ${cause}` })
6758
+ catch: (cause) => new StoreError({ message: `memory.dream: persist: ${cause}` })
6319
6759
  });
6320
- yield* Effect29.logInfo("dream.completed").pipe(Effect29.annotateLogs({
6760
+ yield* Effect30.logInfo("dream.completed").pipe(Effect30.annotateLogs({
6321
6761
  scope: "@dimple/store",
6322
6762
  event: "dream.completed",
6323
6763
  modelKey: model.id,
@@ -6328,24 +6768,24 @@ var runDream = Effect29.fn("MemoryDream.run")(function* (model, classify) {
6328
6768
  return { skipped: false, classified: dreamPairs.length, created, superseded };
6329
6769
  });
6330
6770
  function graphDreamHandler(deps) {
6331
- const run = (payload, ctx) => Effect29.gen(function* () {
6771
+ const run = (payload, ctx) => Effect30.gen(function* () {
6332
6772
  const envelope = payload;
6333
6773
  const model = deps.getModelConfig(envelope.modelKey);
6334
6774
  if (!model) {
6335
- return yield* Effect29.fail(new JobFailure({
6775
+ return yield* Effect30.fail(new JobFailure({
6336
6776
  jobId: ctx.jobId,
6337
6777
  message: `memory.dream: unknown model "${envelope.modelKey}"`
6338
6778
  }));
6339
6779
  }
6340
- return yield* runDream(model, deps.classify).pipe(Effect29.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `memory.dream: ${error}` })));
6780
+ return yield* runDream(model, deps.classify).pipe(Effect30.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `memory.dream: ${error}` })));
6341
6781
  });
6342
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect29.provideService(Service2, deps.backend)) : run;
6782
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect30.provideService(Service2, deps.backend)) : run;
6343
6783
  }
6344
6784
  // ../store/src/graph/repair.ts
6345
- import { Clock as Clock8, Effect as Effect30 } from "effect";
6785
+ import { Clock as Clock8, Effect as Effect31 } from "effect";
6346
6786
  import { and as and11, eq as eq11, isNotNull as isNotNull2, lt, or as or2, sql as sql10 } from "drizzle-orm";
6347
6787
  var DEFAULT_EXPIRED_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
6348
- var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect30.tryPromise({
6788
+ var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect31.tryPromise({
6349
6789
  try: () => db.transaction(async (tx) => {
6350
6790
  let orphanedEdges = 0;
6351
6791
  let staleAnnotations = 0;
@@ -6439,10 +6879,10 @@ var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect30.tryPr
6439
6879
  catch: (cause) => new StoreError({ message: `graph.repair: ${cause}` })
6440
6880
  });
6441
6881
  function graphRepairHandler(deps) {
6442
- const run = (payload, ctx) => Effect30.gen(function* () {
6882
+ const run = (payload, ctx) => Effect31.gen(function* () {
6443
6883
  const envelope = payload;
6444
6884
  if (!deps.getModelConfig(envelope.modelKey)) {
6445
- return yield* Effect30.fail(new JobFailure({
6885
+ return yield* Effect31.fail(new JobFailure({
6446
6886
  jobId: ctx.jobId,
6447
6887
  message: `graph.repair: unknown model "${envelope.modelKey}"`
6448
6888
  }));
@@ -6450,8 +6890,8 @@ function graphRepairHandler(deps) {
6450
6890
  const backend = yield* Service2;
6451
6891
  const db = backend.db;
6452
6892
  const now = yield* Clock8.currentTimeMillis;
6453
- const result = yield* runRepair(db, deps.includePurgeExpired ?? false, deps.expiredGraceMs ?? DEFAULT_EXPIRED_GRACE_MS, now).pipe(Effect30.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `graph.repair: ${error}` })));
6454
- yield* Effect30.logInfo("repair.completed").pipe(Effect30.annotateLogs({
6893
+ const result = yield* runRepair(db, deps.includePurgeExpired ?? false, deps.expiredGraceMs ?? DEFAULT_EXPIRED_GRACE_MS, now).pipe(Effect31.mapError((error) => new JobFailure({ jobId: ctx.jobId, message: `graph.repair: ${error}` })));
6894
+ yield* Effect31.logInfo("repair.completed").pipe(Effect31.annotateLogs({
6455
6895
  scope: "@dimple/store",
6456
6896
  event: "repair.completed",
6457
6897
  modelKey: envelope.modelKey,
@@ -6461,7 +6901,7 @@ function graphRepairHandler(deps) {
6461
6901
  }));
6462
6902
  return result;
6463
6903
  });
6464
- return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect30.provideService(Service2, deps.backend)) : run;
6904
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect31.provideService(Service2, deps.backend)) : run;
6465
6905
  }
6466
6906
  // ../store/src/models.ts
6467
6907
  import { Schema as Schema20 } from "effect";
@@ -6548,15 +6988,21 @@ class EmbeddingError extends Schema21.TaggedErrorClass()("EmbeddingProvider.Embe
6548
6988
  class Service9 extends Context9.Service()("@dimple/embedder/EmbeddingModelRegistry") {
6549
6989
  }
6550
6990
  // ../embedder/src/transformers.ts
6551
- import { Config as Config5, Effect as Effect32, Layer as Layer11 } from "effect";
6991
+ import { Config as Config5, Effect as Effect33, Layer as Layer11 } from "effect";
6552
6992
  import { existsSync as existsSync2, readFileSync } from "node:fs";
6553
6993
  import { join as join2 } from "node:path";
6554
6994
  import { pathToFileURL } from "node:url";
6555
6995
 
6556
- // ../embedder/src/auto-install.ts
6557
- import { execFile } from "node:child_process";
6996
+ // ../installer/src/index.ts
6558
6997
  import { existsSync } from "node:fs";
6559
6998
  import { join } from "node:path";
6999
+
7000
+ class InstallerError extends Error {
7001
+ constructor(message) {
7002
+ super(message);
7003
+ this.name = "InstallerError";
7004
+ }
7005
+ }
6560
7006
  var detectPackageManager = (cwd) => {
6561
7007
  let dir = cwd;
6562
7008
  while (true) {
@@ -6571,38 +7017,74 @@ var detectPackageManager = (cwd) => {
6571
7017
  return { pm: "npm", root: dir };
6572
7018
  const parent = join(dir, "..");
6573
7019
  if (parent === dir)
6574
- return { pm: "bun", root: cwd };
7020
+ return { pm: "npm", root: cwd };
6575
7021
  dir = parent;
6576
7022
  }
6577
7023
  };
6578
- var installCommand = (pm) => {
6579
- switch (pm) {
6580
- case "bun":
6581
- return ["bun", "add", "@huggingface/transformers"];
6582
- case "npm":
6583
- return [
6584
- "npm",
6585
- "install",
6586
- "@huggingface/transformers",
6587
- "--ignore-scripts",
6588
- "--no-audit",
6589
- "--no-fund"
6590
- ];
6591
- case "pnpm":
6592
- return ["pnpm", "add", "@huggingface/transformers", "--ignore-scripts"];
6593
- case "yarn":
6594
- return ["yarn", "add", "@huggingface/transformers", "--ignore-scripts"];
6595
- }
7024
+ var resolveExecutable = (pm, platform = process.platform) => platform === "win32" && pm !== "bun" ? `${pm}.cmd` : pm;
7025
+ var installArgs = (pm, packages, root) => {
7026
+ const argv = [pm === "npm" ? "install" : "add", ...packages];
7027
+ if (pm !== "bun")
7028
+ argv.push("--ignore-scripts");
7029
+ if (pm === "npm")
7030
+ argv.push("--no-audit", "--no-fund");
7031
+ if (pm === "pnpm" && existsSync(join(root, "pnpm-workspace.yaml")))
7032
+ argv.push("-w");
7033
+ return argv;
7034
+ };
7035
+ var installOnce = async (target, packages, opts) => {
7036
+ const { spawn } = await import("node:child_process");
7037
+ return new Promise((resolve, reject) => {
7038
+ const timeoutMs = opts.timeoutMs ?? 300000;
7039
+ const cmd = resolveExecutable(target.pm, opts.platform);
7040
+ const args = installArgs(target.pm, packages, target.root);
7041
+ const manual = `${cmd} ${args.join(" ")}`;
7042
+ const child = spawn(cmd, args, { cwd: target.root, stdio: "ignore" });
7043
+ let settled = false;
7044
+ const timer = setTimeout(() => {
7045
+ child.kill("SIGKILL");
7046
+ if (settled)
7047
+ return;
7048
+ settled = true;
7049
+ reject(new InstallerError(`auto-install timed out after ${timeoutMs} ms — the child was killed; run it manually: ${manual}`));
7050
+ }, timeoutMs);
7051
+ child.on("error", (cause) => {
7052
+ clearTimeout(timer);
7053
+ if (settled)
7054
+ return;
7055
+ settled = true;
7056
+ reject(new InstallerError(`auto-install failed to start (${manual}): ${cause.message} — run it manually`));
7057
+ });
7058
+ child.on("exit", (code, signal) => {
7059
+ clearTimeout(timer);
7060
+ if (settled)
7061
+ return;
7062
+ settled = true;
7063
+ if (code === 0) {
7064
+ resolve();
7065
+ return;
7066
+ }
7067
+ reject(new InstallerError(`auto-install failed (${manual} exited ${signal !== null ? `with ${signal}` : `code ${code ?? "?"}`}) — run it manually`));
7068
+ });
7069
+ });
6596
7070
  };
6597
- var installTransformers = (cwd) => new Promise((resolve, reject) => {
7071
+ var inflight = new Map;
7072
+ var installOptionalPeers = (packages, opts) => {
7073
+ const cwd = opts?.cwd ?? process.cwd();
6598
7074
  const target = detectPackageManager(cwd);
6599
- const [cmd, ...args] = installCommand(target.pm);
6600
- execFile(cmd, args, { cwd: target.root, encoding: "utf8" }, (error) => {
6601
- if (error) {
6602
- reject(new Error(`auto-install of @huggingface/transformers failed (${target.pm} add …): ${error.message} — ` + "run it manually or set `autoInstall: false` and configure a hosted embedding provider"));
6603
- return;
6604
- }
6605
- resolve();
7075
+ const key = `${target.root}\x00${packages.join(",")}`;
7076
+ const existing = inflight.get(key);
7077
+ if (existing !== undefined)
7078
+ return existing;
7079
+ const run = installOnce(target, packages, opts ?? {});
7080
+ inflight.set(key, run);
7081
+ run.then(() => inflight.delete(key), () => inflight.delete(key));
7082
+ return run;
7083
+ };
7084
+ // ../embedder/src/auto-install.ts
7085
+ var installTransformers = (cwd) => installOptionalPeers(["@huggingface/transformers"], { cwd, timeoutMs: 300000 }).catch((cause) => {
7086
+ throw new EmbeddingError({
7087
+ message: cause instanceof InstallerError ? `${cause.message} — or set \`autoInstall: false\` and configure a hosted embedding provider` : `auto-install of @huggingface/transformers failed: ${String(cause)} — run it manually ` + "or set `autoInstall: false` and configure a hosted embedding provider"
6606
7088
  });
6607
7089
  });
6608
7090
 
@@ -6653,44 +7135,59 @@ var defaults2 = {
6653
7135
  pooling: "mean",
6654
7136
  normalize: true
6655
7137
  };
7138
+ var outputVectors = (result) => {
7139
+ const { data, dims } = result;
7140
+ const stride = dims.length > 0 ? dims[dims.length - 1] : data.length;
7141
+ if (stride > 0 && data.length % stride === 0) {
7142
+ const vectors = [];
7143
+ for (let offset = 0;offset < data.length; offset += stride) {
7144
+ vectors.push(data.subarray(offset, offset + stride));
7145
+ }
7146
+ return vectors;
7147
+ }
7148
+ return result.tolist().map((v) => new Float32Array(v));
7149
+ };
6656
7150
  var make5 = (options) => {
6657
7151
  let pipe = null;
6658
- const getPipeline = Effect32.fnUntraced(function* () {
7152
+ const getPipeline = Effect33.fnUntraced(function* () {
6659
7153
  if (pipe)
6660
7154
  return pipe;
6661
- const loadModule = () => Effect32.tryPromise(() => import("@huggingface/transformers"));
6662
- let transformers = yield* loadModule().pipe(Effect32.catch(() => {
7155
+ const loadModule = () => Effect33.tryPromise(() => import("@huggingface/transformers"));
7156
+ let transformers = yield* loadModule().pipe(Effect33.catch(() => {
6663
7157
  if (options.autoInstall === false) {
6664
- return Effect32.fail(new EmbeddingError({
7158
+ return Effect33.fail(new EmbeddingError({
6665
7159
  message: "@huggingface/transformers is not installed — run `bun add @huggingface/transformers` " + "to use local embeddings (or configure a hosted embedding provider)"
6666
7160
  }));
6667
7161
  }
6668
- return Effect32.gen(function* () {
6669
- yield* Effect32.logInfo("auto-installing @huggingface/transformers (first local-embed use — scripts skipped)").pipe(Effect32.annotateLogs({ scope: "@dimple/embedder", event: "embedder.auto-install" }));
6670
- yield* Effect32.tryPromise(() => installTransformers(process.cwd())).pipe(Effect32.timeout("5 minutes"), Effect32.catch((cause) => Effect32.fail(new EmbeddingError({
6671
- message: cause._tag === "TimeoutError" ? "auto-install of @huggingface/transformers timed out (5 minutes) — run " + "`bun add @huggingface/transformers` manually" : cause.cause instanceof Error ? cause.cause.message : cause instanceof Error ? cause.message : `auto-install failed: ${String(cause)}`
6672
- }))));
6673
- return yield* Effect32.tryPromise(importFromDisk).pipe(Effect32.mapError(() => new EmbeddingError({
7162
+ return Effect33.gen(function* () {
7163
+ yield* Effect33.logInfo("auto-installing @huggingface/transformers (first local-embed use — scripts skipped)").pipe(Effect33.annotateLogs({ scope: "@dimple/embedder", event: "embedder.auto-install" }));
7164
+ yield* Effect33.tryPromise(() => installTransformers(process.cwd())).pipe(Effect33.catch((cause) => {
7165
+ const inner = cause.cause;
7166
+ return Effect33.fail(inner instanceof EmbeddingError ? inner : new EmbeddingError({
7167
+ message: `auto-install failed: ${inner instanceof Error ? inner.message : String(cause)}`
7168
+ }));
7169
+ }));
7170
+ return yield* Effect33.tryPromise(importFromDisk).pipe(Effect33.mapError(() => new EmbeddingError({
6674
7171
  message: "@huggingface/transformers still unavailable after auto-install — run `bun add " + "@huggingface/transformers` manually (or set `autoInstall: false` and configure a hosted embedding provider)"
6675
7172
  })));
6676
7173
  });
6677
7174
  }));
6678
7175
  if (options.cacheDir)
6679
7176
  transformers.env.cacheDir = options.cacheDir;
6680
- pipe = yield* Effect32.tryPromise(() => transformers.pipeline("feature-extraction", options.modelId ?? defaults2.modelId, {
7177
+ pipe = yield* Effect33.tryPromise(() => transformers.pipeline("feature-extraction", options.modelId ?? defaults2.modelId, {
6681
7178
  device: options.device ?? defaults2.device,
6682
7179
  dtype: options.dtype ?? defaults2.dtype
6683
- })).pipe(Effect32.mapError((cause) => new EmbeddingError({ message: `load model: ${cause}` })));
7180
+ })).pipe(Effect33.mapError((cause) => new EmbeddingError({ message: `load model: ${cause}` })));
6684
7181
  return pipe;
6685
7182
  });
6686
- const embed = Effect32.fn("TransformersEmbedding.embed")(function* (texts) {
7183
+ const embed = Effect33.fn("TransformersEmbedding.embed")(function* (texts) {
6687
7184
  const started = Date.now();
6688
7185
  const p = yield* getPipeline();
6689
- const result = yield* Effect32.promise(() => p(texts, {
7186
+ const result = yield* Effect33.promise(() => p(texts, {
6690
7187
  pooling: options.pooling ?? defaults2.pooling,
6691
7188
  normalize: options.normalize ?? defaults2.normalize
6692
- })).pipe(Effect32.mapError((cause) => new EmbeddingError({ message: `embed: ${cause}` })));
6693
- const vectors = result.tolist().map((v) => new Float32Array(v));
7189
+ })).pipe(Effect33.mapError((cause) => new EmbeddingError({ message: `embed: ${cause}` })));
7190
+ const vectors = outputVectors(result);
6694
7191
  for (const vector of vectors) {
6695
7192
  if (options.dimensions !== undefined && vector.length !== options.dimensions) {
6696
7193
  return yield* new EmbeddingError({
@@ -6698,7 +7195,7 @@ var make5 = (options) => {
6698
7195
  });
6699
7196
  }
6700
7197
  }
6701
- yield* Effect32.logDebug("embedder.embed").pipe(Effect32.annotateLogs({
7198
+ yield* Effect33.logDebug("embedder.embed").pipe(Effect33.annotateLogs({
6702
7199
  scope: "@dimple/embedder",
6703
7200
  event: "embedder.embed",
6704
7201
  count: texts.length,
@@ -6706,13 +7203,13 @@ var make5 = (options) => {
6706
7203
  }));
6707
7204
  return vectors;
6708
7205
  });
6709
- const embedSingle = Effect32.fn("TransformersEmbedding.embedSingle")(function* (text4) {
7206
+ const embedSingle = Effect33.fn("TransformersEmbedding.embedSingle")(function* (text4) {
6710
7207
  const p = yield* getPipeline();
6711
- const result = yield* Effect32.promise(() => p(text4, {
7208
+ const result = yield* Effect33.promise(() => p(text4, {
6712
7209
  pooling: options.pooling ?? defaults2.pooling,
6713
7210
  normalize: options.normalize ?? defaults2.normalize
6714
- })).pipe(Effect32.mapError((cause) => new EmbeddingError({ message: `embedSingle: ${cause}` })));
6715
- const vector = new Float32Array(result.tolist()[0]);
7211
+ })).pipe(Effect33.mapError((cause) => new EmbeddingError({ message: `embedSingle: ${cause}` })));
7212
+ const vector = outputVectors(result)[0] ?? new Float32Array(0);
6716
7213
  if (options.dimensions !== undefined && vector.length !== options.dimensions) {
6717
7214
  return yield* new EmbeddingError({
6718
7215
  message: `embedder dimension mismatch: model outputs ${vector.length} dims but config declares ${String(options.dimensions)}`
@@ -6733,14 +7230,14 @@ var makeTransformersModel = (options = {}) => {
6733
7230
  };
6734
7231
  };
6735
7232
  // ../embedder/src/registry.ts
6736
- import { Effect as Effect33, Layer as Layer12 } from "effect";
7233
+ import { Effect as Effect34, Layer as Layer12 } from "effect";
6737
7234
  // ../core/src/hosted-embedding.ts
6738
7235
  import { embedMany } from "ai";
6739
7236
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
6740
7237
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
6741
7238
  import { createCohere } from "@ai-sdk/cohere";
6742
7239
  import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
6743
- import { Effect as Effect34 } from "effect";
7240
+ import { Effect as Effect35 } from "effect";
6744
7241
  var makeHostedEmbeddingModel = (options) => {
6745
7242
  const provider = options.provider ?? "openai";
6746
7243
  const model = provider === "google" ? createGoogleGenerativeAI({
@@ -6771,8 +7268,8 @@ var makeHostedEmbeddingModel = (options) => {
6771
7268
  return { openaiCompatible: { dimensions: options.dimensions } };
6772
7269
  }
6773
7270
  })();
6774
- const embed = Effect34.fn("HostedEmbedding.embed")(function* (texts) {
6775
- const result = yield* Effect34.tryPromise({
7271
+ const embed = Effect35.fn("HostedEmbedding.embed")(function* (texts) {
7272
+ const result = yield* Effect35.tryPromise({
6776
7273
  try: () => embedMany({ model, values: [...texts], providerOptions }),
6777
7274
  catch: (cause) => new EmbeddingError({ message: `hosted embedMany: ${cause}` })
6778
7275
  });
@@ -6785,7 +7282,7 @@ var makeHostedEmbeddingModel = (options) => {
6785
7282
  }
6786
7283
  return result.embeddings.map((e) => new Float32Array(e));
6787
7284
  });
6788
- const embedSingle = Effect34.fn("HostedEmbedding.embedSingle")(function* (text4) {
7285
+ const embedSingle = Effect35.fn("HostedEmbedding.embedSingle")(function* (text4) {
6789
7286
  const results = yield* embed([text4]);
6790
7287
  return results[0];
6791
7288
  });
@@ -6799,7 +7296,7 @@ var makeHostedEmbeddingModel = (options) => {
6799
7296
  };
6800
7297
 
6801
7298
  // ../retriever/src/hybrid.ts
6802
- import { Context as Context10, Effect as Effect35, Layer as Layer13, Schema as Schema22 } from "effect";
7299
+ import { Context as Context10, Effect as Effect36, Layer as Layer13, Schema as Schema22 } from "effect";
6803
7300
 
6804
7301
  // ../retriever/src/rrf.ts
6805
7302
  function fuse(denseRanks, fts5Ranks, k, limit) {
@@ -6842,15 +7339,15 @@ class HybridError extends Schema22.TaggedErrorClass()("HybridRetrieval.HybridErr
6842
7339
 
6843
7340
  class Service10 extends Context10.Service()("@dimple/retriever/HybridRetrieval") {
6844
7341
  }
6845
- var wrapError = (tag) => (effect) => effect.pipe(Effect35.mapError((cause) => new HybridError({ message: `${tag}: ${cause}` })));
6846
- var layer11 = (options) => Layer13.effect(Service10, Effect35.gen(function* () {
7342
+ var wrapError = (tag) => (effect) => effect.pipe(Effect36.mapError((cause) => new HybridError({ message: `${tag}: ${cause}` })));
7343
+ var layer11 = (options) => Layer13.effect(Service10, Effect36.gen(function* () {
6847
7344
  const store = yield* Service5;
6848
7345
  const registry = yield* Service9;
6849
7346
  const defaultId = yield* registry.defaultModelId.pipe(wrapError("defaultModelId"));
6850
7347
  const defaultModel = yield* registry.getModel(defaultId).pipe(wrapError("getModel"));
6851
7348
  const defaultK = options?.defaultK ?? 60;
6852
7349
  const defaultLimit = options?.defaultLimit ?? 10;
6853
- const hybrid = Effect35.fn("HybridRetrieval.hybrid")(function* (query2, options2) {
7350
+ const hybrid = Effect36.fn("HybridRetrieval.hybrid")(function* (query2, options2) {
6854
7351
  const namespace = options2?.namespace ?? "default";
6855
7352
  const limit = options2?.limit ?? defaultLimit;
6856
7353
  const k = options2?.k ?? defaultK;
@@ -6867,12 +7364,16 @@ var layer11 = (options) => Layer13.effect(Service10, Effect35.gen(function* () {
6867
7364
  const vectorResults = yield* store.searchVector([...embedding], namespace, limit * fetchMultiplier, {
6868
7365
  ...vectorOpts,
6869
7366
  ...options2?.contentType !== undefined ? { contentType: options2.contentType } : {},
6870
- audit: false
7367
+ audit: false,
7368
+ augment: false,
7369
+ includeEmbedding: options2?.includeEmbedding === true
6871
7370
  }).pipe(wrapError("searchVector"));
6872
7371
  const fts5Results = yield* store.searchFts5(query2, namespace, limit * fetchMultiplier, {
6873
7372
  ...topicKeys !== undefined ? { topicKeys } : {},
6874
7373
  ...options2?.contentType !== undefined ? { contentType: options2.contentType } : {},
6875
- audit: false
7374
+ audit: false,
7375
+ augment: false,
7376
+ includeEmbedding: options2?.includeEmbedding === true
6876
7377
  }).pipe(wrapError("searchFts5"));
6877
7378
  const vWeight = profile.modeWeights?.vector ?? 1;
6878
7379
  const fWeight = profile.modeWeights?.fts ?? 1;
@@ -6908,25 +7409,15 @@ var layer11 = (options) => Layer13.effect(Service10, Effect35.gen(function* () {
6908
7409
  const now = Date.now();
6909
7410
  const DAY_MS = 86400000;
6910
7411
  const recencyBoost = profile.recencyBoost ?? 0;
6911
- const augmentedById = new Map;
6912
- for (const sm of vectorResults)
6913
- augmentedById.set(sm.memory.id, sm);
6914
- for (const sm of fts5Results)
6915
- if (!augmentedById.has(sm.memory.id))
6916
- augmentedById.set(sm.memory.id, sm);
6917
7412
  const boosted = fused.map((r) => {
6918
7413
  const memory = memoryMap.get(r.memoryId);
6919
7414
  const ageDays = (now - memory.createdAt) / DAY_MS;
6920
7415
  const recencyFactor = 1 / (1 + ageDays);
6921
7416
  const score = r.rrfScore * (1 + recencyBoost * recencyFactor);
6922
- const aug = augmentedById.get(r.memoryId);
6923
7417
  return {
6924
7418
  memory,
6925
7419
  score,
6926
- source: "hybrid",
6927
- ...aug?.supersededBy !== undefined ? { supersededBy: aug.supersededBy } : {},
6928
- ...aug?.conflicts !== undefined ? { conflicts: aug.conflicts } : {},
6929
- ...aug?.supportedBy !== undefined ? { supportedBy: aug.supportedBy } : {}
7420
+ source: "hybrid"
6930
7421
  };
6931
7422
  });
6932
7423
  boosted.sort((a, b) => b.score - a.score);
@@ -6959,7 +7450,7 @@ var layer11 = (options) => Layer13.effect(Service10, Effect35.gen(function* () {
6959
7450
  fts: fts5Results.map((s) => s.memory.id)
6960
7451
  })
6961
7452
  }).pipe(wrapError("logAudit"));
6962
- yield* Effect35.logInfo("hybrid.search").pipe(Effect35.annotateLogs({
7453
+ yield* Effect36.logInfo("hybrid.search").pipe(Effect36.annotateLogs({
6963
7454
  scope: "@dimple/retriever",
6964
7455
  event: "hybrid.search",
6965
7456
  mode,
@@ -6973,7 +7464,7 @@ var layer11 = (options) => Layer13.effect(Service10, Effect35.gen(function* () {
6973
7464
  }));
6974
7465
  var hybridRetrievalLayer = layer11();
6975
7466
  // ../llm/src/llm.ts
6976
- import { Cause as Cause4, Effect as Effect36, Schema as Schema23 } from "effect";
7467
+ import { Cause as Cause4, Effect as Effect37, Schema as Schema23 } from "effect";
6977
7468
  import {
6978
7469
  generateText as generateTextCall,
6979
7470
  NoSuchToolError,
@@ -7027,9 +7518,9 @@ var makeLLM = (model, defaults3 = {}) => {
7027
7518
  ...reasoningEffort !== undefined ? { providerOptions: { openaiCompatible: { reasoningEffort } } } : {}
7028
7519
  };
7029
7520
  };
7030
- const generateText = Effect36.fn("LLM.generateText")(function* (input) {
7521
+ const generateText = Effect37.fn("LLM.generateText")(function* (input) {
7031
7522
  const started = Date.now();
7032
- const text4 = yield* Effect36.tryPromise({
7523
+ const text4 = yield* Effect37.tryPromise({
7033
7524
  try: () => generateTextCall({
7034
7525
  model,
7035
7526
  prompt: input.prompt,
@@ -7038,16 +7529,16 @@ var makeLLM = (model, defaults3 = {}) => {
7038
7529
  }).then((result) => result.text),
7039
7530
  catch: (cause) => new LLMError({ message: "LLM text generation failed", cause: Cause4.die(cause) })
7040
7531
  });
7041
- yield* Effect36.logDebug("llm.generate-text").pipe(Effect36.annotateLogs({
7532
+ yield* Effect37.logDebug("llm.generate-text").pipe(Effect37.annotateLogs({
7042
7533
  scope: "@dimple/llm",
7043
7534
  event: "llm.generate-text",
7044
7535
  latencyMs: Date.now() - started
7045
7536
  }));
7046
7537
  return text4;
7047
7538
  });
7048
- const generateObject = Effect36.fn("LLM.generateObject")(function* (input) {
7539
+ const generateObject = Effect37.fn("LLM.generateObject")(function* (input) {
7049
7540
  const started = Date.now();
7050
- const result = yield* Effect36.tryPromise({
7541
+ const result = yield* Effect37.tryPromise({
7051
7542
  try: async () => {
7052
7543
  try {
7053
7544
  const result3 = await generateTextCall({
@@ -7090,7 +7581,7 @@ ${schemaJson}`,
7090
7581
  },
7091
7582
  catch: (cause) => new LLMError({ message: "LLM structured generation failed", cause: Cause4.die(cause) })
7092
7583
  });
7093
- yield* Effect36.logDebug("llm.generate-object").pipe(Effect36.annotateLogs({
7584
+ yield* Effect37.logDebug("llm.generate-object").pipe(Effect37.annotateLogs({
7094
7585
  scope: "@dimple/llm",
7095
7586
  event: "llm.generate-object",
7096
7587
  latencyMs: Date.now() - started
@@ -7110,7 +7601,7 @@ var openAICompatible = (options) => {
7110
7601
  return provider.languageModel(options.modelId ?? "default");
7111
7602
  };
7112
7603
  // ../llm/src/dream-classifier.ts
7113
- import { Effect as Effect37 } from "effect";
7604
+ import { Effect as Effect38 } from "effect";
7114
7605
  import { z as z2 } from "zod";
7115
7606
  var EDGE_KINDS = ["memory_supersedes", "memory_contradicts", "memory_supports"];
7116
7607
  var EDGE_KIND_SET = new Set(EDGE_KINDS);
@@ -7157,7 +7648,7 @@ var buildPrompt = (pairs, maxDerived) => [
7157
7648
  ].join(`
7158
7649
  `);
7159
7650
  var makeDreamClassifier = (deps) => {
7160
- const classify = Effect37.fn("DreamClassifier.classify")(function* (pairs, opts) {
7651
+ const classify = Effect38.fn("DreamClassifier.classify")(function* (pairs, opts) {
7161
7652
  const system = deps.modelId === undefined ? SYSTEM_PROMPT : `${SYSTEM_PROMPT}
7162
7653
  Judging for model: ${deps.modelId}`;
7163
7654
  const judged = yield* deps.llm.generateObject({
@@ -7187,7 +7678,7 @@ Judging for model: ${deps.modelId}`;
7187
7678
  return { classify };
7188
7679
  };
7189
7680
  // ../llm/src/service.ts
7190
- import { Config as Config6, Context as Context11, Effect as Effect38, Layer as Layer14 } from "effect";
7681
+ import { Config as Config6, Context as Context11, Effect as Effect39, Layer as Layer14 } from "effect";
7191
7682
  class Service11 extends Context11.Service()("@dimple/LLM") {
7192
7683
  }
7193
7684
  // ../core/src/layer.ts
@@ -7197,7 +7688,7 @@ var DISABLED_LOGGING2 = {
7197
7688
  format: "jsonl",
7198
7689
  ringSize: 4096
7199
7690
  };
7200
- var embedderFromConfig = (model) => model.provider === "transformers" ? Effect39.succeed(makeTransformersModel({
7691
+ var embedderFromConfig = (model) => model.provider === "transformers" ? Effect40.succeed(makeTransformersModel({
7201
7692
  modelId: model.model,
7202
7693
  dimensions: model.dimensions,
7203
7694
  ...model.device !== undefined ? { device: model.device } : {},
@@ -7206,14 +7697,14 @@ var embedderFromConfig = (model) => model.provider === "transformers" ? Effect39
7206
7697
  ...model.normalize !== undefined ? { normalize: model.normalize } : {},
7207
7698
  ...model.cacheDir !== undefined ? { cacheDir: model.cacheDir } : {},
7208
7699
  ...model.autoInstall !== undefined ? { autoInstall: model.autoInstall } : {}
7209
- })) : model.provider === "openai" ? model.apiKey === undefined ? Effect39.fail(new EmbeddingError({ message: `OpenAI model ${model.id} requires an apiKey` })) : Effect39.succeed(makeHostedEmbeddingModel({
7700
+ })) : model.provider === "openai" ? model.apiKey === undefined ? Effect40.fail(new EmbeddingError({ message: `OpenAI model ${model.id} requires an apiKey` })) : Effect40.succeed(makeHostedEmbeddingModel({
7210
7701
  modelId: model.id,
7211
7702
  model: model.model,
7212
7703
  dimensions: model.dimensions,
7213
7704
  apiKey: model.apiKey,
7214
7705
  ...model.provider !== "openai" ? { provider: model.provider } : {},
7215
7706
  ...model.baseURL !== undefined ? { baseURL: model.baseURL } : {}
7216
- })) : Effect39.fail(new EmbeddingError({ message: `Unknown provider: ${model.provider}` }));
7707
+ })) : Effect40.fail(new EmbeddingError({ message: `Unknown provider: ${model.provider}` }));
7217
7708
  var layer13 = (options) => {
7218
7709
  const topicConfig = Schema24.decodeUnknownSync(TopicConfig)(options.topics?.config ?? {});
7219
7710
  const graphConfig = Schema24.decodeUnknownSync(exports_graph.GraphConfig)(options.graph ?? {});
@@ -7221,11 +7712,31 @@ var layer13 = (options) => {
7221
7712
  const defaultModel = models2.get(options.defaultModelId);
7222
7713
  const loggingConfig = options.logging ?? DISABLED_LOGGING2;
7223
7714
  const getModelConfig = (key) => models2.get(key);
7224
- const storeWithSeams = Layer15.unwrap(Effect39.gen(function* () {
7715
+ const storeWithSeams = Layer15.unwrap(Effect40.gen(function* () {
7225
7716
  const rt = yield* Service7;
7226
- const enqueueSplit = (topicKey, modelKey) => rt.enqueue(TopicSplit, { topicId: topicKey, modelKey }).pipe(Effect39.asVoid);
7227
- const enqueueMerge = (topicKey, modelKey) => rt.enqueue(TopicMerge, { topicId: topicKey, modelKey }).pipe(Effect39.asVoid);
7228
- const enqueueAudit = (row) => rt.enqueue(AuditWrite, { rows: [row] }).pipe(Effect39.asVoid);
7717
+ const enqueueSplit = (topicKey, modelKey) => rt.enqueue(TopicSplit, { topicId: topicKey, modelKey }).pipe(Effect40.asVoid);
7718
+ const enqueueMerge = (topicKey, modelKey) => rt.enqueue(TopicMerge, { topicId: topicKey, modelKey }).pipe(Effect40.asVoid);
7719
+ const auditBuffer = [];
7720
+ let auditTimer;
7721
+ const flushAudit = () => Effect40.gen(function* () {
7722
+ auditTimer = undefined;
7723
+ if (auditBuffer.length === 0)
7724
+ return;
7725
+ const rows = auditBuffer.splice(0, auditBuffer.length);
7726
+ yield* rt.enqueue(AuditWrite, { rows }).pipe(Effect40.asVoid);
7727
+ });
7728
+ const enqueueAudit = (row) => Effect40.gen(function* () {
7729
+ auditBuffer.push(row);
7730
+ if (auditTimer === undefined) {
7731
+ auditTimer = setTimeout(() => {
7732
+ Effect40.runPromise(flushAudit()).catch(() => {});
7733
+ }, 1000);
7734
+ auditTimer.unref?.();
7735
+ }
7736
+ if (auditBuffer.length >= 50) {
7737
+ yield* flushAudit();
7738
+ }
7739
+ });
7229
7740
  return layer8(options.storeUrl, {
7230
7741
  ...options.defaultTtlSecs !== undefined ? { defaultTtlSecs: options.defaultTtlSecs } : {},
7231
7742
  ...options.migrationFolder !== undefined ? { migrationFolder: options.migrationFolder } : {},
@@ -7244,15 +7755,15 @@ var layer13 = (options) => {
7244
7755
  ...options.audit === false ? {} : { enqueueAudit }
7245
7756
  });
7246
7757
  }));
7247
- const embedderRegistry = Layer15.effect(Service9, Effect39.sync(() => Service9.of({
7758
+ const embedderRegistry = Layer15.effect(Service9, Effect40.sync(() => Service9.of({
7248
7759
  getModel: (id) => {
7249
7760
  const model = models2.get(id);
7250
- return model !== undefined ? options.embedderFor(model) : Effect39.fail(new EmbeddingError({ message: `Unknown model: ${id}` }));
7761
+ return model !== undefined ? options.embedderFor(model) : Effect40.fail(new EmbeddingError({ message: `Unknown model: ${id}` }));
7251
7762
  },
7252
- defaultModelId: Effect39.succeed(options.defaultModelId),
7253
- registerModel: () => Effect39.void
7763
+ defaultModelId: Effect40.succeed(options.defaultModelId),
7764
+ registerModel: () => Effect40.void
7254
7765
  })));
7255
- const coreService = Layer15.effect(Service, Effect39.gen(function* () {
7766
+ const coreService = Layer15.effect(Service, Effect40.gen(function* () {
7256
7767
  if (defaultModel === undefined) {
7257
7768
  return yield* new CoreConfigError({
7258
7769
  message: `@dimple/core: unknown defaultModelId "${options.defaultModelId}"`
@@ -7265,20 +7776,20 @@ var layer13 = (options) => {
7265
7776
  const hybrid = yield* Service10;
7266
7777
  const logging = yield* Service3;
7267
7778
  const extraByName = new Map((options.extraHandlers ?? []).map((h) => [h.name, h.handler]));
7268
- const register = (name, handler) => extraByName.has(name) ? Effect39.void : registry.register(name, handler);
7779
+ const register = (name, handler) => extraByName.has(name) ? Effect40.void : registry.register(name, handler);
7269
7780
  yield* register("topic.split", exports_topics.topicSplitHandler({
7270
7781
  getModelConfig,
7271
7782
  config: topicConfig,
7272
7783
  backend,
7273
- onUnevenSplit: (childKey, modelKey) => rt.enqueue(TopicRebalance, { topicId: childKey, modelKey }).pipe(Effect39.asVoid),
7274
- onChildOverflow: (childKey, modelKey) => rt.enqueue(TopicSplit, { topicId: childKey, modelKey }).pipe(Effect39.asVoid)
7784
+ onUnevenSplit: (childKey, modelKey) => rt.enqueue(TopicRebalance, { topicId: childKey, modelKey }).pipe(Effect40.asVoid),
7785
+ onChildOverflow: (childKey, modelKey) => rt.enqueue(TopicSplit, { topicId: childKey, modelKey }).pipe(Effect40.asVoid)
7275
7786
  }));
7276
7787
  yield* register("topic.merge", exports_topics.topicMergeHandler({ getModelConfig, config: topicConfig, backend }));
7277
7788
  yield* register("topic.rebalance", exports_topics.topicRebalanceHandler({ getModelConfig, config: topicConfig, backend }));
7278
7789
  yield* register("memory.embed", exports_topics.memoryEmbedHandler({
7279
7790
  getModelConfig,
7280
- embedOne: (text4, model) => options.embedderFor(model).pipe(Effect39.flatMap((m) => m.embedSingle(text4))),
7281
- getMemoryContent: (memoryId) => store.get(memoryId).pipe(Effect39.map((m) => m.content)),
7791
+ embedOne: (text4, model) => options.embedderFor(model).pipe(Effect40.flatMap((m) => m.embedSingle(text4))),
7792
+ getMemoryContent: (memoryId) => store.get(memoryId).pipe(Effect40.map((m) => m.content)),
7282
7793
  config: topicConfig,
7283
7794
  backend
7284
7795
  }));
@@ -7293,12 +7804,12 @@ var layer13 = (options) => {
7293
7804
  yield* register("memory.dream", (payload, ctx) => {
7294
7805
  const envelope = payload;
7295
7806
  if (getModelConfig(envelope.modelKey) === undefined) {
7296
- return Effect39.fail(new JobFailure({
7807
+ return Effect40.fail(new JobFailure({
7297
7808
  jobId: ctx.jobId,
7298
7809
  message: `memory.dream: unknown model "${envelope.modelKey}"`
7299
7810
  }));
7300
7811
  }
7301
- return Effect39.fail(new JobFailure({
7812
+ return Effect40.fail(new JobFailure({
7302
7813
  jobId: ctx.jobId,
7303
7814
  message: "memory.dream: LLM not configured — provide `llm` to @dimple/core"
7304
7815
  }));
@@ -7333,7 +7844,7 @@ var layer13 = (options) => {
7333
7844
  ...options.defaultLimit !== undefined ? { defaultLimit: options.defaultLimit } : {}
7334
7845
  })), Layer15.provideMerge(embedderRegistry), Layer15.provideMerge(storeWithSeams), Layer15.provideMerge(Layer15.mergeAll(jobStack, layer7, layer(loggingConfig))));
7335
7846
  };
7336
- var layerConfig7 = (options) => Layer15.unwrap(Effect39.gen(function* () {
7847
+ var layerConfig7 = (options) => Layer15.unwrap(Effect40.gen(function* () {
7337
7848
  const cfg = yield* Service4;
7338
7849
  const embedding = cfg.config.embedding;
7339
7850
  if (embedding === undefined || embedding.models.length === 0) {
@@ -7367,7 +7878,7 @@ var layerConfig7 = (options) => Layer15.unwrap(Effect39.gen(function* () {
7367
7878
  });
7368
7879
  }));
7369
7880
  // ../extractor/src/code-extractor.ts
7370
- import { Context as Context12, Effect as Effect40, Layer as Layer16 } from "effect";
7881
+ import { Context as Context12, Effect as Effect41, Layer as Layer16 } from "effect";
7371
7882
 
7372
7883
  // ../extractor/src/models/extraction.ts
7373
7884
  import { Schema as Schema25 } from "effect";
@@ -7813,9 +8324,9 @@ function extract3(filePath, tree2) {
7813
8324
  // ../extractor/src/code-extractor.ts
7814
8325
  class Service12 extends Context12.Service()("@dimple/extractor/CodeExtractor") {
7815
8326
  }
7816
- var layer14 = Layer16.effect(Service12, Effect40.succeed(Service12.of({
7817
- extract: Effect40.fn("CodeExtractor.extract")(function* (request) {
7818
- const result = yield* Effect40.try({
8327
+ var layer14 = Layer16.effect(Service12, Effect41.succeed(Service12.of({
8328
+ extract: Effect41.fn("CodeExtractor.extract")(function* (request) {
8329
+ const result = yield* Effect41.try({
7819
8330
  try: () => extract3(request.filePath, request.tree),
7820
8331
  catch: (cause) => new ExtractionError({ message: `Extraction failed for ${request.filePath}`, cause })
7821
8332
  });
@@ -7848,14 +8359,14 @@ var layer15 = Layer17.succeed(Service13, Service13.of({
7848
8359
  extractText: (content) => content
7849
8360
  }));
7850
8361
  // ../parser/src/code-parser.ts
7851
- import { Context as Context19, Effect as Effect47, Layer as Layer24 } from "effect";
8362
+ import { Context as Context19, Effect as Effect48, Layer as Layer24 } from "effect";
7852
8363
  import { Parser as Parser2 } from "web-tree-sitter";
7853
8364
 
7854
8365
  // ../parser/src/language-store.ts
7855
- import { Cache, Context as Context18, Effect as Effect46, Layer as Layer23 } from "effect";
8366
+ import { Cache, Context as Context18, Effect as Effect47, Layer as Layer23 } from "effect";
7856
8367
 
7857
8368
  // ../wasm/src/wasm-registry.ts
7858
- import { Context as Context14, Effect as Effect41, Layer as Layer18, Schema as Schema30 } from "effect";
8369
+ import { Context as Context14, Effect as Effect42, Layer as Layer18, Schema as Schema30 } from "effect";
7859
8370
 
7860
8371
  // ../shared/src/errors/wasm.ts
7861
8372
  import { Schema as Schema27 } from "effect";
@@ -7957,7 +8468,7 @@ for (const grammar of grammars.grammars) {
7957
8468
 
7958
8469
  class Service14 extends Context14.Service()("@dimple/wasm/TreeSitterRegistry") {
7959
8470
  static layer = Layer18.succeed(Service14, Service14.of({
7960
- resolve: Effect41.fn("TreeSitterRegistry.resolve")(function* (lang) {
8471
+ resolve: Effect42.fn("TreeSitterRegistry.resolve")(function* (lang) {
7961
8472
  const found = registryMap.get(lang);
7962
8473
  if (found === undefined) {
7963
8474
  return yield* new RegistryError2({ message: `Language not found in registry: ${lang}` });
@@ -7967,11 +8478,11 @@ class Service14 extends Context14.Service()("@dimple/wasm/TreeSitterRegistry") {
7967
8478
  }));
7968
8479
  }
7969
8480
  // ../wasm/src/wasm-downloader.ts
7970
- import { Context as Context15, Effect as Effect42, Layer as Layer19 } from "effect";
8481
+ import { Context as Context15, Effect as Effect43, Layer as Layer19 } from "effect";
7971
8482
  class Service15 extends Context15.Service()("@dimple/wasm/WasmDownloader") {
7972
- static layer = Layer19.effect(Service15, Effect42.succeed(Service15.of({
7973
- download: Effect42.fn("WasmDownloader.download")(function* (url) {
7974
- const response = yield* Effect42.tryPromise({
8483
+ static layer = Layer19.effect(Service15, Effect43.succeed(Service15.of({
8484
+ download: Effect43.fn("WasmDownloader.download")(function* (url) {
8485
+ const response = yield* Effect43.tryPromise({
7975
8486
  try: () => fetch(url),
7976
8487
  catch: (cause) => new WasmDownloadError({
7977
8488
  message: `Failed to fetch from ${url}`,
@@ -7985,7 +8496,7 @@ class Service15 extends Context15.Service()("@dimple/wasm/WasmDownloader") {
7985
8496
  url
7986
8497
  });
7987
8498
  }
7988
- const arrayBuffer = yield* Effect42.tryPromise({
8499
+ const arrayBuffer = yield* Effect43.tryPromise({
7989
8500
  try: () => response.arrayBuffer(),
7990
8501
  catch: (cause) => new WasmDownloadError({
7991
8502
  message: `Failed to read response from ${url}`,
@@ -7998,7 +8509,7 @@ class Service15 extends Context15.Service()("@dimple/wasm/WasmDownloader") {
7998
8509
  })));
7999
8510
  }
8000
8511
  // ../wasm/src/wasm-cache.ts
8001
- import { Context as Context16, Effect as Effect43, Layer as Layer20 } from "effect";
8512
+ import { Context as Context16, Effect as Effect44, Layer as Layer20 } from "effect";
8002
8513
  import { existsSync as existsSync3, mkdirSync, renameSync, writeFileSync } from "node:fs";
8003
8514
  import { join as join3 } from "node:path";
8004
8515
 
@@ -8095,35 +8606,35 @@ var paths = envPaths("dimple");
8095
8606
 
8096
8607
  class Service16 extends Context16.Service()("@dimple/wasm/WasmCache") {
8097
8608
  }
8098
- var layer16 = Layer20.effect(Service16, Effect43.succeed(Service16.of({
8099
- ensure: Effect43.fn("WasmCache.ensure")(function* (lang, version, download) {
8609
+ var layer16 = Layer20.effect(Service16, Effect44.succeed(Service16.of({
8610
+ ensure: Effect44.fn("WasmCache.ensure")(function* (lang, version, download) {
8100
8611
  const versionDir = join3(paths.cache, "tree-sitter", lang, version);
8101
8612
  const finalPath = join3(versionDir, "parser.wasm");
8102
- const exists = yield* Effect43.try({
8613
+ const exists = yield* Effect44.try({
8103
8614
  try: () => existsSync3(finalPath),
8104
8615
  catch: (cause) => new WasmCacheError({ message: `Failed to stat ${finalPath}`, cause })
8105
8616
  });
8106
8617
  if (exists)
8107
8618
  return finalPath;
8108
- yield* Effect43.try({
8619
+ yield* Effect44.try({
8109
8620
  try: () => mkdirSync(versionDir, { recursive: true }),
8110
8621
  catch: (cause) => new WasmCacheError({ message: `Failed to create ${versionDir}`, cause })
8111
8622
  });
8112
8623
  const tempPath = join3(versionDir, `.tmp-${process.pid}`);
8113
8624
  const wasm = yield* download;
8114
- yield* Effect43.try({
8625
+ yield* Effect44.try({
8115
8626
  try: () => writeFileSync(tempPath, wasm),
8116
8627
  catch: (cause) => new WasmCacheError({ message: `Failed to write ${tempPath}`, cause })
8117
8628
  });
8118
- const renamed = yield* Effect43.try({
8629
+ const renamed = yield* Effect44.try({
8119
8630
  try: () => {
8120
8631
  renameSync(tempPath, finalPath);
8121
8632
  return true;
8122
8633
  },
8123
8634
  catch: (cause) => new WasmCacheError({ message: `Failed to rename ${tempPath}`, cause })
8124
- }).pipe(Effect43.catchCause(() => Effect43.succeed(false)));
8635
+ }).pipe(Effect44.catchCause(() => Effect44.succeed(false)));
8125
8636
  if (!renamed) {
8126
- const existsAfter = yield* Effect43.try({
8637
+ const existsAfter = yield* Effect44.try({
8127
8638
  try: () => existsSync3(finalPath),
8128
8639
  catch: (cause) => new WasmCacheError({ message: `Failed to stat ${finalPath}`, cause })
8129
8640
  });
@@ -8137,25 +8648,25 @@ var layer16 = Layer20.effect(Service16, Effect43.succeed(Service16.of({
8137
8648
  })
8138
8649
  })));
8139
8650
  // ../wasm/src/grammar-store.ts
8140
- import { Context as Context17, Effect as Effect44, Layer as Layer21 } from "effect";
8651
+ import { Context as Context17, Effect as Effect45, Layer as Layer21 } from "effect";
8141
8652
  class Service17 extends Context17.Service()("@dimple/wasm/GrammarStore") {
8142
8653
  }
8143
- var layer17 = Layer21.effect(Service17, Effect44.gen(function* () {
8654
+ var layer17 = Layer21.effect(Service17, Effect45.gen(function* () {
8144
8655
  const registry = yield* Service14;
8145
8656
  const downloader = yield* Service15;
8146
8657
  const cache = yield* Service16;
8147
- const ensure = Effect44.fn("GrammarStore.ensure")(function* (lang) {
8148
- const metadata = yield* registry.resolve(lang).pipe(Effect44.mapError((cause) => new GrammarProvisionError({
8658
+ const ensure = Effect45.fn("GrammarStore.ensure")(function* (lang) {
8659
+ const metadata = yield* registry.resolve(lang).pipe(Effect45.mapError((cause) => new GrammarProvisionError({
8149
8660
  language: lang,
8150
8661
  message: `Failed to resolve grammar metadata for: ${lang}`,
8151
8662
  cause
8152
8663
  })));
8153
- const wasmBytes = yield* downloader.download(metadata.url).pipe(Effect44.mapError((cause) => new GrammarProvisionError({
8664
+ const wasmBytes = yield* downloader.download(metadata.url).pipe(Effect45.mapError((cause) => new GrammarProvisionError({
8154
8665
  language: lang,
8155
8666
  message: `Failed to download grammar for: ${lang}`,
8156
8667
  cause
8157
8668
  })));
8158
- const path2 = yield* cache.ensure(lang, metadata.version, Effect44.succeed(wasmBytes)).pipe(Effect44.mapError((cause) => new GrammarProvisionError({
8669
+ const path2 = yield* cache.ensure(lang, metadata.version, Effect45.succeed(wasmBytes)).pipe(Effect45.mapError((cause) => new GrammarProvisionError({
8159
8670
  language: lang,
8160
8671
  message: `Failed to provision grammar for: ${lang}`,
8161
8672
  cause
@@ -8169,9 +8680,9 @@ var allLive = layer17.pipe(Layer21.provideMerge(Layer21.mergeAll(Service14.layer
8169
8680
  import { Language } from "web-tree-sitter";
8170
8681
 
8171
8682
  // ../parser/src/tree-sitter-engine.ts
8172
- import { Effect as Effect45, Layer as Layer22 } from "effect";
8683
+ import { Effect as Effect46, Layer as Layer22 } from "effect";
8173
8684
  import { Parser } from "web-tree-sitter";
8174
- var layer18 = Layer22.effectDiscard(Effect45.tryPromise({
8685
+ var layer18 = Layer22.effectDiscard(Effect46.tryPromise({
8175
8686
  try: () => Parser.init(),
8176
8687
  catch: (cause) => new ParserInitError({ message: "Failed to initialize tree sitter", cause })
8177
8688
  }));
@@ -8179,20 +8690,20 @@ var layer18 = Layer22.effectDiscard(Effect45.tryPromise({
8179
8690
  // ../parser/src/language-store.ts
8180
8691
  class Service18 extends Context18.Service()("@dimple/parser/LanguageStore") {
8181
8692
  }
8182
- var layer19 = Layer23.effect(Service18, Effect46.gen(function* () {
8693
+ var layer19 = Layer23.effect(Service18, Effect47.gen(function* () {
8183
8694
  const grammarStore = yield* Service17;
8184
8695
  const resolveLang = (lang) => lang === "jsx" ? "javascript" : lang;
8185
8696
  const cache = yield* Cache.make({
8186
8697
  capacity: 100,
8187
8698
  timeToLive: Infinity,
8188
- lookup: (lang) => Effect46.gen(function* () {
8699
+ lookup: (lang) => Effect47.gen(function* () {
8189
8700
  const grammarLang = resolveLang(lang);
8190
- const wasmPath = yield* grammarStore.ensure(grammarLang).pipe(Effect46.mapError((e) => new LanguageLoadError({
8701
+ const wasmPath = yield* grammarStore.ensure(grammarLang).pipe(Effect47.mapError((e) => new LanguageLoadError({
8191
8702
  lang,
8192
8703
  message: `Failed to provision grammar for ${lang}`,
8193
8704
  cause: e
8194
8705
  })));
8195
- return yield* Effect46.tryPromise({
8706
+ return yield* Effect47.tryPromise({
8196
8707
  try: () => Language.load(wasmPath),
8197
8708
  catch: (cause) => new LanguageLoadError({ lang, message: `Failed to load language: ${lang}`, cause })
8198
8709
  });
@@ -8215,15 +8726,20 @@ var TreeHandle = Schema31.Struct({
8215
8726
  // ../parser/src/code-parser.ts
8216
8727
  class Service19 extends Context19.Service()("@dimple/parser/CodeParser") {
8217
8728
  }
8218
- var layer20 = Layer24.effect(Service19, Effect47.gen(function* () {
8729
+ var layer20 = Layer24.effect(Service19, Effect48.gen(function* () {
8219
8730
  const languageStore = yield* Service18;
8220
- const parse2 = Effect47.fn("CodeParser.parse")(function* (request) {
8221
- const language = yield* languageStore.get(request.lang).pipe(Effect47.mapError((cause) => new ParseError({
8731
+ const parserByLanguage = new Map;
8732
+ const parse2 = Effect48.fn("CodeParser.parse")(function* (request) {
8733
+ const language = yield* languageStore.get(request.lang).pipe(Effect48.mapError((cause) => new ParseError({
8222
8734
  message: `Failed to load language '${request.lang}' for parsing`,
8223
8735
  cause
8224
8736
  })));
8225
- const parser = new Parser2;
8226
- parser.setLanguage(language);
8737
+ let parser = parserByLanguage.get(request.lang);
8738
+ if (parser === undefined) {
8739
+ parser = new Parser2;
8740
+ parser.setLanguage(language);
8741
+ parserByLanguage.set(request.lang, parser);
8742
+ }
8227
8743
  const tree2 = parser.parse(request.source);
8228
8744
  if (!tree2) {
8229
8745
  return yield* new ParseError({ message: "Failed to parse source" });
@@ -8239,7 +8755,7 @@ var ParseRequest = Schema32.Struct({
8239
8755
  source: Schema32.String
8240
8756
  });
8241
8757
  // src/config.ts
8242
- import { Effect as Effect48, Layer as Layer25 } from "effect";
8758
+ import { Effect as Effect49, Layer as Layer25 } from "effect";
8243
8759
 
8244
8760
  // src/errors.ts
8245
8761
  import { Schema as Schema33 } from "effect";
@@ -8363,13 +8879,13 @@ var remapError = (error) => {
8363
8879
  };
8364
8880
 
8365
8881
  // src/config.ts
8366
- var resolveConfigInput = Effect48.fn("Sdk.resolveConfigInput")(function* (input) {
8367
- return yield* (typeof input === "string" ? loadConfigFromFile(input) : loadConfig(input)).pipe(Effect48.mapError((error) => new ConfigError({ message: error.message })));
8882
+ var resolveConfigInput = Effect49.fn("Sdk.resolveConfigInput")(function* (input) {
8883
+ return yield* (typeof input === "string" ? loadConfigFromFile(input) : loadConfig(input)).pipe(Effect49.mapError((error) => new ConfigError({ message: error.message })));
8368
8884
  });
8369
- var configLayerFromInput = (input) => Layer25.effect(Service4, resolveConfigInput(input).pipe(Effect48.map((config) => Service4.of({ config }))));
8885
+ var configLayerFromInput = (input) => Layer25.effect(Service4, resolveConfigInput(input).pipe(Effect49.map((config) => Service4.of({ config }))));
8370
8886
 
8371
8887
  // src/dimple.ts
8372
- import { Cause as Cause5, Effect as Effect49, Exit as Exit6 } from "effect";
8888
+ import { Cause as Cause5, Effect as Effect50, Exit as Exit7 } from "effect";
8373
8889
 
8374
8890
  // src/indexer.ts
8375
8891
  import { execFileSync } from "node:child_process";
@@ -8417,8 +8933,8 @@ var walkFiles = (paths2, extensions) => {
8417
8933
  const files = [];
8418
8934
  const errors3 = [];
8419
8935
  let unsupported = 0;
8420
- const resolved = paths2.map((p) => resolve(p));
8421
- const root = commonRoot([...resolved, process.cwd()]);
8936
+ const resolved = paths2.map((p) => resolve(process.env["DIMPLE_CWD"] ?? process.cwd(), p));
8937
+ const root = commonRoot([...resolved, process.env["DIMPLE_CWD"] ?? process.cwd()]);
8422
8938
  const visit = (path2) => {
8423
8939
  let stat;
8424
8940
  try {
@@ -8609,7 +9125,7 @@ var makeFacade = (state, reload, dispose) => {
8609
9125
  }
8610
9126
  return;
8611
9127
  };
8612
- const runQuery = (text4, options) => run(Effect49.gen(function* () {
9128
+ const runQuery = (text4, options) => run(Effect50.gen(function* () {
8613
9129
  if (typeof text4 !== "string") {
8614
9130
  return yield* new InputError({ message: "query: text must be a string" });
8615
9131
  }
@@ -8622,7 +9138,7 @@ var makeFacade = (state, reload, dispose) => {
8622
9138
  let rows = yield* core().hybrid.hybrid(text4, {
8623
9139
  ...rest,
8624
9140
  ...capLimit !== undefined ? { limit: capLimit } : {}
8625
- }).pipe(Effect49.map((r) => [...r]));
9141
+ }).pipe(Effect50.map((r) => [...r]));
8626
9142
  let truncated = false;
8627
9143
  if (budget?.maxChars !== undefined && budget.maxChars > 0) {
8628
9144
  let used = 0;
@@ -8712,7 +9228,7 @@ var makeFacade = (state, reload, dispose) => {
8712
9228
  }
8713
9229
  return;
8714
9230
  };
8715
- const writeUnit = (input, embed) => Effect49.gen(function* () {
9231
+ const writeUnit = (input, embed) => Effect50.gen(function* () {
8716
9232
  const mem = yield* core().store.write(input);
8717
9233
  if (embed) {
8718
9234
  yield* core().store.ensureTopicTree(core().defaultModel.id);
@@ -8722,8 +9238,8 @@ var makeFacade = (state, reload, dispose) => {
8722
9238
  }
8723
9239
  return mem;
8724
9240
  });
8725
- const upsertUnit = (input, embed) => Effect49.gen(function* () {
8726
- const existing = yield* core().store.getByKey(input.logicalKey, input.namespace ?? "default").pipe(Effect49.catchTag("Store.NotFound", () => Effect49.succeed(undefined)));
9241
+ const upsertUnit = (input, embed) => Effect50.gen(function* () {
9242
+ const existing = yield* core().store.getByKey(input.logicalKey, input.namespace ?? "default").pipe(Effect50.catchTag("Store.NotFound", () => Effect50.succeed(undefined)));
8727
9243
  if (existing !== undefined && existing.content === input.content) {
8728
9244
  const stamp = codeVersionOf(input.metadata);
8729
9245
  if (stamp !== undefined && codeVersionOf(existing.metadata) === undefined) {
@@ -8788,7 +9304,7 @@ var makeFacade = (state, reload, dispose) => {
8788
9304
  return;
8789
9305
  };
8790
9306
  const messageOf3 = (cause) => cause instanceof Error ? cause.message : String(cause);
8791
- const resolveMemory = (idOrKey) => core().store.get(idOrKey).pipe(Effect49.catchTag("Store.NotFound", () => core().store.getByKey(idOrKey, "default").pipe(Effect49.catchTag("Store.NotFound", () => Effect49.fail(new NotFoundError({ id: idOrKey, message: `Memory not found: ${idOrKey}` }))))));
9307
+ const resolveMemory = (idOrKey) => core().store.get(idOrKey).pipe(Effect50.catchTag("Store.NotFound", () => core().store.getByKey(idOrKey, "default").pipe(Effect50.catchTag("Store.NotFound", () => Effect50.fail(new NotFoundError({ id: idOrKey, message: `Memory not found: ${idOrKey}` }))))));
8792
9308
  const codeVersionOf = (blob) => {
8793
9309
  if (blob === undefined || blob === null)
8794
9310
  return;
@@ -8805,16 +9321,42 @@ var makeFacade = (state, reload, dispose) => {
8805
9321
  get defaultModelId() {
8806
9322
  return core().defaultModel.id;
8807
9323
  },
8808
- write: (input, options) => run(Effect49.gen(function* () {
9324
+ write: (input, options) => run(Effect50.gen(function* () {
8809
9325
  const invalid = validateWriteInput(input);
8810
9326
  if (invalid !== undefined)
8811
9327
  return yield* invalid;
8812
9328
  return yield* writeUnit(input, options?.embed !== false);
8813
9329
  })),
9330
+ writeMany: (units, options) => run(Effect50.gen(function* () {
9331
+ if (!Array.isArray(units)) {
9332
+ return yield* new InputError({ message: "writeMany: units must be an array" });
9333
+ }
9334
+ for (const input of units) {
9335
+ const invalid = validateWriteInput(input);
9336
+ if (invalid !== undefined)
9337
+ return yield* invalid;
9338
+ }
9339
+ const mems = yield* core().store.writeMany(units);
9340
+ if (options?.embed !== false) {
9341
+ yield* core().store.ensureTopicTree(core().defaultModel.id);
9342
+ const EMBED_CHUNK = 64;
9343
+ const vectors = [];
9344
+ for (let i = 0;i < mems.length; i += EMBED_CHUNK) {
9345
+ const chunk = mems.slice(i, i + EMBED_CHUNK).map((m) => m.content);
9346
+ const vs = yield* core().defaultEmbedder.embed(chunk);
9347
+ vectors.push(...vs);
9348
+ }
9349
+ yield* core().store.updateEmbeddingMany(mems.map((mem, i) => ({ id: mem.id, embedding: [...vectors[i]] })), core().defaultModel.id);
9350
+ return yield* Effect50.forEach(mems, (mem) => core().store.get(mem.id), {
9351
+ discard: false
9352
+ });
9353
+ }
9354
+ return mems;
9355
+ })),
8814
9356
  get: (id) => run(resolveMemory(id)),
8815
9357
  query: runQuery,
8816
- forget: (id) => run(resolveMemory(id).pipe(Effect49.flatMap((memory) => core().store.forget(memory.id)))),
8817
- index: (options) => run(Effect49.gen(function* () {
9358
+ forget: (id) => run(resolveMemory(id).pipe(Effect50.flatMap((memory) => core().store.forget(memory.id)))),
9359
+ index: (options) => run(Effect50.gen(function* () {
8818
9360
  const invalid = validateIndexOptions(options);
8819
9361
  if (invalid !== undefined)
8820
9362
  return yield* invalid;
@@ -8824,7 +9366,7 @@ var makeFacade = (state, reload, dispose) => {
8824
9366
  const maxFileBytes = options.maxFileBytes ?? 64 * 1024;
8825
9367
  const maxChunkBytes = options.maxChunkBytes ?? 8 * 1024;
8826
9368
  const extensions = [...options.extensions ?? INDEXABLE_EXTENSIONS];
8827
- const walk = yield* Effect49.try({
9369
+ const walk = yield* Effect50.try({
8828
9370
  try: () => walkFiles([...options.paths], extensions),
8829
9371
  catch: (cause) => new InputError({
8830
9372
  message: `index: failed to walk paths: ${messageOf3(cause)}`
@@ -8854,8 +9396,8 @@ var makeFacade = (state, reload, dispose) => {
8854
9396
  if (language === undefined)
8855
9397
  continue;
8856
9398
  const chunkKeyOf = (chunkId) => `${keyPrefix}${relative}#${stableChunkId(chunkId)}`;
8857
- const exit = yield* Effect49.exit(Effect49.gen(function* () {
8858
- const source = yield* Effect49.try({
9399
+ const exit = yield* Effect50.exit(Effect50.gen(function* () {
9400
+ const source = yield* Effect50.try({
8859
9401
  try: () => readCapped(filePath, maxFileBytes),
8860
9402
  catch: (cause) => new InputError({ message: `read failed: ${messageOf3(cause)}` })
8861
9403
  });
@@ -8937,13 +9479,14 @@ var makeFacade = (state, reload, dispose) => {
8937
9479
  }
8938
9480
  }
8939
9481
  }
8940
- for (const target of resolveImports(filePath, (extracted.imports ?? []).map((imp) => imp.source), extensions)) {
9482
+ const importTargets = resolveImports(filePath, (extracted.imports ?? []).map((imp) => imp.source), extensions);
9483
+ for (const target of importTargets) {
8941
9484
  pendingEdges.push({
8942
9485
  from: `${keyPrefix}${keyForPath(walk.root, target)}`,
8943
9486
  to: fileKey
8944
9487
  });
8945
9488
  }
8946
- const importRels = resolveImports(filePath, (extracted.imports ?? []).map((imp) => imp.source), extensions).map((target) => keyForPath(walk.root, target));
9489
+ const importRels = importTargets.map((target) => keyForPath(walk.root, target));
8947
9490
  for (const chunk of extracted.chunks) {
8948
9491
  const callerKey = chunkKeyOf(chunk.id);
8949
9492
  if (!unitIds.has(callerKey))
@@ -8961,7 +9504,7 @@ var makeFacade = (state, reload, dispose) => {
8961
9504
  }
8962
9505
  return { units, upd, dups };
8963
9506
  }));
8964
- if (Exit6.isFailure(exit)) {
9507
+ if (Exit7.isFailure(exit)) {
8965
9508
  failed.push({ filePath, message: Cause5.pretty(exit.cause) });
8966
9509
  } else {
8967
9510
  indexed += exit.value.units;
@@ -8975,14 +9518,14 @@ var makeFacade = (state, reload, dispose) => {
8975
9518
  const resolveKey = (key) => {
8976
9519
  const fromMap = unitIds.get(key);
8977
9520
  if (fromMap !== undefined)
8978
- return Effect49.succeed(fromMap);
8979
- return core().store.getByKey(key, namespace ?? "default").pipe(Effect49.map((m) => m.id), Effect49.catchTag("Store.NotFound", () => Effect49.succeed(undefined)));
9521
+ return Effect50.succeed(fromMap);
9522
+ return core().store.getByKey(key, namespace ?? "default").pipe(Effect50.map((m) => m.id), Effect50.catchTag("Store.NotFound", () => Effect50.succeed(undefined)));
8980
9523
  };
8981
- const edgeEffect = (edge) => Effect49.gen(function* () {
8982
- const [from, to] = yield* Effect49.all([resolveKey(edge.from), resolveKey(edge.to)]);
9524
+ const edgeEffect = (edge) => Effect50.gen(function* () {
9525
+ const [from, to] = yield* Effect50.all([resolveKey(edge.from), resolveKey(edge.to)]);
8983
9526
  if (from === undefined || to === undefined || from === to)
8984
9527
  return;
8985
- yield* Effect49.exit(core().store.addEdge({
9528
+ yield* Effect50.exit(core().store.addEdge({
8986
9529
  fromMemoryId: from,
8987
9530
  toMemoryId: to,
8988
9531
  edgeKind: edge.edgeKind,
@@ -8992,10 +9535,10 @@ var makeFacade = (state, reload, dispose) => {
8992
9535
  }));
8993
9536
  });
8994
9537
  for (const edge of pendingEdges) {
8995
- yield* Effect49.exit(edgeEffect({ ...edge, edgeKind: "memory_supports" }));
9538
+ yield* Effect50.exit(edgeEffect({ ...edge, edgeKind: "memory_supports" }));
8996
9539
  }
8997
9540
  for (const edge of pendingCallEdges) {
8998
- yield* Effect49.exit(edgeEffect({ ...edge, edgeKind: "code_calls" }));
9541
+ yield* Effect50.exit(edgeEffect({ ...edge, edgeKind: "code_calls" }));
8999
9542
  }
9000
9543
  return {
9001
9544
  files: walk.files.length,
@@ -9008,7 +9551,7 @@ var makeFacade = (state, reload, dispose) => {
9008
9551
  failed
9009
9552
  };
9010
9553
  })),
9011
- updateEmbedding: (id, embedding, modelKey) => run(Effect49.gen(function* () {
9554
+ updateEmbedding: (id, embedding, modelKey) => run(Effect50.gen(function* () {
9012
9555
  if (!Array.isArray(embedding) || embedding.length === 0 || embedding.some((v) => typeof v !== "number" || !Number.isFinite(v))) {
9013
9556
  return yield* new InputError({
9014
9557
  message: "updateEmbedding: embedding must be a non-empty array of finite numbers"
@@ -9019,10 +9562,10 @@ var makeFacade = (state, reload, dispose) => {
9019
9562
  yield* core().store.ensureTopicTree(key);
9020
9563
  yield* core().store.updateEmbedding(memory.id, [...embedding], key);
9021
9564
  })),
9022
- embed: (text4) => run(core().defaultEmbedder.embedSingle(text4).pipe(Effect49.map((vector) => [...vector]))),
9023
- dream: (modelKey) => run(core().runtime.enqueue(MemoryDream, { modelKey, runId: crypto.randomUUID() }).pipe(Effect49.flatMap((jobId) => core().runtime.wait(jobId)), Effect49.asVoid)),
9565
+ embed: (text4) => run(core().defaultEmbedder.embedSingle(text4).pipe(Effect50.map((vector) => [...vector]))),
9566
+ dream: (modelKey) => run(core().runtime.enqueue(MemoryDream, { modelKey, runId: crypto.randomUUID() }).pipe(Effect50.flatMap((jobId) => core().runtime.wait(jobId)), Effect50.asVoid)),
9024
9567
  dbSchema: () => run(core().store.listSchema()),
9025
- repair: (modelKey) => run(core().runtime.enqueue(GraphRepair, { modelKey, runId: crypto.randomUUID() }).pipe(Effect49.flatMap((jobId) => core().runtime.wait(jobId)), Effect49.asVoid)),
9568
+ repair: (modelKey) => run(core().runtime.enqueue(GraphRepair, { modelKey, runId: crypto.randomUUID() }).pipe(Effect50.flatMap((jobId) => core().runtime.wait(jobId)), Effect50.asVoid)),
9026
9569
  explain: (input, options) => run(core().store.explain(input, {
9027
9570
  ...options?.edgeKinds !== undefined ? { edgeKinds: options.edgeKinds } : {},
9028
9571
  ...options?.maxDepth !== undefined ? { maxDepth: options.maxDepth } : {},
@@ -9032,7 +9575,7 @@ var makeFacade = (state, reload, dispose) => {
9032
9575
  })),
9033
9576
  health: (modelKey) => run(core().store.topicHealth(modelKey)),
9034
9577
  graph: {
9035
- addEdge: (input) => run(Effect49.gen(function* () {
9578
+ addEdge: (input) => run(Effect50.gen(function* () {
9036
9579
  const invalid = validateAddEdgeInput(input);
9037
9580
  if (invalid !== undefined)
9038
9581
  return yield* invalid;
@@ -9074,24 +9617,24 @@ var diffConfig = (oldC, newC) => {
9074
9617
  };
9075
9618
  var RELOAD_DRAIN_MS = 30000;
9076
9619
  var bootStack = async (options) => {
9077
- const scope = await Effect50.runPromise(Scope5.make());
9620
+ const scope = await Effect51.runPromise(Scope5.make());
9078
9621
  let runtime;
9079
9622
  try {
9080
9623
  const layer21 = ("core" in options ? layer13(options.core) : layerConfig7(options.migrationFolder !== undefined ? { migrationFolder: options.migrationFolder } : {}).pipe(Layer26.provide(configLayerFromInput(options.config)))).pipe(Layer26.provide(Layer26.succeed(Scope5.Scope, scope)));
9081
9624
  const appLayer = Layer26.mergeAll(layer21, layer20, layer14);
9082
9625
  const rt = ManagedRuntime.make(appLayer);
9083
9626
  runtime = rt;
9084
- const [core, parser, extractor] = await rt.runPromise(Effect50.all([Service, Service19, Service12]));
9627
+ const [core, parser, extractor] = await rt.runPromise(Effect51.all([Service, Service19, Service12]));
9085
9628
  return {
9086
9629
  runPromise: (effect) => rt.runPromise(effect),
9087
9630
  disposeEffect: rt.disposeEffect,
9088
9631
  core,
9089
9632
  parser,
9090
9633
  extractor,
9091
- dispose: () => Effect50.runPromise(Scope5.close(scope, Exit7.void).pipe(Effect50.andThen(rt.disposeEffect)))
9634
+ dispose: () => Effect51.runPromise(Scope5.close(scope, Exit8.void).pipe(Effect51.andThen(rt.disposeEffect)))
9092
9635
  };
9093
9636
  } catch (cause) {
9094
- await Effect50.runPromise(Scope5.close(scope, Exit7.void).pipe(Effect50.andThen(runtime?.disposeEffect ?? Effect50.void))).catch(() => {
9637
+ await Effect51.runPromise(Scope5.close(scope, Exit8.void).pipe(Effect51.andThen(runtime?.disposeEffect ?? Effect51.void))).catch(() => {
9095
9638
  return;
9096
9639
  });
9097
9640
  return Promise.reject(cause);
@@ -9102,7 +9645,7 @@ var makeDimple = async (options) => {
9102
9645
  const migrationFolder = "config" in options ? options.migrationFolder : undefined;
9103
9646
  let currentConfig;
9104
9647
  if (configSource !== undefined) {
9105
- currentConfig = await Effect50.runPromise(resolveConfigInput(configSource)).catch((cause) => Promise.reject(remapError(cause)));
9648
+ currentConfig = await Effect51.runPromise(resolveConfigInput(configSource)).catch((cause) => Promise.reject(remapError(cause)));
9106
9649
  }
9107
9650
  let current = await bootStack(configSource !== undefined && currentConfig !== undefined ? {
9108
9651
  config: currentConfig,
@@ -9111,7 +9654,7 @@ var makeDimple = async (options) => {
9111
9654
  let disposed = false;
9112
9655
  let gate = Promise.resolve();
9113
9656
  let releaseGate;
9114
- let inflight = 0;
9657
+ let inflight2 = 0;
9115
9658
  let drainWaiters = [];
9116
9659
  let reloadChain = Promise.resolve();
9117
9660
  const run = (effect) => {
@@ -9123,12 +9666,12 @@ var makeDimple = async (options) => {
9123
9666
  return Promise.reject(new StoreFailure({ message: "SDK disposed" }));
9124
9667
  }
9125
9668
  const stack = current;
9126
- inflight += 1;
9127
- return stack.runPromise(effect.pipe(Effect50.mapError(remapError))).catch((cause) => {
9669
+ inflight2 += 1;
9670
+ return stack.runPromise(effect.pipe(Effect51.mapError(remapError))).catch((cause) => {
9128
9671
  return Promise.reject(remapError(cause));
9129
9672
  }).finally(() => {
9130
- inflight -= 1;
9131
- if (inflight === 0 && drainWaiters.length > 0) {
9673
+ inflight2 -= 1;
9674
+ if (inflight2 === 0 && drainWaiters.length > 0) {
9132
9675
  const waiters = drainWaiters;
9133
9676
  drainWaiters = [];
9134
9677
  for (const wake of waiters)
@@ -9149,7 +9692,7 @@ var makeDimple = async (options) => {
9149
9692
  release?.();
9150
9693
  };
9151
9694
  const drain = () => {
9152
- if (inflight === 0)
9695
+ if (inflight2 === 0)
9153
9696
  return Promise.resolve();
9154
9697
  return new Promise((resolve2) => {
9155
9698
  drainWaiters.push(resolve2);
@@ -9180,7 +9723,7 @@ var makeDimple = async (options) => {
9180
9723
  }));
9181
9724
  }
9182
9725
  const nextInput = input ?? configSource;
9183
- const resolved = await Effect50.runPromise(resolveConfigInput(nextInput));
9726
+ const resolved = await Effect51.runPromise(resolveConfigInput(nextInput));
9184
9727
  if (currentConfig !== undefined && stableKey(resolved) === stableKey(currentConfig)) {
9185
9728
  return {
9186
9729
  config: resolved,
@@ -9203,10 +9746,10 @@ var makeDimple = async (options) => {
9203
9746
  currentConfig = resolved;
9204
9747
  await old.dispose();
9205
9748
  if (changes.storeUrl) {
9206
- await Effect50.runPromise(Effect50.logWarning(`reload: store database switched "${oldStoreUrl}" → "${resolved.storeUrl}" — ` + "the old db is preserved but no longer served (nothing was migrated)").pipe(Effect50.annotateLogs({ scope: "@dimple/sdk", event: "reload.store.switch" })));
9749
+ await Effect51.runPromise(Effect51.logWarning(`reload: store database switched "${oldStoreUrl}" → "${resolved.storeUrl}" — ` + "the old db is preserved but no longer served (nothing was migrated)").pipe(Effect51.annotateLogs({ scope: "@dimple/sdk", event: "reload.store.switch" })));
9207
9750
  }
9208
9751
  if (changes.jobsUrl) {
9209
- await Effect50.runPromise(Effect50.logWarning(`reload: jobs database switched "${oldJobsUrl}" → "${resolved.jobs?.dbUrl}" — ` + "pending jobs remain durable in the old db").pipe(Effect50.annotateLogs({ scope: "@dimple/sdk", event: "reload.jobs.switch" })));
9752
+ await Effect51.runPromise(Effect51.logWarning(`reload: jobs database switched "${oldJobsUrl}" → "${resolved.jobs?.dbUrl}" — ` + "pending jobs remain durable in the old db").pipe(Effect51.annotateLogs({ scope: "@dimple/sdk", event: "reload.jobs.switch" })));
9210
9753
  }
9211
9754
  return { config: resolved, changed: changes, reloaded: true };
9212
9755
  } finally {