@hasna/mementos 0.14.58 → 0.14.59

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.
@@ -467,6 +467,10 @@ var init_storage = __esm(() => {
467
467
  });
468
468
 
469
469
  // src/db/api-mode.ts
470
+ import { tmpdir } from "os";
471
+ import { join as join3 } from "path";
472
+ import { writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "fs";
473
+ import { randomUUID } from "crypto";
470
474
  function firstEnv(...keys) {
471
475
  for (const k of keys) {
472
476
  const v = process.env[k]?.trim();
@@ -502,33 +506,60 @@ function apiRequestRaw(method, path, body) {
502
506
  throw new Error("api-mode: not configured (HASNA_MEMENTOS_API_URL / HASNA_MEMENTOS_API_KEY)");
503
507
  const url = `${cfg.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
504
508
  const hasBody = body !== undefined && body !== null;
505
- const payload = hasBody ? JSON.stringify(body) : "";
506
- const script = [
507
- "curl -sS --fail-with-body",
508
- `-m "$MEM_API_TIMEOUT"`,
509
- `-X "$MEM_API_METHOD"`,
510
- `-H "Authorization: Bearer $MEM_API_KEY"`,
511
- `-H "x-api-key: $MEM_API_KEY"`,
512
- `-H "Content-Type: application/json"`,
513
- `-H "Accept: application/json"`,
514
- hasBody ? "--data-binary @-" : "",
515
- `-w '\\n%{http_code}'`,
516
- `"$MEM_API_URL"`
517
- ].filter(Boolean).join(" ");
518
- const proc = Bun.spawnSync(["bash", "-c", script], {
519
- stdin: hasBody ? Buffer.from(payload) : undefined,
520
- env: {
521
- ...process.env,
522
- MEM_API_KEY: cfg.apiKey,
523
- MEM_API_URL: url,
524
- MEM_API_METHOD: method,
525
- MEM_API_TIMEOUT: process.env["HASNA_MEMENTOS_API_TIMEOUT"] || DEFAULT_TIMEOUT_S
526
- },
527
- stdout: "pipe",
528
- stderr: "pipe"
529
- });
530
- const out = proc.stdout ? new TextDecoder().decode(proc.stdout) : "";
531
- const err = proc.stderr ? new TextDecoder().decode(proc.stderr) : "";
509
+ const timeout = process.env["HASNA_MEMENTOS_API_TIMEOUT"] || DEFAULT_TIMEOUT_S;
510
+ const headerLines = `Authorization: Bearer ${cfg.apiKey}
511
+ x-api-key: ${cfg.apiKey}
512
+ `;
513
+ const args = [
514
+ "curl",
515
+ "-sS",
516
+ "--fail-with-body",
517
+ "-m",
518
+ timeout,
519
+ "-X",
520
+ method,
521
+ "-H",
522
+ "@-",
523
+ "-H",
524
+ "Content-Type: application/json",
525
+ "-H",
526
+ "Accept: application/json",
527
+ "-w",
528
+ "\\n%{http_code}"
529
+ ];
530
+ let bodyFile;
531
+ if (hasBody) {
532
+ bodyFile = join3(tmpdir(), `mem-req-${process.pid}-${randomUUID()}.json`);
533
+ writeFileSync3(bodyFile, JSON.stringify(body), { mode: 384 });
534
+ args.push("--data-binary", `@${bodyFile}`);
535
+ }
536
+ args.push(url);
537
+ const childEnv = {};
538
+ for (const [k, v] of Object.entries(process.env)) {
539
+ if (v === undefined)
540
+ continue;
541
+ if (k === "HASNA_MEMENTOS_API_KEY" || k === "MEMENTOS_API_KEY")
542
+ continue;
543
+ childEnv[k] = v;
544
+ }
545
+ let out = "";
546
+ let err = "";
547
+ try {
548
+ const proc = Bun.spawnSync(args, {
549
+ stdin: Buffer.from(headerLines),
550
+ stdout: "pipe",
551
+ stderr: "pipe",
552
+ env: childEnv
553
+ });
554
+ out = proc.stdout ? new TextDecoder().decode(proc.stdout) : "";
555
+ err = proc.stderr ? new TextDecoder().decode(proc.stderr) : "";
556
+ } finally {
557
+ if (bodyFile) {
558
+ try {
559
+ unlinkSync2(bodyFile);
560
+ } catch {}
561
+ }
562
+ }
532
563
  const nl = out.lastIndexOf(`
533
564
  `);
534
565
  const codeStr = nl >= 0 ? out.slice(nl + 1).trim() : "";
@@ -1507,7 +1538,7 @@ __export(exports_database, {
1507
1538
  closeDatabase: () => closeDatabase
1508
1539
  });
1509
1540
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, cpSync as cpSync2 } from "fs";
1510
- import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
1541
+ import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
1511
1542
  function isInMemoryDb2(path) {
1512
1543
  return path === ":memory:" || path.startsWith("file::memory:");
1513
1544
  }
@@ -1516,7 +1547,7 @@ function findNearestMementosDb(startDir) {
1516
1547
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1517
1548
  const legacyHomeDb = resolve2(home, ".mementos", "mementos.db");
1518
1549
  while (true) {
1519
- const candidate = join3(dir, ".mementos", "mementos.db");
1550
+ const candidate = join4(dir, ".mementos", "mementos.db");
1520
1551
  if (existsSync3(candidate) && resolve2(candidate) !== legacyHomeDb)
1521
1552
  return candidate;
1522
1553
  const parent = dirname2(dir);
@@ -1529,7 +1560,7 @@ function findNearestMementosDb(startDir) {
1529
1560
  function findGitRoot2(startDir) {
1530
1561
  let dir = resolve2(startDir);
1531
1562
  while (true) {
1532
- if (existsSync3(join3(dir, ".git")))
1563
+ if (existsSync3(join4(dir, ".git")))
1533
1564
  return dir;
1534
1565
  const parent = dirname2(dir);
1535
1566
  if (parent === dir)
@@ -1540,10 +1571,10 @@ function findGitRoot2(startDir) {
1540
1571
  }
1541
1572
  function migrateGlobalDir() {
1542
1573
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1543
- const newDir = join3(home, ".hasna", "mementos");
1544
- const oldDir = join3(home, ".mementos");
1574
+ const newDir = join4(home, ".hasna", "mementos");
1575
+ const oldDir = join4(home, ".mementos");
1545
1576
  if (!existsSync3(newDir) && existsSync3(oldDir)) {
1546
- mkdirSync3(join3(home, ".hasna"), { recursive: true });
1577
+ mkdirSync3(join4(home, ".hasna"), { recursive: true });
1547
1578
  cpSync2(oldDir, newDir, { recursive: true });
1548
1579
  }
1549
1580
  }
@@ -1559,12 +1590,12 @@ function getDbPath2() {
1559
1590
  if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
1560
1591
  const gitRoot = findGitRoot2(cwd);
1561
1592
  if (gitRoot) {
1562
- return join3(gitRoot, ".mementos", "mementos.db");
1593
+ return join4(gitRoot, ".mementos", "mementos.db");
1563
1594
  }
1564
1595
  }
1565
1596
  migrateGlobalDir();
1566
1597
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1567
- return join3(home, ".hasna", "mementos", "mementos.db");
1598
+ return join4(home, ".hasna", "mementos", "mementos.db");
1568
1599
  }
1569
1600
  function ensureDir2(filePath) {
1570
1601
  if (isInMemoryDb2(filePath))
@@ -2029,6 +2060,18 @@ var init_poisoning = __esm(() => {
2029
2060
  });
2030
2061
 
2031
2062
  // src/db/entity-memories.ts
2063
+ function parseEntityRow(row) {
2064
+ return {
2065
+ id: row["id"],
2066
+ name: row["name"],
2067
+ type: row["type"],
2068
+ description: row["description"] || null,
2069
+ metadata: JSON.parse(row["metadata"] || "{}"),
2070
+ project_id: row["project_id"] || null,
2071
+ created_at: row["created_at"],
2072
+ updated_at: row["updated_at"]
2073
+ };
2074
+ }
2032
2075
  function parseEntityMemoryRow(row) {
2033
2076
  return {
2034
2077
  entity_id: row["entity_id"],
@@ -2072,6 +2115,14 @@ function getMemoriesForEntity(entityId, db) {
2072
2115
  ORDER BY m.importance DESC, m.created_at DESC`).all(entityId);
2073
2116
  return rows.map(parseMemoryRow);
2074
2117
  }
2118
+ function getEntitiesForMemory(memoryId, db) {
2119
+ const d = db || getDatabase();
2120
+ const rows = d.query(`SELECT e.* FROM entities e
2121
+ INNER JOIN entity_memories em ON em.entity_id = e.id
2122
+ WHERE em.memory_id = ?
2123
+ ORDER BY e.name ASC`).all(memoryId);
2124
+ return rows.map(parseEntityRow);
2125
+ }
2075
2126
  function getEntityMemoryLinks(entityId, memoryId, db) {
2076
2127
  const d = db || getDatabase();
2077
2128
  const conditions = [];
@@ -2107,12 +2158,14 @@ __export(exports_memories, {
2107
2158
  parseMemoryRow: () => parseMemoryRow,
2108
2159
  listMemoryHistory: () => listMemoryHistory,
2109
2160
  listMemories: () => listMemories,
2161
+ listLowTrustMemories: () => listLowTrustMemories,
2110
2162
  indexMemoryEmbedding: () => indexMemoryEmbedding,
2111
2163
  incrementRecallCount: () => incrementRecallCount,
2112
2164
  getMemoryVersions: () => getMemoryVersions,
2113
2165
  getMemoryEmbeddings: () => getMemoryEmbeddings,
2114
2166
  getMemoryChain: () => getMemoryChain,
2115
2167
  getMemoryByKey: () => getMemoryByKey,
2168
+ getMemoryBriefing: () => getMemoryBriefing,
2116
2169
  getMemory: () => getMemory,
2117
2170
  getMemoriesByKey: () => getMemoriesByKey,
2118
2171
  deleteMemory: () => deleteMemory,
@@ -2122,6 +2175,14 @@ __export(exports_memories, {
2122
2175
  bulkDeleteMemories: () => bulkDeleteMemories
2123
2176
  });
2124
2177
  function runEntityExtraction(_memory, _projectId, _d) {}
2178
+ function applyContentType(d, id, memory, contentType) {
2179
+ if (!contentType || contentType === "text")
2180
+ return;
2181
+ try {
2182
+ d.run("UPDATE memories SET content_type = ? WHERE id = ?", [contentType, id]);
2183
+ memory.content_type = contentType;
2184
+ } catch {}
2185
+ }
2125
2186
  function parseMemoryRow(row) {
2126
2187
  return {
2127
2188
  id: row["id"],
@@ -2226,6 +2287,7 @@ function createMemory(input, dedupeMode = "merge", db) {
2226
2287
  insertTag2.run(existing.id, tag);
2227
2288
  }
2228
2289
  const merged = getMemory(existing.id, d);
2290
+ applyContentType(d, existing.id, merged, input.content_type);
2229
2291
  try {
2230
2292
  const existingMemories = listMemoriesByKey(input.key, d);
2231
2293
  const trustScore = computeTrustScore(safeValue, input.key, existingMemories, input.importance);
@@ -2275,6 +2337,7 @@ function createMemory(input, dedupeMode = "merge", db) {
2275
2337
  insertTag.run(id, tag);
2276
2338
  }
2277
2339
  const memory = getMemory(id, d);
2340
+ applyContentType(d, id, memory, input.content_type);
2278
2341
  try {
2279
2342
  const existingMemories = listMemoriesByKey(input.key, d);
2280
2343
  const trustScore = computeTrustScore(safeValue, input.key, existingMemories, input.importance);
@@ -2609,6 +2672,66 @@ function listMemories(filter, db) {
2609
2672
  const rows = d.query(sql).all(...params);
2610
2673
  return rows.map(parseMemoryRow);
2611
2674
  }
2675
+ function getMemoryBriefing(opts, db) {
2676
+ const limit = opts.limit ?? 20;
2677
+ if (!db && isApiMode()) {
2678
+ const q = toQuery({
2679
+ since: opts.since,
2680
+ scope: opts.scope,
2681
+ project_id: opts.project_id,
2682
+ machine_agnostic: opts.visible_machine_id === null || opts.visible_machine_id === undefined ? true : undefined,
2683
+ visible_machine_id: typeof opts.visible_machine_id === "string" ? opts.visible_machine_id : undefined,
2684
+ limit
2685
+ });
2686
+ const { data } = apiJson("GET", `/memories/briefing${q}`);
2687
+ return data ?? { new: [], updated: [], expired: [] };
2688
+ }
2689
+ const d = db || getDatabase();
2690
+ const visibleMachineId = opts.visible_machine_id;
2691
+ const scopeClause = opts.scope ? "AND scope = ?" : "";
2692
+ const projectClause = opts.project_id ? "AND project_id = ?" : "";
2693
+ const machineClause = typeof visibleMachineId === "string" ? "AND (machine_id IS NULL OR machine_id = ?)" : "AND machine_id IS NULL";
2694
+ const extraParams = [
2695
+ ...opts.scope ? [opts.scope] : [],
2696
+ ...opts.project_id ? [opts.project_id] : [],
2697
+ ...typeof visibleMachineId === "string" ? [visibleMachineId] : []
2698
+ ];
2699
+ const newRows = d.prepare(`SELECT * FROM memories
2700
+ WHERE status = 'active' AND created_at > ? ${scopeClause} ${projectClause} ${machineClause}
2701
+ ORDER BY importance DESC, created_at DESC LIMIT ?`).all(opts.since, ...extraParams, limit);
2702
+ const updatedRows = d.prepare(`SELECT * FROM memories
2703
+ WHERE status = 'active' AND updated_at > ? AND created_at <= ? ${scopeClause} ${projectClause} ${machineClause}
2704
+ ORDER BY importance DESC, updated_at DESC LIMIT ?`).all(opts.since, opts.since, ...extraParams, limit);
2705
+ const expiredRows = d.prepare(`SELECT * FROM memories
2706
+ WHERE status != 'active' AND updated_at > ? ${scopeClause} ${projectClause} ${machineClause}
2707
+ ORDER BY updated_at DESC LIMIT ?`).all(opts.since, ...extraParams, Math.min(limit, 10));
2708
+ return {
2709
+ new: newRows.map(parseMemoryRow),
2710
+ updated: updatedRows.map(parseMemoryRow),
2711
+ expired: expiredRows.map(parseMemoryRow)
2712
+ };
2713
+ }
2714
+ function listLowTrustMemories(opts = {}, db) {
2715
+ const threshold = opts.threshold ?? 0.8;
2716
+ const limit = opts.limit ?? 20;
2717
+ const offset = opts.offset ?? 0;
2718
+ if (!db && isApiMode()) {
2719
+ const q = toQuery({ threshold, project_id: opts.project_id, limit, offset });
2720
+ const { data } = apiJson("GET", `/memories/audit${q}`);
2721
+ return data?.memories ?? [];
2722
+ }
2723
+ const d = db || getDatabase();
2724
+ const conditions = ["trust_score < ?", "status = 'active'"];
2725
+ const params = [threshold];
2726
+ if (opts.project_id) {
2727
+ const resolved = resolvePartialId(d, "projects", opts.project_id);
2728
+ conditions.push("project_id = ?");
2729
+ params.push(resolved ?? opts.project_id);
2730
+ }
2731
+ params.push(limit, offset);
2732
+ const rows = d.prepare(`SELECT * FROM memories WHERE ${conditions.join(" AND ")} ORDER BY trust_score ASC LIMIT ? OFFSET ?`).all(...params);
2733
+ return rows.map(parseMemoryRow);
2734
+ }
2612
2735
  function listMemoryHistory(opts = {}, db) {
2613
2736
  const limit = opts.limit ?? 20;
2614
2737
  const offset = opts.offset ?? 0;
@@ -2820,6 +2943,10 @@ function incrementRecallCount(id, db) {
2820
2943
  } catch {}
2821
2944
  }
2822
2945
  function cleanExpiredMemories(db) {
2946
+ if (!db && isApiMode()) {
2947
+ const { data } = apiJson("POST", "/memories/clean");
2948
+ return data?.cleaned ?? 0;
2949
+ }
2823
2950
  const d = db || getDatabase();
2824
2951
  const timestamp = now();
2825
2952
  const countRow = d.query("SELECT COUNT(*) as c FROM memories WHERE expires_at IS NOT NULL AND expires_at < ?").get(timestamp);
@@ -2872,6 +2999,12 @@ async function semanticSearch(queryText, options = {}, db) {
2872
2999
  }
2873
3000
  const d = db || getDatabase();
2874
3001
  const { threshold = 0.5, limit = 10, scope, agent_id, project_id } = options;
3002
+ if (options.index_missing) {
3003
+ const unindexed = d.prepare(`SELECT id, value, summary, when_to_use FROM memories
3004
+ WHERE status = 'active' AND id NOT IN (SELECT memory_id FROM memory_embeddings)
3005
+ LIMIT 100`).all();
3006
+ await Promise.all(unindexed.map((m) => indexMemoryEmbedding(m.id, m.when_to_use || [m.value, m.summary].filter(Boolean).join(" "), d)));
3007
+ }
2875
3008
  const { embedding: queryEmbedding } = await generateEmbedding(queryText);
2876
3009
  const conditions = ["m.status = 'active'", "e.embedding IS NOT NULL"];
2877
3010
  const params = [];
@@ -2917,7 +3050,7 @@ var init_memories = __esm(() => {
2917
3050
  });
2918
3051
 
2919
3052
  // src/db/entities.ts
2920
- function parseEntityRow(row) {
3053
+ function parseEntityRow2(row) {
2921
3054
  return {
2922
3055
  id: row["id"],
2923
3056
  name: row["name"],
@@ -2987,7 +3120,7 @@ function getEntity(id, db) {
2987
3120
  const row = d.query("SELECT * FROM entities WHERE id = ?").get(id);
2988
3121
  if (!row)
2989
3122
  throw new EntityNotFoundError(id);
2990
- return parseEntityRow(row);
3123
+ return parseEntityRow2(row);
2991
3124
  }
2992
3125
  function getEntityByName(name, type, projectId, db) {
2993
3126
  if (!db && isApiMode()) {
@@ -3012,7 +3145,7 @@ function getEntityByName(name, type, projectId, db) {
3012
3145
  const row = d.query(sql).get(...params);
3013
3146
  if (!row)
3014
3147
  return null;
3015
- return parseEntityRow(row);
3148
+ return parseEntityRow2(row);
3016
3149
  }
3017
3150
  function listEntities(filter = {}, db) {
3018
3151
  if (!db && isApiMode()) {
@@ -3056,7 +3189,7 @@ function listEntities(filter = {}, db) {
3056
3189
  params.push(filter.offset);
3057
3190
  }
3058
3191
  const rows = d.query(sql).all(...params);
3059
- return rows.map(parseEntityRow);
3192
+ return rows.map(parseEntityRow2);
3060
3193
  }
3061
3194
  function updateEntity(id, input, db) {
3062
3195
  if (!db && isApiMode()) {
@@ -3961,7 +4094,7 @@ function parseRelationRow(row) {
3961
4094
  created_at: row["created_at"]
3962
4095
  };
3963
4096
  }
3964
- function parseEntityRow2(row) {
4097
+ function parseEntityRow3(row) {
3965
4098
  return {
3966
4099
  id: row["id"],
3967
4100
  name: row["name"],
@@ -4071,7 +4204,7 @@ function getEntityGraph(entityId, depth = 2, db) {
4071
4204
  WHERE g.depth < ?
4072
4205
  )
4073
4206
  SELECT DISTINCT e.* FROM entities e JOIN graph g ON e.id = g.id`).all(entityId, depth);
4074
- const entities = entityRows.map(parseEntityRow2);
4207
+ const entities = entityRows.map(parseEntityRow3);
4075
4208
  const entityIds = new Set(entities.map((e) => e.id));
4076
4209
  if (entityIds.size === 0) {
4077
4210
  return { entities: [], relations: [] };
@@ -4111,7 +4244,7 @@ function findPath(fromEntityId, toEntityId, maxDepth = 5, db) {
4111
4244
  for (const id of ids) {
4112
4245
  const row = d.query("SELECT * FROM entities WHERE id = ?").get(id);
4113
4246
  if (row)
4114
- entities.push(parseEntityRow2(row));
4247
+ entities.push(parseEntityRow3(row));
4115
4248
  }
4116
4249
  return entities.length > 0 ? entities : null;
4117
4250
  }
@@ -52199,7 +52332,7 @@ var init_openapi = __esm(() => {
52199
52332
  // src/server/index.ts
52200
52333
  import { existsSync as existsSync5 } from "fs";
52201
52334
  import { createRequire } from "module";
52202
- import { join as join5, resolve as resolve4, sep } from "path";
52335
+ import { join as join6, resolve as resolve4, sep } from "path";
52203
52336
 
52204
52337
  // src/lib/config.ts
52205
52338
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync, cpSync } from "fs";
@@ -53820,7 +53953,7 @@ init_router();
53820
53953
 
53821
53954
  // src/server/helpers.ts
53822
53955
  import { existsSync as existsSync4 } from "fs";
53823
- import { dirname as dirname3, extname, join as join4 } from "path";
53956
+ import { dirname as dirname3, extname, join as join5 } from "path";
53824
53957
  import { fileURLToPath as fileURLToPath2 } from "url";
53825
53958
  var CORS_HEADERS = {
53826
53959
  "Access-Control-Allow-Origin": process.env["MEMENTOS_CORS_ORIGIN"] ?? "http://localhost:19428",
@@ -53905,20 +54038,20 @@ function resolveDashboardDir() {
53905
54038
  const candidates = [];
53906
54039
  try {
53907
54040
  const scriptDir = dirname3(fileURLToPath2(import.meta.url));
53908
- candidates.push(join4(scriptDir, "..", "dashboard", "dist"));
53909
- candidates.push(join4(scriptDir, "..", "..", "dashboard", "dist"));
54041
+ candidates.push(join5(scriptDir, "..", "dashboard", "dist"));
54042
+ candidates.push(join5(scriptDir, "..", "..", "dashboard", "dist"));
53910
54043
  } catch {}
53911
54044
  if (process.argv[1]) {
53912
54045
  const mainDir = dirname3(process.argv[1]);
53913
- candidates.push(join4(mainDir, "..", "dashboard", "dist"));
53914
- candidates.push(join4(mainDir, "..", "..", "dashboard", "dist"));
54046
+ candidates.push(join5(mainDir, "..", "dashboard", "dist"));
54047
+ candidates.push(join5(mainDir, "..", "..", "dashboard", "dist"));
53915
54048
  }
53916
- candidates.push(join4(process.cwd(), "dashboard", "dist"));
54049
+ candidates.push(join5(process.cwd(), "dashboard", "dist"));
53917
54050
  for (const c of candidates) {
53918
54051
  if (existsSync4(c))
53919
54052
  return c;
53920
54053
  }
53921
- return join4(process.cwd(), "dashboard", "dist");
54054
+ return join5(process.cwd(), "dashboard", "dist");
53922
54055
  }
53923
54056
  function serveStaticFile(filePath) {
53924
54057
  if (!existsSync4(filePath))
@@ -54468,6 +54601,500 @@ addRoute("GET", "/api/report", (_req, url) => {
54468
54601
  // src/server/routes/memories-search.ts
54469
54602
  init_search();
54470
54603
  init_memories();
54604
+
54605
+ // src/lib/asmr/fact-agent.ts
54606
+ init_memories();
54607
+ function escapeFts5Token(token) {
54608
+ return `"${token.replace(/"/g, '""')}"`;
54609
+ }
54610
+ function hasFts5Table2(db) {
54611
+ try {
54612
+ const row = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='memories_fts'").get();
54613
+ return !!row;
54614
+ } catch {
54615
+ return false;
54616
+ }
54617
+ }
54618
+ function buildScopeFilter(opts) {
54619
+ const conditions = [
54620
+ "m.status = 'active'",
54621
+ "(m.expires_at IS NULL OR m.expires_at >= datetime('now'))",
54622
+ "m.category IN ('preference', 'fact', 'knowledge')"
54623
+ ];
54624
+ const params = [];
54625
+ if (opts.project_id) {
54626
+ conditions.push("m.project_id = ?");
54627
+ params.push(opts.project_id);
54628
+ }
54629
+ if (opts.agent_id) {
54630
+ conditions.push("m.agent_id = ?");
54631
+ params.push(opts.agent_id);
54632
+ }
54633
+ return { conditions, params };
54634
+ }
54635
+ function scoreFactMemory(memory, queryLower) {
54636
+ const keyLower = memory.key.toLowerCase();
54637
+ const valueLower = memory.value.toLowerCase();
54638
+ let score = 0;
54639
+ let matchField = "value";
54640
+ if (keyLower === queryLower) {
54641
+ score = 10;
54642
+ matchField = "key (exact)";
54643
+ } else if (keyLower.includes(queryLower)) {
54644
+ score = 7;
54645
+ matchField = "key (partial)";
54646
+ } else if (valueLower.includes(queryLower)) {
54647
+ score = 3;
54648
+ matchField = "value";
54649
+ }
54650
+ if (score === 0) {
54651
+ const tokens = queryLower.split(/\s+/).filter(Boolean);
54652
+ if (tokens.length > 1) {
54653
+ let tokenHits = 0;
54654
+ for (const token of tokens) {
54655
+ if (keyLower.includes(token))
54656
+ tokenHits += 2;
54657
+ else if (valueLower.includes(token))
54658
+ tokenHits += 1;
54659
+ }
54660
+ if (tokenHits > 0) {
54661
+ score = tokenHits / tokens.length * 5;
54662
+ matchField = "tokens";
54663
+ }
54664
+ }
54665
+ }
54666
+ if (memory.pinned)
54667
+ score *= 1.3;
54668
+ const effectiveImportance = computeDecayScore(memory);
54669
+ score = score * effectiveImportance / 10;
54670
+ if (memory.importance >= 7)
54671
+ score *= 1.2;
54672
+ return { score, matchField };
54673
+ }
54674
+ async function runFactAgent(db, query, opts) {
54675
+ const queryLower = query.toLowerCase().trim();
54676
+ if (!queryLower)
54677
+ return { memories: [], reasoning: "Empty query" };
54678
+ const { conditions, params } = buildScopeFilter(opts);
54679
+ const limit = (opts.max_results ?? 20) * 2;
54680
+ let rows;
54681
+ if (hasFts5Table2(db)) {
54682
+ const tokens = queryLower.split(/\s+/).filter(Boolean);
54683
+ const ftsQuery = tokens.map(escapeFts5Token).join(" ");
54684
+ try {
54685
+ const ftsCondition = `m.rowid IN (SELECT f.rowid FROM memories_fts f WHERE memories_fts MATCH ?)`;
54686
+ const sql = `SELECT m.* FROM memories m WHERE ${ftsCondition} AND ${conditions.join(" AND ")} LIMIT ?`;
54687
+ rows = db.query(sql).all(ftsQuery, ...params, limit);
54688
+ } catch {
54689
+ rows = [];
54690
+ }
54691
+ } else {
54692
+ rows = [];
54693
+ }
54694
+ if (rows.length === 0) {
54695
+ const likePattern = `%${queryLower}%`;
54696
+ const likeSql = `SELECT m.* FROM memories m WHERE (m.key LIKE ? OR m.value LIKE ?) AND ${conditions.join(" AND ")} LIMIT ?`;
54697
+ rows = db.query(likeSql).all(likePattern, likePattern, ...params, limit);
54698
+ }
54699
+ const results = [];
54700
+ for (const row of rows) {
54701
+ const memory = parseMemoryRow(row);
54702
+ const { score, matchField } = scoreFactMemory(memory, queryLower);
54703
+ if (score <= 0)
54704
+ continue;
54705
+ results.push({
54706
+ memory,
54707
+ score,
54708
+ source_agent: "facts",
54709
+ reasoning: `Direct fact match on ${matchField}`,
54710
+ verbatim_excerpt: memory.value
54711
+ });
54712
+ }
54713
+ results.sort((a, b) => b.score - a.score);
54714
+ const trimmed = results.slice(0, opts.max_results ?? 20);
54715
+ return {
54716
+ memories: trimmed,
54717
+ reasoning: `Fact agent searched ${rows.length} candidate memories, returned ${trimmed.length} factual matches`
54718
+ };
54719
+ }
54720
+
54721
+ // src/lib/asmr/context-agent.ts
54722
+ init_memories();
54723
+ init_entities();
54724
+ init_entity_memories();
54725
+ function deduplicateCandidates(candidates) {
54726
+ const seen = new Map;
54727
+ for (const c of candidates) {
54728
+ const existing = seen.get(c.memory.id);
54729
+ if (!existing) {
54730
+ seen.set(c.memory.id, c);
54731
+ } else {
54732
+ const existingTotal = existing.semanticScore + existing.entityLinkStrength;
54733
+ const currentTotal = c.semanticScore + c.entityLinkStrength;
54734
+ if (currentTotal > existingTotal) {
54735
+ seen.set(c.memory.id, c);
54736
+ }
54737
+ }
54738
+ }
54739
+ return Array.from(seen.values());
54740
+ }
54741
+ async function runContextAgent(db, query, opts) {
54742
+ const queryLower = query.toLowerCase().trim();
54743
+ if (!queryLower)
54744
+ return { memories: [], reasoning: "Empty query" };
54745
+ const maxResults = opts.max_results ?? 20;
54746
+ const candidates = [];
54747
+ let semanticCount = 0;
54748
+ let entityQueryCount = 0;
54749
+ try {
54750
+ const semanticResults = await semanticSearch(query, {
54751
+ threshold: 0.3,
54752
+ limit: maxResults * 2,
54753
+ project_id: opts.project_id,
54754
+ agent_id: opts.agent_id
54755
+ }, db);
54756
+ for (const sr of semanticResults) {
54757
+ candidates.push({
54758
+ memory: sr.memory,
54759
+ semanticScore: sr.score,
54760
+ entityLinkStrength: 0,
54761
+ entityName: null,
54762
+ entityRelation: null
54763
+ });
54764
+ }
54765
+ semanticCount = semanticResults.length;
54766
+ } catch {}
54767
+ const topSemantic = candidates.slice(0, 10);
54768
+ const entityMemoryIds = new Set;
54769
+ for (const candidate of topSemantic) {
54770
+ try {
54771
+ const entities = getEntitiesForMemory(candidate.memory.id, db);
54772
+ for (const entity of entities) {
54773
+ const linkedMemories = getMemoriesForEntity(entity.id, db);
54774
+ for (const mem of linkedMemories) {
54775
+ if (entityMemoryIds.has(mem.id))
54776
+ continue;
54777
+ entityMemoryIds.add(mem.id);
54778
+ candidates.push({
54779
+ memory: mem,
54780
+ semanticScore: 0,
54781
+ entityLinkStrength: 1,
54782
+ entityName: entity.name,
54783
+ entityRelation: entity.type
54784
+ });
54785
+ }
54786
+ }
54787
+ } catch {}
54788
+ }
54789
+ try {
54790
+ const matchingEntities = listEntities({ search: query, limit: 10, project_id: opts.project_id }, db);
54791
+ const exactEntity = getEntityByName(query, undefined, opts.project_id, db);
54792
+ if (exactEntity && !matchingEntities.find((e) => e.id === exactEntity.id)) {
54793
+ matchingEntities.unshift(exactEntity);
54794
+ }
54795
+ for (const entity of matchingEntities) {
54796
+ entityQueryCount++;
54797
+ const linkedMemories = getMemoriesForEntity(entity.id, db);
54798
+ for (const mem of linkedMemories) {
54799
+ if (entityMemoryIds.has(mem.id))
54800
+ continue;
54801
+ entityMemoryIds.add(mem.id);
54802
+ const nameMatch = entity.name.toLowerCase() === queryLower ? 1 : 0.7;
54803
+ candidates.push({
54804
+ memory: mem,
54805
+ semanticScore: 0,
54806
+ entityLinkStrength: nameMatch,
54807
+ entityName: entity.name,
54808
+ entityRelation: entity.type
54809
+ });
54810
+ }
54811
+ }
54812
+ } catch {}
54813
+ const unique = deduplicateCandidates(candidates);
54814
+ const results = [];
54815
+ for (const c of unique) {
54816
+ if (opts.project_id && c.memory.project_id && c.memory.project_id !== opts.project_id)
54817
+ continue;
54818
+ if (c.memory.status !== "active")
54819
+ continue;
54820
+ const effectiveImportance = computeDecayScore(c.memory) / 10;
54821
+ const score = c.semanticScore * 0.4 + c.entityLinkStrength * 0.3 + effectiveImportance * 0.3;
54822
+ let reasoning;
54823
+ if (c.entityName) {
54824
+ reasoning = `Found via entity ${c.entityName} (relation: ${c.entityRelation})`;
54825
+ } else {
54826
+ reasoning = `Semantic similarity match (score: ${c.semanticScore.toFixed(3)})`;
54827
+ }
54828
+ results.push({
54829
+ memory: c.memory,
54830
+ score,
54831
+ source_agent: "context",
54832
+ reasoning,
54833
+ verbatim_excerpt: c.memory.value
54834
+ });
54835
+ }
54836
+ results.sort((a, b) => b.score - a.score);
54837
+ const trimmed = results.slice(0, maxResults);
54838
+ return {
54839
+ memories: trimmed,
54840
+ reasoning: `Context agent found ${semanticCount} semantic matches, traversed ${entityQueryCount} entities, returned ${trimmed.length} contextual results`
54841
+ };
54842
+ }
54843
+
54844
+ // src/lib/asmr/temporal-agent.ts
54845
+ init_memories();
54846
+ function hasFts5Table3(db) {
54847
+ try {
54848
+ const row = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='memories_fts'").get();
54849
+ return !!row;
54850
+ } catch {
54851
+ return false;
54852
+ }
54853
+ }
54854
+ function escapeFts5Token2(token) {
54855
+ return `"${token.replace(/"/g, '""')}"`;
54856
+ }
54857
+ function queryRelevance(memory, queryLower) {
54858
+ const keyLower = memory.key.toLowerCase();
54859
+ const valueLower = memory.value.toLowerCase();
54860
+ if (keyLower === queryLower)
54861
+ return 1;
54862
+ if (keyLower.includes(queryLower))
54863
+ return 0.8;
54864
+ if (valueLower.includes(queryLower))
54865
+ return 0.5;
54866
+ const tokens = queryLower.split(/\s+/).filter(Boolean);
54867
+ if (tokens.length > 1) {
54868
+ let hits = 0;
54869
+ for (const t of tokens) {
54870
+ if (keyLower.includes(t) || valueLower.includes(t))
54871
+ hits++;
54872
+ }
54873
+ return hits / tokens.length * 0.6;
54874
+ }
54875
+ return 0.1;
54876
+ }
54877
+ function formatDate(iso) {
54878
+ if (!iso)
54879
+ return "unknown";
54880
+ return iso.slice(0, 10);
54881
+ }
54882
+ async function runTemporalAgent(db, query, opts) {
54883
+ const queryLower = query.toLowerCase().trim();
54884
+ if (!queryLower)
54885
+ return { memories: [], reasoning: "Empty query" };
54886
+ const maxResults = opts.max_results ?? 20;
54887
+ const limit = maxResults * 3;
54888
+ const conditions = [
54889
+ "(m.expires_at IS NULL OR m.expires_at >= datetime('now'))"
54890
+ ];
54891
+ const params = [];
54892
+ if (opts.project_id) {
54893
+ conditions.push("m.project_id = ?");
54894
+ params.push(opts.project_id);
54895
+ }
54896
+ if (opts.agent_id) {
54897
+ conditions.push("m.agent_id = ?");
54898
+ params.push(opts.agent_id);
54899
+ }
54900
+ let temporalRows = [];
54901
+ const temporalCondition = "(m.valid_from IS NOT NULL OR m.valid_until IS NOT NULL)";
54902
+ if (hasFts5Table3(db)) {
54903
+ const tokens = queryLower.split(/\s+/).filter(Boolean);
54904
+ const ftsQuery = tokens.map(escapeFts5Token2).join(" ");
54905
+ try {
54906
+ const ftsCondition = `m.rowid IN (SELECT f.rowid FROM memories_fts f WHERE memories_fts MATCH ?)`;
54907
+ const sql = `SELECT m.* FROM memories m WHERE ${ftsCondition} AND ${temporalCondition} AND ${conditions.join(" AND ")} ORDER BY m.valid_from DESC NULLS LAST LIMIT ?`;
54908
+ temporalRows = db.query(sql).all(ftsQuery, ...params, limit);
54909
+ } catch {
54910
+ temporalRows = [];
54911
+ }
54912
+ }
54913
+ if (temporalRows.length === 0) {
54914
+ const likePattern = `%${queryLower}%`;
54915
+ const sql = `SELECT m.* FROM memories m WHERE (m.key LIKE ? OR m.value LIKE ?) AND ${temporalCondition} AND ${conditions.join(" AND ")} ORDER BY m.valid_from DESC NULLS LAST LIMIT ?`;
54916
+ temporalRows = db.query(sql).all(likePattern, likePattern, ...params, limit);
54917
+ }
54918
+ let supersededRows = [];
54919
+ try {
54920
+ const likePattern = `%${queryLower}%`;
54921
+ const sql = `SELECT m.* FROM memories m WHERE (m.key LIKE ? OR m.value LIKE ?) AND (m.status = 'archived' OR m.valid_until IS NOT NULL) ${conditions.length > 0 ? "AND " + conditions.join(" AND ") : ""} ORDER BY m.updated_at DESC LIMIT ?`;
54922
+ supersededRows = db.query(sql).all(likePattern, likePattern, ...params, limit);
54923
+ } catch {}
54924
+ const seen = new Map;
54925
+ const allRows = [...temporalRows, ...supersededRows];
54926
+ for (const row of allRows) {
54927
+ const memory = parseMemoryRow(row);
54928
+ if (!seen.has(memory.id)) {
54929
+ seen.set(memory.id, memory);
54930
+ }
54931
+ }
54932
+ const memories = Array.from(seen.values());
54933
+ const timeline = [];
54934
+ const results = [];
54935
+ for (const memory of memories) {
54936
+ const hasTemporal = memory.valid_from !== null || memory.valid_until !== null;
54937
+ const isSuperseded = memory.status === "archived";
54938
+ const relevance = queryRelevance(memory, queryLower);
54939
+ let versions = [];
54940
+ let newerVersionNote = "";
54941
+ try {
54942
+ versions = getMemoryVersions(memory.id, db);
54943
+ if (versions.length > 1) {
54944
+ const latest = versions[versions.length - 1];
54945
+ if (latest.version > memory.version) {
54946
+ newerVersionNote = `, superseded at version ${latest.version}`;
54947
+ }
54948
+ }
54949
+ } catch {}
54950
+ if (hasTemporal || isSuperseded) {
54951
+ const from = formatDate(memory.valid_from);
54952
+ const until = formatDate(memory.valid_until);
54953
+ const status = isSuperseded ? " [superseded]" : "";
54954
+ timeline.push(`${from} - ${until}: ${memory.key}${status}`);
54955
+ }
54956
+ const recencyMs = memory.valid_from ? Date.now() - new Date(memory.valid_from).getTime() : Date.now() - new Date(memory.created_at).getTime();
54957
+ const daysSince = recencyMs / (1000 * 60 * 60 * 24);
54958
+ const recencyScore = Math.max(0, 1 - daysSince / 365);
54959
+ const temporalBonus = hasTemporal ? 1 : 0.3;
54960
+ const effectiveImportance = computeDecayScore(memory) / 10;
54961
+ const score = recencyScore * 0.4 + relevance * 0.3 + temporalBonus * 0.3;
54962
+ const finalScore = score * effectiveImportance;
54963
+ let reasoning;
54964
+ if (isSuperseded) {
54965
+ reasoning = `Superseded memory from ${formatDate(memory.valid_from)}${newerVersionNote}`;
54966
+ } else if (memory.valid_until && new Date(memory.valid_until) < new Date) {
54967
+ reasoning = `Expired temporal fact (valid ${formatDate(memory.valid_from)} to ${formatDate(memory.valid_until)})${newerVersionNote}`;
54968
+ } else if (hasTemporal) {
54969
+ reasoning = `Current as of ${formatDate(memory.valid_from)}${newerVersionNote}`;
54970
+ } else {
54971
+ reasoning = `Historical record from ${formatDate(memory.created_at)}${newerVersionNote}`;
54972
+ }
54973
+ results.push({
54974
+ memory,
54975
+ score: finalScore,
54976
+ source_agent: "temporal",
54977
+ reasoning,
54978
+ verbatim_excerpt: memory.value
54979
+ });
54980
+ }
54981
+ timeline.sort();
54982
+ results.sort((a, b) => b.score - a.score);
54983
+ const trimmed = results.slice(0, maxResults);
54984
+ return {
54985
+ memories: trimmed,
54986
+ reasoning: `Temporal agent found ${temporalRows.length} temporal memories and ${supersededRows.length} superseded records, built ${timeline.length}-entry timeline`
54987
+ };
54988
+ }
54989
+
54990
+ // src/lib/asmr/orchestrator.ts
54991
+ var DEFAULT_MAX_RESULTS = 20;
54992
+ function mergeResults(agentResults) {
54993
+ const byId = new Map;
54994
+ for (const { agent, result } of agentResults) {
54995
+ for (const mem of result.memories) {
54996
+ const existing = byId.get(mem.memory.id);
54997
+ if (!existing) {
54998
+ byId.set(mem.memory.id, {
54999
+ best: mem,
55000
+ sources: new Set([agent]),
55001
+ maxScore: mem.score
55002
+ });
55003
+ } else {
55004
+ existing.sources.add(agent);
55005
+ if (mem.score > existing.maxScore) {
55006
+ existing.best = mem;
55007
+ existing.maxScore = mem.score;
55008
+ }
55009
+ }
55010
+ }
55011
+ }
55012
+ const merged = [];
55013
+ for (const [, entry] of byId) {
55014
+ const multiAgentBoost = entry.sources.size > 1 ? 1.5 : 1;
55015
+ merged.push({
55016
+ ...entry.best,
55017
+ score: entry.maxScore * multiAgentBoost
55018
+ });
55019
+ }
55020
+ merged.sort((a, b) => b.score - a.score);
55021
+ return merged;
55022
+ }
55023
+ function extractFacts(factResult) {
55024
+ const facts = [];
55025
+ const seen = new Set;
55026
+ for (const mem of factResult.memories) {
55027
+ const statement = `${mem.memory.key}: ${mem.memory.value}`;
55028
+ if (!seen.has(mem.memory.id)) {
55029
+ seen.add(mem.memory.id);
55030
+ facts.push(statement);
55031
+ }
55032
+ }
55033
+ return facts;
55034
+ }
55035
+ function extractTimeline(temporalResult) {
55036
+ const entries = [];
55037
+ const seen = new Set;
55038
+ for (const mem of temporalResult.memories) {
55039
+ if (seen.has(mem.memory.id))
55040
+ continue;
55041
+ seen.add(mem.memory.id);
55042
+ const date = mem.memory.valid_from ?? mem.memory.created_at;
55043
+ const status = mem.memory.status === "archived" ? " [superseded]" : "";
55044
+ entries.push({
55045
+ date,
55046
+ label: `${date.slice(0, 10)}: ${mem.memory.key}${status}`
55047
+ });
55048
+ }
55049
+ entries.sort((a, b) => a.date.localeCompare(b.date));
55050
+ return entries.map((e) => e.label);
55051
+ }
55052
+ async function asmrRecall(db, query, opts) {
55053
+ const options = {
55054
+ max_results: DEFAULT_MAX_RESULTS,
55055
+ include_reasoning: true,
55056
+ ...opts
55057
+ };
55058
+ const start = performance.now();
55059
+ const [factResult, contextResult, temporalResult] = await Promise.all([
55060
+ runFactAgent(db, query, options),
55061
+ runContextAgent(db, query, options),
55062
+ runTemporalAgent(db, query, options)
55063
+ ]);
55064
+ const agentResults = [
55065
+ { agent: "facts", result: factResult },
55066
+ { agent: "context", result: contextResult },
55067
+ { agent: "temporal", result: temporalResult }
55068
+ ];
55069
+ const merged = mergeResults(agentResults);
55070
+ const trimmed = merged.slice(0, options.max_results ?? DEFAULT_MAX_RESULTS);
55071
+ const facts = extractFacts(factResult);
55072
+ const timeline = extractTimeline(temporalResult);
55073
+ const agentsUsed = [];
55074
+ if (factResult.memories.length > 0)
55075
+ agentsUsed.push("facts");
55076
+ if (contextResult.memories.length > 0)
55077
+ agentsUsed.push("context");
55078
+ if (temporalResult.memories.length > 0)
55079
+ agentsUsed.push("temporal");
55080
+ const reasoningParts = [
55081
+ factResult.reasoning,
55082
+ contextResult.reasoning,
55083
+ temporalResult.reasoning
55084
+ ].filter(Boolean);
55085
+ const duration = Math.round(performance.now() - start);
55086
+ return {
55087
+ memories: trimmed,
55088
+ facts,
55089
+ timeline,
55090
+ reasoning: reasoningParts.join(". "),
55091
+ agents_used: agentsUsed,
55092
+ duration_ms: duration
55093
+ };
55094
+ }
55095
+
55096
+ // src/server/routes/memories-search.ts
55097
+ init_database();
54471
55098
  init_router();
54472
55099
  addRoute("POST", "/api/memories/search/semantic", async (req) => {
54473
55100
  const body = await readJson(req);
@@ -54479,10 +55106,23 @@ addRoute("POST", "/api/memories/search/semantic", async (req) => {
54479
55106
  limit: body["limit"] ?? undefined,
54480
55107
  scope: body["scope"] ?? undefined,
54481
55108
  agent_id: body["agent_id"] ?? undefined,
54482
- project_id: body["project_id"] ?? undefined
55109
+ project_id: body["project_id"] ?? undefined,
55110
+ index_missing: body["index_missing"] === true || body["index_missing"] === "true"
54483
55111
  });
54484
55112
  return json({ results, count: results.length });
54485
55113
  });
55114
+ addRoute("POST", "/api/memories/recall/deep", async (req) => {
55115
+ const body = await readJson(req);
55116
+ if (!body || typeof body["query"] !== "string") {
55117
+ return errorResponse("Missing required field: query", 400);
55118
+ }
55119
+ const result = await asmrRecall(getDatabase(), body["query"], {
55120
+ max_results: body["max_results"] ?? undefined,
55121
+ project_id: body["project_id"] ?? undefined,
55122
+ agent_id: body["agent_id"] ?? undefined
55123
+ });
55124
+ return json(result);
55125
+ });
54486
55126
  addRoute("POST", "/api/memories/search", async (req) => {
54487
55127
  const body = await readJson(req);
54488
55128
  if (!body || typeof body["query"] !== "string") {
@@ -54678,6 +55318,35 @@ init_router();
54678
55318
  addRoute("GET", "/api/health", () => {
54679
55319
  return json({ ok: true, version: "1", db: getDbPath() });
54680
55320
  });
55321
+ addRoute("GET", "/api/memories/briefing", (_req, url) => {
55322
+ const q = getSearchParams(url);
55323
+ const since = q["since"];
55324
+ if (!since)
55325
+ return errorResponse("Missing required field: since", 400);
55326
+ let visibleMachineId;
55327
+ if (q["machine_agnostic"] === "true")
55328
+ visibleMachineId = null;
55329
+ else if (q["visible_machine_id"])
55330
+ visibleMachineId = q["visible_machine_id"];
55331
+ const result = getMemoryBriefing({
55332
+ since,
55333
+ scope: q["scope"] || undefined,
55334
+ project_id: q["project_id"] || undefined,
55335
+ visible_machine_id: visibleMachineId,
55336
+ limit: q["limit"] ? parseInt(q["limit"], 10) : undefined
55337
+ });
55338
+ return json(result);
55339
+ });
55340
+ addRoute("GET", "/api/memories/audit", (_req, url) => {
55341
+ const q = getSearchParams(url);
55342
+ const memories = listLowTrustMemories({
55343
+ threshold: q["threshold"] ? parseFloat(q["threshold"]) : undefined,
55344
+ project_id: q["project_id"] || undefined,
55345
+ limit: q["limit"] ? parseInt(q["limit"], 10) : undefined,
55346
+ offset: q["offset"] ? parseInt(q["offset"], 10) : undefined
55347
+ });
55348
+ return json({ memories, count: memories.length });
55349
+ });
54681
55350
  addRoute("POST", "/api/memories/extract", async (req) => {
54682
55351
  const body = await readJson(req);
54683
55352
  if (!body)
@@ -58214,14 +58883,14 @@ function startServer(port) {
58214
58883
  if (existsSync5(dashDir) && (req.method === "GET" || req.method === "HEAD")) {
58215
58884
  if (pathname !== "/") {
58216
58885
  const resolvedDash = resolve4(dashDir) + sep;
58217
- const requestedPath = resolve4(join5(dashDir, pathname));
58886
+ const requestedPath = resolve4(join6(dashDir, pathname));
58218
58887
  if (requestedPath.startsWith(resolvedDash)) {
58219
58888
  const staticRes = serveStaticFile(requestedPath);
58220
58889
  if (staticRes)
58221
58890
  return staticRes;
58222
58891
  }
58223
58892
  }
58224
- const indexRes = serveStaticFile(join5(dashDir, "index.html"));
58893
+ const indexRes = serveStaticFile(join6(dashDir, "index.html"));
58225
58894
  if (indexRes)
58226
58895
  return indexRes;
58227
58896
  }