@joshuaswarren/openclaw-engram 9.0.32 → 9.0.34

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 CHANGED
@@ -36,7 +36,7 @@ AI agents forget everything between conversations. Engram fixes that.
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
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
  - **Trust-zone recall** — Engram can now, when `trustZoneRecallEnabled` is enabled, inject prompt-relevant `working` and `trusted` trust-zone records into recall context as a separate `Trust Zones` section while keeping `quarantine` material out of recall by default.
39
- - **Provenance trust scoring** — Engram can now, when `memoryPoisoningDefenseEnabled` is enabled, score trust-zone provenance deterministically from source class and evidence anchors so poisoning defenses start with measurable trust signals instead of opaque heuristics.
39
+ - **Poisoning-defense corroboration** — Engram can now, when `memoryPoisoningDefenseEnabled` is enabled, score trust-zone provenance deterministically and require independent non-quarantine corroboration before risky `working -> trusted` promotions succeed.
40
40
  - **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.
41
41
 
42
42
  ## Quick Start
@@ -163,7 +163,7 @@ openclaw engram benchmark-ci-gate # Compare base vs candidate eval st
163
163
  openclaw engram objective-state-status # Objective-state snapshot counts and latest stored snapshot
164
164
  openclaw engram causal-trajectory-status # Causal-trajectory record counts and latest stored chain
165
165
  openclaw engram trust-zone-status # Trust-zone record counts and latest stored record
166
- openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance enforcement
166
+ openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance/corroboration enforcement
167
167
  openclaw engram conversation-index-health # Conversation index status
168
168
  openclaw engram graph-health # Entity graph status
169
169
  openclaw engram tier-status # Hot/cold tier metrics
@@ -198,7 +198,7 @@ Key settings:
198
198
  | `quarantinePromotionEnabled` | `false` | Allow explicit trust-zone promotions such as `quarantine -> working` and guarded `working -> trusted` |
199
199
  | `trustZoneStoreDir` | `{memoryDir}/state/trust-zones` | Root directory for trust-zone records |
200
200
  | `trustZoneRecallEnabled` | `false` | Inject prompt-relevant working and trusted trust-zone records into recall context |
201
- | `memoryPoisoningDefenseEnabled` | `false` | Enable deterministic provenance trust scoring in trust-zone status output as the first poisoning-defense signal |
201
+ | `memoryPoisoningDefenseEnabled` | `false` | Enable deterministic provenance trust scoring and corroboration requirements for risky trusted promotions |
202
202
 
203
203
  Full reference: [Config Reference](docs/config-reference.md)
204
204
 
package/dist/index.js CHANGED
@@ -6333,7 +6333,7 @@ import path6 from "path";
6333
6333
  import { mkdir as mkdir2, readFile as readFile3, stat, writeFile as writeFile2 } from "fs/promises";
6334
6334
  import path5 from "path";
6335
6335
  function toSafeTimestamp(ts) {
6336
- return ts.toISOString().replace(/[:.]/g, "").replace("Z", "Z");
6336
+ return ts.toISOString().replace(/[:.]/g, "");
6337
6337
  }
6338
6338
  async function lintWorkspaceFiles(opts) {
6339
6339
  const warnings = [];
@@ -14673,6 +14673,27 @@ function dedupeStrings(values) {
14673
14673
  if (out.length === 0) return void 0;
14674
14674
  return [...new Set(out)];
14675
14675
  }
14676
+ function hasOverlap(left, right) {
14677
+ if (!left || !right || left.length === 0 || right.length === 0) return false;
14678
+ const rightSet = new Set(right);
14679
+ return left.some((value) => rightSet.has(value));
14680
+ }
14681
+ function requiresCorroboration(record, targetZone, poisoningDefenseEnabled) {
14682
+ return poisoningDefenseEnabled === true && targetZone === "trusted" && record.zone === "working" && ["tool_output", "web_content", "subagent_trace"].includes(record.provenance.sourceClass);
14683
+ }
14684
+ function summarizeCorroboration(options) {
14685
+ const corroborating = options.records.filter((candidate) => {
14686
+ if (candidate.recordId === options.sourceRecord.recordId) return false;
14687
+ if (candidate.zone === "quarantine") return false;
14688
+ if (hasAnchoredProvenance(candidate) !== true) return false;
14689
+ if (candidate.provenance.sourceClass === options.sourceRecord.provenance.sourceClass) return false;
14690
+ return hasOverlap(candidate.entityRefs, options.sourceRecord.entityRefs) || hasOverlap(candidate.tags, options.sourceRecord.tags);
14691
+ });
14692
+ return {
14693
+ count: corroborating.length,
14694
+ sourceClasses: [...new Set(corroborating.map((record) => record.provenance.sourceClass))]
14695
+ };
14696
+ }
14676
14697
  var SOURCE_CLASS_WEIGHTS = {
14677
14698
  manual: 0.9,
14678
14699
  system_memory: 0.85,
@@ -14768,6 +14789,16 @@ async function promoteTrustZoneRecord(options) {
14768
14789
  if (!plan.allowed) {
14769
14790
  throw new Error(`trust-zone promotion denied: ${plan.reasons.join("; ")}`);
14770
14791
  }
14792
+ const corroboration = requiresCorroboration(sourceRecord, options.targetZone, options.poisoningDefenseEnabled === true) ? summarizeCorroboration({
14793
+ sourceRecord,
14794
+ records: (await readTrustZoneRecords({
14795
+ memoryDir: options.memoryDir,
14796
+ trustZoneStoreDir: options.trustZoneStoreDir
14797
+ })).records
14798
+ }) : null;
14799
+ if (corroboration && corroboration.count === 0) {
14800
+ throw new Error("trust-zone promotion denied: corroboration is required for risky trusted promotions");
14801
+ }
14771
14802
  const recordedAt = assertIsoRecordedAt(assertString2(options.recordedAt, "recordedAt"));
14772
14803
  const promotionReason = assertString2(options.promotionReason, "promotionReason");
14773
14804
  const nextRecord = {
@@ -14784,7 +14815,12 @@ async function promoteTrustZoneRecord(options) {
14784
14815
  metadata: {
14785
14816
  ...sourceRecord.metadata ?? {},
14786
14817
  sourceRecordId: sourceRecord.recordId,
14787
- promotionReason
14818
+ promotionReason,
14819
+ ...corroboration ? {
14820
+ corroborated: "true",
14821
+ corroborationCount: String(corroboration.count),
14822
+ corroborationSources: corroboration.sourceClasses.join(",")
14823
+ } : {}
14788
14824
  }
14789
14825
  };
14790
14826
  if (options.dryRun === true) {
@@ -26866,6 +26902,7 @@ async function runTrustZonePromoteCliCommand(options) {
26866
26902
  trustZoneStoreDir: options.trustZoneStoreDir,
26867
26903
  enabled: options.trustZonesEnabled,
26868
26904
  promotionEnabled: options.quarantinePromotionEnabled,
26905
+ poisoningDefenseEnabled: options.memoryPoisoningDefenseEnabled,
26869
26906
  sourceRecordId: options.sourceRecordId,
26870
26907
  targetZone: options.targetZone,
26871
26908
  recordedAt: options.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
@@ -28054,6 +28091,7 @@ function registerCli(api, orchestrator) {
28054
28091
  trustZoneStoreDir: orchestrator.config.trustZoneStoreDir,
28055
28092
  trustZonesEnabled: orchestrator.config.trustZonesEnabled,
28056
28093
  quarantinePromotionEnabled: orchestrator.config.quarantinePromotionEnabled,
28094
+ memoryPoisoningDefenseEnabled: orchestrator.config.memoryPoisoningDefenseEnabled,
28057
28095
  sourceRecordId: String(options.recordId ?? ""),
28058
28096
  targetZone: String(options.targetZone ?? ""),
28059
28097
  promotionReason: String(options.reason ?? ""),