@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.
- package/README.md +31 -1
- package/docs/configuration.md +23 -0
- package/openclaw.plugin.json +75 -0
- package/package.json +2 -1
- package/skills/lossless-claw/SKILL.md +33 -0
- package/skills/lossless-claw/references/architecture.md +52 -0
- package/skills/lossless-claw/references/config.md +263 -0
- package/skills/lossless-claw/references/diagnostics.md +79 -0
- package/skills/lossless-claw/references/recall-tools.md +55 -0
- package/skills/lossless-claw/references/session-lifecycle.md +59 -0
- package/src/assembler.ts +132 -36
- package/src/compaction.ts +17 -1
- package/src/db/config.ts +52 -20
- package/src/db/migration.ts +50 -13
- package/src/engine.ts +721 -131
- package/src/plugin/index.ts +45 -0
- package/src/plugin/lcm-command.ts +759 -0
- package/src/plugin/lcm-doctor-apply.ts +546 -0
- package/src/plugin/lcm-doctor-shared.ts +210 -0
- package/src/store/conversation-store.ts +60 -21
- package/src/store/parse-utc-timestamp.ts +25 -0
- package/src/store/summary-store.ts +380 -11
- package/src/summarize.ts +107 -20
- package/src/tools/lcm-expand-query-tool.ts +58 -25
- package/src/tools/lcm-expansion-recursion-guard.ts +87 -0
package/src/db/migration.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
}
|