@martian-engineering/lossless-claw 0.7.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 (54) hide show
  1. package/README.md +19 -3
  2. package/dist/index.js +19240 -0
  3. package/docs/agent-tools.md +9 -4
  4. package/docs/configuration.md +24 -5
  5. package/openclaw.plugin.json +27 -3
  6. package/package.json +7 -6
  7. package/skills/lossless-claw/SKILL.md +3 -2
  8. package/skills/lossless-claw/references/architecture.md +12 -0
  9. package/skills/lossless-claw/references/config.md +37 -0
  10. package/skills/lossless-claw/references/diagnostics.md +13 -0
  11. package/index.ts +0 -2
  12. package/src/assembler.ts +0 -1188
  13. package/src/compaction.ts +0 -1756
  14. package/src/db/config.ts +0 -345
  15. package/src/db/connection.ts +0 -141
  16. package/src/db/features.ts +0 -42
  17. package/src/db/migration.ts +0 -746
  18. package/src/engine.ts +0 -4306
  19. package/src/expansion-auth.ts +0 -365
  20. package/src/expansion-policy.ts +0 -303
  21. package/src/expansion.ts +0 -383
  22. package/src/integrity.ts +0 -600
  23. package/src/large-files.ts +0 -546
  24. package/src/lcm-log.ts +0 -37
  25. package/src/openclaw-bridge.ts +0 -22
  26. package/src/plugin/index.ts +0 -1960
  27. package/src/plugin/lcm-command.ts +0 -765
  28. package/src/plugin/lcm-doctor-apply.ts +0 -542
  29. package/src/plugin/lcm-doctor-shared.ts +0 -210
  30. package/src/plugin/shared-init.ts +0 -59
  31. package/src/prune.ts +0 -391
  32. package/src/retrieval.ts +0 -363
  33. package/src/session-patterns.ts +0 -23
  34. package/src/startup-banner-log.ts +0 -49
  35. package/src/store/compaction-telemetry-store.ts +0 -156
  36. package/src/store/conversation-store.ts +0 -929
  37. package/src/store/fts5-sanitize.ts +0 -50
  38. package/src/store/full-text-fallback.ts +0 -83
  39. package/src/store/full-text-sort.ts +0 -21
  40. package/src/store/index.ts +0 -39
  41. package/src/store/parse-utc-timestamp.ts +0 -25
  42. package/src/store/summary-store.ts +0 -1519
  43. package/src/summarize.ts +0 -1511
  44. package/src/tools/common.ts +0 -53
  45. package/src/tools/lcm-conversation-scope.ts +0 -127
  46. package/src/tools/lcm-describe-tool.ts +0 -245
  47. package/src/tools/lcm-expand-query-tool.ts +0 -831
  48. package/src/tools/lcm-expand-tool.delegation.ts +0 -580
  49. package/src/tools/lcm-expand-tool.ts +0 -453
  50. package/src/tools/lcm-expansion-recursion-guard.ts +0 -373
  51. package/src/tools/lcm-grep-tool.ts +0 -228
  52. package/src/transaction-mutex.ts +0 -136
  53. package/src/transcript-repair.ts +0 -301
  54. 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
- }