@memtensor/memos-local-openclaw-plugin 1.0.2-beta.4 → 1.0.2-beta.5

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.
@@ -48,6 +48,14 @@ export class ViewerServer {
48
48
  private readonly ctx?: PluginContext;
49
49
 
50
50
  private static readonly SESSION_TTL = 24 * 60 * 60 * 1000;
51
+ private static readonly PLUGIN_VERSION: string = (() => {
52
+ try {
53
+ const pkgPath = path.resolve(__dirname, "../../package.json");
54
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
55
+ } catch {
56
+ return "unknown";
57
+ }
58
+ })();
51
59
  private resetToken: string;
52
60
  private migrationRunning = false;
53
61
  private migrationAbort = false;
@@ -363,7 +371,7 @@ export class ViewerServer {
363
371
 
364
372
  private serveViewer(res: http.ServerResponse): void {
365
373
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0", "Pragma": "no-cache", "Expires": "0" });
366
- res.end(viewerHTML);
374
+ res.end(viewerHTML(ViewerServer.PLUGIN_VERSION));
367
375
  }
368
376
 
369
377
  // ─── Data APIs ───
@@ -563,44 +571,71 @@ export class ViewerServer {
563
571
 
564
572
  const role = url.searchParams.get("role") ?? undefined;
565
573
  const kind = url.searchParams.get("kind") ?? undefined;
574
+ const session = url.searchParams.get("session") ?? undefined;
575
+ const owner = url.searchParams.get("owner") ?? undefined;
566
576
  const dateFrom = url.searchParams.get("dateFrom") ?? undefined;
567
577
  const dateTo = url.searchParams.get("dateTo") ?? undefined;
568
578
 
569
579
  const passesFilter = (r: any): boolean => {
570
580
  if (role && r.role !== role) return false;
571
581
  if (kind && r.kind !== kind) return false;
582
+ if (session && r.session_key !== session) return false;
583
+ if (owner && r.owner !== owner) return false;
572
584
  if (dateFrom && r.created_at < new Date(dateFrom).getTime()) return false;
573
585
  if (dateTo && r.created_at > new Date(dateTo).getTime()) return false;
574
586
  return true;
575
587
  };
576
588
 
589
+ const ftsFilters: string[] = [];
590
+ const likeFilters: string[] = [];
591
+ const sqlParams: any[] = [];
592
+ if (session) { ftsFilters.push("c.session_key = ?"); likeFilters.push("session_key = ?"); sqlParams.push(session); }
593
+ if (owner) { ftsFilters.push("c.owner = ?"); likeFilters.push("owner = ?"); sqlParams.push(owner); }
594
+ const ftsWhere = ftsFilters.length > 0 ? " AND " + ftsFilters.join(" AND ") : "";
595
+ const likeWhere = likeFilters.length > 0 ? " AND " + likeFilters.join(" AND ") : "";
596
+
577
597
  const db = (this.store as any).db;
578
598
  let ftsResults: any[] = [];
579
599
  try {
580
600
  ftsResults = db.prepare(
581
- "SELECT c.* FROM chunks_fts f JOIN chunks c ON f.rowid = c.rowid WHERE chunks_fts MATCH ? ORDER BY rank LIMIT 100",
582
- ).all(q).filter(passesFilter);
601
+ `SELECT c.* FROM chunks_fts f JOIN chunks c ON f.rowid = c.rowid WHERE chunks_fts MATCH ?${ftsWhere} ORDER BY rank LIMIT 100`,
602
+ ).all(q, ...sqlParams).filter(passesFilter);
583
603
  } catch { /* FTS syntax error, fall through */ }
584
604
  if (ftsResults.length === 0) {
585
- ftsResults = db.prepare(
586
- "SELECT * FROM chunks WHERE content LIKE ? OR summary LIKE ? ORDER BY created_at DESC LIMIT 100",
587
- ).all(`%${q}%`, `%${q}%`).filter(passesFilter);
605
+ try {
606
+ ftsResults = db.prepare(
607
+ `SELECT * FROM chunks WHERE (content LIKE ? OR summary LIKE ?)${likeWhere} ORDER BY created_at DESC LIMIT 100`,
608
+ ).all(`%${q}%`, `%${q}%`, ...sqlParams).filter(passesFilter);
609
+ } catch (err) {
610
+ this.log.warn(`LIKE search failed: ${err}`);
611
+ }
588
612
  }
589
613
 
590
614
  const SEMANTIC_THRESHOLD = 0.64;
615
+ const VECTOR_TIMEOUT_MS = 8000;
591
616
  let vectorResults: any[] = [];
592
617
  let scoreMap = new Map<string, number>();
593
618
  try {
594
- const queryVec = await this.embedder.embedQuery(q);
595
- const hits = vectorSearch(this.store, queryVec, 40);
596
- scoreMap = new Map(hits.map(h => [h.chunkId, h.score]));
597
- const hitIds = new Set(hits.filter(h => h.score >= SEMANTIC_THRESHOLD).map(h => h.chunkId));
598
- if (hitIds.size > 0) {
599
- const placeholders = [...hitIds].map(() => "?").join(",");
600
- const rows = db.prepare(`SELECT * FROM chunks WHERE id IN (${placeholders})`).all(...hitIds).filter(passesFilter);
601
- rows.forEach((r: any) => { r._vscore = scoreMap.get(r.id) ?? 0; });
602
- rows.sort((a: any, b: any) => (b._vscore ?? 0) - (a._vscore ?? 0));
603
- vectorResults = rows;
619
+ const vecPromise = (async () => {
620
+ const queryVec = await this.embedder.embedQuery(q);
621
+ return vectorSearch(this.store, queryVec, 40);
622
+ })();
623
+ const hits = await Promise.race([
624
+ vecPromise,
625
+ new Promise<null>((resolve) => setTimeout(() => resolve(null), VECTOR_TIMEOUT_MS)),
626
+ ]);
627
+ if (hits) {
628
+ scoreMap = new Map(hits.map(h => [h.chunkId, h.score]));
629
+ const hitIds = new Set(hits.filter(h => h.score >= SEMANTIC_THRESHOLD).map(h => h.chunkId));
630
+ if (hitIds.size > 0) {
631
+ const placeholders = [...hitIds].map(() => "?").join(",");
632
+ const rows = db.prepare(`SELECT * FROM chunks WHERE id IN (${placeholders})${likeWhere}`).all(...hitIds, ...sqlParams).filter(passesFilter);
633
+ rows.forEach((r: any) => { r._vscore = scoreMap.get(r.id) ?? 0; });
634
+ rows.sort((a: any, b: any) => (b._vscore ?? 0) - (a._vscore ?? 0));
635
+ vectorResults = rows;
636
+ }
637
+ } else {
638
+ this.log.warn("Vector search timed out, returning FTS results only");
604
639
  }
605
640
  } catch (err) {
606
641
  this.log.warn(`Vector search failed (falling back to FTS only): ${err}`);