@martian-engineering/lossless-claw 0.8.0 → 0.8.1

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.
Files changed (52) hide show
  1. package/README.md +8 -0
  2. package/dist/index.js +19240 -0
  3. package/docs/configuration.md +15 -5
  4. package/openclaw.plugin.json +27 -3
  5. package/package.json +7 -6
  6. package/skills/lossless-claw/references/config.md +37 -0
  7. package/index.ts +0 -2
  8. package/src/assembler.ts +0 -1196
  9. package/src/compaction.ts +0 -1753
  10. package/src/db/config.ts +0 -345
  11. package/src/db/connection.ts +0 -151
  12. package/src/db/features.ts +0 -61
  13. package/src/db/migration.ts +0 -868
  14. package/src/engine.ts +0 -4486
  15. package/src/estimate-tokens.ts +0 -80
  16. package/src/expansion-auth.ts +0 -365
  17. package/src/expansion-policy.ts +0 -303
  18. package/src/expansion.ts +0 -383
  19. package/src/integrity.ts +0 -600
  20. package/src/large-files.ts +0 -546
  21. package/src/lcm-log.ts +0 -37
  22. package/src/openclaw-bridge.ts +0 -22
  23. package/src/plugin/index.ts +0 -2037
  24. package/src/plugin/lcm-command.ts +0 -1040
  25. package/src/plugin/lcm-doctor-apply.ts +0 -540
  26. package/src/plugin/lcm-doctor-cleaners.ts +0 -655
  27. package/src/plugin/lcm-doctor-shared.ts +0 -210
  28. package/src/plugin/shared-init.ts +0 -59
  29. package/src/prune.ts +0 -391
  30. package/src/retrieval.ts +0 -360
  31. package/src/session-patterns.ts +0 -23
  32. package/src/startup-banner-log.ts +0 -49
  33. package/src/store/compaction-telemetry-store.ts +0 -156
  34. package/src/store/conversation-store.ts +0 -929
  35. package/src/store/fts5-sanitize.ts +0 -50
  36. package/src/store/full-text-fallback.ts +0 -83
  37. package/src/store/full-text-sort.ts +0 -21
  38. package/src/store/index.ts +0 -39
  39. package/src/store/parse-utc-timestamp.ts +0 -25
  40. package/src/store/summary-store.ts +0 -1519
  41. package/src/summarize.ts +0 -1508
  42. package/src/tools/common.ts +0 -53
  43. package/src/tools/lcm-conversation-scope.ts +0 -127
  44. package/src/tools/lcm-describe-tool.ts +0 -245
  45. package/src/tools/lcm-expand-query-tool.ts +0 -1235
  46. package/src/tools/lcm-expand-tool.delegation.ts +0 -580
  47. package/src/tools/lcm-expand-tool.ts +0 -453
  48. package/src/tools/lcm-expansion-recursion-guard.ts +0 -373
  49. package/src/tools/lcm-grep-tool.ts +0 -228
  50. package/src/transaction-mutex.ts +0 -136
  51. package/src/transcript-repair.ts +0 -301
  52. package/src/types.ts +0 -165
@@ -1,50 +0,0 @@
1
- /**
2
- * Sanitize a user-provided query for use in an FTS5 MATCH expression.
3
- *
4
- * FTS5 treats certain characters as operators:
5
- * - `-` (NOT), `+` (required), `*` (prefix), `^` (initial token)
6
- * - `OR`, `AND`, `NOT` (boolean operators)
7
- * - `:` (column filter — e.g. `agent:foo` means "search column agent")
8
- * - `"` (phrase query), `(` `)` (grouping)
9
- * - `NEAR` (proximity)
10
- *
11
- * If the query contains any of these, naive MATCH will either error
12
- * ("no such column") or return unexpected results.
13
- *
14
- * Strategy: wrap each whitespace-delimited token in double quotes so FTS5
15
- * treats it as a literal phrase token. Internal double quotes are stripped.
16
- * Empty tokens are dropped. Tokens are joined with spaces (implicit AND).
17
- *
18
- * Examples:
19
- * "sub-agent restrict" → '"sub-agent" "restrict"'
20
- * "lcm_expand OR crash" → '"lcm_expand" "OR" "crash"'
21
- * 'hello "world"' → '"hello" "world"'
22
- */
23
- export function sanitizeFts5Query(raw: string): string {
24
- // Preserve user-quoted phrases: extract "..." groups first, then tokenize the rest.
25
- const parts: string[] = [];
26
- const phraseRegex = /"([^"]+)"/g;
27
- let match: RegExpExecArray | null;
28
- let lastIndex = 0;
29
-
30
- while ((match = phraseRegex.exec(raw)) !== null) {
31
- // Process unquoted text before this phrase
32
- const before = raw.slice(lastIndex, match.index);
33
- for (const t of before.split(/\s+/).filter(Boolean)) {
34
- parts.push(`"${t.replace(/"/g, "")}"`);
35
- }
36
- // Preserve the phrase as-is (strip internal quotes for safety)
37
- const phrase = match[1].replace(/"/g, "").trim();
38
- if (phrase) {
39
- parts.push(`"${phrase}"`);
40
- }
41
- lastIndex = match.index + match[0].length;
42
- }
43
-
44
- // Process unquoted text after last phrase
45
- for (const t of raw.slice(lastIndex).split(/\s+/).filter(Boolean)) {
46
- parts.push(`"${t.replace(/"/g, "")}"`);
47
- }
48
-
49
- return parts.length > 0 ? parts.join(" ") : '""';
50
- }
@@ -1,83 +0,0 @@
1
- const RAW_TERM_RE = /"([^"]+)"|(\S+)/g;
2
-
3
- /**
4
- * Detect whether a query contains CJK characters that FTS5 unicode61
5
- * tokenizer cannot index properly.
6
- */
7
- const CJK_RE = /[\u2E80-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\uAC00-\uD7AF\u3040-\u309F\u30A0-\u30FF]/;
8
- export function containsCjk(text: string): boolean {
9
- return CJK_RE.test(text);
10
- }
11
- const EDGE_PUNCTUATION_RE = /^[`'"()[\]{}<>.,:;!?*_+=|\\/-]+|[`'"()[\]{}<>.,:;!?*_+=|\\/-]+$/g;
12
-
13
- export type LikeSearchPlan = {
14
- terms: string[];
15
- where: string[];
16
- args: string[];
17
- };
18
-
19
- function normalizeFallbackTerm(raw: string): string {
20
- return raw.trim().replace(EDGE_PUNCTUATION_RE, "").toLowerCase();
21
- }
22
-
23
- function escapeLike(term: string): string {
24
- return term.replace(/([\\%_])/g, "\\$1");
25
- }
26
-
27
- /**
28
- * Convert a free-text query into a conservative LIKE search plan.
29
- *
30
- * The fallback keeps phrase tokens when the query uses double quotes, and
31
- * otherwise searches for all normalized tokens as case-insensitive substrings.
32
- */
33
- export function buildLikeSearchPlan(column: string, query: string): LikeSearchPlan {
34
- const terms: string[] = [];
35
- for (const match of query.matchAll(RAW_TERM_RE)) {
36
- const raw = match[1] ?? match[2] ?? "";
37
- const normalized = normalizeFallbackTerm(raw);
38
- if (normalized.length > 0 && !terms.includes(normalized)) {
39
- terms.push(normalized);
40
- }
41
- }
42
-
43
- if (terms.length === 0) {
44
- const fallback = normalizeFallbackTerm(query);
45
- if (fallback.length > 0) {
46
- terms.push(fallback);
47
- }
48
- }
49
-
50
- return {
51
- terms,
52
- where: terms.map(() => `LOWER(${column}) LIKE ? ESCAPE '\\'`),
53
- args: terms.map((term) => `%${escapeLike(term)}%`),
54
- };
55
- }
56
-
57
- /**
58
- * Build a compact snippet centered around the earliest matching term.
59
- */
60
- export function createFallbackSnippet(content: string, terms: string[]): string {
61
- const haystack = content.toLowerCase();
62
- let matchIndex = -1;
63
- let matchLength = 0;
64
-
65
- for (const term of terms) {
66
- const idx = haystack.indexOf(term);
67
- if (idx !== -1 && (matchIndex === -1 || idx < matchIndex)) {
68
- matchIndex = idx;
69
- matchLength = term.length;
70
- }
71
- }
72
-
73
- if (matchIndex === -1) {
74
- const head = content.trim();
75
- return head.length <= 80 ? head : `${head.slice(0, 77).trimEnd()}...`;
76
- }
77
-
78
- const start = Math.max(0, matchIndex - 24);
79
- const end = Math.min(content.length, matchIndex + Math.max(matchLength, 1) + 40);
80
- const prefix = start > 0 ? "..." : "";
81
- const suffix = end < content.length ? "..." : "";
82
- return `${prefix}${content.slice(start, end).trim()}${suffix}`;
83
- }
@@ -1,21 +0,0 @@
1
- export type SearchSort = "recency" | "relevance" | "hybrid";
2
-
3
- export const AGE_DECAY_RATE = 0.001;
4
-
5
- /**
6
- * Build the ORDER BY clause for FTS5-backed searches.
7
- *
8
- * `rank` is FTS5's BM25 score where lower (more negative) is better.
9
- * `hybrid` keeps that relevance signal but applies a mild age penalty before
10
- * LIMIT is enforced so older strong matches can still surface.
11
- */
12
- export function buildFtsOrderBy(sort: SearchSort | undefined, createdAtExpr: string): string {
13
- switch (sort ?? "recency") {
14
- case "relevance":
15
- return `rank ASC, ${createdAtExpr} DESC`;
16
- case "hybrid":
17
- return `(rank / (1 + ((julianday('now') - julianday(${createdAtExpr})) * 24 * ${AGE_DECAY_RATE}))) ASC, ${createdAtExpr} DESC`;
18
- default:
19
- return `${createdAtExpr} DESC`;
20
- }
21
- }
@@ -1,39 +0,0 @@
1
- export { ConversationStore } from "./conversation-store.js";
2
- export type {
3
- ConversationId,
4
- MessageId,
5
- SummaryId,
6
- MessageRole,
7
- MessagePartType,
8
- MessageRecord,
9
- MessagePartRecord,
10
- ConversationRecord,
11
- CreateMessageInput,
12
- CreateMessagePartInput,
13
- CreateConversationInput,
14
- MessageSearchInput,
15
- MessageSearchResult,
16
- } from "./conversation-store.js";
17
-
18
- export { SummaryStore } from "./summary-store.js";
19
- export type {
20
- SummaryKind,
21
- ContextItemType,
22
- CreateSummaryInput,
23
- SummaryRecord,
24
- ContextItemRecord,
25
- SummarySearchInput,
26
- SummarySearchResult,
27
- CreateLargeFileInput,
28
- LargeFileRecord,
29
- UpsertConversationBootstrapStateInput,
30
- ConversationBootstrapStateRecord,
31
- } from "./summary-store.js";
32
-
33
- export { CompactionTelemetryStore } from "./compaction-telemetry-store.js";
34
- export type {
35
- CacheState,
36
- ActivityBand,
37
- ConversationCompactionTelemetryRecord,
38
- UpsertConversationCompactionTelemetryInput,
39
- } from "./compaction-telemetry-store.js";
@@ -1,25 +0,0 @@
1
- /**
2
- * Parse a SQLite UTC timestamp string into a Date object.
3
- * SQLite stores timestamps via datetime('now') without a Z suffix,
4
- * which causes JS to parse them as local time instead of UTC.
5
- * See: https://github.com/Martian-Engineering/lossless-claw/issues/216
6
- */
7
- export function parseUtcTimestamp(value: string): Date {
8
- const s = value.trim();
9
- if (/(?:[zZ]|[+-]\d{2}:\d{2})$/.test(s)) {
10
- return new Date(s);
11
- }
12
-
13
- const normalized = s.includes("T") ? s : s.replace(" ", "T");
14
- return new Date(`${normalized}Z`);
15
- }
16
-
17
- /**
18
- * Parse a nullable SQLite UTC timestamp string into a Date object.
19
- */
20
- export function parseUtcTimestampOrNull(
21
- value: string | null | undefined,
22
- ): Date | null {
23
- if (value == null) return null;
24
- return parseUtcTimestamp(value);
25
- }