engrm 0.4.41 → 0.4.43
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 +13 -1
- package/dist/cli.js +40 -4
- package/dist/hooks/elicitation-result.js +9 -2
- package/dist/hooks/post-tool-use.js +9 -2
- package/dist/hooks/pre-compact.js +9 -2
- package/dist/hooks/sentinel.js +9 -2
- package/dist/hooks/session-start.js +13 -3
- package/dist/hooks/stop.js +81 -112
- package/dist/hooks/user-prompt-submit.js +9 -2
- package/dist/server.js +47 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,7 @@ Add this to `~/.engrm/settings.json`:
|
|
|
110
110
|
"port": 3767,
|
|
111
111
|
"bearer_tokens": ["replace-with-a-long-random-token"]
|
|
112
112
|
},
|
|
113
|
+
"tool_profile": "memory",
|
|
113
114
|
"fleet": {
|
|
114
115
|
"project_name": "shared-experience",
|
|
115
116
|
"namespace": "ns_fleet_shared",
|
|
@@ -139,12 +140,23 @@ Hermes should connect with a bearer token header:
|
|
|
139
140
|
}
|
|
140
141
|
```
|
|
141
142
|
|
|
143
|
+
If Hermes and OpenClaw run on the same machine, use `http://localhost:3767/mcp`.
|
|
144
|
+
That is the correct MCP URL for same-host deployments.
|
|
145
|
+
|
|
146
|
+
On macOS, watch out for mixed global npm prefixes. If you have both
|
|
147
|
+
`~/.npm-global` and `/opt/homebrew` installs, make sure the long-running Engrm
|
|
148
|
+
HTTP service points at the same package location that your `npm install -g`
|
|
149
|
+
updates. A stale LaunchAgent path can keep serving an older Engrm build even
|
|
150
|
+
after a successful npm upgrade.
|
|
151
|
+
|
|
142
152
|
Fleet writes:
|
|
143
153
|
- use the reserved project name `shared-experience` by default
|
|
144
154
|
- stay `shared` unless explicitly overridden
|
|
145
155
|
- sync to the dedicated fleet namespace/key instead of the normal org namespace
|
|
146
156
|
- get an extra outbound scrub pass that redacts hostnames, IPs, and MACs before upload
|
|
147
157
|
|
|
158
|
+
For Hermes-style shared learning deployments, set `"tool_profile": "memory"` to expose a reduced Engrm tool set focused on durable memory, recall, and thread resumption instead of the full developer-oriented surface.
|
|
159
|
+
|
|
148
160
|
---
|
|
149
161
|
|
|
150
162
|
## How It Works
|
|
@@ -655,7 +667,7 @@ Engrm auto-registers in:
|
|
|
655
667
|
- **Local storage:** SQLite via `better-sqlite3`, FTS5 full-text search, `sqlite-vec` for embeddings
|
|
656
668
|
- **Embeddings:** all-MiniLM-L6-v2 via `@xenova/transformers` (384 dims, ~23MB)
|
|
657
669
|
- **Remote backend:** Candengo Vector (BGE-M3, Qdrant, hybrid dense+sparse search)
|
|
658
|
-
- **MCP:** `@modelcontextprotocol/sdk` (stdio
|
|
670
|
+
- **MCP:** `@modelcontextprotocol/sdk` (stdio for local agents, Streamable HTTP + SSE compatibility for Hermes-style remote clients)
|
|
659
671
|
- **AI extraction:** `@anthropic-ai/claude-agent-sdk` (optional, for richer observations)
|
|
660
672
|
|
|
661
673
|
---
|
package/dist/cli.js
CHANGED
|
@@ -114,7 +114,8 @@ function createDefaultConfig() {
|
|
|
114
114
|
project_name: "shared-experience",
|
|
115
115
|
namespace: "",
|
|
116
116
|
api_key: ""
|
|
117
|
-
}
|
|
117
|
+
},
|
|
118
|
+
tool_profile: "full"
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
function loadConfig() {
|
|
@@ -185,7 +186,8 @@ function loadConfig() {
|
|
|
185
186
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
186
187
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
187
188
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
188
|
-
}
|
|
189
|
+
},
|
|
190
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
189
191
|
};
|
|
190
192
|
}
|
|
191
193
|
function saveConfig(config) {
|
|
@@ -240,6 +242,11 @@ function asObserverMode(value, fallback) {
|
|
|
240
242
|
return value;
|
|
241
243
|
return fallback;
|
|
242
244
|
}
|
|
245
|
+
function asToolProfile(value, fallback) {
|
|
246
|
+
if (value === "full" || value === "memory")
|
|
247
|
+
return value;
|
|
248
|
+
return fallback;
|
|
249
|
+
}
|
|
243
250
|
function asTeams(value, fallback) {
|
|
244
251
|
if (!Array.isArray(value))
|
|
245
252
|
return fallback;
|
|
@@ -4006,7 +4013,8 @@ function createDefaultConfig2() {
|
|
|
4006
4013
|
project_name: "shared-experience",
|
|
4007
4014
|
namespace: "",
|
|
4008
4015
|
api_key: ""
|
|
4009
|
-
}
|
|
4016
|
+
},
|
|
4017
|
+
tool_profile: "full"
|
|
4010
4018
|
};
|
|
4011
4019
|
}
|
|
4012
4020
|
function loadConfig2() {
|
|
@@ -4077,7 +4085,8 @@ function loadConfig2() {
|
|
|
4077
4085
|
project_name: asString2(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
4078
4086
|
namespace: asString2(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
4079
4087
|
api_key: asString2(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
4080
|
-
}
|
|
4088
|
+
},
|
|
4089
|
+
tool_profile: asToolProfile2(config["tool_profile"], defaults.tool_profile)
|
|
4081
4090
|
};
|
|
4082
4091
|
}
|
|
4083
4092
|
function saveConfig2(config) {
|
|
@@ -4132,12 +4141,35 @@ function asObserverMode2(value, fallback) {
|
|
|
4132
4141
|
return value;
|
|
4133
4142
|
return fallback;
|
|
4134
4143
|
}
|
|
4144
|
+
function asToolProfile2(value, fallback) {
|
|
4145
|
+
if (value === "full" || value === "memory")
|
|
4146
|
+
return value;
|
|
4147
|
+
return fallback;
|
|
4148
|
+
}
|
|
4135
4149
|
function asTeams2(value, fallback) {
|
|
4136
4150
|
if (!Array.isArray(value))
|
|
4137
4151
|
return fallback;
|
|
4138
4152
|
return value.filter((t) => typeof t === "object" && t !== null && typeof t.id === "string" && typeof t.name === "string" && typeof t.namespace === "string");
|
|
4139
4153
|
}
|
|
4140
4154
|
|
|
4155
|
+
// src/tool-profiles.ts
|
|
4156
|
+
var MEMORY_PROFILE_TOOLS = [
|
|
4157
|
+
"save_observation",
|
|
4158
|
+
"search_recall",
|
|
4159
|
+
"resume_thread",
|
|
4160
|
+
"list_recall_items",
|
|
4161
|
+
"load_recall_item",
|
|
4162
|
+
"recent_chat",
|
|
4163
|
+
"search_chat",
|
|
4164
|
+
"refresh_chat_recall",
|
|
4165
|
+
"repair_recall"
|
|
4166
|
+
];
|
|
4167
|
+
function getEnabledToolNames(profile) {
|
|
4168
|
+
if (!profile || profile === "full")
|
|
4169
|
+
return null;
|
|
4170
|
+
return new Set(MEMORY_PROFILE_TOOLS);
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4141
4173
|
// src/tools/capture-status.ts
|
|
4142
4174
|
var LEGACY_CODEX_SERVER_NAME2 = `candengo-${"mem"}`;
|
|
4143
4175
|
function getCaptureStatus(db, input = {}) {
|
|
@@ -4242,6 +4274,8 @@ function getCaptureStatus(db, input = {}) {
|
|
|
4242
4274
|
http_enabled: Boolean(config?.http?.enabled || process.env.ENGRM_HTTP_PORT),
|
|
4243
4275
|
http_port: config?.http?.port ?? (process.env.ENGRM_HTTP_PORT ? Number(process.env.ENGRM_HTTP_PORT) : null),
|
|
4244
4276
|
http_bearer_token_count: config?.http?.bearer_tokens?.length ?? 0,
|
|
4277
|
+
tool_profile: config?.tool_profile ?? "full",
|
|
4278
|
+
enabled_tool_count: config ? getEnabledToolNames(config.tool_profile)?.size ?? null : null,
|
|
4245
4279
|
fleet_project_name: config?.fleet?.project_name ?? null,
|
|
4246
4280
|
fleet_configured: Boolean(config?.fleet?.namespace && config?.fleet?.api_key),
|
|
4247
4281
|
claude_mcp_registered: claudeMcpRegistered,
|
|
@@ -4724,6 +4758,7 @@ function handleStatus() {
|
|
|
4724
4758
|
console.log(` Sync: ${config.sync.enabled ? "enabled" : "disabled"}`);
|
|
4725
4759
|
console.log(` HTTP MCP: ${config.http.enabled ? `enabled (:${config.http.port})` : "disabled"}`);
|
|
4726
4760
|
console.log(` HTTP tokens: ${config.http.bearer_tokens.length}`);
|
|
4761
|
+
console.log(` Tool profile: ${config.tool_profile ?? "full"}`);
|
|
4727
4762
|
console.log(` Fleet project: ${config.fleet.project_name || "(not set)"}`);
|
|
4728
4763
|
console.log(` Fleet sync: ${config.fleet.namespace && config.fleet.api_key ? "configured" : "not configured"}`);
|
|
4729
4764
|
const claudeJson = join8(homedir5(), ".claude.json");
|
|
@@ -5083,6 +5118,7 @@ async function handleDoctor() {
|
|
|
5083
5118
|
} else {
|
|
5084
5119
|
info("HTTP MCP disabled");
|
|
5085
5120
|
}
|
|
5121
|
+
info(`Tool profile: ${config.tool_profile ?? "full"}`);
|
|
5086
5122
|
if (config.fleet.project_name) {
|
|
5087
5123
|
if (config.fleet.namespace && config.fleet.api_key) {
|
|
5088
5124
|
pass(`Fleet project '${config.fleet.project_name}' is configured`);
|
|
@@ -986,7 +986,8 @@ function createDefaultConfig() {
|
|
|
986
986
|
project_name: "shared-experience",
|
|
987
987
|
namespace: "",
|
|
988
988
|
api_key: ""
|
|
989
|
-
}
|
|
989
|
+
},
|
|
990
|
+
tool_profile: "full"
|
|
990
991
|
};
|
|
991
992
|
}
|
|
992
993
|
function loadConfig() {
|
|
@@ -1057,7 +1058,8 @@ function loadConfig() {
|
|
|
1057
1058
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
1058
1059
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
1059
1060
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
1060
|
-
}
|
|
1061
|
+
},
|
|
1062
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
1061
1063
|
};
|
|
1062
1064
|
}
|
|
1063
1065
|
function saveConfig(config) {
|
|
@@ -1112,6 +1114,11 @@ function asObserverMode(value, fallback) {
|
|
|
1112
1114
|
return value;
|
|
1113
1115
|
return fallback;
|
|
1114
1116
|
}
|
|
1117
|
+
function asToolProfile(value, fallback) {
|
|
1118
|
+
if (value === "full" || value === "memory")
|
|
1119
|
+
return value;
|
|
1120
|
+
return fallback;
|
|
1121
|
+
}
|
|
1115
1122
|
function asTeams(value, fallback) {
|
|
1116
1123
|
if (!Array.isArray(value))
|
|
1117
1124
|
return fallback;
|
|
@@ -292,7 +292,8 @@ function createDefaultConfig() {
|
|
|
292
292
|
project_name: "shared-experience",
|
|
293
293
|
namespace: "",
|
|
294
294
|
api_key: ""
|
|
295
|
-
}
|
|
295
|
+
},
|
|
296
|
+
tool_profile: "full"
|
|
296
297
|
};
|
|
297
298
|
}
|
|
298
299
|
function loadConfig() {
|
|
@@ -363,7 +364,8 @@ function loadConfig() {
|
|
|
363
364
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
364
365
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
365
366
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
366
|
-
}
|
|
367
|
+
},
|
|
368
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
367
369
|
};
|
|
368
370
|
}
|
|
369
371
|
function saveConfig(config) {
|
|
@@ -418,6 +420,11 @@ function asObserverMode(value, fallback) {
|
|
|
418
420
|
return value;
|
|
419
421
|
return fallback;
|
|
420
422
|
}
|
|
423
|
+
function asToolProfile(value, fallback) {
|
|
424
|
+
if (value === "full" || value === "memory")
|
|
425
|
+
return value;
|
|
426
|
+
return fallback;
|
|
427
|
+
}
|
|
421
428
|
function asTeams(value, fallback) {
|
|
422
429
|
if (!Array.isArray(value))
|
|
423
430
|
return fallback;
|
|
@@ -86,7 +86,8 @@ function createDefaultConfig() {
|
|
|
86
86
|
project_name: "shared-experience",
|
|
87
87
|
namespace: "",
|
|
88
88
|
api_key: ""
|
|
89
|
-
}
|
|
89
|
+
},
|
|
90
|
+
tool_profile: "full"
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
93
|
function loadConfig() {
|
|
@@ -157,7 +158,8 @@ function loadConfig() {
|
|
|
157
158
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
158
159
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
159
160
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
160
|
-
}
|
|
161
|
+
},
|
|
162
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
161
163
|
};
|
|
162
164
|
}
|
|
163
165
|
function saveConfig(config) {
|
|
@@ -212,6 +214,11 @@ function asObserverMode(value, fallback) {
|
|
|
212
214
|
return value;
|
|
213
215
|
return fallback;
|
|
214
216
|
}
|
|
217
|
+
function asToolProfile(value, fallback) {
|
|
218
|
+
if (value === "full" || value === "memory")
|
|
219
|
+
return value;
|
|
220
|
+
return fallback;
|
|
221
|
+
}
|
|
215
222
|
function asTeams(value, fallback) {
|
|
216
223
|
if (!Array.isArray(value))
|
|
217
224
|
return fallback;
|
package/dist/hooks/sentinel.js
CHANGED
|
@@ -162,7 +162,8 @@ function createDefaultConfig() {
|
|
|
162
162
|
project_name: "shared-experience",
|
|
163
163
|
namespace: "",
|
|
164
164
|
api_key: ""
|
|
165
|
-
}
|
|
165
|
+
},
|
|
166
|
+
tool_profile: "full"
|
|
166
167
|
};
|
|
167
168
|
}
|
|
168
169
|
function loadConfig() {
|
|
@@ -233,7 +234,8 @@ function loadConfig() {
|
|
|
233
234
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
234
235
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
235
236
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
236
|
-
}
|
|
237
|
+
},
|
|
238
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
237
239
|
};
|
|
238
240
|
}
|
|
239
241
|
function saveConfig(config) {
|
|
@@ -288,6 +290,11 @@ function asObserverMode(value, fallback) {
|
|
|
288
290
|
return value;
|
|
289
291
|
return fallback;
|
|
290
292
|
}
|
|
293
|
+
function asToolProfile(value, fallback) {
|
|
294
|
+
if (value === "full" || value === "memory")
|
|
295
|
+
return value;
|
|
296
|
+
return fallback;
|
|
297
|
+
}
|
|
291
298
|
function asTeams(value, fallback) {
|
|
292
299
|
if (!Array.isArray(value))
|
|
293
300
|
return fallback;
|
|
@@ -3266,7 +3266,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync
|
|
|
3266
3266
|
import { join as join3 } from "node:path";
|
|
3267
3267
|
import { homedir } from "node:os";
|
|
3268
3268
|
var STATE_PATH = join3(homedir(), ".engrm", "config-fingerprint.json");
|
|
3269
|
-
var CLIENT_VERSION = "0.4.
|
|
3269
|
+
var CLIENT_VERSION = "0.4.42";
|
|
3270
3270
|
function hashFile(filePath) {
|
|
3271
3271
|
try {
|
|
3272
3272
|
if (!existsSync3(filePath))
|
|
@@ -3480,7 +3480,8 @@ function createDefaultConfig() {
|
|
|
3480
3480
|
project_name: "shared-experience",
|
|
3481
3481
|
namespace: "",
|
|
3482
3482
|
api_key: ""
|
|
3483
|
-
}
|
|
3483
|
+
},
|
|
3484
|
+
tool_profile: "full"
|
|
3484
3485
|
};
|
|
3485
3486
|
}
|
|
3486
3487
|
function loadConfig() {
|
|
@@ -3551,7 +3552,8 @@ function loadConfig() {
|
|
|
3551
3552
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
3552
3553
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
3553
3554
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
3554
|
-
}
|
|
3555
|
+
},
|
|
3556
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
3555
3557
|
};
|
|
3556
3558
|
}
|
|
3557
3559
|
function saveConfig(config) {
|
|
@@ -3606,6 +3608,11 @@ function asObserverMode(value, fallback) {
|
|
|
3606
3608
|
return value;
|
|
3607
3609
|
return fallback;
|
|
3608
3610
|
}
|
|
3611
|
+
function asToolProfile(value, fallback) {
|
|
3612
|
+
if (value === "full" || value === "memory")
|
|
3613
|
+
return value;
|
|
3614
|
+
return fallback;
|
|
3615
|
+
}
|
|
3609
3616
|
function asTeams(value, fallback) {
|
|
3610
3617
|
if (!Array.isArray(value))
|
|
3611
3618
|
return fallback;
|
|
@@ -3905,6 +3912,7 @@ class VectorClient {
|
|
|
3905
3912
|
apiKey;
|
|
3906
3913
|
siteId;
|
|
3907
3914
|
namespace;
|
|
3915
|
+
timeoutMs;
|
|
3908
3916
|
constructor(config, overrides = {}) {
|
|
3909
3917
|
const baseUrl = getBaseUrl(config);
|
|
3910
3918
|
const apiKey = overrides.apiKey ?? getApiKey(config);
|
|
@@ -3915,6 +3923,7 @@ class VectorClient {
|
|
|
3915
3923
|
this.apiKey = apiKey;
|
|
3916
3924
|
this.siteId = overrides.siteId ?? config.site_id;
|
|
3917
3925
|
this.namespace = overrides.namespace ?? config.namespace;
|
|
3926
|
+
this.timeoutMs = overrides.timeoutMs ?? 1e4;
|
|
3918
3927
|
}
|
|
3919
3928
|
static isConfigured(config) {
|
|
3920
3929
|
return getApiKey(config) !== null && getBaseUrl(config) !== null;
|
|
@@ -3977,6 +3986,7 @@ class VectorClient {
|
|
|
3977
3986
|
if (body && method !== "GET") {
|
|
3978
3987
|
init.body = JSON.stringify(body);
|
|
3979
3988
|
}
|
|
3989
|
+
init.signal = AbortSignal.timeout(this.timeoutMs);
|
|
3980
3990
|
const response = await fetch(url, init);
|
|
3981
3991
|
if (!response.ok) {
|
|
3982
3992
|
const text = await response.text().catch(() => "");
|
package/dist/hooks/stop.js
CHANGED
|
@@ -399,7 +399,8 @@ function createDefaultConfig() {
|
|
|
399
399
|
project_name: "shared-experience",
|
|
400
400
|
namespace: "",
|
|
401
401
|
api_key: ""
|
|
402
|
-
}
|
|
402
|
+
},
|
|
403
|
+
tool_profile: "full"
|
|
403
404
|
};
|
|
404
405
|
}
|
|
405
406
|
function loadConfig() {
|
|
@@ -470,7 +471,8 @@ function loadConfig() {
|
|
|
470
471
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
471
472
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
472
473
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
473
|
-
}
|
|
474
|
+
},
|
|
475
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
474
476
|
};
|
|
475
477
|
}
|
|
476
478
|
function saveConfig(config) {
|
|
@@ -525,6 +527,11 @@ function asObserverMode(value, fallback) {
|
|
|
525
527
|
return value;
|
|
526
528
|
return fallback;
|
|
527
529
|
}
|
|
530
|
+
function asToolProfile(value, fallback) {
|
|
531
|
+
if (value === "full" || value === "memory")
|
|
532
|
+
return value;
|
|
533
|
+
return fallback;
|
|
534
|
+
}
|
|
528
535
|
function asTeams(value, fallback) {
|
|
529
536
|
if (!Array.isArray(value))
|
|
530
537
|
return fallback;
|
|
@@ -2271,6 +2278,7 @@ class VectorClient {
|
|
|
2271
2278
|
apiKey;
|
|
2272
2279
|
siteId;
|
|
2273
2280
|
namespace;
|
|
2281
|
+
timeoutMs;
|
|
2274
2282
|
constructor(config, overrides = {}) {
|
|
2275
2283
|
const baseUrl = getBaseUrl(config);
|
|
2276
2284
|
const apiKey = overrides.apiKey ?? getApiKey(config);
|
|
@@ -2281,6 +2289,7 @@ class VectorClient {
|
|
|
2281
2289
|
this.apiKey = apiKey;
|
|
2282
2290
|
this.siteId = overrides.siteId ?? config.site_id;
|
|
2283
2291
|
this.namespace = overrides.namespace ?? config.namespace;
|
|
2292
|
+
this.timeoutMs = overrides.timeoutMs ?? 1e4;
|
|
2284
2293
|
}
|
|
2285
2294
|
static isConfigured(config) {
|
|
2286
2295
|
return getApiKey(config) !== null && getBaseUrl(config) !== null;
|
|
@@ -2343,6 +2352,7 @@ class VectorClient {
|
|
|
2343
2352
|
if (body && method !== "GET") {
|
|
2344
2353
|
init.body = JSON.stringify(body);
|
|
2345
2354
|
}
|
|
2355
|
+
init.signal = AbortSignal.timeout(this.timeoutMs);
|
|
2346
2356
|
const response = await fetch(url, init);
|
|
2347
2357
|
if (!response.ok) {
|
|
2348
2358
|
const text = await response.text().catch(() => "");
|
|
@@ -2811,7 +2821,7 @@ function buildSummaryVectorDocument(summary, config, project, targetOrObservatio
|
|
|
2811
2821
|
}
|
|
2812
2822
|
};
|
|
2813
2823
|
}
|
|
2814
|
-
async function pushOutbox(db, config, batchSize = 50) {
|
|
2824
|
+
async function pushOutbox(db, config, batchSize = 50, options = {}) {
|
|
2815
2825
|
const entries = getPendingEntries(db, batchSize);
|
|
2816
2826
|
let pushed = 0;
|
|
2817
2827
|
let failed = 0;
|
|
@@ -2922,7 +2932,8 @@ async function pushOutbox(db, config, batchSize = 50) {
|
|
|
2922
2932
|
const client = new VectorClient(config, {
|
|
2923
2933
|
apiKey: target.apiKey,
|
|
2924
2934
|
namespace: target.namespace,
|
|
2925
|
-
siteId: target.siteId
|
|
2935
|
+
siteId: target.siteId,
|
|
2936
|
+
timeoutMs: options.timeoutMs
|
|
2926
2937
|
});
|
|
2927
2938
|
try {
|
|
2928
2939
|
await client.batchIngest(items.map((b) => b.doc));
|
|
@@ -3003,118 +3014,14 @@ function summarizeObservationSourceTools(observations) {
|
|
|
3003
3014
|
});
|
|
3004
3015
|
}
|
|
3005
3016
|
|
|
3006
|
-
// src/embeddings/embedder.ts
|
|
3007
|
-
var _available = null;
|
|
3008
|
-
var _pipeline = null;
|
|
3009
|
-
var MODEL_NAME = "Xenova/all-MiniLM-L6-v2";
|
|
3010
|
-
async function embedText(text) {
|
|
3011
|
-
const pipe = await getPipeline();
|
|
3012
|
-
if (!pipe)
|
|
3013
|
-
return null;
|
|
3014
|
-
try {
|
|
3015
|
-
const output = await pipe(text, { pooling: "mean", normalize: true });
|
|
3016
|
-
return new Float32Array(output.data);
|
|
3017
|
-
} catch {
|
|
3018
|
-
return null;
|
|
3019
|
-
}
|
|
3020
|
-
}
|
|
3021
|
-
function composeEmbeddingText(obs) {
|
|
3022
|
-
const parts = [obs.title];
|
|
3023
|
-
if (obs.narrative)
|
|
3024
|
-
parts.push(obs.narrative);
|
|
3025
|
-
if (obs.facts) {
|
|
3026
|
-
try {
|
|
3027
|
-
const facts = JSON.parse(obs.facts);
|
|
3028
|
-
if (Array.isArray(facts) && facts.length > 0) {
|
|
3029
|
-
parts.push(facts.map((f) => `- ${f}`).join(`
|
|
3030
|
-
`));
|
|
3031
|
-
}
|
|
3032
|
-
} catch {
|
|
3033
|
-
parts.push(obs.facts);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
if (obs.concepts) {
|
|
3037
|
-
try {
|
|
3038
|
-
const concepts = JSON.parse(obs.concepts);
|
|
3039
|
-
if (Array.isArray(concepts) && concepts.length > 0) {
|
|
3040
|
-
parts.push(concepts.join(", "));
|
|
3041
|
-
}
|
|
3042
|
-
} catch {}
|
|
3043
|
-
}
|
|
3044
|
-
return parts.join(`
|
|
3045
|
-
|
|
3046
|
-
`);
|
|
3047
|
-
}
|
|
3048
|
-
function composeChatEmbeddingText(text) {
|
|
3049
|
-
return text.replace(/\s+/g, " ").trim().slice(0, 2000);
|
|
3050
|
-
}
|
|
3051
|
-
async function getPipeline() {
|
|
3052
|
-
if (_pipeline)
|
|
3053
|
-
return _pipeline;
|
|
3054
|
-
if (_available === false)
|
|
3055
|
-
return null;
|
|
3056
|
-
try {
|
|
3057
|
-
const { pipeline } = await import("@xenova/transformers");
|
|
3058
|
-
_pipeline = await pipeline("feature-extraction", MODEL_NAME);
|
|
3059
|
-
_available = true;
|
|
3060
|
-
return _pipeline;
|
|
3061
|
-
} catch (err) {
|
|
3062
|
-
_available = false;
|
|
3063
|
-
console.error(`[engrm] Local embedding model unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
3064
|
-
return null;
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
|
|
3068
|
-
// src/sync/pull.ts
|
|
3069
|
-
async function pullSettings(client, config) {
|
|
3070
|
-
try {
|
|
3071
|
-
const settings = await client.fetchSettings();
|
|
3072
|
-
if (!settings)
|
|
3073
|
-
return false;
|
|
3074
|
-
let changed = false;
|
|
3075
|
-
if (settings.transcript_analysis !== undefined) {
|
|
3076
|
-
const ta = settings.transcript_analysis;
|
|
3077
|
-
if (typeof ta === "object" && ta !== null) {
|
|
3078
|
-
const taObj = ta;
|
|
3079
|
-
if (taObj.enabled !== undefined && taObj.enabled !== config.transcript_analysis.enabled) {
|
|
3080
|
-
config.transcript_analysis.enabled = !!taObj.enabled;
|
|
3081
|
-
changed = true;
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
if (settings.observer !== undefined) {
|
|
3086
|
-
const obs = settings.observer;
|
|
3087
|
-
if (typeof obs === "object" && obs !== null) {
|
|
3088
|
-
const obsObj = obs;
|
|
3089
|
-
if (obsObj.enabled !== undefined && obsObj.enabled !== config.observer.enabled) {
|
|
3090
|
-
config.observer.enabled = !!obsObj.enabled;
|
|
3091
|
-
changed = true;
|
|
3092
|
-
}
|
|
3093
|
-
if (obsObj.model !== undefined && typeof obsObj.model === "string" && obsObj.model !== config.observer.model) {
|
|
3094
|
-
config.observer.model = obsObj.model;
|
|
3095
|
-
changed = true;
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
if (changed) {
|
|
3100
|
-
saveConfig(config);
|
|
3101
|
-
}
|
|
3102
|
-
return changed;
|
|
3103
|
-
} catch {
|
|
3104
|
-
return false;
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
|
|
3108
3017
|
// src/sync/push-once.ts
|
|
3109
|
-
async function pushOnce(db, config) {
|
|
3018
|
+
async function pushOnce(db, config, options = {}) {
|
|
3110
3019
|
if (!config.sync.enabled)
|
|
3111
3020
|
return 0;
|
|
3112
3021
|
if (!VectorClient.isConfigured(config))
|
|
3113
3022
|
return 0;
|
|
3114
3023
|
try {
|
|
3115
|
-
const
|
|
3116
|
-
const result = await pushOutbox(db, config, config.sync.batch_size);
|
|
3117
|
-
await pullSettings(client, config);
|
|
3024
|
+
const result = await pushOutbox(db, config, config.sync.batch_size, { timeoutMs: options.timeoutMs ?? 4000 });
|
|
3118
3025
|
return result.pushed;
|
|
3119
3026
|
} catch {
|
|
3120
3027
|
return 0;
|
|
@@ -3338,7 +3245,7 @@ function buildBeacon(db, config, sessionId, metrics) {
|
|
|
3338
3245
|
sentinel_used: valueSignals.security_findings_count > 0,
|
|
3339
3246
|
risk_score: riskScore,
|
|
3340
3247
|
stacks_detected: stacks,
|
|
3341
|
-
client_version: "0.4.
|
|
3248
|
+
client_version: "0.4.42",
|
|
3342
3249
|
context_observations_injected: metrics?.contextObsInjected ?? 0,
|
|
3343
3250
|
context_total_available: metrics?.contextTotalAvailable ?? 0,
|
|
3344
3251
|
recall_attempts: metrics?.recallAttempts ?? 0,
|
|
@@ -3531,6 +3438,68 @@ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "node:f
|
|
|
3531
3438
|
import { join as join5 } from "node:path";
|
|
3532
3439
|
import { homedir as homedir3 } from "node:os";
|
|
3533
3440
|
|
|
3441
|
+
// src/embeddings/embedder.ts
|
|
3442
|
+
var _available = null;
|
|
3443
|
+
var _pipeline = null;
|
|
3444
|
+
var MODEL_NAME = "Xenova/all-MiniLM-L6-v2";
|
|
3445
|
+
async function embedText(text) {
|
|
3446
|
+
const pipe = await getPipeline();
|
|
3447
|
+
if (!pipe)
|
|
3448
|
+
return null;
|
|
3449
|
+
try {
|
|
3450
|
+
const output = await pipe(text, { pooling: "mean", normalize: true });
|
|
3451
|
+
return new Float32Array(output.data);
|
|
3452
|
+
} catch {
|
|
3453
|
+
return null;
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
function composeEmbeddingText(obs) {
|
|
3457
|
+
const parts = [obs.title];
|
|
3458
|
+
if (obs.narrative)
|
|
3459
|
+
parts.push(obs.narrative);
|
|
3460
|
+
if (obs.facts) {
|
|
3461
|
+
try {
|
|
3462
|
+
const facts = JSON.parse(obs.facts);
|
|
3463
|
+
if (Array.isArray(facts) && facts.length > 0) {
|
|
3464
|
+
parts.push(facts.map((f) => `- ${f}`).join(`
|
|
3465
|
+
`));
|
|
3466
|
+
}
|
|
3467
|
+
} catch {
|
|
3468
|
+
parts.push(obs.facts);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
if (obs.concepts) {
|
|
3472
|
+
try {
|
|
3473
|
+
const concepts = JSON.parse(obs.concepts);
|
|
3474
|
+
if (Array.isArray(concepts) && concepts.length > 0) {
|
|
3475
|
+
parts.push(concepts.join(", "));
|
|
3476
|
+
}
|
|
3477
|
+
} catch {}
|
|
3478
|
+
}
|
|
3479
|
+
return parts.join(`
|
|
3480
|
+
|
|
3481
|
+
`);
|
|
3482
|
+
}
|
|
3483
|
+
function composeChatEmbeddingText(text) {
|
|
3484
|
+
return text.replace(/\s+/g, " ").trim().slice(0, 2000);
|
|
3485
|
+
}
|
|
3486
|
+
async function getPipeline() {
|
|
3487
|
+
if (_pipeline)
|
|
3488
|
+
return _pipeline;
|
|
3489
|
+
if (_available === false)
|
|
3490
|
+
return null;
|
|
3491
|
+
try {
|
|
3492
|
+
const { pipeline } = await import("@xenova/transformers");
|
|
3493
|
+
_pipeline = await pipeline("feature-extraction", MODEL_NAME);
|
|
3494
|
+
_available = true;
|
|
3495
|
+
return _pipeline;
|
|
3496
|
+
} catch (err) {
|
|
3497
|
+
_available = false;
|
|
3498
|
+
console.error(`[engrm] Local embedding model unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
3499
|
+
return null;
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3534
3503
|
// src/tools/save.ts
|
|
3535
3504
|
import { relative, isAbsolute } from "node:path";
|
|
3536
3505
|
|
|
@@ -4924,7 +4893,7 @@ async function main() {
|
|
|
4924
4893
|
}
|
|
4925
4894
|
} catch {}
|
|
4926
4895
|
}
|
|
4927
|
-
await pushOnce(db, config);
|
|
4896
|
+
await pushOnce(db, config, { timeoutMs: 4000 });
|
|
4928
4897
|
try {
|
|
4929
4898
|
if (event.session_id) {
|
|
4930
4899
|
const metrics = readSessionMetrics(event.session_id);
|
|
@@ -230,7 +230,8 @@ function createDefaultConfig() {
|
|
|
230
230
|
project_name: "shared-experience",
|
|
231
231
|
namespace: "",
|
|
232
232
|
api_key: ""
|
|
233
|
-
}
|
|
233
|
+
},
|
|
234
|
+
tool_profile: "full"
|
|
234
235
|
};
|
|
235
236
|
}
|
|
236
237
|
function loadConfig() {
|
|
@@ -301,7 +302,8 @@ function loadConfig() {
|
|
|
301
302
|
project_name: asString(config["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
302
303
|
namespace: asString(config["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
303
304
|
api_key: asString(config["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
304
|
-
}
|
|
305
|
+
},
|
|
306
|
+
tool_profile: asToolProfile(config["tool_profile"], defaults.tool_profile)
|
|
305
307
|
};
|
|
306
308
|
}
|
|
307
309
|
function saveConfig(config) {
|
|
@@ -356,6 +358,11 @@ function asObserverMode(value, fallback) {
|
|
|
356
358
|
return value;
|
|
357
359
|
return fallback;
|
|
358
360
|
}
|
|
361
|
+
function asToolProfile(value, fallback) {
|
|
362
|
+
if (value === "full" || value === "memory")
|
|
363
|
+
return value;
|
|
364
|
+
return fallback;
|
|
365
|
+
}
|
|
359
366
|
function asTeams(value, fallback) {
|
|
360
367
|
if (!Array.isArray(value))
|
|
361
368
|
return fallback;
|
package/dist/server.js
CHANGED
|
@@ -13643,7 +13643,8 @@ function createDefaultConfig() {
|
|
|
13643
13643
|
project_name: "shared-experience",
|
|
13644
13644
|
namespace: "",
|
|
13645
13645
|
api_key: ""
|
|
13646
|
-
}
|
|
13646
|
+
},
|
|
13647
|
+
tool_profile: "full"
|
|
13647
13648
|
};
|
|
13648
13649
|
}
|
|
13649
13650
|
function loadConfig() {
|
|
@@ -13714,7 +13715,8 @@ function loadConfig() {
|
|
|
13714
13715
|
project_name: asString(config2["fleet"]?.["project_name"], defaults.fleet.project_name),
|
|
13715
13716
|
namespace: asString(config2["fleet"]?.["namespace"], defaults.fleet.namespace),
|
|
13716
13717
|
api_key: asString(config2["fleet"]?.["api_key"], defaults.fleet.api_key)
|
|
13717
|
-
}
|
|
13718
|
+
},
|
|
13719
|
+
tool_profile: asToolProfile(config2["tool_profile"], defaults.tool_profile)
|
|
13718
13720
|
};
|
|
13719
13721
|
}
|
|
13720
13722
|
function saveConfig(config2) {
|
|
@@ -13769,6 +13771,11 @@ function asObserverMode(value, fallback) {
|
|
|
13769
13771
|
return value;
|
|
13770
13772
|
return fallback;
|
|
13771
13773
|
}
|
|
13774
|
+
function asToolProfile(value, fallback) {
|
|
13775
|
+
if (value === "full" || value === "memory")
|
|
13776
|
+
return value;
|
|
13777
|
+
return fallback;
|
|
13778
|
+
}
|
|
13772
13779
|
function asTeams(value, fallback) {
|
|
13773
13780
|
if (!Array.isArray(value))
|
|
13774
13781
|
return fallback;
|
|
@@ -19471,6 +19478,26 @@ function getActivityFeed(db, input) {
|
|
|
19471
19478
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
19472
19479
|
import { homedir as homedir2 } from "node:os";
|
|
19473
19480
|
import { join as join3 } from "node:path";
|
|
19481
|
+
|
|
19482
|
+
// src/tool-profiles.ts
|
|
19483
|
+
var MEMORY_PROFILE_TOOLS = [
|
|
19484
|
+
"save_observation",
|
|
19485
|
+
"search_recall",
|
|
19486
|
+
"resume_thread",
|
|
19487
|
+
"list_recall_items",
|
|
19488
|
+
"load_recall_item",
|
|
19489
|
+
"recent_chat",
|
|
19490
|
+
"search_chat",
|
|
19491
|
+
"refresh_chat_recall",
|
|
19492
|
+
"repair_recall"
|
|
19493
|
+
];
|
|
19494
|
+
function getEnabledToolNames(profile) {
|
|
19495
|
+
if (!profile || profile === "full")
|
|
19496
|
+
return null;
|
|
19497
|
+
return new Set(MEMORY_PROFILE_TOOLS);
|
|
19498
|
+
}
|
|
19499
|
+
|
|
19500
|
+
// src/tools/capture-status.ts
|
|
19474
19501
|
var LEGACY_CODEX_SERVER_NAME = `candengo-${"mem"}`;
|
|
19475
19502
|
function getCaptureStatus(db, input = {}) {
|
|
19476
19503
|
const hours = Math.max(1, Math.min(input.lookback_hours ?? 24, 24 * 30));
|
|
@@ -19574,6 +19601,8 @@ function getCaptureStatus(db, input = {}) {
|
|
|
19574
19601
|
http_enabled: Boolean(config2?.http?.enabled || process.env.ENGRM_HTTP_PORT),
|
|
19575
19602
|
http_port: config2?.http?.port ?? (process.env.ENGRM_HTTP_PORT ? Number(process.env.ENGRM_HTTP_PORT) : null),
|
|
19576
19603
|
http_bearer_token_count: config2?.http?.bearer_tokens?.length ?? 0,
|
|
19604
|
+
tool_profile: config2?.tool_profile ?? "full",
|
|
19605
|
+
enabled_tool_count: config2 ? getEnabledToolNames(config2.tool_profile)?.size ?? null : null,
|
|
19577
19606
|
fleet_project_name: config2?.fleet?.project_name ?? null,
|
|
19578
19607
|
fleet_configured: Boolean(config2?.fleet?.namespace && config2?.fleet?.api_key),
|
|
19579
19608
|
claude_mcp_registered: claudeMcpRegistered,
|
|
@@ -21598,6 +21627,7 @@ class VectorClient {
|
|
|
21598
21627
|
apiKey;
|
|
21599
21628
|
siteId;
|
|
21600
21629
|
namespace;
|
|
21630
|
+
timeoutMs;
|
|
21601
21631
|
constructor(config2, overrides = {}) {
|
|
21602
21632
|
const baseUrl = getBaseUrl(config2);
|
|
21603
21633
|
const apiKey = overrides.apiKey ?? getApiKey(config2);
|
|
@@ -21608,6 +21638,7 @@ class VectorClient {
|
|
|
21608
21638
|
this.apiKey = apiKey;
|
|
21609
21639
|
this.siteId = overrides.siteId ?? config2.site_id;
|
|
21610
21640
|
this.namespace = overrides.namespace ?? config2.namespace;
|
|
21641
|
+
this.timeoutMs = overrides.timeoutMs ?? 1e4;
|
|
21611
21642
|
}
|
|
21612
21643
|
static isConfigured(config2) {
|
|
21613
21644
|
return getApiKey(config2) !== null && getBaseUrl(config2) !== null;
|
|
@@ -21670,6 +21701,7 @@ class VectorClient {
|
|
|
21670
21701
|
if (body && method !== "GET") {
|
|
21671
21702
|
init.body = JSON.stringify(body);
|
|
21672
21703
|
}
|
|
21704
|
+
init.signal = AbortSignal.timeout(this.timeoutMs);
|
|
21673
21705
|
const response = await fetch(url2, init);
|
|
21674
21706
|
if (!response.ok) {
|
|
21675
21707
|
const text = await response.text().catch(() => "");
|
|
@@ -21919,7 +21951,7 @@ function buildSummaryVectorDocument(summary, config2, project, targetOrObservati
|
|
|
21919
21951
|
}
|
|
21920
21952
|
};
|
|
21921
21953
|
}
|
|
21922
|
-
async function pushOutbox(db, config2, batchSize = 50) {
|
|
21954
|
+
async function pushOutbox(db, config2, batchSize = 50, options = {}) {
|
|
21923
21955
|
const entries = getPendingEntries(db, batchSize);
|
|
21924
21956
|
let pushed = 0;
|
|
21925
21957
|
let failed = 0;
|
|
@@ -22030,7 +22062,8 @@ async function pushOutbox(db, config2, batchSize = 50) {
|
|
|
22030
22062
|
const client = new VectorClient(config2, {
|
|
22031
22063
|
apiKey: target.apiKey,
|
|
22032
22064
|
namespace: target.namespace,
|
|
22033
|
-
siteId: target.siteId
|
|
22065
|
+
siteId: target.siteId,
|
|
22066
|
+
timeoutMs: options.timeoutMs
|
|
22034
22067
|
});
|
|
22035
22068
|
try {
|
|
22036
22069
|
await client.batchIngest(items.map((b) => b.doc));
|
|
@@ -23083,8 +23116,16 @@ process.on("SIGTERM", () => {
|
|
|
23083
23116
|
function buildServer() {
|
|
23084
23117
|
const server = new McpServer({
|
|
23085
23118
|
name: "engrm",
|
|
23086
|
-
version: "0.4.
|
|
23119
|
+
version: "0.4.42"
|
|
23087
23120
|
});
|
|
23121
|
+
const enabledToolNames = getEnabledToolNames(config2.tool_profile);
|
|
23122
|
+
const originalTool = server.tool.bind(server);
|
|
23123
|
+
server.tool = (name, ...args) => {
|
|
23124
|
+
if (enabledToolNames && !enabledToolNames.has(name)) {
|
|
23125
|
+
return server;
|
|
23126
|
+
}
|
|
23127
|
+
return originalTool(name, ...args);
|
|
23128
|
+
};
|
|
23088
23129
|
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.", {
|
|
23089
23130
|
type: exports_external.enum([
|
|
23090
23131
|
"bugfix",
|
|
@@ -24146,6 +24187,7 @@ ${observationLines}`
|
|
|
24146
24187
|
text: `Schema: v${result.schema_version} (${result.schema_current ? "current" : "outdated"})
|
|
24147
24188
|
` + `HTTP MCP: ${result.http_enabled ? `enabled${result.http_port ? ` (:${result.http_port})` : ""}` : "disabled"}
|
|
24148
24189
|
` + `HTTP bearer tokens: ${result.http_bearer_token_count}
|
|
24190
|
+
` + `Tool profile: ${result.tool_profile}${typeof result.enabled_tool_count === "number" ? ` (${result.enabled_tool_count} tools)` : ""}
|
|
24149
24191
|
` + `Fleet project: ${result.fleet_project_name ?? "none"}
|
|
24150
24192
|
` + `Fleet sync: ${result.fleet_configured ? "configured" : "not configured"}
|
|
24151
24193
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engrm",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.43",
|
|
4
4
|
"description": "Shared memory across devices, sessions, and agents, with thin MCP tools for durable capture, live continuity, and Hermes-ready remote MCP support",
|
|
5
5
|
"mcpName": "io.github.dr12hes/engrm",
|
|
6
6
|
"type": "module",
|