@joshuaswarren/openclaw-engram 9.0.88 → 9.0.91
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 -1
- package/dist/{chunk-C26MLXQM.js → chunk-DDJJSZIV.js} +85 -7
- package/dist/chunk-DDJJSZIV.js.map +1 -0
- package/dist/{chunk-ECZTNFXJ.js → chunk-LARLNIEW.js} +2 -2
- package/dist/{engine-E3NR57DB.js → engine-A33VEWKG.js} +3 -3
- package/dist/index.js +524 -44
- package/dist/index.js.map +1 -1
- package/dist/{storage-3HMTG57X.js → storage-IGAVYRWY.js} +2 -2
- package/package.json +4 -1
- package/dist/chunk-C26MLXQM.js.map +0 -1
- /package/dist/{chunk-ECZTNFXJ.js.map → chunk-LARLNIEW.js.map} +0 -0
- /package/dist/{engine-E3NR57DB.js.map → engine-A33VEWKG.js.map} +0 -0
- /package/dist/{storage-3HMTG57X.js.map → storage-IGAVYRWY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -134,7 +134,24 @@ Run the stdio MCP server:
|
|
|
134
134
|
openclaw engram access mcp-serve
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
Point your MCP client's command at `openclaw engram access mcp-serve`. Works with Claude Code, and any other MCP-compatible client. The server exposes the same
|
|
137
|
+
Point your MCP client's command at `openclaw engram access mcp-serve`. Works with Claude Code, and any other MCP-compatible client. The server exposes the same tools as the HTTP endpoint.
|
|
138
|
+
|
|
139
|
+
**Claude Code (MCP over HTTP):** Start the Engram HTTP server, then add to `~/.claude.json`:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"mcpServers": {
|
|
144
|
+
"engram": {
|
|
145
|
+
"url": "http://localhost:4318/mcp",
|
|
146
|
+
"headers": {
|
|
147
|
+
"Authorization": "Bearer ${ENGRAM_TOKEN}"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
See the [Standalone Server Guide](docs/guides/standalone-server.md) for multi-tenant setups and connecting multiple agent harnesses.
|
|
138
155
|
|
|
139
156
|
## How It Works
|
|
140
157
|
|
|
@@ -202,6 +219,10 @@ Use a preset to jump to a recommended level: `conservative`, `balanced`, `resear
|
|
|
202
219
|
- **Scripts & automation** — Authenticated REST API for custom integrations
|
|
203
220
|
- **Local LLMs** — Run extraction and reranking with local models (Ollama, LM Studio, etc.)
|
|
204
221
|
|
|
222
|
+
### Standalone Multi-Tenant Server
|
|
223
|
+
|
|
224
|
+
Run Engram as a standalone HTTP server that multiple agent harnesses share. Isolate tenants with namespace policies, feed conversations from any client via the observe endpoint, and search archived history with LCM full-text search. Works with OpenClaw, Codex CLI, Claude Code, and custom HTTP agents. See the [Standalone Server Guide](docs/guides/standalone-server.md).
|
|
225
|
+
|
|
205
226
|
### Built for production
|
|
206
227
|
|
|
207
228
|
- **672 tests** with CI enforcement
|
|
@@ -215,6 +236,7 @@ Use a preset to jump to a recommended level: `conservative`, `balanced`, `resear
|
|
|
215
236
|
### Core
|
|
216
237
|
|
|
217
238
|
- **Automatic memory extraction** — Facts, decisions, preferences, corrections extracted from conversations
|
|
239
|
+
- **Observe endpoint** — Feed conversation messages from any agent into the extraction pipeline via HTTP or MCP
|
|
218
240
|
- **Recall injection** — Relevant memories injected before each agent turn
|
|
219
241
|
- **Entity tracking** — People, projects, tools, companies tracked as structured entities
|
|
220
242
|
- **Lifecycle management** — Memories age through active, validated, stale, archived states
|
|
@@ -320,6 +342,8 @@ Available via both stdio and HTTP transports:
|
|
|
320
342
|
| `engram.suggestion_submit` | Queue a memory for review |
|
|
321
343
|
| `engram.entity_get` | Look up a known entity |
|
|
322
344
|
| `engram.review_queue_list` | View the governance review queue |
|
|
345
|
+
| `engram.observe` | Feed conversation messages into memory pipeline (LCM + extraction) |
|
|
346
|
+
| `engram.lcm_search` | Full-text search over LCM-archived conversations |
|
|
323
347
|
| `engram_context_search` | Full-text search across all archived conversation history (LCM) |
|
|
324
348
|
| `engram_context_describe` | Get a compressed summary of a turn range (LCM) |
|
|
325
349
|
| `engram_context_expand` | Retrieve raw lossless messages for a turn range (LCM) |
|
|
@@ -395,6 +419,7 @@ All settings live in `openclaw.json` under `plugins.entries.openclaw-engram.conf
|
|
|
395
419
|
- [Writing a Search Backend](docs/writing-a-search-backend.md) — Build your own adapter
|
|
396
420
|
- [API Reference](docs/api.md) — HTTP, MCP, and CLI documentation
|
|
397
421
|
- [Codex CLI Integration](docs/guides/codex-cli.md) — Setup Engram with OpenAI's Codex
|
|
422
|
+
- [Standalone Server Guide](docs/guides/standalone-server.md) — Multi-tenant HTTP server for multiple agent harnesses
|
|
398
423
|
- [Local LLM Guide](docs/guides/local-llm.md) — Local-first extraction and reranking
|
|
399
424
|
- [Cost Control Guide](docs/guides/cost-control.md) — Budget mappings and presets
|
|
400
425
|
- [Namespaces](docs/namespaces.md) — Multi-agent memory isolation
|
|
@@ -109,6 +109,7 @@ var SPECULATIVE_TTL_DAYS = 30;
|
|
|
109
109
|
|
|
110
110
|
// src/memory-projection-store.ts
|
|
111
111
|
import path2 from "path";
|
|
112
|
+
import { readFileSync } from "fs";
|
|
112
113
|
import Database from "better-sqlite3";
|
|
113
114
|
var MEMORY_PROJECTION_SCHEMA_VERSION = 2;
|
|
114
115
|
function getMemoryProjectionPath(memoryDir) {
|
|
@@ -453,9 +454,6 @@ function readProjectedMemoryTimeline(memoryDir, memoryId, limit) {
|
|
|
453
454
|
function readProjectedMemoryBrowse(memoryDir, options) {
|
|
454
455
|
return withProjectionReadonly(memoryDir, (db) => {
|
|
455
456
|
const normalizedQuery = options.query?.trim().toLowerCase() ?? "";
|
|
456
|
-
if (normalizedQuery) {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
457
|
const currentSelect = memoryCurrentSelectExpressions(db);
|
|
460
458
|
const whereClauses = [];
|
|
461
459
|
const params = [];
|
|
@@ -482,6 +480,57 @@ function readProjectedMemoryBrowse(memoryDir, options) {
|
|
|
482
480
|
}
|
|
483
481
|
})();
|
|
484
482
|
const whereSql = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
483
|
+
if (normalizedQuery) {
|
|
484
|
+
const allRows = db.prepare(`
|
|
485
|
+
SELECT
|
|
486
|
+
memory_id,
|
|
487
|
+
path_rel,
|
|
488
|
+
category,
|
|
489
|
+
status,
|
|
490
|
+
created_at,
|
|
491
|
+
updated_at,
|
|
492
|
+
entity_ref,
|
|
493
|
+
${currentSelect.tagsJson},
|
|
494
|
+
${currentSelect.previewText}
|
|
495
|
+
FROM memory_current
|
|
496
|
+
${whereSql}
|
|
497
|
+
ORDER BY ${orderBySql}
|
|
498
|
+
`).all(...params);
|
|
499
|
+
const filtered = allRows.filter((row) => {
|
|
500
|
+
if (typeof row.memory_id !== "string" || typeof row.path_rel !== "string") return false;
|
|
501
|
+
const preview = typeof row.preview_text === "string" ? row.preview_text.toLowerCase() : "";
|
|
502
|
+
const category = typeof row.category === "string" ? row.category.toLowerCase() : "";
|
|
503
|
+
const entityRef = typeof row.entity_ref === "string" ? row.entity_ref.toLowerCase() : "";
|
|
504
|
+
const tags = typeof row.tags_json === "string" ? row.tags_json.toLowerCase() : "";
|
|
505
|
+
if (preview.includes(normalizedQuery) || category.includes(normalizedQuery) || entityRef.includes(normalizedQuery) || tags.includes(normalizedQuery)) {
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const filePath = path2.join(memoryDir, row.path_rel);
|
|
510
|
+
const content = readFileSync(filePath, "utf-8").toLowerCase();
|
|
511
|
+
return content.includes(normalizedQuery);
|
|
512
|
+
} catch {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
const pageRows = filtered.slice(options.offset, options.offset + options.limit);
|
|
517
|
+
return {
|
|
518
|
+
total: filtered.length,
|
|
519
|
+
memories: pageRows.filter(
|
|
520
|
+
(row) => typeof row.memory_id === "string" && typeof row.path_rel === "string" && typeof row.category === "string" && typeof row.status === "string"
|
|
521
|
+
).map((row) => ({
|
|
522
|
+
id: row.memory_id,
|
|
523
|
+
path: path2.join(memoryDir, row.path_rel),
|
|
524
|
+
category: row.category,
|
|
525
|
+
status: row.status,
|
|
526
|
+
created: typeof row.created_at === "string" ? row.created_at : void 0,
|
|
527
|
+
updated: typeof row.updated_at === "string" ? row.updated_at : void 0,
|
|
528
|
+
tags: parseStringArray(row.tags_json),
|
|
529
|
+
entityRef: typeof row.entity_ref === "string" ? row.entity_ref : void 0,
|
|
530
|
+
preview: typeof row.preview_text === "string" ? row.preview_text : ""
|
|
531
|
+
}))
|
|
532
|
+
};
|
|
533
|
+
}
|
|
485
534
|
const totalRow = db.prepare(`SELECT COUNT(*) AS total FROM memory_current ${whereSql}`).get(...params);
|
|
486
535
|
const rows = db.prepare(`
|
|
487
536
|
SELECT
|
|
@@ -1167,9 +1216,29 @@ function serializeFrontmatter(fm) {
|
|
|
1167
1216
|
if (fm.sourceMemoryId) lines.push(`sourceMemoryId: ${fm.sourceMemoryId}`);
|
|
1168
1217
|
if (fm.sourceTurnId) lines.push(`sourceTurnId: ${fm.sourceTurnId}`);
|
|
1169
1218
|
if (fm.memoryKind) lines.push(`memoryKind: ${fm.memoryKind}`);
|
|
1219
|
+
if (fm.structuredAttributes && Object.keys(fm.structuredAttributes).length > 0) {
|
|
1220
|
+
lines.push(`structuredAttributes: ${JSON.stringify(fm.structuredAttributes)}`);
|
|
1221
|
+
}
|
|
1170
1222
|
lines.push("---");
|
|
1171
1223
|
return lines.join("\n");
|
|
1172
1224
|
}
|
|
1225
|
+
function parseStructuredAttributes(raw) {
|
|
1226
|
+
if (!raw || !raw.trim()) return void 0;
|
|
1227
|
+
try {
|
|
1228
|
+
const parsed = JSON.parse(raw);
|
|
1229
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1230
|
+
const result = {};
|
|
1231
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
1232
|
+
if (typeof k === "string" && typeof v === "string") {
|
|
1233
|
+
result[k] = v;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1237
|
+
}
|
|
1238
|
+
} catch {
|
|
1239
|
+
}
|
|
1240
|
+
return void 0;
|
|
1241
|
+
}
|
|
1173
1242
|
function parseLinkReasonValue(rawValue) {
|
|
1174
1243
|
const legacyValue = rawValue.replace(/\\"/g, '"');
|
|
1175
1244
|
const looksLikeLegacyPath = !rawValue.includes("\\\\") && (/[A-Za-z]:\\[A-Za-z0-9._ -]+(?:\\[A-Za-z0-9._ -]+)*/.test(rawValue) || /\\[A-Za-z0-9._ -]+\\[A-Za-z0-9._ -]+/.test(rawValue));
|
|
@@ -1282,7 +1351,9 @@ function parseFrontmatter2(raw) {
|
|
|
1282
1351
|
sourceMemoryId: fm.sourceMemoryId || void 0,
|
|
1283
1352
|
sourceTurnId: fm.sourceTurnId || void 0,
|
|
1284
1353
|
// v8.0 Phase 2B: HiMem episode/note classification
|
|
1285
|
-
memoryKind: fm.memoryKind || void 0
|
|
1354
|
+
memoryKind: fm.memoryKind || void 0,
|
|
1355
|
+
// Structured attributes (JSON on a single line)
|
|
1356
|
+
structuredAttributes: parseStructuredAttributes(fm.structuredAttributes)
|
|
1286
1357
|
},
|
|
1287
1358
|
content
|
|
1288
1359
|
};
|
|
@@ -1763,9 +1834,16 @@ var StorageManager = class _StorageManager {
|
|
|
1763
1834
|
artifactType: options.artifactType,
|
|
1764
1835
|
sourceMemoryId: options.sourceMemoryId,
|
|
1765
1836
|
sourceTurnId: options.sourceTurnId,
|
|
1766
|
-
memoryKind: options.memoryKind
|
|
1837
|
+
memoryKind: options.memoryKind,
|
|
1838
|
+
structuredAttributes: options.structuredAttributes
|
|
1767
1839
|
};
|
|
1768
|
-
|
|
1840
|
+
let enrichedContent = content;
|
|
1841
|
+
if (options.structuredAttributes && Object.keys(options.structuredAttributes).length > 0) {
|
|
1842
|
+
const attrLines = Object.entries(options.structuredAttributes).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
1843
|
+
enrichedContent = `${content}
|
|
1844
|
+
[Attributes: ${attrLines}]`;
|
|
1845
|
+
}
|
|
1846
|
+
const sanitized = sanitizeMemoryContent(enrichedContent);
|
|
1769
1847
|
if (!sanitized.clean) {
|
|
1770
1848
|
log.warn(`memory content sanitized for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
1771
1849
|
}
|
|
@@ -3810,4 +3888,4 @@ export {
|
|
|
3810
3888
|
serializeEntityFile,
|
|
3811
3889
|
StorageManager
|
|
3812
3890
|
};
|
|
3813
|
-
//# sourceMappingURL=chunk-
|
|
3891
|
+
//# sourceMappingURL=chunk-DDJJSZIV.js.map
|