@joshuaswarren/openclaw-engram 9.0.29 → 9.0.30
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 +5 -3
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ AI agents forget everything between conversations. Engram fixes that.
|
|
|
34
34
|
- **Objective-state recall** — Engram can now store normalized file, process, and tool outcomes and, when `objectiveStateRecallEnabled` is enabled, inject the most relevant objective-state snapshots back into recall context as a separate `Objective State` section.
|
|
35
35
|
- **Causal trajectory graph foundation** — Engram can now persist typed `goal -> action -> observation -> outcome -> follow-up` chains when `causalTrajectoryMemoryEnabled` is enabled and, with `actionGraphRecallEnabled`, emit deterministic action-conditioned edges into the causal graph for later trajectory-aware retrieval.
|
|
36
36
|
- **Causal trajectory recall** — Engram can now, when `causalTrajectoryRecallEnabled` is enabled, inject prompt-relevant causal chains back into recall context as a separate `Causal Trajectories` section with lightweight match explainability.
|
|
37
|
-
- **Trust-zone
|
|
37
|
+
- **Trust-zone promotion path** — Engram can now, when `trustZonesEnabled` and `quarantinePromotionEnabled` are enabled, persist typed quarantine, working, and trusted records, plan explicit promotions, block direct `quarantine -> trusted` jumps, and require anchored provenance before promoting risky working records into `trusted`.
|
|
38
38
|
- **Zero-config start** — Install, add an API key, restart. Engram works out of the box with sensible defaults and progressively unlocks advanced features as you enable them.
|
|
39
39
|
|
|
40
40
|
## Quick Start
|
|
@@ -160,6 +160,8 @@ openclaw engram benchmark-import <path> # Import a validated benchmark pack
|
|
|
160
160
|
openclaw engram benchmark-ci-gate # Compare base vs candidate eval stores and fail on regressions
|
|
161
161
|
openclaw engram objective-state-status # Objective-state snapshot counts and latest stored snapshot
|
|
162
162
|
openclaw engram causal-trajectory-status # Causal-trajectory record counts and latest stored chain
|
|
163
|
+
openclaw engram trust-zone-status # Trust-zone record counts and latest stored record
|
|
164
|
+
openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance enforcement
|
|
163
165
|
openclaw engram conversation-index-health # Conversation index status
|
|
164
166
|
openclaw engram graph-health # Entity graph status
|
|
165
167
|
openclaw engram tier-status # Hot/cold tier metrics
|
|
@@ -190,8 +192,8 @@ Key settings:
|
|
|
190
192
|
| `causalTrajectoryStoreDir` | `{memoryDir}/state/causal-trajectories` | Root directory for causal-trajectory records |
|
|
191
193
|
| `causalTrajectoryRecallEnabled` | `false` | Inject prompt-relevant causal trajectories into recall context |
|
|
192
194
|
| `actionGraphRecallEnabled` | `false` | Write action-conditioned causal-stage edges from typed trajectory records into the causal graph |
|
|
193
|
-
| `trustZonesEnabled` | `false` | Enable the trust-zone memory foundation for quarantine, working, and trusted records |
|
|
194
|
-
| `quarantinePromotionEnabled` | `false` |
|
|
195
|
+
| `trustZonesEnabled` | `false` | Enable the trust-zone memory foundation and operator-facing promotion path for quarantine, working, and trusted records |
|
|
196
|
+
| `quarantinePromotionEnabled` | `false` | Allow explicit trust-zone promotions such as `quarantine -> working` and guarded `working -> trusted` |
|
|
195
197
|
| `trustZoneStoreDir` | `{memoryDir}/state/trust-zones` | Root directory for trust-zone records |
|
|
196
198
|
|
|
197
199
|
Full reference: [Config Reference](docs/config-reference.md)
|
package/dist/index.js
CHANGED
|
@@ -26294,6 +26294,129 @@ function validateTrustZoneRecord(raw) {
|
|
|
26294
26294
|
metadata: validateMetadata3(raw.metadata)
|
|
26295
26295
|
};
|
|
26296
26296
|
}
|
|
26297
|
+
async function recordTrustZoneRecord(options) {
|
|
26298
|
+
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
26299
|
+
const validated = validateTrustZoneRecord(options.record);
|
|
26300
|
+
const day = recordStoreDay(validated.recordedAt);
|
|
26301
|
+
const zoneDir = path53.join(rootDir, "zones", validated.zone, day);
|
|
26302
|
+
const filePath = path53.join(zoneDir, `${validated.recordId}.json`);
|
|
26303
|
+
await mkdir35(zoneDir, { recursive: true });
|
|
26304
|
+
await writeFile32(filePath, JSON.stringify(validated, null, 2), "utf8");
|
|
26305
|
+
return filePath;
|
|
26306
|
+
}
|
|
26307
|
+
function hasAnchoredProvenance(record) {
|
|
26308
|
+
return Boolean(record.provenance.sourceId && record.provenance.evidenceHash);
|
|
26309
|
+
}
|
|
26310
|
+
function buildPromotionRecordId(sourceRecordId, targetZone, recordedAt) {
|
|
26311
|
+
const suffix = recordedAt.replace(/[^0-9]/g, "").slice(0, 14);
|
|
26312
|
+
return `${sourceRecordId}-${targetZone}-${suffix}`;
|
|
26313
|
+
}
|
|
26314
|
+
function dedupeStrings(values) {
|
|
26315
|
+
const out = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
26316
|
+
if (out.length === 0) return void 0;
|
|
26317
|
+
return [...new Set(out)];
|
|
26318
|
+
}
|
|
26319
|
+
function planTrustZonePromotion(options) {
|
|
26320
|
+
const { record, targetZone } = options;
|
|
26321
|
+
const reasons = [];
|
|
26322
|
+
const provenanceAnchored = hasAnchoredProvenance(record);
|
|
26323
|
+
if (record.zone === targetZone) {
|
|
26324
|
+
reasons.push(`record is already in the ${targetZone} zone`);
|
|
26325
|
+
}
|
|
26326
|
+
if (record.zone === "trusted") {
|
|
26327
|
+
reasons.push("trusted records are terminal and cannot be promoted again");
|
|
26328
|
+
}
|
|
26329
|
+
if (record.zone === "quarantine" && targetZone === "trusted") {
|
|
26330
|
+
reasons.push("quarantine records must pass through working before trusted promotion");
|
|
26331
|
+
}
|
|
26332
|
+
if (record.zone === "working" && targetZone === "quarantine") {
|
|
26333
|
+
reasons.push("working records cannot be demoted back into quarantine in this promotion path");
|
|
26334
|
+
}
|
|
26335
|
+
if (record.zone === "quarantine" && targetZone !== "working") {
|
|
26336
|
+
reasons.push("quarantine promotions only support the working zone");
|
|
26337
|
+
}
|
|
26338
|
+
if (record.zone === "working" && targetZone !== "trusted") {
|
|
26339
|
+
reasons.push("working promotions only support the trusted zone");
|
|
26340
|
+
}
|
|
26341
|
+
if (targetZone === "trusted" && ["tool_output", "web_content", "subagent_trace"].includes(record.provenance.sourceClass) && provenanceAnchored !== true) {
|
|
26342
|
+
reasons.push("trusted promotion for external/tool-derived provenance requires both provenance.sourceId and provenance.evidenceHash");
|
|
26343
|
+
}
|
|
26344
|
+
return {
|
|
26345
|
+
allowed: reasons.length === 0,
|
|
26346
|
+
reasons,
|
|
26347
|
+
sourceRecordId: record.recordId,
|
|
26348
|
+
sourceZone: record.zone,
|
|
26349
|
+
targetZone,
|
|
26350
|
+
provenanceAnchored
|
|
26351
|
+
};
|
|
26352
|
+
}
|
|
26353
|
+
async function findTrustZoneRecordById(options) {
|
|
26354
|
+
const { records } = await readTrustZoneRecords(options);
|
|
26355
|
+
records.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
26356
|
+
return records.find((record) => record.recordId === options.recordId) ?? null;
|
|
26357
|
+
}
|
|
26358
|
+
async function promoteTrustZoneRecord(options) {
|
|
26359
|
+
if (options.enabled !== true) {
|
|
26360
|
+
throw new Error("trust zone promotion requires trustZonesEnabled=true");
|
|
26361
|
+
}
|
|
26362
|
+
if (options.promotionEnabled !== true) {
|
|
26363
|
+
throw new Error("trust zone promotion requires quarantinePromotionEnabled=true");
|
|
26364
|
+
}
|
|
26365
|
+
const sourceRecord = await findTrustZoneRecordById({
|
|
26366
|
+
memoryDir: options.memoryDir,
|
|
26367
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
26368
|
+
recordId: assertSafePathSegment(assertString2(options.sourceRecordId, "sourceRecordId"), "sourceRecordId")
|
|
26369
|
+
});
|
|
26370
|
+
if (!sourceRecord) {
|
|
26371
|
+
throw new Error(`source trust-zone record not found: ${options.sourceRecordId}`);
|
|
26372
|
+
}
|
|
26373
|
+
const plan = planTrustZonePromotion({
|
|
26374
|
+
record: sourceRecord,
|
|
26375
|
+
targetZone: options.targetZone
|
|
26376
|
+
});
|
|
26377
|
+
if (!plan.allowed) {
|
|
26378
|
+
throw new Error(`trust-zone promotion denied: ${plan.reasons.join("; ")}`);
|
|
26379
|
+
}
|
|
26380
|
+
const recordedAt = assertIsoRecordedAt(assertString2(options.recordedAt, "recordedAt"));
|
|
26381
|
+
const promotionReason = assertString2(options.promotionReason, "promotionReason");
|
|
26382
|
+
const nextRecord = {
|
|
26383
|
+
schemaVersion: 1,
|
|
26384
|
+
recordId: buildPromotionRecordId(sourceRecord.recordId, options.targetZone, recordedAt),
|
|
26385
|
+
zone: options.targetZone,
|
|
26386
|
+
recordedAt,
|
|
26387
|
+
kind: sourceRecord.kind,
|
|
26388
|
+
summary: optionalString(options.summary) ?? sourceRecord.summary,
|
|
26389
|
+
provenance: sourceRecord.provenance,
|
|
26390
|
+
promotedFromZone: sourceRecord.zone,
|
|
26391
|
+
entityRefs: sourceRecord.entityRefs,
|
|
26392
|
+
tags: dedupeStrings([...sourceRecord.tags ?? [], "promotion"]),
|
|
26393
|
+
metadata: {
|
|
26394
|
+
...sourceRecord.metadata ?? {},
|
|
26395
|
+
sourceRecordId: sourceRecord.recordId,
|
|
26396
|
+
promotionReason
|
|
26397
|
+
}
|
|
26398
|
+
};
|
|
26399
|
+
if (options.dryRun === true) {
|
|
26400
|
+
return {
|
|
26401
|
+
plan,
|
|
26402
|
+
wroteRecord: false,
|
|
26403
|
+
record: nextRecord,
|
|
26404
|
+
sourceRecord
|
|
26405
|
+
};
|
|
26406
|
+
}
|
|
26407
|
+
const filePath = await recordTrustZoneRecord({
|
|
26408
|
+
memoryDir: options.memoryDir,
|
|
26409
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
26410
|
+
record: nextRecord
|
|
26411
|
+
});
|
|
26412
|
+
return {
|
|
26413
|
+
plan,
|
|
26414
|
+
wroteRecord: true,
|
|
26415
|
+
record: nextRecord,
|
|
26416
|
+
filePath,
|
|
26417
|
+
sourceRecord
|
|
26418
|
+
};
|
|
26419
|
+
}
|
|
26297
26420
|
async function readTrustZoneRecords(options) {
|
|
26298
26421
|
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
26299
26422
|
const files = await listJsonFiles(path53.join(rootDir, "zones"));
|
|
@@ -26546,6 +26669,24 @@ async function runTrustZoneStatusCliCommand(options) {
|
|
|
26546
26669
|
promotionEnabled: options.quarantinePromotionEnabled
|
|
26547
26670
|
});
|
|
26548
26671
|
}
|
|
26672
|
+
async function runTrustZonePromoteCliCommand(options) {
|
|
26673
|
+
const result = await promoteTrustZoneRecord({
|
|
26674
|
+
memoryDir: options.memoryDir,
|
|
26675
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
26676
|
+
enabled: options.trustZonesEnabled,
|
|
26677
|
+
promotionEnabled: options.quarantinePromotionEnabled,
|
|
26678
|
+
sourceRecordId: options.sourceRecordId,
|
|
26679
|
+
targetZone: options.targetZone,
|
|
26680
|
+
recordedAt: options.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
26681
|
+
promotionReason: options.promotionReason,
|
|
26682
|
+
summary: options.summary,
|
|
26683
|
+
dryRun: options.dryRun === true
|
|
26684
|
+
});
|
|
26685
|
+
return {
|
|
26686
|
+
...result,
|
|
26687
|
+
dryRun: options.dryRun === true
|
|
26688
|
+
};
|
|
26689
|
+
}
|
|
26549
26690
|
async function runSessionCheckCliCommand(options) {
|
|
26550
26691
|
return analyzeSessionIntegrity({ memoryDir: options.memoryDir });
|
|
26551
26692
|
}
|
|
@@ -27714,6 +27855,23 @@ function registerCli(api, orchestrator) {
|
|
|
27714
27855
|
console.log(JSON.stringify(status, null, 2));
|
|
27715
27856
|
console.log("OK");
|
|
27716
27857
|
});
|
|
27858
|
+
cmd.command("trust-zone-promote").description("Dry-run or apply a trust-zone promotion with provenance enforcement").requiredOption("--record-id <recordId>", "Source trust-zone record id").requiredOption("--target-zone <targetZone>", "Promotion target zone (working|trusted)").requiredOption("--reason <reason>", "Human-readable promotion reason").option("--recorded-at <isoTimestamp>", "Promotion timestamp (defaults to now)").option("--summary <summary>", "Optional replacement summary for the promoted record").option("--dry-run", "Show the promotion plan without writing the promoted record").action(async (...args) => {
|
|
27859
|
+
const options = args[0] ?? {};
|
|
27860
|
+
const result = await runTrustZonePromoteCliCommand({
|
|
27861
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
27862
|
+
trustZoneStoreDir: orchestrator.config.trustZoneStoreDir,
|
|
27863
|
+
trustZonesEnabled: orchestrator.config.trustZonesEnabled,
|
|
27864
|
+
quarantinePromotionEnabled: orchestrator.config.quarantinePromotionEnabled,
|
|
27865
|
+
sourceRecordId: String(options.recordId ?? ""),
|
|
27866
|
+
targetZone: String(options.targetZone ?? ""),
|
|
27867
|
+
promotionReason: String(options.reason ?? ""),
|
|
27868
|
+
recordedAt: typeof options.recordedAt === "string" ? options.recordedAt : void 0,
|
|
27869
|
+
summary: typeof options.summary === "string" ? options.summary : void 0,
|
|
27870
|
+
dryRun: options.dryRun === true
|
|
27871
|
+
});
|
|
27872
|
+
console.log(JSON.stringify(result, null, 2));
|
|
27873
|
+
console.log("OK");
|
|
27874
|
+
});
|
|
27717
27875
|
cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
|
|
27718
27876
|
const health = await runConversationIndexHealthCliCommand(orchestrator);
|
|
27719
27877
|
console.log(JSON.stringify(health, null, 2));
|