@martian-engineering/lossless-claw 0.6.3 → 0.8.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 +26 -6
- package/docs/agent-tools.md +16 -5
- package/docs/configuration.md +223 -214
- package/openclaw.plugin.json +123 -0
- package/package.json +1 -1
- package/skills/lossless-claw/SKILL.md +3 -2
- package/skills/lossless-claw/references/architecture.md +12 -0
- package/skills/lossless-claw/references/config.md +135 -3
- package/skills/lossless-claw/references/diagnostics.md +13 -0
- package/src/assembler.ts +17 -5
- package/src/compaction.ts +161 -53
- package/src/db/config.ts +102 -4
- package/src/db/connection.ts +35 -7
- package/src/db/features.ts +24 -5
- package/src/db/migration.ts +257 -78
- package/src/engine.ts +1007 -110
- package/src/estimate-tokens.ts +80 -0
- package/src/lcm-log.ts +37 -0
- package/src/plugin/index.ts +493 -101
- package/src/plugin/lcm-command.ts +288 -7
- package/src/plugin/lcm-doctor-apply.ts +1 -3
- package/src/plugin/lcm-doctor-cleaners.ts +655 -0
- package/src/plugin/shared-init.ts +59 -0
- package/src/prune.ts +391 -0
- package/src/retrieval.ts +8 -9
- package/src/startup-banner-log.ts +1 -0
- package/src/store/compaction-telemetry-store.ts +156 -0
- package/src/store/conversation-store.ts +6 -1
- package/src/store/fts5-sanitize.ts +25 -4
- package/src/store/full-text-sort.ts +21 -0
- package/src/store/index.ts +8 -0
- package/src/store/summary-store.ts +21 -14
- package/src/summarize.ts +55 -34
- package/src/tools/lcm-describe-tool.ts +9 -4
- package/src/tools/lcm-expand-query-tool.ts +609 -200
- package/src/tools/lcm-expand-tool.ts +9 -4
- package/src/tools/lcm-grep-tool.ts +22 -8
- package/src/types.ts +1 -0
|
@@ -122,7 +122,8 @@ function buildOrchestrationObservability(input: {
|
|
|
122
122
|
*/
|
|
123
123
|
export function createLcmExpandTool(input: {
|
|
124
124
|
deps: LcmDependencies;
|
|
125
|
-
lcm
|
|
125
|
+
lcm?: LcmContextEngine;
|
|
126
|
+
getLcm?: () => Promise<LcmContextEngine>;
|
|
126
127
|
/** Runtime session key (used for delegated expansion auth scoping). */
|
|
127
128
|
sessionId?: string;
|
|
128
129
|
sessionKey?: string;
|
|
@@ -136,10 +137,14 @@ export function createLcmExpandTool(input: {
|
|
|
136
137
|
"Use this to drill into previously-compacted context when you need detail " +
|
|
137
138
|
"that was summarised away. Provide either summaryIds (direct expansion) or " +
|
|
138
139
|
"query (grep-first, then expand top matches). Returns a compact text payload " +
|
|
139
|
-
"
|
|
140
|
+
"plus cited IDs in tool output for follow-up.",
|
|
140
141
|
parameters: LcmExpandSchema,
|
|
141
142
|
async execute(_toolCallId, params) {
|
|
142
|
-
const
|
|
143
|
+
const lcm = input.lcm ?? (await input.getLcm?.());
|
|
144
|
+
if (!lcm) {
|
|
145
|
+
throw new Error("LCM engine is unavailable.");
|
|
146
|
+
}
|
|
147
|
+
const retrieval = lcm.getRetrieval();
|
|
143
148
|
const orchestrator = new ExpansionOrchestrator(retrieval);
|
|
144
149
|
const runtimeAuthManager = getRuntimeExpansionAuthManager();
|
|
145
150
|
|
|
@@ -178,7 +183,7 @@ export function createLcmExpandTool(input: {
|
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
const conversationScope = await resolveLcmConversationScope({
|
|
181
|
-
lcm
|
|
186
|
+
lcm,
|
|
182
187
|
deps: input.deps,
|
|
183
188
|
sessionId: input.sessionId,
|
|
184
189
|
sessionKey: input.sessionKey,
|
|
@@ -25,7 +25,7 @@ function formatDisplayTime(
|
|
|
25
25
|
const LcmGrepSchema = Type.Object({
|
|
26
26
|
pattern: Type.String({
|
|
27
27
|
description:
|
|
28
|
-
|
|
28
|
+
'Search pattern. Interpreted as regex when mode is "regex", or as an FTS5 text query when mode is "full_text". In full_text mode, FTS5 defaults to AND matching, so prefer 1-3 distinctive terms or one quoted multi-word phrase instead of padding with synonyms or extra keywords.',
|
|
29
29
|
}),
|
|
30
30
|
mode: Type.Optional(
|
|
31
31
|
Type.String({
|
|
@@ -70,6 +70,13 @@ const LcmGrepSchema = Type.Object({
|
|
|
70
70
|
maximum: 200,
|
|
71
71
|
}),
|
|
72
72
|
),
|
|
73
|
+
sort: Type.Optional(
|
|
74
|
+
Type.String({
|
|
75
|
+
description:
|
|
76
|
+
'Sort order: "recency" (newest first, default), "relevance" (best FTS5 match first, full_text mode only), or "hybrid" (full_text mode only; balances relevance with recency). Applied before limit is enforced.',
|
|
77
|
+
enum: ["recency", "relevance", "hybrid"],
|
|
78
|
+
}),
|
|
79
|
+
),
|
|
73
80
|
});
|
|
74
81
|
|
|
75
82
|
function truncateSnippet(content: string, maxLen: number = 200): string {
|
|
@@ -82,7 +89,8 @@ function truncateSnippet(content: string, maxLen: number = 200): string {
|
|
|
82
89
|
|
|
83
90
|
export function createLcmGrepTool(input: {
|
|
84
91
|
deps: LcmDependencies;
|
|
85
|
-
lcm
|
|
92
|
+
lcm?: LcmContextEngine;
|
|
93
|
+
getLcm?: () => Promise<LcmContextEngine>;
|
|
86
94
|
sessionId?: string;
|
|
87
95
|
sessionKey?: string;
|
|
88
96
|
}): AnyAgentTool {
|
|
@@ -93,18 +101,24 @@ export function createLcmGrepTool(input: {
|
|
|
93
101
|
"Search compacted conversation history using regex or full-text search. " +
|
|
94
102
|
"Searches across messages and/or summaries stored by LCM. " +
|
|
95
103
|
"Use this to find specific content that may have been compacted away from " +
|
|
96
|
-
"active context. Returns matching snippets with their summary/message IDs " +
|
|
104
|
+
"active context. In full_text mode, queries use FTS5 AND semantics by default, so keep them short and focused; quoted phrases stay intact and optional sort modes can prioritize relevance for older topics. Returns matching snippets with their summary/message IDs " +
|
|
97
105
|
"for follow-up with lcm_expand or lcm_describe.",
|
|
98
106
|
parameters: LcmGrepSchema,
|
|
99
107
|
async execute(_toolCallId, params) {
|
|
100
|
-
const
|
|
101
|
-
|
|
108
|
+
const lcm = input.lcm ?? (await input.getLcm?.());
|
|
109
|
+
if (!lcm) {
|
|
110
|
+
throw new Error("LCM engine is unavailable.");
|
|
111
|
+
}
|
|
112
|
+
const retrieval = lcm.getRetrieval();
|
|
113
|
+
const timezone = lcm.timezone;
|
|
102
114
|
|
|
103
115
|
const p = params as Record<string, unknown>;
|
|
104
116
|
const pattern = (p.pattern as string).trim();
|
|
105
117
|
const mode = (p.mode as "regex" | "full_text") ?? "regex";
|
|
106
118
|
const scope = (p.scope as "messages" | "summaries" | "both") ?? "both";
|
|
107
119
|
const limit = typeof p.limit === "number" ? Math.trunc(p.limit) : 50;
|
|
120
|
+
const requestedSort = (p.sort as "recency" | "relevance" | "hybrid") ?? "recency";
|
|
121
|
+
const effectiveSort = mode === "full_text" ? requestedSort : "recency";
|
|
108
122
|
let since: Date | undefined;
|
|
109
123
|
let before: Date | undefined;
|
|
110
124
|
try {
|
|
@@ -121,7 +135,7 @@ export function createLcmGrepTool(input: {
|
|
|
121
135
|
});
|
|
122
136
|
}
|
|
123
137
|
const conversationScope = await resolveLcmConversationScope({
|
|
124
|
-
lcm
|
|
138
|
+
lcm,
|
|
125
139
|
deps: input.deps,
|
|
126
140
|
sessionId: input.sessionId,
|
|
127
141
|
sessionKey: input.sessionKey,
|
|
@@ -133,7 +147,6 @@ export function createLcmGrepTool(input: {
|
|
|
133
147
|
"No LCM conversation found for this session. Provide conversationId or set allConversations=true.",
|
|
134
148
|
});
|
|
135
149
|
}
|
|
136
|
-
|
|
137
150
|
const result = await retrieval.grep({
|
|
138
151
|
query: pattern,
|
|
139
152
|
mode,
|
|
@@ -142,12 +155,13 @@ export function createLcmGrepTool(input: {
|
|
|
142
155
|
limit,
|
|
143
156
|
since,
|
|
144
157
|
before,
|
|
158
|
+
sort: effectiveSort,
|
|
145
159
|
});
|
|
146
160
|
|
|
147
161
|
const lines: string[] = [];
|
|
148
162
|
lines.push("## LCM Grep Results");
|
|
149
163
|
lines.push(`**Pattern:** \`${pattern}\``);
|
|
150
|
-
lines.push(`**Mode:** ${mode} | **Scope:** ${scope}`);
|
|
164
|
+
lines.push(`**Mode:** ${mode} | **Scope:** ${scope} | **Sort:** ${effectiveSort}`);
|
|
151
165
|
if (conversationScope.allConversations) {
|
|
152
166
|
lines.push("**Conversation scope:** all conversations");
|
|
153
167
|
} else if (conversationScope.conversationId != null) {
|
package/src/types.ts
CHANGED