@remnic/plugin-openclaw 1.0.10 → 1.0.11
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/dist/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
- package/dist/capsule-cli-TFKLAG3S.js +329 -0
- package/dist/capsule-crypto-K3IRTKRH.js +17 -0
- package/dist/capsule-export-CVA3CKUQ.js +265 -0
- package/dist/capsule-import-CFX7BY5W.js +16 -0
- package/dist/capsule-merge-7RVOHJK3.js +189 -0
- package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
- package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-JD6KJJH6.js} +16 -12
- package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
- package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
- package/dist/chunk-37NKFWSO.js +233 -0
- package/dist/chunk-3G7FAF6S.js +60 -0
- package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
- package/dist/chunk-3I7RHWYT.js +214 -0
- package/dist/chunk-4G2XCSD2.js +186 -0
- package/dist/chunk-6IWEAUN6.js +148 -0
- package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
- package/dist/chunk-7OQEPGQF.js +529 -0
- package/dist/chunk-B52XADV3.js +244 -0
- package/dist/chunk-BU5KJVWF.js +78 -0
- package/dist/chunk-CXM7EBAO.js +289 -0
- package/dist/chunk-ETJZRIAM.js +227 -0
- package/dist/chunk-FQRSVYY4.js +110 -0
- package/dist/chunk-HRGFO6AW.js +349 -0
- package/dist/chunk-I6B2W2IY.js +47 -0
- package/dist/chunk-JZBOXOUC.js +259 -0
- package/dist/chunk-K7EUBNDD.js +185 -0
- package/dist/chunk-L4PRBB2A.js +1860 -0
- package/dist/chunk-MBIFE6SA.js +250 -0
- package/dist/chunk-N7EOZY6F.js +400 -0
- package/dist/chunk-NKVIN6RD.js +118 -0
- package/dist/chunk-OEI7GLV2.js +17 -0
- package/dist/{chunk-S2ISS4AH.js → chunk-P3DIW2SD.js} +10 -10
- package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
- package/dist/chunk-SSFTU6LP.js +182 -0
- package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
- package/dist/chunk-TLVIQLB4.js +874 -0
- package/dist/{chunk-JJSNPSCD.js → chunk-TNH24SF6.js} +352 -50
- package/dist/chunk-TVKKIS53.js +720 -0
- package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
- package/dist/{chunk-HCFFXBLV.js → chunk-XMSDA5WA.js} +5 -1861
- package/dist/chunk-YGGGUTG3.js +125 -0
- package/dist/chunk-YGXXBRV7.js +10 -0
- package/dist/cipher-VHAFCG7Z.js +27 -0
- package/dist/dreams-ledger-3I52ISYR.js +285 -0
- package/dist/{engine-65C2J63X.js → engine-VMTFKFGO.js} +5 -2
- package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
- package/dist/first-start-migration-I24M2JEE.js +258 -0
- package/dist/forget-NI4RBDPB.js +68 -0
- package/dist/fs-utils-PZRI2HDZ.js +29 -0
- package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
- package/dist/index.js +9775 -2900
- package/dist/kdf-H5B23ZM2.js +25 -0
- package/dist/memory-governance-DWGFV4FX.js +25 -0
- package/dist/metadata-JAGIWHEA.js +20 -0
- package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
- package/dist/path-5LCUBAAZ.js +8 -0
- package/dist/peers-JF2I6RCR.js +43 -0
- package/dist/purge-XN2VSPZ2.js +204 -0
- package/dist/secure-store-FWJ7LBPH.js +149 -0
- package/dist/state-PVISYXRH.js +7 -0
- package/dist/state-store-LP5BO6SF.js +15 -0
- package/dist/{storage-DM4ZGOCN.js → storage-T2OGFUF4.js} +3 -1
- package/dist/tier-stats-IZNW66NC.js +147 -0
- package/dist/trace-NJESSGH7.js +289 -0
- package/dist/tui-MGK2LYJY.js +12 -0
- package/dist/types-H5R5D3WF.js +30 -0
- package/openclaw.plugin.json +519 -4
- package/package.json +1 -1
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createVersion
|
|
3
3
|
} from "./chunk-6OJAU466.js";
|
|
4
|
+
import {
|
|
5
|
+
SecureStoreLockedError,
|
|
6
|
+
readMaybeEncryptedFile,
|
|
7
|
+
writeMaybeEncryptedFile
|
|
8
|
+
} from "./chunk-3I7RHWYT.js";
|
|
4
9
|
import {
|
|
5
10
|
log
|
|
6
11
|
} from "./chunk-UFU5GGGA.js";
|
|
7
12
|
|
|
8
13
|
// ../remnic-core/src/storage.ts
|
|
9
|
-
import { access, readdir, readFile as readFile2, stat as stat2, writeFile as writeFile2, mkdir as mkdir2, unlink,
|
|
14
|
+
import { access, readdir, readFile as readFile2, stat as stat2, writeFile as writeFile2, mkdir as mkdir2, unlink, appendFile } from "fs/promises";
|
|
10
15
|
import { appendFileSync, mkdirSync, statSync } from "fs";
|
|
11
16
|
import { createHash } from "crypto";
|
|
12
17
|
import path4 from "path";
|
|
@@ -40,7 +45,7 @@ function getCachedEpisodeMap(baseDir, currentVersion) {
|
|
|
40
45
|
function setCachedEpisodeMap(baseDir, memories, version) {
|
|
41
46
|
const map = /* @__PURE__ */ new Map();
|
|
42
47
|
for (const m of memories) {
|
|
43
|
-
if (m.frontmatter.status === "archived") continue;
|
|
48
|
+
if (m.frontmatter.status === "archived" || m.frontmatter.status === "forgotten") continue;
|
|
44
49
|
if (m.frontmatter.memoryKind !== "episode") continue;
|
|
45
50
|
map.set(m.frontmatter.id, m);
|
|
46
51
|
}
|
|
@@ -58,7 +63,7 @@ function setCachedRuleMemories(baseDir, memories, version) {
|
|
|
58
63
|
const all = [];
|
|
59
64
|
for (const m of memories) {
|
|
60
65
|
byId.set(m.frontmatter.id, m);
|
|
61
|
-
if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived") {
|
|
66
|
+
if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived" && m.frontmatter.status !== "forgotten") {
|
|
62
67
|
all.push(m);
|
|
63
68
|
}
|
|
64
69
|
}
|
|
@@ -180,21 +185,41 @@ function sanitizeMemoryContent(text) {
|
|
|
180
185
|
var CONSOLIDATION_OPERATORS = [
|
|
181
186
|
"split",
|
|
182
187
|
"merge",
|
|
183
|
-
"update"
|
|
188
|
+
"update",
|
|
189
|
+
"pattern-reinforcement"
|
|
184
190
|
];
|
|
185
191
|
var DERIVED_FROM_ENTRY_RE = /^(.+):(\d+)$/;
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
var DERIVED_FROM_MEMORY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_:-]*$/;
|
|
193
|
+
function looksLikeSnapshotEntry(entry) {
|
|
188
194
|
const match = entry.match(DERIVED_FROM_ENTRY_RE);
|
|
189
195
|
if (!match) return false;
|
|
190
196
|
const pathPart = match[1];
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
197
|
+
return pathPart.includes("/") || pathPart.includes(".");
|
|
198
|
+
}
|
|
199
|
+
function isValidDerivedFromEntry(entry) {
|
|
200
|
+
if (typeof entry !== "string") return false;
|
|
201
|
+
if (entry.length === 0) return false;
|
|
202
|
+
if (looksLikeSnapshotEntry(entry)) {
|
|
203
|
+
const match = entry.match(DERIVED_FROM_ENTRY_RE);
|
|
204
|
+
if (!match) return false;
|
|
205
|
+
const pathPart = match[1];
|
|
206
|
+
if (pathPart.length === 0 || pathPart.trim().length === 0) return false;
|
|
207
|
+
const versionNum = Number(match[2]);
|
|
208
|
+
return Number.isInteger(versionNum) && versionNum >= 0;
|
|
209
|
+
}
|
|
210
|
+
return DERIVED_FROM_MEMORY_ID_RE.test(entry);
|
|
194
211
|
}
|
|
195
212
|
function isConsolidationOperator(value) {
|
|
196
213
|
return typeof value === "string" && CONSOLIDATION_OPERATORS.includes(value);
|
|
197
214
|
}
|
|
215
|
+
var SEMANTIC_CONSOLIDATION_LLM_OPERATORS = [
|
|
216
|
+
"split",
|
|
217
|
+
"merge",
|
|
218
|
+
"update"
|
|
219
|
+
];
|
|
220
|
+
function isSemanticConsolidationLlmOperator(value) {
|
|
221
|
+
return typeof value === "string" && SEMANTIC_CONSOLIDATION_LLM_OPERATORS.includes(value);
|
|
222
|
+
}
|
|
198
223
|
|
|
199
224
|
// ../remnic-core/src/entity-schema.ts
|
|
200
225
|
var DEFAULT_ENTITY_SCHEMAS = {
|
|
@@ -541,6 +566,15 @@ function stripCitationForTemplate(text, template) {
|
|
|
541
566
|
}
|
|
542
567
|
|
|
543
568
|
// ../remnic-core/src/types.ts
|
|
569
|
+
var RECALL_DISCLOSURE_LEVELS = [
|
|
570
|
+
"chunk",
|
|
571
|
+
"section",
|
|
572
|
+
"raw"
|
|
573
|
+
];
|
|
574
|
+
var DEFAULT_RECALL_DISCLOSURE = "chunk";
|
|
575
|
+
function isRecallDisclosure(value) {
|
|
576
|
+
return typeof value === "string" && RECALL_DISCLOSURE_LEVELS.includes(value);
|
|
577
|
+
}
|
|
544
578
|
function confidenceTier(score) {
|
|
545
579
|
if (score >= 0.95) return "explicit";
|
|
546
580
|
if (score >= 0.7) return "implied";
|
|
@@ -1269,6 +1303,9 @@ function inferMemoryStatus(frontmatter, pathRel, fallbackStatus = "active") {
|
|
|
1269
1303
|
if (frontmatter.status) return frontmatter.status;
|
|
1270
1304
|
return fallbackStatus;
|
|
1271
1305
|
}
|
|
1306
|
+
function isActiveMemoryStatus(status) {
|
|
1307
|
+
return status === void 0 || status === "active";
|
|
1308
|
+
}
|
|
1272
1309
|
function summarizeMemoryLifecycleState(memory) {
|
|
1273
1310
|
return {
|
|
1274
1311
|
category: memory.frontmatter.category,
|
|
@@ -1632,6 +1669,88 @@ function assertMemoryWorthCounter(field, value) {
|
|
|
1632
1669
|
throw new Error(`${field} must be >= 0, got ${value}`);
|
|
1633
1670
|
}
|
|
1634
1671
|
}
|
|
1672
|
+
function isErrnoCode(error, code) {
|
|
1673
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1674
|
+
}
|
|
1675
|
+
function trimTrailingSpacesAndTabs(value) {
|
|
1676
|
+
let end = value.length;
|
|
1677
|
+
while (end > 0 && (value[end - 1] === " " || value[end - 1] === " ")) {
|
|
1678
|
+
end -= 1;
|
|
1679
|
+
}
|
|
1680
|
+
return end === value.length ? value : value.slice(0, end);
|
|
1681
|
+
}
|
|
1682
|
+
function trimLeadingSpacesAndTabs(value) {
|
|
1683
|
+
let start = 0;
|
|
1684
|
+
while (start < value.length && (value[start] === " " || value[start] === " ")) {
|
|
1685
|
+
start += 1;
|
|
1686
|
+
}
|
|
1687
|
+
return start === 0 ? value : value.slice(start);
|
|
1688
|
+
}
|
|
1689
|
+
function stripDefaultCitationMarkersWithoutRegex(value) {
|
|
1690
|
+
return stripCitationMarkersForHashRemoval(value, DEFAULT_CITATION_FORMAT);
|
|
1691
|
+
}
|
|
1692
|
+
function citationTemplateLiteralParts(template) {
|
|
1693
|
+
const parts = [];
|
|
1694
|
+
let cursor = 0;
|
|
1695
|
+
while (cursor < template.length) {
|
|
1696
|
+
const open = template.indexOf("{", cursor);
|
|
1697
|
+
if (open === -1) {
|
|
1698
|
+
parts.push(template.slice(cursor));
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
parts.push(template.slice(cursor, open));
|
|
1702
|
+
const close = template.indexOf("}", open + 1);
|
|
1703
|
+
if (close === -1) {
|
|
1704
|
+
cursor = open + 1;
|
|
1705
|
+
} else {
|
|
1706
|
+
cursor = close + 1;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return parts.filter((part) => part.length > 0);
|
|
1710
|
+
}
|
|
1711
|
+
function stripCitationMarkersForHashRemoval(value, template) {
|
|
1712
|
+
const parts = citationTemplateLiteralParts(template);
|
|
1713
|
+
if (parts.length === 0) return value;
|
|
1714
|
+
const first = parts[0];
|
|
1715
|
+
const lowerValue = value.toLowerCase();
|
|
1716
|
+
const lowerFirst = first.toLowerCase();
|
|
1717
|
+
const lowerParts = parts.map((part) => part.toLowerCase());
|
|
1718
|
+
if (!lowerValue.includes(lowerFirst)) return value;
|
|
1719
|
+
let result = "";
|
|
1720
|
+
let cursor = 0;
|
|
1721
|
+
let removed = false;
|
|
1722
|
+
while (cursor < value.length) {
|
|
1723
|
+
const markerStart = lowerValue.indexOf(lowerFirst, cursor);
|
|
1724
|
+
if (markerStart === -1) {
|
|
1725
|
+
result += value.slice(cursor);
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
const boundedEnd = first.startsWith("[") ? value.indexOf("]", markerStart + first.length) : -1;
|
|
1729
|
+
if (first.startsWith("[") && boundedEnd === -1) {
|
|
1730
|
+
result += value.slice(cursor);
|
|
1731
|
+
break;
|
|
1732
|
+
}
|
|
1733
|
+
const searchLimit = boundedEnd === -1 ? value.length : boundedEnd + 1;
|
|
1734
|
+
let markerEnd = markerStart + first.length;
|
|
1735
|
+
let matched = true;
|
|
1736
|
+
for (let i = 1; i < lowerParts.length; i += 1) {
|
|
1737
|
+
const partIndex = lowerValue.indexOf(lowerParts[i], markerEnd);
|
|
1738
|
+
if (partIndex === -1 || partIndex + parts[i].length > searchLimit) {
|
|
1739
|
+
matched = false;
|
|
1740
|
+
break;
|
|
1741
|
+
}
|
|
1742
|
+
markerEnd = partIndex + parts[i].length;
|
|
1743
|
+
}
|
|
1744
|
+
if (!matched) {
|
|
1745
|
+
result += value.slice(cursor);
|
|
1746
|
+
break;
|
|
1747
|
+
}
|
|
1748
|
+
result += trimTrailingSpacesAndTabs(value.slice(cursor, markerStart));
|
|
1749
|
+
cursor = markerEnd;
|
|
1750
|
+
removed = true;
|
|
1751
|
+
}
|
|
1752
|
+
return removed ? trimLeadingSpacesAndTabs(result) : value;
|
|
1753
|
+
}
|
|
1635
1754
|
function serializeFrontmatter(fm) {
|
|
1636
1755
|
const lines = [
|
|
1637
1756
|
"---",
|
|
@@ -1654,6 +1773,10 @@ function serializeFrontmatter(fm) {
|
|
|
1654
1773
|
if (fm.supersededBy) lines.push(`supersededBy: ${fm.supersededBy}`);
|
|
1655
1774
|
if (fm.supersededAt) lines.push(`supersededAt: ${fm.supersededAt}`);
|
|
1656
1775
|
if (fm.archivedAt) lines.push(`archivedAt: ${fm.archivedAt}`);
|
|
1776
|
+
if (fm.valid_at) lines.push(`validAt: ${fm.valid_at}`);
|
|
1777
|
+
if (fm.invalid_at) lines.push(`invalidAt: ${fm.invalid_at}`);
|
|
1778
|
+
if (fm.forgottenAt) lines.push(`forgottenAt: ${fm.forgottenAt}`);
|
|
1779
|
+
if (fm.forgottenReason) lines.push(`forgottenReason: ${JSON.stringify(fm.forgottenReason)}`);
|
|
1657
1780
|
if (fm.lifecycleState) lines.push(`lifecycleState: ${fm.lifecycleState}`);
|
|
1658
1781
|
if (fm.verificationState) lines.push(`verificationState: ${fm.verificationState}`);
|
|
1659
1782
|
if (fm.policyClass) lines.push(`policyClass: ${fm.policyClass}`);
|
|
@@ -1731,11 +1854,22 @@ function serializeFrontmatter(fm) {
|
|
|
1731
1854
|
if (fm.derived_via !== void 0) {
|
|
1732
1855
|
if (!isConsolidationOperator(fm.derived_via)) {
|
|
1733
1856
|
throw new Error(
|
|
1734
|
-
`serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update"`
|
|
1857
|
+
`serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update" | "pattern-reinforcement"`
|
|
1735
1858
|
);
|
|
1736
1859
|
}
|
|
1737
1860
|
lines.push(`derived_via: ${fm.derived_via}`);
|
|
1738
1861
|
}
|
|
1862
|
+
if (fm.reinforcement_count !== void 0) {
|
|
1863
|
+
if (!Number.isInteger(fm.reinforcement_count) || fm.reinforcement_count <= 0) {
|
|
1864
|
+
throw new Error(
|
|
1865
|
+
`serializeFrontmatter: reinforcement_count must be a positive integer (got ${JSON.stringify(fm.reinforcement_count)})`
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
lines.push(`reinforcement_count: ${fm.reinforcement_count}`);
|
|
1869
|
+
}
|
|
1870
|
+
if (fm.last_reinforced_at) {
|
|
1871
|
+
lines.push(`last_reinforced_at: ${fm.last_reinforced_at}`);
|
|
1872
|
+
}
|
|
1739
1873
|
lines.push("---");
|
|
1740
1874
|
return lines.join("\n");
|
|
1741
1875
|
}
|
|
@@ -1768,6 +1902,20 @@ function parseLinkReasonValue(rawValue) {
|
|
|
1768
1902
|
return legacyValue;
|
|
1769
1903
|
}
|
|
1770
1904
|
}
|
|
1905
|
+
function parseFrontmatterStringValue(rawValue) {
|
|
1906
|
+
if (rawValue === void 0) return void 0;
|
|
1907
|
+
const trimmed = rawValue.trim();
|
|
1908
|
+
if (trimmed.length === 0) return void 0;
|
|
1909
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
1910
|
+
try {
|
|
1911
|
+
const parsed = JSON.parse(trimmed);
|
|
1912
|
+
return typeof parsed === "string" ? parsed : trimmed;
|
|
1913
|
+
} catch {
|
|
1914
|
+
return trimmed.slice(1, -1).replace(/\\"/g, '"');
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
return trimmed;
|
|
1918
|
+
}
|
|
1771
1919
|
function parseMemoryWorthCounterField(raw) {
|
|
1772
1920
|
if (raw === void 0) return void 0;
|
|
1773
1921
|
const trimmed = raw.trim();
|
|
@@ -1776,6 +1924,14 @@ function parseMemoryWorthCounterField(raw) {
|
|
|
1776
1924
|
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return void 0;
|
|
1777
1925
|
return n;
|
|
1778
1926
|
}
|
|
1927
|
+
function parseReinforcementCountField(raw) {
|
|
1928
|
+
if (raw === void 0) return void 0;
|
|
1929
|
+
const trimmed = raw.trim();
|
|
1930
|
+
if (trimmed.length === 0) return void 0;
|
|
1931
|
+
const n = Number(trimmed);
|
|
1932
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return void 0;
|
|
1933
|
+
return n;
|
|
1934
|
+
}
|
|
1779
1935
|
function parseFrontmatter2(raw) {
|
|
1780
1936
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
1781
1937
|
if (!match) return null;
|
|
@@ -1961,6 +2117,11 @@ function parseFrontmatter2(raw) {
|
|
|
1961
2117
|
supersededBy: fm.supersededBy || void 0,
|
|
1962
2118
|
supersededAt: fm.supersededAt || void 0,
|
|
1963
2119
|
archivedAt: fm.archivedAt || void 0,
|
|
2120
|
+
// Issue #680 — explicit fact lifecycle round-trip.
|
|
2121
|
+
valid_at: fm.validAt || void 0,
|
|
2122
|
+
invalid_at: fm.invalidAt || void 0,
|
|
2123
|
+
forgottenAt: fm.forgottenAt || void 0,
|
|
2124
|
+
forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
|
|
1964
2125
|
lifecycleState: fm.lifecycleState || void 0,
|
|
1965
2126
|
verificationState: fm.verificationState || void 0,
|
|
1966
2127
|
policyClass: fm.policyClass || void 0,
|
|
@@ -1995,7 +2156,14 @@ function parseFrontmatter2(raw) {
|
|
|
1995
2156
|
// Consolidation provenance (issue #561) — read-through only in this
|
|
1996
2157
|
// PR; no code produces these fields yet.
|
|
1997
2158
|
derived_from,
|
|
1998
|
-
derived_via
|
|
2159
|
+
derived_via,
|
|
2160
|
+
// Pattern-reinforcement metadata (issue #687 PR 2/4). Parse
|
|
2161
|
+
// permissively: invalid values (negative, non-integer, blank
|
|
2162
|
+
// ISO-strings) are dropped to undefined so a corrupt frontmatter
|
|
2163
|
+
// never poisons downstream scoring. Validation lives on the
|
|
2164
|
+
// write path in serializeFrontmatter.
|
|
2165
|
+
reinforcement_count: parseReinforcementCountField(fm.reinforcement_count),
|
|
2166
|
+
last_reinforced_at: fm.last_reinforced_at || void 0
|
|
1999
2167
|
},
|
|
2000
2168
|
content
|
|
2001
2169
|
};
|
|
@@ -3061,6 +3229,76 @@ var StorageManager = class _StorageManager {
|
|
|
3061
3229
|
setVersioningConfig(config) {
|
|
3062
3230
|
this._versioningConfig = config;
|
|
3063
3231
|
}
|
|
3232
|
+
/**
|
|
3233
|
+
* At-rest encryption key (issue #690 PR 3/4).
|
|
3234
|
+
*
|
|
3235
|
+
* When non-null, every memory file read is decrypted and every write
|
|
3236
|
+
* is encrypted using the secure-fs layer. When null, the storage
|
|
3237
|
+
* layer operates in plain-text mode (legacy/unencrypted store).
|
|
3238
|
+
*
|
|
3239
|
+
* Set by the orchestrator after init/unlock; cleared on lock.
|
|
3240
|
+
* The key buffer is NEVER logged or serialized.
|
|
3241
|
+
*/
|
|
3242
|
+
_secureStoreKey = null;
|
|
3243
|
+
/**
|
|
3244
|
+
* When true (and `_secureStoreKey` is non-null), new writes are
|
|
3245
|
+
* encrypted. Set to false to pause encryption of new writes while
|
|
3246
|
+
* still decrypting existing files.
|
|
3247
|
+
*/
|
|
3248
|
+
_secureStoreEncryptOnWrite = true;
|
|
3249
|
+
/**
|
|
3250
|
+
* When true, the secure-store is configured as required — writes
|
|
3251
|
+
* MUST be encrypted and a locked store MUST reject writes rather
|
|
3252
|
+
* than silently falling back to plaintext. Set by the orchestrator
|
|
3253
|
+
* from `config.secureStoreEnabled`.
|
|
3254
|
+
*/
|
|
3255
|
+
_secureStoreRequired = false;
|
|
3256
|
+
/**
|
|
3257
|
+
* Set or clear the at-rest encryption key.
|
|
3258
|
+
*
|
|
3259
|
+
* Pass a 32-byte Buffer to enable encryption; pass null to clear
|
|
3260
|
+
* (lock) the store. The caller is responsible for key lifecycle —
|
|
3261
|
+
* this method does not zero the buffer on replacement; the keyring
|
|
3262
|
+
* module (`keyring.ts`) owns zeroization.
|
|
3263
|
+
*/
|
|
3264
|
+
setSecureStoreKey(key, encryptOnWrite = true) {
|
|
3265
|
+
this._secureStoreKey = key;
|
|
3266
|
+
this._secureStoreEncryptOnWrite = encryptOnWrite;
|
|
3267
|
+
}
|
|
3268
|
+
/**
|
|
3269
|
+
* Mark the secure-store as required for this storage instance.
|
|
3270
|
+
* When required and locked, writes throw SecureStoreLockedError
|
|
3271
|
+
* rather than silently writing plaintext.
|
|
3272
|
+
*/
|
|
3273
|
+
setSecureStoreRequired(required) {
|
|
3274
|
+
this._secureStoreRequired = required;
|
|
3275
|
+
}
|
|
3276
|
+
/** Return true iff the secure-store key is currently set (store is unlocked). */
|
|
3277
|
+
isSecureStoreUnlocked() {
|
|
3278
|
+
return this._secureStoreKey !== null;
|
|
3279
|
+
}
|
|
3280
|
+
/**
|
|
3281
|
+
* Resolve the effective write key.
|
|
3282
|
+
*
|
|
3283
|
+
* - If `_secureStoreEncryptOnWrite` is false: returns null (plain write).
|
|
3284
|
+
* - If `_secureStoreEncryptOnWrite` is true AND key is set: returns key.
|
|
3285
|
+
* - If `_secureStoreEncryptOnWrite` is true AND key is null AND
|
|
3286
|
+
* `_secureStoreRequired` is true: throws SecureStoreLockedError so the
|
|
3287
|
+
* write fails loudly rather than silently writing plaintext (P1 finding
|
|
3288
|
+
* from Cursor review of PR #767).
|
|
3289
|
+
* - If `_secureStoreEncryptOnWrite` is true AND key is null AND
|
|
3290
|
+
* `_secureStoreRequired` is false: returns null (unencrypted store).
|
|
3291
|
+
*/
|
|
3292
|
+
resolveWriteKey() {
|
|
3293
|
+
if (!this._secureStoreEncryptOnWrite) return null;
|
|
3294
|
+
if (this._secureStoreKey !== null) return this._secureStoreKey;
|
|
3295
|
+
if (this._secureStoreRequired) {
|
|
3296
|
+
throw new SecureStoreLockedError(
|
|
3297
|
+
"secure-store is locked \u2014 cannot write memory file. Run `remnic secure-store unlock` to decrypt, or restart the daemon after unlocking."
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
return null;
|
|
3301
|
+
}
|
|
3064
3302
|
/**
|
|
3065
3303
|
* Snapshot the current content of a page before overwriting.
|
|
3066
3304
|
* No-op when versioning is disabled or the file does not yet exist.
|
|
@@ -3068,7 +3306,7 @@ var StorageManager = class _StorageManager {
|
|
|
3068
3306
|
async snapshotBeforeWrite(filePath, trigger) {
|
|
3069
3307
|
if (!this._versioningConfig || !this._versioningConfig.enabled) return;
|
|
3070
3308
|
try {
|
|
3071
|
-
const existing = await
|
|
3309
|
+
const existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
3072
3310
|
await createVersion(filePath, existing, trigger, this._versioningConfig, log, void 0, this.baseDir);
|
|
3073
3311
|
} catch {
|
|
3074
3312
|
}
|
|
@@ -3091,7 +3329,7 @@ var StorageManager = class _StorageManager {
|
|
|
3091
3329
|
if (!this._versioningConfig || !this._versioningConfig.enabled) return null;
|
|
3092
3330
|
let existing;
|
|
3093
3331
|
try {
|
|
3094
|
-
existing = await
|
|
3332
|
+
existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
3095
3333
|
} catch {
|
|
3096
3334
|
return null;
|
|
3097
3335
|
}
|
|
@@ -3433,7 +3671,7 @@ ${sanitized.text}
|
|
|
3433
3671
|
filePath = path4.join(this.factsDir, today, `${id}.md`);
|
|
3434
3672
|
}
|
|
3435
3673
|
await this.snapshotBeforeWrite(filePath, "write");
|
|
3436
|
-
await
|
|
3674
|
+
await writeMaybeEncryptedFile(filePath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
3437
3675
|
this.invalidateAllMemoriesCache();
|
|
3438
3676
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
|
|
3439
3677
|
memoryId: id,
|
|
@@ -3469,6 +3707,50 @@ ${sanitized.text}
|
|
|
3469
3707
|
const sanitized = sanitizeMemoryContent(content);
|
|
3470
3708
|
return factHashIndex.has(sanitized.text);
|
|
3471
3709
|
}
|
|
3710
|
+
factContentHashForRemoval(memory) {
|
|
3711
|
+
if (memory.frontmatter.category !== "fact") return null;
|
|
3712
|
+
if (typeof memory.frontmatter.contentHash === "string" && memory.frontmatter.contentHash.length > 0) {
|
|
3713
|
+
return memory.frontmatter.contentHash;
|
|
3714
|
+
}
|
|
3715
|
+
const configuredHashSource = stripCitationMarkersForHashRemoval(memory.content, this.citationTemplate);
|
|
3716
|
+
const hashSource = configuredHashSource !== memory.content ? configuredHashSource : stripDefaultCitationMarkersWithoutRegex(memory.content);
|
|
3717
|
+
return ContentHashIndex.computeHash(sanitizeMemoryContent(hashSource).text);
|
|
3718
|
+
}
|
|
3719
|
+
async removeFactContentHashesForMemories(memories) {
|
|
3720
|
+
await this.ensureFactHashIndexAuthoritative();
|
|
3721
|
+
const factHashIndex = await this.getFactHashIndex();
|
|
3722
|
+
const removedIds = new Set(
|
|
3723
|
+
memories.map((memory) => memory.frontmatter.id).filter((id) => typeof id === "string" && id.length > 0)
|
|
3724
|
+
);
|
|
3725
|
+
const removedHashes = /* @__PURE__ */ new Map();
|
|
3726
|
+
for (const memory of memories) {
|
|
3727
|
+
const hash = this.factContentHashForRemoval(memory);
|
|
3728
|
+
if (hash) {
|
|
3729
|
+
removedHashes.set(memory, hash);
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
if (removedHashes.size === 0) return;
|
|
3733
|
+
const remainingActiveHashes = /* @__PURE__ */ new Set();
|
|
3734
|
+
const remainingMemories = [
|
|
3735
|
+
...await this.readAllMemories(),
|
|
3736
|
+
...await this.readAllColdMemories()
|
|
3737
|
+
];
|
|
3738
|
+
for (const memory of remainingMemories) {
|
|
3739
|
+
if (memory.frontmatter.category !== "fact") continue;
|
|
3740
|
+
if (removedIds.has(memory.frontmatter.id)) continue;
|
|
3741
|
+
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
3742
|
+
const hash = this.factContentHashForRemoval(memory);
|
|
3743
|
+
if (hash) {
|
|
3744
|
+
remainingActiveHashes.add(hash);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
for (const hash of removedHashes.values()) {
|
|
3748
|
+
if (!remainingActiveHashes.has(hash)) {
|
|
3749
|
+
factHashIndex.removeByHash(hash);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
await factHashIndex.save();
|
|
3753
|
+
}
|
|
3472
3754
|
async isFactContentHashAuthoritative() {
|
|
3473
3755
|
await this.ensureFactHashIndexAuthoritative();
|
|
3474
3756
|
return true;
|
|
@@ -3502,10 +3784,10 @@ ${sanitized.text}
|
|
|
3502
3784
|
return "";
|
|
3503
3785
|
}
|
|
3504
3786
|
const filePath = path4.join(dir, `${id}.md`);
|
|
3505
|
-
await
|
|
3787
|
+
await writeMaybeEncryptedFile(filePath, `${serializeFrontmatter(fm)}
|
|
3506
3788
|
|
|
3507
3789
|
${sanitized.text}
|
|
3508
|
-
`,
|
|
3790
|
+
`, this.resolveWriteKey(), {}, this.baseDir);
|
|
3509
3791
|
const actor = typeof options.actor === "string" && options.actor.length > 0 ? options.actor : "storage.writeArtifact";
|
|
3510
3792
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeArtifact", {
|
|
3511
3793
|
memoryId: id,
|
|
@@ -3680,15 +3962,19 @@ ${sanitized.text}
|
|
|
3680
3962
|
}
|
|
3681
3963
|
async readProfile() {
|
|
3682
3964
|
try {
|
|
3683
|
-
return await
|
|
3684
|
-
} catch {
|
|
3685
|
-
|
|
3965
|
+
return await readMaybeEncryptedFile(this.profilePath, this._secureStoreKey, this.baseDir);
|
|
3966
|
+
} catch (error) {
|
|
3967
|
+
if (error instanceof SecureStoreLockedError) {
|
|
3968
|
+
throw error;
|
|
3969
|
+
}
|
|
3970
|
+
if (isErrnoCode(error, "ENOENT")) return "";
|
|
3971
|
+
throw error;
|
|
3686
3972
|
}
|
|
3687
3973
|
}
|
|
3688
3974
|
async writeProfile(content) {
|
|
3689
3975
|
await this.ensureDirectories();
|
|
3690
3976
|
await this.snapshotBeforeWrite(this.profilePath, "consolidation");
|
|
3691
|
-
await
|
|
3977
|
+
await writeMaybeEncryptedFile(this.profilePath, content, this.resolveWriteKey(), {}, this.baseDir);
|
|
3692
3978
|
log.debug("updated profile.md");
|
|
3693
3979
|
}
|
|
3694
3980
|
/**
|
|
@@ -3771,6 +4057,24 @@ ${sanitized.text}
|
|
|
3771
4057
|
invalidateAllMemoriesCacheForDir() {
|
|
3772
4058
|
this.invalidateAllMemoriesCache();
|
|
3773
4059
|
}
|
|
4060
|
+
/** Invalidate only the cache layers affected by direct tier file deletes. */
|
|
4061
|
+
invalidateMemoryCachesForTiers(tiers) {
|
|
4062
|
+
let hotChanged = false;
|
|
4063
|
+
let coldChanged = false;
|
|
4064
|
+
for (const tier of tiers) {
|
|
4065
|
+
if (tier === "cold") {
|
|
4066
|
+
coldChanged = true;
|
|
4067
|
+
} else if (tier === "hot" || tier === "archive") {
|
|
4068
|
+
hotChanged = true;
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
if (hotChanged) {
|
|
4072
|
+
this.invalidateAllMemoriesCache();
|
|
4073
|
+
}
|
|
4074
|
+
if (coldChanged) {
|
|
4075
|
+
this.invalidateColdMemoriesCache();
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
3774
4078
|
/** Clear ALL static caches. Use in tests that write files directly
|
|
3775
4079
|
* (bypassing StorageManager.writeMemory) to avoid stale reads. */
|
|
3776
4080
|
static clearAllStaticCaches() {
|
|
@@ -3862,7 +4166,7 @@ ${sanitized.text}
|
|
|
3862
4166
|
const results = await Promise.all(
|
|
3863
4167
|
batch.map(async (fullPath) => {
|
|
3864
4168
|
try {
|
|
3865
|
-
const raw = await
|
|
4169
|
+
const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
|
|
3866
4170
|
const parsed = parseFrontmatter2(raw);
|
|
3867
4171
|
if (!parsed) return null;
|
|
3868
4172
|
return {
|
|
@@ -3874,7 +4178,8 @@ ${sanitized.text}
|
|
|
3874
4178
|
),
|
|
3875
4179
|
content: parsed.content
|
|
3876
4180
|
};
|
|
3877
|
-
} catch {
|
|
4181
|
+
} catch (err) {
|
|
4182
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
3878
4183
|
return null;
|
|
3879
4184
|
}
|
|
3880
4185
|
})
|
|
@@ -3887,7 +4192,7 @@ ${sanitized.text}
|
|
|
3887
4192
|
}
|
|
3888
4193
|
async readWindowUpdatedMs(filePath) {
|
|
3889
4194
|
try {
|
|
3890
|
-
const raw = await
|
|
4195
|
+
const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
3891
4196
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
3892
4197
|
if (!match) return null;
|
|
3893
4198
|
const frontmatterBlock = match[1];
|
|
@@ -4071,7 +4376,7 @@ ${sanitized.text}
|
|
|
4071
4376
|
await readDir(fullPath);
|
|
4072
4377
|
} else if (entry.name.endsWith(".md")) {
|
|
4073
4378
|
try {
|
|
4074
|
-
const raw = await
|
|
4379
|
+
const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
|
|
4075
4380
|
const parsed = parseFrontmatter2(raw);
|
|
4076
4381
|
if (parsed) {
|
|
4077
4382
|
memories.push({
|
|
@@ -4084,7 +4389,8 @@ ${sanitized.text}
|
|
|
4084
4389
|
content: parsed.content
|
|
4085
4390
|
});
|
|
4086
4391
|
}
|
|
4087
|
-
} catch {
|
|
4392
|
+
} catch (err) {
|
|
4393
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
4088
4394
|
}
|
|
4089
4395
|
}
|
|
4090
4396
|
}
|
|
@@ -4097,7 +4403,7 @@ ${sanitized.text}
|
|
|
4097
4403
|
/** Read a single memory file by its absolute path. Returns null if unreadable. */
|
|
4098
4404
|
async readMemoryByPath(filePath) {
|
|
4099
4405
|
try {
|
|
4100
|
-
const raw = await
|
|
4406
|
+
const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
4101
4407
|
const parsed = parseFrontmatter2(raw);
|
|
4102
4408
|
if (parsed) {
|
|
4103
4409
|
return {
|
|
@@ -4132,7 +4438,8 @@ ${sanitized.text}
|
|
|
4132
4438
|
};
|
|
4133
4439
|
}
|
|
4134
4440
|
return null;
|
|
4135
|
-
} catch {
|
|
4441
|
+
} catch (err) {
|
|
4442
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
4136
4443
|
return null;
|
|
4137
4444
|
}
|
|
4138
4445
|
}
|
|
@@ -4170,19 +4477,8 @@ ${sanitized.text}
|
|
|
4170
4477
|
|
|
4171
4478
|
${memory.content}
|
|
4172
4479
|
`;
|
|
4173
|
-
await
|
|
4174
|
-
|
|
4175
|
-
try {
|
|
4176
|
-
await writeFile2(tempPath, fileContent, "utf-8");
|
|
4177
|
-
await rename(tempPath, targetPath);
|
|
4178
|
-
this.invalidateAllMemoriesCache();
|
|
4179
|
-
} catch (err) {
|
|
4180
|
-
try {
|
|
4181
|
-
await unlink(tempPath);
|
|
4182
|
-
} catch {
|
|
4183
|
-
}
|
|
4184
|
-
throw err;
|
|
4185
|
-
}
|
|
4480
|
+
await writeMaybeEncryptedFile(targetPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
4481
|
+
this.invalidateAllMemoriesCache();
|
|
4186
4482
|
}
|
|
4187
4483
|
async moveMemoryToPath(memory, targetPath) {
|
|
4188
4484
|
await this.writeMemoryFileAtomic(targetPath, memory);
|
|
@@ -4253,7 +4549,7 @@ ${memory.content}
|
|
|
4253
4549
|
${memory.content}
|
|
4254
4550
|
`;
|
|
4255
4551
|
const destPath = path4.join(destDir, path4.basename(memory.path));
|
|
4256
|
-
await
|
|
4552
|
+
await writeMaybeEncryptedFile(destPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
4257
4553
|
await unlink(memory.path);
|
|
4258
4554
|
this.invalidateAllMemoriesCache();
|
|
4259
4555
|
await this.appendGeneratedMemoryLifecycleEventFailOpen(
|
|
@@ -4374,7 +4670,7 @@ ${memory.content}
|
|
|
4374
4670
|
|
|
4375
4671
|
${sanitized.text}
|
|
4376
4672
|
`;
|
|
4377
|
-
await
|
|
4673
|
+
await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
4378
4674
|
this.invalidateAllMemoriesCache();
|
|
4379
4675
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
|
|
4380
4676
|
memoryId: id,
|
|
@@ -4406,7 +4702,7 @@ ${sanitized.text}
|
|
|
4406
4702
|
|
|
4407
4703
|
${memory.content}
|
|
4408
4704
|
`;
|
|
4409
|
-
await
|
|
4705
|
+
await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
4410
4706
|
this.invalidateAllMemoriesCache();
|
|
4411
4707
|
if (memory.path.includes(`${path4.sep}cold${path4.sep}`)) {
|
|
4412
4708
|
this.invalidateColdMemoriesCache();
|
|
@@ -5088,7 +5384,7 @@ ${question}
|
|
|
5088
5384
|
for (const file of files) {
|
|
5089
5385
|
if (!file.endsWith(".md")) continue;
|
|
5090
5386
|
const filePath = path4.join(this.questionsDir, file);
|
|
5091
|
-
const raw = await
|
|
5387
|
+
const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
5092
5388
|
const parsed = this.parseQuestionFile(raw, filePath);
|
|
5093
5389
|
if (parsed) {
|
|
5094
5390
|
questions.push(parsed);
|
|
@@ -5996,7 +6292,7 @@ ${sanitized.text}
|
|
|
5996
6292
|
${oldMemory.content}
|
|
5997
6293
|
`;
|
|
5998
6294
|
try {
|
|
5999
|
-
await
|
|
6295
|
+
await writeMaybeEncryptedFile(oldMemory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
|
|
6000
6296
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.supersedeMemory", {
|
|
6001
6297
|
memoryId: oldMemoryId,
|
|
6002
6298
|
eventType: "superseded",
|
|
@@ -6195,6 +6491,12 @@ export {
|
|
|
6195
6491
|
normalizeEntitySchemas,
|
|
6196
6492
|
resolveRequestedEntitySectionKeys,
|
|
6197
6493
|
sanitizeMemoryContent,
|
|
6494
|
+
MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
|
|
6495
|
+
toMemoryPathRel,
|
|
6496
|
+
inferMemoryStatus,
|
|
6497
|
+
isActiveMemoryStatus,
|
|
6498
|
+
buildLifecycleEventsForMemory,
|
|
6499
|
+
sortMemoryLifecycleEvents,
|
|
6198
6500
|
hasCitationForTemplate,
|
|
6199
6501
|
attachCitation,
|
|
6200
6502
|
stripCitationForTemplate,
|
|
@@ -6206,7 +6508,12 @@ export {
|
|
|
6206
6508
|
setCachedQmdSearch,
|
|
6207
6509
|
lintWorkspaceFiles,
|
|
6208
6510
|
rotateMarkdownFileToArchive,
|
|
6511
|
+
DERIVED_FROM_MEMORY_ID_RE,
|
|
6209
6512
|
isConsolidationOperator,
|
|
6513
|
+
isSemanticConsolidationLlmOperator,
|
|
6514
|
+
RECALL_DISCLOSURE_LEVELS,
|
|
6515
|
+
DEFAULT_RECALL_DISCLOSURE,
|
|
6516
|
+
isRecallDisclosure,
|
|
6210
6517
|
confidenceTier,
|
|
6211
6518
|
openBetterSqlite3,
|
|
6212
6519
|
MEMORY_PROJECTION_SCHEMA_VERSION,
|
|
@@ -6218,11 +6525,6 @@ export {
|
|
|
6218
6525
|
readProjectedEntityMentions,
|
|
6219
6526
|
readProjectedNativeKnowledgeChunks,
|
|
6220
6527
|
readProjectedGovernanceRecord,
|
|
6221
|
-
MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
|
|
6222
|
-
toMemoryPathRel,
|
|
6223
|
-
inferMemoryStatus,
|
|
6224
|
-
buildLifecycleEventsForMemory,
|
|
6225
|
-
sortMemoryLifecycleEvents,
|
|
6226
6528
|
normalizeProjectionPreview,
|
|
6227
6529
|
normalizeProjectionTags,
|
|
6228
6530
|
parseContinuityIncident,
|