@martian-engineering/lossless-claw 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import type { DatabaseSync } from "node:sqlite";
2
2
  import { getLcmDbFeatures } from "./features.js";
3
+ import { parseUtcTimestampOrNull } from "../store/parse-utc-timestamp.js";
3
4
 
4
5
  type SummaryColumnInfo = {
5
6
  name?: string;
@@ -62,18 +63,7 @@ function ensureSummaryMetadataColumns(db: DatabaseSync): void {
62
63
  }
63
64
 
64
65
  function parseTimestamp(value: string | null | undefined): Date | null {
65
- if (typeof value !== "string" || !value.trim()) {
66
- return null;
67
- }
68
-
69
- const direct = new Date(value);
70
- if (!Number.isNaN(direct.getTime())) {
71
- return direct;
72
- }
73
-
74
- const normalized = value.includes("T") ? value : `${value.replace(" ", "T")}Z`;
75
- const parsed = new Date(normalized);
76
- return Number.isNaN(parsed.getTime()) ? null : parsed;
66
+ return parseUtcTimestampOrNull(value);
77
67
  }
78
68
 
79
69
  function isoStringOrNull(value: Date | null): string | null {
@@ -434,6 +424,8 @@ export function runLcmMigrations(
434
424
  conversation_id INTEGER PRIMARY KEY AUTOINCREMENT,
435
425
  session_id TEXT NOT NULL,
436
426
  session_key TEXT,
427
+ active INTEGER NOT NULL DEFAULT 1,
428
+ archived_at TEXT,
437
429
  title TEXT,
438
430
  bootstrapped_at TEXT,
439
431
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
@@ -579,7 +571,27 @@ export function runLcmMigrations(
579
571
  db.exec(`ALTER TABLE conversations ADD COLUMN session_key TEXT`);
580
572
  }
581
573
 
582
- db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS conversations_session_key_idx ON conversations (session_key)`);
574
+ const hasActive = conversationColumns.some((col) => col.name === "active");
575
+ if (!hasActive) {
576
+ db.exec(`ALTER TABLE conversations ADD COLUMN active INTEGER NOT NULL DEFAULT 1`);
577
+ }
578
+
579
+ const hasArchivedAt = conversationColumns.some((col) => col.name === "archived_at");
580
+ if (!hasArchivedAt) {
581
+ db.exec(`ALTER TABLE conversations ADD COLUMN archived_at TEXT`);
582
+ }
583
+
584
+ db.exec(`UPDATE conversations SET active = 1 WHERE active IS NULL`);
585
+ db.exec(`
586
+ CREATE UNIQUE INDEX IF NOT EXISTS conversations_active_session_key_idx
587
+ ON conversations (session_key)
588
+ WHERE session_key IS NOT NULL AND active = 1
589
+ `);
590
+ db.exec(`
591
+ CREATE INDEX IF NOT EXISTS conversations_session_key_active_created_idx
592
+ ON conversations (session_key, active, created_at)
593
+ `);
594
+ db.exec(`DROP INDEX IF EXISTS conversations_session_key_idx`);
583
595
  ensureSummaryDepthColumn(db);
584
596
  ensureSummaryMetadataColumns(db);
585
597
  ensureSummaryModelColumn(db);
@@ -649,4 +661,29 @@ export function runLcmMigrations(
649
661
  SELECT summary_id, content FROM summaries;
650
662
  `);
651
663
  }
664
+
665
+ // ── CJK trigram FTS table ────────────────────────────────────────────────
666
+ // FTS5 unicode61 (porter) tokenizer cannot segment CJK ideographs, so CJK
667
+ // queries currently fall back to a LIKE path with AND logic. When the user's
668
+ // phrasing doesn't match the summary verbatim (e.g. "端到端测试结果" vs
669
+ // "端到端测试"), ALL terms must match and the query returns 0 candidates.
670
+ //
671
+ // A trigram-tokenized table indexes every 3-character substring, enabling
672
+ // native CJK substring matching via FTS5 MATCH with OR semantics.
673
+ const cjkTableExists = db
674
+ .prepare(
675
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name='summaries_fts_cjk'",
676
+ )
677
+ .get();
678
+ if (!cjkTableExists) {
679
+ db.exec(`
680
+ CREATE VIRTUAL TABLE summaries_fts_cjk USING fts5(
681
+ summary_id UNINDEXED,
682
+ content,
683
+ tokenize='trigram'
684
+ );
685
+ INSERT INTO summaries_fts_cjk(summary_id, content)
686
+ SELECT summary_id, content FROM summaries;
687
+ `);
688
+ }
652
689
  }