engrm 0.4.36 → 0.4.38
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 +19 -0
- package/dist/hooks/session-start.js +1 -1
- package/dist/hooks/stop.js +1 -1
- package/dist/server.js +17 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -288,6 +288,25 @@ How to use it:
|
|
|
288
288
|
- `load_recall_item` opens an exact handoff, thread, chat, or memory key returned by the index
|
|
289
289
|
- `repair_recall` is the repair step when continuity is still thin, hook-only, or under-captured
|
|
290
290
|
|
|
291
|
+
### Explicit Save Protocol
|
|
292
|
+
|
|
293
|
+
When something should be remembered on purpose, do not wait for an end-of-session
|
|
294
|
+
digest if an explicit write is more appropriate.
|
|
295
|
+
|
|
296
|
+
Use:
|
|
297
|
+
|
|
298
|
+
- `save_observation`
|
|
299
|
+
- direct durable memory write for a bugfix, decision, discovery, pattern,
|
|
300
|
+
feature, or change
|
|
301
|
+
- `create_handoff` / `refresh_handoff`
|
|
302
|
+
- preserve the active thread for another device or a later session
|
|
303
|
+
- `capture_openclaw_content`
|
|
304
|
+
- save OpenClaw-style research, posting, outcomes, and next actions as
|
|
305
|
+
reusable memory
|
|
306
|
+
|
|
307
|
+
Automatic session digests are a safety net.
|
|
308
|
+
They are not the only path for preserving important work.
|
|
309
|
+
|
|
291
310
|
### Thin Tools, Thick Memory
|
|
292
311
|
|
|
293
312
|
Engrm now has a real thin-tool layer, not just a plugin spec.
|
|
@@ -3225,7 +3225,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync
|
|
|
3225
3225
|
import { join as join3 } from "node:path";
|
|
3226
3226
|
import { homedir } from "node:os";
|
|
3227
3227
|
var STATE_PATH = join3(homedir(), ".engrm", "config-fingerprint.json");
|
|
3228
|
-
var CLIENT_VERSION = "0.4.
|
|
3228
|
+
var CLIENT_VERSION = "0.4.38";
|
|
3229
3229
|
function hashFile(filePath) {
|
|
3230
3230
|
try {
|
|
3231
3231
|
if (!existsSync3(filePath))
|
package/dist/hooks/stop.js
CHANGED
|
@@ -3087,7 +3087,7 @@ function buildBeacon(db, config, sessionId, metrics) {
|
|
|
3087
3087
|
sentinel_used: valueSignals.security_findings_count > 0,
|
|
3088
3088
|
risk_score: riskScore,
|
|
3089
3089
|
stacks_detected: stacks,
|
|
3090
|
-
client_version: "0.4.
|
|
3090
|
+
client_version: "0.4.38",
|
|
3091
3091
|
context_observations_injected: metrics?.contextObsInjected ?? 0,
|
|
3092
3092
|
context_total_available: metrics?.contextTotalAvailable ?? 0,
|
|
3093
3093
|
recall_attempts: metrics?.recallAttempts ?? 0,
|
package/dist/server.js
CHANGED
|
@@ -16329,7 +16329,14 @@ async function searchObservations(db, input) {
|
|
|
16329
16329
|
}
|
|
16330
16330
|
}
|
|
16331
16331
|
const safeQuery = sanitizeFtsQuery(query);
|
|
16332
|
-
|
|
16332
|
+
let ftsResults = [];
|
|
16333
|
+
if (safeQuery) {
|
|
16334
|
+
try {
|
|
16335
|
+
ftsResults = db.searchFts(safeQuery, projectId, undefined, limit * 2, input.user_id);
|
|
16336
|
+
} catch {
|
|
16337
|
+
ftsResults = [];
|
|
16338
|
+
}
|
|
16339
|
+
}
|
|
16333
16340
|
let vecResults = [];
|
|
16334
16341
|
const queryEmbedding = await embedText(query);
|
|
16335
16342
|
if (queryEmbedding && db.vecAvailable) {
|
|
@@ -16392,11 +16399,13 @@ function mergeResults(ftsResults, vecResults, limit) {
|
|
|
16392
16399
|
return Array.from(scores.entries()).map(([id, score]) => ({ id, score })).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
16393
16400
|
}
|
|
16394
16401
|
function sanitizeFtsQuery(query) {
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16402
|
+
const normalized = String(query ?? "").normalize("NFKC").replace(/[^\p{L}\p{N}_\s]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
16403
|
+
if (!normalized)
|
|
16404
|
+
return "";
|
|
16405
|
+
const terms = normalized.split(" ").map((term) => term.trim()).filter(Boolean).slice(0, 16);
|
|
16406
|
+
if (terms.length === 0)
|
|
16398
16407
|
return "";
|
|
16399
|
-
return
|
|
16408
|
+
return terms.map((term) => `"${term}"`).join(" ");
|
|
16400
16409
|
}
|
|
16401
16410
|
|
|
16402
16411
|
// src/tools/recent-chat.ts
|
|
@@ -22871,9 +22880,9 @@ process.on("SIGTERM", () => {
|
|
|
22871
22880
|
});
|
|
22872
22881
|
var server = new McpServer({
|
|
22873
22882
|
name: "engrm",
|
|
22874
|
-
version: "0.4.
|
|
22883
|
+
version: "0.4.38"
|
|
22875
22884
|
});
|
|
22876
|
-
server.tool("save_observation", "
|
|
22885
|
+
server.tool("save_observation", "Directly save a durable memory item now. Use this when something should be remembered on purpose instead of waiting for an end-of-session digest.", {
|
|
22877
22886
|
type: exports_external.enum([
|
|
22878
22887
|
"bugfix",
|
|
22879
22888
|
"discovery",
|
|
@@ -23170,7 +23179,7 @@ Findings: ${findingSummary}` : ""}`
|
|
|
23170
23179
|
]
|
|
23171
23180
|
};
|
|
23172
23181
|
});
|
|
23173
|
-
server.tool("capture_openclaw_content", "
|
|
23182
|
+
server.tool("capture_openclaw_content", "Directly save OpenClaw content, research, and follow-up work as durable memory. Best for preserving posted outcomes, discoveries, and next actions during or right after the run.", {
|
|
23174
23183
|
title: exports_external.string().optional().describe("Short content, campaign, or research title."),
|
|
23175
23184
|
posted: exports_external.array(exports_external.string()).optional().describe("Concrete posted items or shipped content outcomes."),
|
|
23176
23185
|
researched: exports_external.array(exports_external.string()).optional().describe("Research or discovery items worth retaining."),
|
package/package.json
CHANGED