engrm 0.4.25 → 0.4.26
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 +6 -4
- package/dist/hooks/pre-compact.js +47 -11
- package/dist/hooks/session-start.js +858 -96
- package/dist/hooks/stop.js +1 -1
- package/dist/server.js +193 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,8 +212,8 @@ The MCP server exposes tools that supported agents can call directly:
|
|
|
212
212
|
| `memory_stats` | View high-level capture and sync health |
|
|
213
213
|
| `capture_status` | Check whether local hooks are registered and raw prompt/tool chronology is actually being captured |
|
|
214
214
|
| `activity_feed` | Inspect one chronological local feed across prompts, tools, chat, handoffs, observations, and summaries |
|
|
215
|
-
| `memory_console` | Show a high-signal local memory console for the current project |
|
|
216
|
-
| `project_memory_index` | Show typed local memory by project, including hot files
|
|
215
|
+
| `memory_console` | Show a high-signal local memory console for the current project, including continuity state |
|
|
216
|
+
| `project_memory_index` | Show typed local memory by project, including hot files, recent sessions, and continuity state |
|
|
217
217
|
| `workspace_memory_index` | Show cross-project local memory coverage across the whole workspace |
|
|
218
218
|
| `tool_memory_index` | Show which source tools and plugins are creating durable memory |
|
|
219
219
|
| `session_tool_memory` | Show which tools in one session produced reusable memory and which produced none |
|
|
@@ -357,14 +357,16 @@ Recommended flow:
|
|
|
357
357
|
What each tool is good for:
|
|
358
358
|
|
|
359
359
|
- `capture_status` tells you whether prompt/tool hooks are live on this machine
|
|
360
|
-
- `
|
|
360
|
+
- `capture_quality` shows whether chat recall is transcript-backed or still hook-only across the workspace
|
|
361
|
+
- `memory_console` gives the quickest project snapshot, including whether continuity is `fresh`, `thin`, or `cold`
|
|
361
362
|
- `activity_feed` shows the merged chronology across prompts, tools, chat, handoffs, observations, and summaries
|
|
362
363
|
- `recent_sessions` helps you pick a session worth opening
|
|
363
364
|
- `session_story` reconstructs one session in detail, including handoffs and chat recall
|
|
364
365
|
- `tool_memory_index` shows which tools and plugins are actually producing durable memory
|
|
365
366
|
- `session_tool_memory` shows which tool calls in one session turned into reusable memory and which did not
|
|
366
|
-
- `project_memory_index` shows typed memory by repo
|
|
367
|
+
- `project_memory_index` shows typed memory by repo, including continuity state and hot files
|
|
367
368
|
- `workspace_memory_index` shows coverage across all repos on the machine
|
|
369
|
+
- `recent_chat` / `search_chat` now report transcript-vs-hook coverage too, so weak OpenClaw recall is easier to diagnose and refresh
|
|
368
370
|
|
|
369
371
|
### Thin Tool Workflow
|
|
370
372
|
|
|
@@ -3225,6 +3225,7 @@ function compactLine(value) {
|
|
|
3225
3225
|
}
|
|
3226
3226
|
|
|
3227
3227
|
// src/context/inject.ts
|
|
3228
|
+
var FRESH_CONTINUITY_WINDOW_DAYS = 3;
|
|
3228
3229
|
function tokenizeProjectHint(text) {
|
|
3229
3230
|
return Array.from(new Set((text.toLowerCase().match(/[a-z0-9_+-]{4,}/g) ?? []).filter(Boolean)));
|
|
3230
3231
|
}
|
|
@@ -3364,20 +3365,21 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
3364
3365
|
const canonicalId = project?.canonical_id ?? detected.canonical_id;
|
|
3365
3366
|
if (maxCount !== undefined) {
|
|
3366
3367
|
const remaining = Math.max(0, maxCount - pinned.length - dedupedRecent.length);
|
|
3367
|
-
|
|
3368
|
-
const recentPrompts2 =
|
|
3369
|
-
const recentToolEvents2 =
|
|
3368
|
+
let all = [...pinned, ...dedupedRecent, ...sorted.slice(0, remaining)];
|
|
3369
|
+
const recentPrompts2 = isNewProject ? [] : db.getRecentUserPrompts(projectId, 6, opts.userId);
|
|
3370
|
+
const recentToolEvents2 = isNewProject ? [] : db.getRecentToolEvents(projectId, 6, opts.userId);
|
|
3370
3371
|
const recentSessions2 = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
3371
3372
|
const projectTypeCounts2 = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
3372
3373
|
const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions2);
|
|
3373
|
-
const recentHandoffs2 = getRecentHandoffs(db, {
|
|
3374
|
+
const recentHandoffs2 = isNewProject ? [] : getRecentHandoffs(db, {
|
|
3374
3375
|
cwd,
|
|
3375
|
-
project_scoped:
|
|
3376
|
+
project_scoped: true,
|
|
3376
3377
|
user_id: opts.userId,
|
|
3377
3378
|
current_device_id: opts.currentDeviceId,
|
|
3378
3379
|
limit: 3
|
|
3379
3380
|
}).handoffs;
|
|
3380
3381
|
const recentChatMessages2 = !isNewProject && project ? db.getRecentChatMessages(project.id, 4, opts.userId) : [];
|
|
3382
|
+
all = filterAutoLoadedObservationsForContinuity(all, pinned, isNewProject, recentPrompts2, recentToolEvents2, recentSessions2, recentHandoffs2, recentChatMessages2, summariesFromRecentSessions(db, projectId, recentSessions2));
|
|
3381
3383
|
return {
|
|
3382
3384
|
project_name: projectName,
|
|
3383
3385
|
canonical_id: canonicalId,
|
|
@@ -3413,19 +3415,20 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
3413
3415
|
selected.push(obs);
|
|
3414
3416
|
}
|
|
3415
3417
|
const summaries = isNewProject ? [] : db.getRecentSummaries(projectId, 5);
|
|
3416
|
-
const recentPrompts =
|
|
3417
|
-
const recentToolEvents =
|
|
3418
|
+
const recentPrompts = isNewProject ? [] : db.getRecentUserPrompts(projectId, 6, opts.userId);
|
|
3419
|
+
const recentToolEvents = isNewProject ? [] : db.getRecentToolEvents(projectId, 6, opts.userId);
|
|
3418
3420
|
const recentSessions = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
|
|
3419
3421
|
const projectTypeCounts = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
|
|
3420
3422
|
const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions);
|
|
3421
|
-
const recentHandoffs = getRecentHandoffs(db, {
|
|
3423
|
+
const recentHandoffs = isNewProject ? [] : getRecentHandoffs(db, {
|
|
3422
3424
|
cwd,
|
|
3423
|
-
project_scoped:
|
|
3425
|
+
project_scoped: true,
|
|
3424
3426
|
user_id: opts.userId,
|
|
3425
3427
|
current_device_id: opts.currentDeviceId,
|
|
3426
3428
|
limit: 3
|
|
3427
3429
|
}).handoffs;
|
|
3428
3430
|
const recentChatMessages = !isNewProject ? db.getRecentChatMessages(projectId, 4, opts.userId) : [];
|
|
3431
|
+
const filteredSelected = filterAutoLoadedObservationsForContinuity(selected, pinned, isNewProject, recentPrompts, recentToolEvents, recentSessions, recentHandoffs, recentChatMessages, summaries);
|
|
3429
3432
|
let securityFindings = [];
|
|
3430
3433
|
if (!isNewProject) {
|
|
3431
3434
|
try {
|
|
@@ -3473,8 +3476,8 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
3473
3476
|
return {
|
|
3474
3477
|
project_name: projectName,
|
|
3475
3478
|
canonical_id: canonicalId,
|
|
3476
|
-
observations:
|
|
3477
|
-
session_count:
|
|
3479
|
+
observations: filteredSelected.map(toContextObservation),
|
|
3480
|
+
session_count: filteredSelected.length,
|
|
3478
3481
|
total_active: totalActive,
|
|
3479
3482
|
summaries: summaries.length > 0 ? summaries : undefined,
|
|
3480
3483
|
securityFindings: securityFindings.length > 0 ? securityFindings : undefined,
|
|
@@ -3489,6 +3492,39 @@ function buildSessionContext(db, cwd, options = {}) {
|
|
|
3489
3492
|
recentChatMessages: recentChatMessages.length > 0 ? recentChatMessages : undefined
|
|
3490
3493
|
};
|
|
3491
3494
|
}
|
|
3495
|
+
function filterAutoLoadedObservationsForContinuity(observations, pinned, isNewProject, recentPrompts, recentToolEvents, recentSessions, recentHandoffs, recentChatMessages, summaries) {
|
|
3496
|
+
if (isNewProject)
|
|
3497
|
+
return observations;
|
|
3498
|
+
if (hasFreshProjectContinuity(recentPrompts, recentToolEvents, recentSessions, recentHandoffs, recentChatMessages, summaries)) {
|
|
3499
|
+
return observations;
|
|
3500
|
+
}
|
|
3501
|
+
const pinnedIds = new Set(pinned.map((obs) => obs.id));
|
|
3502
|
+
return observations.filter((obs) => {
|
|
3503
|
+
if (pinnedIds.has(obs.id))
|
|
3504
|
+
return true;
|
|
3505
|
+
return observationAgeDays(obs.created_at_epoch) <= FRESH_CONTINUITY_WINDOW_DAYS;
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
function hasFreshProjectContinuity(recentPrompts, recentToolEvents, recentSessions, recentHandoffs, recentChatMessages, summaries) {
|
|
3509
|
+
const freshEnough = (epoch) => typeof epoch === "number" && observationAgeDays(epoch) <= FRESH_CONTINUITY_WINDOW_DAYS;
|
|
3510
|
+
return recentPrompts.some((item) => freshEnough(item.created_at_epoch)) || recentToolEvents.some((item) => freshEnough(item.created_at_epoch)) || recentSessions.some((item) => freshEnough(item.completed_at_epoch ?? item.started_at_epoch)) || recentHandoffs.some((item) => freshEnough(item.created_at_epoch)) || recentChatMessages.some((item) => freshEnough(item.created_at_epoch)) || summaries.some((item) => freshEnough(item.created_at_epoch));
|
|
3511
|
+
}
|
|
3512
|
+
function summariesFromRecentSessions(db, projectId, recentSessions) {
|
|
3513
|
+
const seen = new Set;
|
|
3514
|
+
const rows = [];
|
|
3515
|
+
for (const session of recentSessions) {
|
|
3516
|
+
if (seen.has(session.session_id))
|
|
3517
|
+
continue;
|
|
3518
|
+
seen.add(session.session_id);
|
|
3519
|
+
const summary = db.getSessionSummary(session.session_id);
|
|
3520
|
+
if (summary && summary.project_id === projectId)
|
|
3521
|
+
rows.push(summary);
|
|
3522
|
+
}
|
|
3523
|
+
return rows;
|
|
3524
|
+
}
|
|
3525
|
+
function observationAgeDays(createdAtEpoch) {
|
|
3526
|
+
return Math.max(0, (Math.floor(Date.now() / 1000) - createdAtEpoch) / 86400);
|
|
3527
|
+
}
|
|
3492
3528
|
function estimateObservationTokens(obs, index) {
|
|
3493
3529
|
const DETAILED_THRESHOLD = 5;
|
|
3494
3530
|
const titleCost = estimateTokens(`- **[${obs.type}]** ${obs.title} (2026-01-01, q=0.5)`);
|