@remnic/core 9.3.753 → 9.3.755
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/access-admin-ops-surface.d.ts +5 -5
- package/dist/access-admin-ops-surface.js +26 -26
- package/dist/access-boundary.d.ts +5 -5
- package/dist/access-boundary.js +27 -27
- package/dist/access-cli.js +75 -75
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +34 -34
- package/dist/access-lcm-surface.d.ts +188 -0
- package/dist/access-lcm-surface.js +128 -0
- package/dist/access-lcm-surface.js.map +1 -0
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +31 -31
- package/dist/access-operations-batch.js +28 -28
- package/dist/access-operations.d.ts +9 -9
- package/dist/access-operations.js +30 -30
- package/dist/access-recall-surface.d.ts +5 -5
- package/dist/access-recall-surface.js +26 -26
- package/dist/access-schema.d.ts +70 -70
- package/dist/access-schema.js +6 -6
- package/dist/{access-service-MitduesD.d.ts → access-service-C3jkLi2U.d.ts} +7 -67
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +26 -26
- package/dist/access-surface-catalog.d.ts +5 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-IEBKPGAM.js → auto-sync-G4YKBPOU.js} +5 -5
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -7
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +2 -2
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-BEmNVRN0.d.ts → catalog-DaKAFbSJ.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +8 -8
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-HAZVVOYT.js → chunk-24463PIV.js} +2 -2
- package/dist/{chunk-GWGWBK5Y.js → chunk-2MR3MFQB.js} +7 -7
- package/dist/{chunk-YTU7WKYN.js → chunk-2OZ2JEUP.js} +2 -2
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/{chunk-EHYB325U.js → chunk-3DOD6ICJ.js} +2 -2
- package/dist/{chunk-KYHGLDFU.js → chunk-4LQUYBZM.js} +6 -6
- package/dist/{chunk-VUA4S7P6.js → chunk-5J4OL7TJ.js} +4 -4
- package/dist/{chunk-TVISHYL5.js → chunk-CIDGUFAL.js} +2 -2
- package/dist/{chunk-OUXPVWTZ.js → chunk-FLVL4L3L.js} +5 -5
- package/dist/{chunk-3HGZX26P.js → chunk-FOJY75BL.js} +8 -8
- package/dist/{chunk-VVSV2QVY.js → chunk-FQLM6ZUP.js} +3 -3
- package/dist/{chunk-LZ73Y56P.js → chunk-G7LCG56O.js} +4 -4
- package/dist/{chunk-ZECLLIMX.js → chunk-GCEV6SXP.js} +4989 -4960
- package/dist/chunk-GCEV6SXP.js.map +1 -0
- package/dist/{chunk-ZMHDSIEX.js → chunk-GGYUR4M2.js} +4 -4
- package/dist/{chunk-YNBEHUZH.js → chunk-GMWKWA6N.js} +2 -2
- package/dist/{chunk-JGPURIEH.js → chunk-GPFXPC2V.js} +2 -2
- package/dist/{chunk-E6LENGYV.js → chunk-H6S3DJVE.js} +2 -2
- package/dist/{chunk-E3MRYDUQ.js → chunk-I3UWEZTK.js} +2 -2
- package/dist/{chunk-ZIFPFKB2.js → chunk-I6IAJCJS.js} +2 -2
- package/dist/{chunk-JES2O5Q6.js → chunk-JITAVROY.js} +4 -4
- package/dist/{chunk-DKIZ4BUQ.js → chunk-KKOCFUI5.js} +7 -7
- package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
- package/dist/{chunk-N3VE7OCY.js → chunk-LFKEFIJA.js} +2 -2
- package/dist/{chunk-EEO4YWQ7.js → chunk-N4AGSRVA.js} +2 -2
- package/dist/{chunk-QIRZI3C3.js → chunk-NCNOYM6F.js} +2 -2
- package/dist/{chunk-3ZIKDH6C.js → chunk-OCFITZRV.js} +4 -4
- package/dist/{chunk-ITLJATQI.js → chunk-PJ7DIEFC.js} +2 -2
- package/dist/{chunk-OZMBT7N3.js → chunk-PJQMISPD.js} +2 -2
- package/dist/{chunk-XOPLMSVV.js → chunk-S6UCPBPL.js} +2 -2
- package/dist/{chunk-243P7YYV.js → chunk-SP42M3XP.js} +2 -2
- package/dist/{chunk-DCWIQFNA.js → chunk-T422SYM6.js} +5 -5
- package/dist/{chunk-AXBRVBT6.js → chunk-T6WHI3JW.js} +2 -2
- package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
- package/dist/{chunk-3NN73CM7.js → chunk-TH7WMHVK.js} +2 -2
- package/dist/{chunk-RNWWAS5V.js → chunk-TJATYB3F.js} +11 -11
- package/dist/{chunk-C3IHRWIC.js → chunk-UQJ6YTKR.js} +2 -2
- package/dist/{chunk-PN5AMCK2.js → chunk-VL5QHTJI.js} +56 -56
- package/dist/{chunk-34THARMH.js → chunk-XBM5DG4O.js} +665 -577
- package/dist/chunk-XBM5DG4O.js.map +1 -0
- package/dist/{chunk-BWOEYBRB.js → chunk-XCXN3D2U.js} +2 -2
- package/dist/{chunk-KY4WOZ3P.js → chunk-XD6JVK5P.js} +42 -42
- package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
- package/dist/{cli-DqonYBZ0.d.ts → cli-BJQ2ahrh.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +55 -55
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +7 -7
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +7 -7
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +3 -3
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +2 -2
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +149 -149
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +4 -4
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +17 -17
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +7 -7
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +21 -21
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +9 -9
- package/dist/{orchestrator-D7ZGOQLX.d.ts → orchestrator-Xm6sK5jw.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +75 -75
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +2 -2
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +100 -100
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-7Sndms2M.d.ts → semantic-consolidation-bfXJNKVf.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +7 -7
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +11 -21
- package/dist/storage.js +14 -6
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DTEFxFuL.d.ts → types-D1iBqhJA.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +7 -7
- package/package.json +2 -2
- package/src/access-lcm-surface.ts +517 -0
- package/src/access-service.ts +21 -417
- package/src/storage/entity-store.ts +672 -0
- package/src/storage.ts +45 -565
- package/dist/chunk-34THARMH.js.map +0 -1
- package/dist/chunk-ZECLLIMX.js.map +0 -1
- /package/dist/{auto-sync-IEBKPGAM.js.map → auto-sync-G4YKBPOU.js.map} +0 -0
- /package/dist/{chunk-HAZVVOYT.js.map → chunk-24463PIV.js.map} +0 -0
- /package/dist/{chunk-GWGWBK5Y.js.map → chunk-2MR3MFQB.js.map} +0 -0
- /package/dist/{chunk-YTU7WKYN.js.map → chunk-2OZ2JEUP.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-EHYB325U.js.map → chunk-3DOD6ICJ.js.map} +0 -0
- /package/dist/{chunk-KYHGLDFU.js.map → chunk-4LQUYBZM.js.map} +0 -0
- /package/dist/{chunk-VUA4S7P6.js.map → chunk-5J4OL7TJ.js.map} +0 -0
- /package/dist/{chunk-TVISHYL5.js.map → chunk-CIDGUFAL.js.map} +0 -0
- /package/dist/{chunk-OUXPVWTZ.js.map → chunk-FLVL4L3L.js.map} +0 -0
- /package/dist/{chunk-3HGZX26P.js.map → chunk-FOJY75BL.js.map} +0 -0
- /package/dist/{chunk-VVSV2QVY.js.map → chunk-FQLM6ZUP.js.map} +0 -0
- /package/dist/{chunk-LZ73Y56P.js.map → chunk-G7LCG56O.js.map} +0 -0
- /package/dist/{chunk-ZMHDSIEX.js.map → chunk-GGYUR4M2.js.map} +0 -0
- /package/dist/{chunk-YNBEHUZH.js.map → chunk-GMWKWA6N.js.map} +0 -0
- /package/dist/{chunk-JGPURIEH.js.map → chunk-GPFXPC2V.js.map} +0 -0
- /package/dist/{chunk-E6LENGYV.js.map → chunk-H6S3DJVE.js.map} +0 -0
- /package/dist/{chunk-E3MRYDUQ.js.map → chunk-I3UWEZTK.js.map} +0 -0
- /package/dist/{chunk-ZIFPFKB2.js.map → chunk-I6IAJCJS.js.map} +0 -0
- /package/dist/{chunk-JES2O5Q6.js.map → chunk-JITAVROY.js.map} +0 -0
- /package/dist/{chunk-DKIZ4BUQ.js.map → chunk-KKOCFUI5.js.map} +0 -0
- /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
- /package/dist/{chunk-N3VE7OCY.js.map → chunk-LFKEFIJA.js.map} +0 -0
- /package/dist/{chunk-EEO4YWQ7.js.map → chunk-N4AGSRVA.js.map} +0 -0
- /package/dist/{chunk-QIRZI3C3.js.map → chunk-NCNOYM6F.js.map} +0 -0
- /package/dist/{chunk-3ZIKDH6C.js.map → chunk-OCFITZRV.js.map} +0 -0
- /package/dist/{chunk-ITLJATQI.js.map → chunk-PJ7DIEFC.js.map} +0 -0
- /package/dist/{chunk-OZMBT7N3.js.map → chunk-PJQMISPD.js.map} +0 -0
- /package/dist/{chunk-XOPLMSVV.js.map → chunk-S6UCPBPL.js.map} +0 -0
- /package/dist/{chunk-243P7YYV.js.map → chunk-SP42M3XP.js.map} +0 -0
- /package/dist/{chunk-DCWIQFNA.js.map → chunk-T422SYM6.js.map} +0 -0
- /package/dist/{chunk-AXBRVBT6.js.map → chunk-T6WHI3JW.js.map} +0 -0
- /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
- /package/dist/{chunk-3NN73CM7.js.map → chunk-TH7WMHVK.js.map} +0 -0
- /package/dist/{chunk-RNWWAS5V.js.map → chunk-TJATYB3F.js.map} +0 -0
- /package/dist/{chunk-C3IHRWIC.js.map → chunk-UQJ6YTKR.js.map} +0 -0
- /package/dist/{chunk-PN5AMCK2.js.map → chunk-VL5QHTJI.js.map} +0 -0
- /package/dist/{chunk-BWOEYBRB.js.map → chunk-XCXN3D2U.js.map} +0 -0
- /package/dist/{chunk-KY4WOZ3P.js.map → chunk-XD6JVK5P.js.map} +0 -0
- /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WEARABLES_DIR_NAME,
|
|
3
|
+
isValidTranscriptDate
|
|
4
|
+
} from "./chunk-M7XQSUBB.js";
|
|
1
5
|
import {
|
|
2
6
|
TombstoneStore,
|
|
3
7
|
buildRetiredFactTombstoneInputs,
|
|
4
8
|
collectRetiredMemoriesForRebuild
|
|
5
9
|
} from "./chunk-L2B3EE7B.js";
|
|
6
|
-
import {
|
|
7
|
-
WEARABLES_DIR_NAME,
|
|
8
|
-
isValidTranscriptDate
|
|
9
|
-
} from "./chunk-M7XQSUBB.js";
|
|
10
10
|
import {
|
|
11
11
|
isErrnoCode
|
|
12
12
|
} from "./chunk-5UZXUTVO.js";
|
|
@@ -28,6 +28,10 @@ import {
|
|
|
28
28
|
SPECULATIVE_TTL_DAYS,
|
|
29
29
|
confidenceTier
|
|
30
30
|
} from "./chunk-JS2HKKOK.js";
|
|
31
|
+
import {
|
|
32
|
+
normalizeProjectionPreview,
|
|
33
|
+
normalizeProjectionTags
|
|
34
|
+
} from "./chunk-SCU65EZI.js";
|
|
31
35
|
import {
|
|
32
36
|
readProjectedGovernanceRecord,
|
|
33
37
|
readProjectedMemoryBrowse,
|
|
@@ -45,10 +49,6 @@ import {
|
|
|
45
49
|
sortMemoryLifecycleEvents,
|
|
46
50
|
toMemoryPathRel
|
|
47
51
|
} from "./chunk-RULE4VG5.js";
|
|
48
|
-
import {
|
|
49
|
-
normalizeProjectionPreview,
|
|
50
|
-
normalizeProjectionTags
|
|
51
|
-
} from "./chunk-SCU65EZI.js";
|
|
52
52
|
import {
|
|
53
53
|
rotateMarkdownFileToArchive
|
|
54
54
|
} from "./chunk-DM2T26WE.js";
|
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
import {
|
|
65
65
|
parseFaithfulnessField,
|
|
66
66
|
serializeFaithfulnessFields
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-2OZ2JEUP.js";
|
|
68
68
|
import {
|
|
69
69
|
isConsolidationOperator,
|
|
70
70
|
isValidDerivedFromEntry
|
|
@@ -112,10 +112,489 @@ import {
|
|
|
112
112
|
} from "./chunk-VS2IYZRU.js";
|
|
113
113
|
|
|
114
114
|
// src/storage.ts
|
|
115
|
-
import { access, lstat, readdir, readFile, realpath, stat, writeFile, mkdir, unlink, appendFile, open } from "fs/promises";
|
|
116
|
-
import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "fs";
|
|
117
|
-
import { createHash } from "crypto";
|
|
118
|
-
import
|
|
115
|
+
import { access, lstat, readdir as readdir2, readFile, realpath, stat, writeFile, mkdir, unlink as unlink2, appendFile, open } from "fs/promises";
|
|
116
|
+
import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "fs";
|
|
117
|
+
import { createHash } from "crypto";
|
|
118
|
+
import path2 from "path";
|
|
119
|
+
|
|
120
|
+
// src/storage/entity-store.ts
|
|
121
|
+
import { readdir, unlink } from "fs/promises";
|
|
122
|
+
import path from "path";
|
|
123
|
+
var EntityStore = class {
|
|
124
|
+
constructor(deps) {
|
|
125
|
+
this.deps = deps;
|
|
126
|
+
}
|
|
127
|
+
deps;
|
|
128
|
+
/** Normalize an entity name using this store's alias table. */
|
|
129
|
+
normalizeEntityName(raw, type) {
|
|
130
|
+
return normalizeEntityName(raw, type, this.deps.userAliases);
|
|
131
|
+
}
|
|
132
|
+
async writeEntity(name, type, facts, options = {}) {
|
|
133
|
+
await this.deps.ensureDirectories();
|
|
134
|
+
if (typeof name !== "string" || !name.trim() || typeof type !== "string" || !type.trim()) {
|
|
135
|
+
log.warn("writeEntity: invalid entity payload, skipping", {
|
|
136
|
+
nameType: typeof name,
|
|
137
|
+
typeType: typeof type
|
|
138
|
+
});
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
const safeFacts = Array.isArray(facts) ? [...new Set(
|
|
142
|
+
facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0)
|
|
143
|
+
)] : [];
|
|
144
|
+
let normalized = this.deps.normalizeEntityName(name, type);
|
|
145
|
+
const match = await this.deps.findMatchingEntity(name, type);
|
|
146
|
+
if (match && match !== normalized) {
|
|
147
|
+
log.debug(`fuzzy match: "${normalized}" \u2192 existing "${match}"`);
|
|
148
|
+
normalized = match;
|
|
149
|
+
}
|
|
150
|
+
const filePath = path.join(this.deps.entitiesDir, `${normalized}.md`);
|
|
151
|
+
let entity = {
|
|
152
|
+
name,
|
|
153
|
+
type,
|
|
154
|
+
created: "",
|
|
155
|
+
updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
156
|
+
facts: [],
|
|
157
|
+
summary: void 0,
|
|
158
|
+
synthesis: void 0,
|
|
159
|
+
synthesisUpdatedAt: void 0,
|
|
160
|
+
synthesisVersion: void 0,
|
|
161
|
+
synthesisStructuredFactCount: void 0,
|
|
162
|
+
synthesisStructuredFactDigest: void 0,
|
|
163
|
+
timeline: [],
|
|
164
|
+
relationships: [],
|
|
165
|
+
activity: [],
|
|
166
|
+
aliases: []
|
|
167
|
+
};
|
|
168
|
+
try {
|
|
169
|
+
const existing = await this.deps.readStorageSecureFile(filePath);
|
|
170
|
+
entity = parseEntityFile(existing, this.deps.entitySchemas);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
173
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
174
|
+
}
|
|
175
|
+
const timestamp = options.timestamp?.trim() || (/* @__PURE__ */ new Date()).toISOString();
|
|
176
|
+
const source = options.source?.trim() || void 0;
|
|
177
|
+
const sessionKey = options.sessionKey?.trim() || void 0;
|
|
178
|
+
const principal = options.principal?.trim() || void 0;
|
|
179
|
+
const structuredSectionMap = new Map(
|
|
180
|
+
(entity.structuredSections ?? []).map((section) => [section.key, {
|
|
181
|
+
...section,
|
|
182
|
+
facts: [...section.facts]
|
|
183
|
+
}])
|
|
184
|
+
);
|
|
185
|
+
for (const section of options.structuredSections ?? []) {
|
|
186
|
+
const normalizedSection = normalizeEntityStructuredSection(type, section, this.deps.entitySchemas);
|
|
187
|
+
const normalizedFacts = normalizeStructuredSectionFacts(section.facts);
|
|
188
|
+
if (normalizedFacts.length === 0) continue;
|
|
189
|
+
const existingSection = structuredSectionMap.get(normalizedSection.key);
|
|
190
|
+
if (!existingSection) {
|
|
191
|
+
structuredSectionMap.set(normalizedSection.key, {
|
|
192
|
+
key: normalizedSection.key,
|
|
193
|
+
title: normalizedSection.title,
|
|
194
|
+
facts: normalizedFacts
|
|
195
|
+
});
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
existingSection.facts = normalizeStructuredSectionFacts([...existingSection.facts, ...normalizedFacts]);
|
|
199
|
+
if (!existingSection.title.trim() && normalizedSection.title.trim()) {
|
|
200
|
+
existingSection.title = normalizedSection.title;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const fact of safeFacts) {
|
|
204
|
+
const nextEntry = {
|
|
205
|
+
timestamp,
|
|
206
|
+
text: fact,
|
|
207
|
+
...source ? { source } : {},
|
|
208
|
+
...sessionKey ? { sessionKey } : {},
|
|
209
|
+
...principal ? { principal } : {}
|
|
210
|
+
};
|
|
211
|
+
const alreadyPresent = entity.timeline.some(
|
|
212
|
+
(entry) => entry.timestamp === nextEntry.timestamp && entry.text === nextEntry.text && entry.source === nextEntry.source && entry.sessionKey === nextEntry.sessionKey && entry.principal === nextEntry.principal
|
|
213
|
+
);
|
|
214
|
+
if (alreadyPresent) continue;
|
|
215
|
+
entity.timeline.push(nextEntry);
|
|
216
|
+
}
|
|
217
|
+
entity.structuredSections = sortStructuredSectionsBySchema(
|
|
218
|
+
type,
|
|
219
|
+
Array.from(structuredSectionMap.values()).filter((section) => section.facts.length > 0),
|
|
220
|
+
this.deps.entitySchemas
|
|
221
|
+
);
|
|
222
|
+
entity.facts = compileEntityFacts(entity.timeline, entity.structuredSections);
|
|
223
|
+
entity.summary = entity.synthesis || entity.summary;
|
|
224
|
+
entity.name = name;
|
|
225
|
+
entity.type = type;
|
|
226
|
+
entity.created = entity.created || timestamp;
|
|
227
|
+
entity.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
228
|
+
await this.deps.snapshotBeforeWrite(filePath, "write");
|
|
229
|
+
await this.deps.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.deps.entitySchemas));
|
|
230
|
+
this.deps.invalidateKnowledgeIndexCache();
|
|
231
|
+
this.deps.bumpMemoryStatusVersion();
|
|
232
|
+
log.debug(`wrote entity ${normalized}`);
|
|
233
|
+
return normalized;
|
|
234
|
+
}
|
|
235
|
+
async readEntity(name) {
|
|
236
|
+
const filePath = this.deps.resolveEntityFilePath(name);
|
|
237
|
+
if (filePath === null) {
|
|
238
|
+
return "";
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
return await this.deps.readStorageSecureFile(filePath);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
244
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
245
|
+
return "";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/** Return sorted list of entity filenames (without .md extension) */
|
|
249
|
+
async listEntityNames() {
|
|
250
|
+
try {
|
|
251
|
+
const entries = await readdir(this.deps.entitiesDir);
|
|
252
|
+
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", "")).sort();
|
|
253
|
+
} catch {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Set or rewrite the synthesis layer of an entity file.
|
|
259
|
+
*/
|
|
260
|
+
async updateEntitySynthesis(name, synthesis, options = {}) {
|
|
261
|
+
const filePath = path.join(this.deps.entitiesDir, `${name}.md`);
|
|
262
|
+
let entity;
|
|
263
|
+
try {
|
|
264
|
+
const content = await this.deps.readStorageSecureFile(filePath);
|
|
265
|
+
entity = parseEntityFile(content, this.deps.entitySchemas);
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
268
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
269
|
+
log.debug(`updateEntitySynthesis: entity file ${name}.md not found`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const updatedAt = options.updatedAt?.trim() || entity.synthesisUpdatedAt?.trim() || void 0;
|
|
273
|
+
const entityUpdatedAt = options.entityUpdatedAt?.trim() || updatedAt || entity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
274
|
+
const synthesisTimelineCount = Number.isInteger(options.synthesisTimelineCount) && (options.synthesisTimelineCount ?? 0) >= 0 ? options.synthesisTimelineCount : void 0;
|
|
275
|
+
const synthesisStructuredFactCount = Number.isInteger(options.synthesisStructuredFactCount) && (options.synthesisStructuredFactCount ?? 0) >= 0 ? options.synthesisStructuredFactCount : countEntityStructuredFacts(entity);
|
|
276
|
+
const synthesisStructuredFactDigest = options.synthesisStructuredFactDigest?.trim() || fingerprintEntityStructuredFacts(entity);
|
|
277
|
+
entity.synthesis = synthesis.trim();
|
|
278
|
+
entity.summary = entity.synthesis;
|
|
279
|
+
entity.synthesisUpdatedAt = updatedAt;
|
|
280
|
+
entity.synthesisTimelineCount = synthesisTimelineCount;
|
|
281
|
+
entity.synthesisStructuredFactCount = synthesisStructuredFactCount;
|
|
282
|
+
entity.synthesisStructuredFactDigest = synthesisStructuredFactDigest;
|
|
283
|
+
entity.synthesisVersion = Math.max(0, entity.synthesisVersion ?? 0) + (options.incrementVersion === false ? 0 : 1);
|
|
284
|
+
entity.updated = entityUpdatedAt;
|
|
285
|
+
await this.deps.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.deps.entitySchemas));
|
|
286
|
+
await this.deps.removeEntitySynthesisQueueEntries([
|
|
287
|
+
.../* @__PURE__ */ new Set([name, this.deps.normalizeEntityName(entity.name, entity.type)])
|
|
288
|
+
]);
|
|
289
|
+
this.deps.invalidateKnowledgeIndexCache();
|
|
290
|
+
this.deps.bumpMemoryStatusVersion();
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Read all entity files and return lightweight EntityFile objects.
|
|
294
|
+
* Parsing is fast (~50-100ms for ~1,800 files) since entity files are small.
|
|
295
|
+
*/
|
|
296
|
+
async readAllEntityFiles() {
|
|
297
|
+
const currentVersion = this.deps.getMemoryStatusVersion();
|
|
298
|
+
const schemaCacheKey = buildEntitySchemaCacheKey(this.deps.entitySchemas);
|
|
299
|
+
const cacheKey = `${this.deps.getEntityCacheSecureStoreKey()}\0${schemaCacheKey}`;
|
|
300
|
+
const cached = getCachedEntities(this.deps.baseDir, currentVersion, cacheKey);
|
|
301
|
+
if (cached) return cached;
|
|
302
|
+
try {
|
|
303
|
+
const entries = await readdir(this.deps.entitiesDir);
|
|
304
|
+
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
305
|
+
if (mdFiles.length === 0) return [];
|
|
306
|
+
const BATCH_SIZE = 100;
|
|
307
|
+
const entities = [];
|
|
308
|
+
for (let i = 0; i < mdFiles.length; i += BATCH_SIZE) {
|
|
309
|
+
const batch = mdFiles.slice(i, i + BATCH_SIZE);
|
|
310
|
+
const results = await Promise.all(
|
|
311
|
+
batch.map(async (entry) => {
|
|
312
|
+
try {
|
|
313
|
+
return await this.deps.readStorageSecureFile(path.join(this.deps.entitiesDir, entry));
|
|
314
|
+
} catch (err) {
|
|
315
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
316
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
for (const content of results) {
|
|
322
|
+
if (content !== null) entities.push(parseEntityFile(content, this.deps.entitySchemas));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
setCachedEntities(this.deps.baseDir, entities, currentVersion, cacheKey);
|
|
326
|
+
return entities;
|
|
327
|
+
} catch (err) {
|
|
328
|
+
if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Build the Knowledge Index: a compact markdown table of top-scored entities.
|
|
334
|
+
* Respects maxEntities and maxChars limits from config.
|
|
335
|
+
*/
|
|
336
|
+
async buildKnowledgeIndex(config, overrides) {
|
|
337
|
+
const useDefaultLimits = overrides?.maxEntities === void 0 && overrides?.maxChars === void 0;
|
|
338
|
+
if (useDefaultLimits && this.deps.knowledgeIndexCache && Date.now() - this.deps.knowledgeIndexCache.builtAt < StorageManager.KNOWLEDGE_INDEX_CACHE_TTL_MS) {
|
|
339
|
+
return { result: this.deps.knowledgeIndexCache.result, cached: true };
|
|
340
|
+
}
|
|
341
|
+
const entities = await this.deps.readAllEntityFiles();
|
|
342
|
+
if (entities.length === 0) {
|
|
343
|
+
if (useDefaultLimits) this.deps.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
344
|
+
return { result: "", cached: false };
|
|
345
|
+
}
|
|
346
|
+
const now = /* @__PURE__ */ new Date();
|
|
347
|
+
const scored = entities.map((e) => ({
|
|
348
|
+
name: e.name,
|
|
349
|
+
type: e.type,
|
|
350
|
+
score: StorageManager.scoreEntity(e, now),
|
|
351
|
+
factCount: e.facts.length,
|
|
352
|
+
summary: e.synthesis ?? e.summary,
|
|
353
|
+
topRelationships: e.relationships.slice(0, 3).map((r) => r.target)
|
|
354
|
+
}));
|
|
355
|
+
scored.sort((a, b) => b.score - a.score);
|
|
356
|
+
const maxEntities = typeof overrides?.maxEntities === "number" ? Math.max(0, Math.floor(overrides.maxEntities)) : config.knowledgeIndexMaxEntities;
|
|
357
|
+
const topN = scored.slice(0, maxEntities);
|
|
358
|
+
if (topN.length === 0) {
|
|
359
|
+
if (useDefaultLimits) this.deps.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
360
|
+
return { result: "", cached: false };
|
|
361
|
+
}
|
|
362
|
+
const header = "## Knowledge Index\n\n| Entity | Type | Summary | Connected to |\n|--------|------|---------|-------------|";
|
|
363
|
+
const rows = [];
|
|
364
|
+
let totalChars = header.length;
|
|
365
|
+
const maxChars = typeof overrides?.maxChars === "number" ? Math.max(0, Math.floor(overrides.maxChars)) : config.knowledgeIndexMaxChars;
|
|
366
|
+
for (const entity of topN) {
|
|
367
|
+
const summary = entity.summary || `${entity.factCount} facts`;
|
|
368
|
+
const connected = entity.topRelationships.length > 0 ? entity.topRelationships.join(", ") : "\u2014";
|
|
369
|
+
const row = `| ${entity.name} | ${entity.type} | ${summary} | ${connected} |`;
|
|
370
|
+
if (totalChars + row.length + 1 > maxChars) break;
|
|
371
|
+
rows.push(row);
|
|
372
|
+
totalChars += row.length + 1;
|
|
373
|
+
}
|
|
374
|
+
const result = rows.length === 0 ? "" : `${header}
|
|
375
|
+
${rows.join("\n")}
|
|
376
|
+
`;
|
|
377
|
+
if (useDefaultLimits) this.deps.knowledgeIndexCache = { result, builtAt: Date.now() };
|
|
378
|
+
return { result, cached: false };
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Merge fragmented entity files that resolve to the same canonical name.
|
|
382
|
+
* Preserves relationships, activity, aliases, and summary from all fragments.
|
|
383
|
+
* Returns count of files merged.
|
|
384
|
+
*/
|
|
385
|
+
async mergeFragmentedEntities() {
|
|
386
|
+
let merged = 0;
|
|
387
|
+
try {
|
|
388
|
+
const entries = await readdir(this.deps.entitiesDir);
|
|
389
|
+
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
390
|
+
const groups = /* @__PURE__ */ new Map();
|
|
391
|
+
for (const file of mdFiles) {
|
|
392
|
+
const baseName = file.replace(".md", "");
|
|
393
|
+
const dashIdx = baseName.indexOf("-");
|
|
394
|
+
if (dashIdx === -1) continue;
|
|
395
|
+
const type = baseName.slice(0, dashIdx);
|
|
396
|
+
const restOfName = baseName.slice(dashIdx + 1);
|
|
397
|
+
const canonical = this.deps.normalizeEntityName(restOfName, type);
|
|
398
|
+
if (!groups.has(canonical)) groups.set(canonical, []);
|
|
399
|
+
groups.get(canonical).push(file);
|
|
400
|
+
}
|
|
401
|
+
for (const [canonical, files] of groups) {
|
|
402
|
+
if (files.length <= 1) continue;
|
|
403
|
+
const mergedEntity = {
|
|
404
|
+
name: "",
|
|
405
|
+
type: "other",
|
|
406
|
+
created: "",
|
|
407
|
+
updated: "",
|
|
408
|
+
extraFrontmatterLines: [],
|
|
409
|
+
preSectionLines: [],
|
|
410
|
+
facts: [],
|
|
411
|
+
summary: void 0,
|
|
412
|
+
synthesis: void 0,
|
|
413
|
+
synthesisUpdatedAt: void 0,
|
|
414
|
+
synthesisTimelineCount: void 0,
|
|
415
|
+
synthesisStructuredFactCount: void 0,
|
|
416
|
+
synthesisStructuredFactDigest: void 0,
|
|
417
|
+
synthesisVersion: void 0,
|
|
418
|
+
timeline: [],
|
|
419
|
+
relationships: [],
|
|
420
|
+
activity: [],
|
|
421
|
+
aliases: [],
|
|
422
|
+
structuredSections: [],
|
|
423
|
+
extraSections: []
|
|
424
|
+
};
|
|
425
|
+
for (const file of files) {
|
|
426
|
+
const filePath = path.join(this.deps.entitiesDir, file);
|
|
427
|
+
try {
|
|
428
|
+
const content = await this.deps.readStorageSecureFile(filePath);
|
|
429
|
+
const parsed = parseEntityFile(content, this.deps.entitySchemas);
|
|
430
|
+
if (!mergedEntity.type || mergedEntity.type === "other") {
|
|
431
|
+
mergedEntity.type = parsed.type;
|
|
432
|
+
}
|
|
433
|
+
if (!mergedEntity.updated || compareEntityTimestamps(parsed.updated, mergedEntity.updated) > 0) {
|
|
434
|
+
mergedEntity.updated = parsed.updated;
|
|
435
|
+
}
|
|
436
|
+
const parsedCreated = parsed.created || parsed.updated;
|
|
437
|
+
const mergedCreated = mergedEntity.created?.trim() || "";
|
|
438
|
+
const parsedCreatedMs = parsedCreated ? Date.parse(parsedCreated) : Number.NaN;
|
|
439
|
+
const mergedCreatedMs = mergedCreated ? Date.parse(mergedCreated) : Number.NaN;
|
|
440
|
+
const parsedCreatedIsValid = Number.isFinite(parsedCreatedMs);
|
|
441
|
+
const mergedCreatedIsValid = Number.isFinite(mergedCreatedMs);
|
|
442
|
+
if (parsedCreated && (!mergedCreated || parsedCreatedIsValid && !mergedCreatedIsValid || parsedCreatedIsValid && mergedCreatedIsValid && parsedCreatedMs < mergedCreatedMs || !parsedCreatedIsValid && !mergedCreatedIsValid && compareEntityTimestamps(parsedCreated, mergedCreated) < 0)) {
|
|
443
|
+
mergedEntity.created = parsedCreated;
|
|
444
|
+
}
|
|
445
|
+
if (parsed.name.length > mergedEntity.name.length) {
|
|
446
|
+
mergedEntity.name = parsed.name;
|
|
447
|
+
}
|
|
448
|
+
const parsedSynthesisUpdatedAt = parsed.synthesisUpdatedAt?.trim() || void 0;
|
|
449
|
+
const mergedSynthesisUpdatedAt = mergedEntity.synthesisUpdatedAt?.trim() || void 0;
|
|
450
|
+
if (parsed.synthesis && (!mergedEntity.synthesis || !mergedSynthesisUpdatedAt && Boolean(parsedSynthesisUpdatedAt) || Boolean(mergedSynthesisUpdatedAt) && Boolean(parsedSynthesisUpdatedAt) && compareEntityTimestamps(parsedSynthesisUpdatedAt, mergedSynthesisUpdatedAt) > 0)) {
|
|
451
|
+
mergedEntity.synthesis = parsed.synthesis;
|
|
452
|
+
mergedEntity.summary = parsed.synthesis;
|
|
453
|
+
mergedEntity.synthesisUpdatedAt = parsedSynthesisUpdatedAt;
|
|
454
|
+
mergedEntity.synthesisTimelineCount = parsed.synthesisTimelineCount;
|
|
455
|
+
mergedEntity.synthesisStructuredFactCount = parsed.synthesisStructuredFactCount;
|
|
456
|
+
mergedEntity.synthesisStructuredFactDigest = parsed.synthesisStructuredFactDigest;
|
|
457
|
+
mergedEntity.synthesisVersion = parsed.synthesisVersion;
|
|
458
|
+
} else if (!mergedEntity.summary && parsed.summary) {
|
|
459
|
+
mergedEntity.summary = parsed.summary;
|
|
460
|
+
mergedEntity.synthesis = parsed.summary;
|
|
461
|
+
mergedEntity.synthesisUpdatedAt = parsedSynthesisUpdatedAt;
|
|
462
|
+
mergedEntity.synthesisTimelineCount = parsed.synthesisTimelineCount;
|
|
463
|
+
mergedEntity.synthesisStructuredFactCount = parsed.synthesisStructuredFactCount;
|
|
464
|
+
mergedEntity.synthesisStructuredFactDigest = parsed.synthesisStructuredFactDigest;
|
|
465
|
+
mergedEntity.synthesisVersion = parsed.synthesisVersion;
|
|
466
|
+
}
|
|
467
|
+
mergedEntity.timeline.push(...parsed.timeline);
|
|
468
|
+
mergedEntity.relationships.push(...parsed.relationships);
|
|
469
|
+
mergedEntity.activity.push(...parsed.activity);
|
|
470
|
+
mergedEntity.aliases.push(...parsed.aliases);
|
|
471
|
+
const mergedStructuredSectionMap = new Map(
|
|
472
|
+
(mergedEntity.structuredSections ?? []).map((section) => [section.key, {
|
|
473
|
+
...section,
|
|
474
|
+
facts: [...section.facts]
|
|
475
|
+
}])
|
|
476
|
+
);
|
|
477
|
+
for (const section of parsed.structuredSections ?? []) {
|
|
478
|
+
const existingSection = mergedStructuredSectionMap.get(section.key);
|
|
479
|
+
if (!existingSection) {
|
|
480
|
+
mergedStructuredSectionMap.set(section.key, {
|
|
481
|
+
key: section.key,
|
|
482
|
+
title: section.title,
|
|
483
|
+
facts: [...new Set(section.facts.map((fact) => fact.trim()).filter((fact) => fact.length > 0))]
|
|
484
|
+
});
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
const mergedFacts = new Set(existingSection.facts.map((fact) => fact.trim()));
|
|
488
|
+
for (const fact of section.facts) {
|
|
489
|
+
const trimmed = fact.trim();
|
|
490
|
+
if (!trimmed) continue;
|
|
491
|
+
mergedFacts.add(trimmed);
|
|
492
|
+
}
|
|
493
|
+
existingSection.facts = Array.from(mergedFacts);
|
|
494
|
+
if (!existingSection.title.trim() && section.title.trim()) {
|
|
495
|
+
existingSection.title = section.title;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
mergedEntity.structuredSections = Array.from(mergedStructuredSectionMap.values());
|
|
499
|
+
mergedEntity.extraFrontmatterLines.push(...parsed.extraFrontmatterLines ?? []);
|
|
500
|
+
mergedEntity.preSectionLines.push(...parsed.preSectionLines ?? []);
|
|
501
|
+
mergedEntity.extraSections.push(...(parsed.extraSections ?? []).map((section) => ({
|
|
502
|
+
title: section.title,
|
|
503
|
+
lines: [...section.lines]
|
|
504
|
+
})));
|
|
505
|
+
} catch (err) {
|
|
506
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
507
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const timelineKeys = /* @__PURE__ */ new Set();
|
|
511
|
+
mergedEntity.timeline = mergedEntity.timeline.filter((entry) => {
|
|
512
|
+
const key = JSON.stringify([
|
|
513
|
+
entry.timestamp,
|
|
514
|
+
entry.source ?? "",
|
|
515
|
+
entry.sessionKey ?? "",
|
|
516
|
+
entry.principal ?? "",
|
|
517
|
+
entry.text
|
|
518
|
+
]);
|
|
519
|
+
if (timelineKeys.has(key)) return false;
|
|
520
|
+
timelineKeys.add(key);
|
|
521
|
+
return true;
|
|
522
|
+
});
|
|
523
|
+
const relKeys = /* @__PURE__ */ new Set();
|
|
524
|
+
mergedEntity.relationships = mergedEntity.relationships.filter((r) => {
|
|
525
|
+
const key = `${r.target}::${r.label}`;
|
|
526
|
+
if (relKeys.has(key)) return false;
|
|
527
|
+
relKeys.add(key);
|
|
528
|
+
return true;
|
|
529
|
+
});
|
|
530
|
+
const actKeys = /* @__PURE__ */ new Set();
|
|
531
|
+
mergedEntity.activity = mergedEntity.activity.filter((a) => {
|
|
532
|
+
const key = `${a.date}::${a.note}`;
|
|
533
|
+
if (actKeys.has(key)) return false;
|
|
534
|
+
actKeys.add(key);
|
|
535
|
+
return true;
|
|
536
|
+
}).sort((a, b) => b.date.localeCompare(a.date));
|
|
537
|
+
mergedEntity.aliases = [...new Set(mergedEntity.aliases)];
|
|
538
|
+
mergedEntity.structuredSections = sortStructuredSectionsBySchema(
|
|
539
|
+
mergedEntity.type,
|
|
540
|
+
mergedEntity.structuredSections ?? [],
|
|
541
|
+
this.deps.entitySchemas
|
|
542
|
+
);
|
|
543
|
+
mergedEntity.facts = compileEntityFacts(mergedEntity.timeline, mergedEntity.structuredSections);
|
|
544
|
+
const extraSectionKeys = /* @__PURE__ */ new Set();
|
|
545
|
+
mergedEntity.extraSections = (mergedEntity.extraSections ?? []).filter((section) => {
|
|
546
|
+
const key = `${section.title}::${section.lines.join("\n")}`;
|
|
547
|
+
if (extraSectionKeys.has(key)) return false;
|
|
548
|
+
extraSectionKeys.add(key);
|
|
549
|
+
return true;
|
|
550
|
+
});
|
|
551
|
+
if (!mergedEntity.name) {
|
|
552
|
+
const dashIdx = canonical.indexOf("-");
|
|
553
|
+
mergedEntity.name = dashIdx !== -1 ? canonical.slice(dashIdx + 1) : canonical;
|
|
554
|
+
}
|
|
555
|
+
mergedEntity.created = mergedEntity.created || mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
556
|
+
mergedEntity.updated = mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
557
|
+
const canonicalPath = path.join(this.deps.entitiesDir, `${canonical}.md`);
|
|
558
|
+
await this.deps.writeStorageSecureFile(canonicalPath, serializeEntityFile(mergedEntity, this.deps.entitySchemas));
|
|
559
|
+
for (const file of files) {
|
|
560
|
+
const filePath = path.join(this.deps.entitiesDir, file);
|
|
561
|
+
if (filePath !== canonicalPath) {
|
|
562
|
+
try {
|
|
563
|
+
await unlink(filePath);
|
|
564
|
+
merged++;
|
|
565
|
+
log.debug(`merged entity ${file} \u2192 ${canonical}.md`);
|
|
566
|
+
} catch {
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} catch (err) {
|
|
572
|
+
if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
|
|
573
|
+
}
|
|
574
|
+
return merged;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// src/orchestration/self-deps.ts
|
|
579
|
+
function selfDeps(self) {
|
|
580
|
+
const source = self;
|
|
581
|
+
return new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
582
|
+
get(_target, prop) {
|
|
583
|
+
const value = source[prop];
|
|
584
|
+
return typeof value === "function" ? value.bind(self) : value;
|
|
585
|
+
},
|
|
586
|
+
set(_target, prop, value) {
|
|
587
|
+
if (!(prop in source)) return false;
|
|
588
|
+
source[prop] = value;
|
|
589
|
+
return true;
|
|
590
|
+
},
|
|
591
|
+
has(_target, prop) {
|
|
592
|
+
return prop in source;
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/storage.ts
|
|
119
598
|
var ARTIFACT_SEARCH_STOPWORDS = /* @__PURE__ */ new Set([
|
|
120
599
|
"a",
|
|
121
600
|
"an",
|
|
@@ -720,17 +1199,17 @@ var KNOWN_ENTITY_FILENAME_PREFIXES = /* @__PURE__ */ new Set([
|
|
|
720
1199
|
"topic"
|
|
721
1200
|
]);
|
|
722
1201
|
function inferEntityTypeFromFilename(pathRel) {
|
|
723
|
-
const basename =
|
|
1202
|
+
const basename = path2.basename(pathRel, ".md").toLowerCase();
|
|
724
1203
|
const separator = basename.indexOf("-");
|
|
725
1204
|
if (separator <= 0) return void 0;
|
|
726
1205
|
const candidate = basename.slice(0, separator);
|
|
727
1206
|
return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : void 0;
|
|
728
1207
|
}
|
|
729
1208
|
function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
|
|
730
|
-
const normalizedPath = pathRel.split(
|
|
1209
|
+
const normalizedPath = pathRel.split(path2.sep).join("/");
|
|
731
1210
|
let normalizedFrontmatter = frontmatter;
|
|
732
1211
|
if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
|
|
733
|
-
const basename =
|
|
1212
|
+
const basename = path2.basename(pathRel, ".md");
|
|
734
1213
|
const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
|
|
735
1214
|
const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
736
1215
|
normalizedFrontmatter = {
|
|
@@ -797,8 +1276,8 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
797
1276
|
secureStoreKeyProvider;
|
|
798
1277
|
secureStoreWriteKeyProvider;
|
|
799
1278
|
memoryDir;
|
|
800
|
-
constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir =
|
|
801
|
-
this.filePath =
|
|
1279
|
+
constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path2.dirname(stateDir)) {
|
|
1280
|
+
this.filePath = path2.join(stateDir, "fact-hashes.txt");
|
|
802
1281
|
this.secureStoreKeyProvider = secureStoreKeyProvider;
|
|
803
1282
|
this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;
|
|
804
1283
|
this.memoryDir = memoryDir;
|
|
@@ -845,7 +1324,7 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
845
1324
|
/** Persist index to disk if changed. */
|
|
846
1325
|
async save() {
|
|
847
1326
|
if (!this.dirty) return;
|
|
848
|
-
await mkdir(
|
|
1327
|
+
await mkdir(path2.dirname(this.filePath), { recursive: true });
|
|
849
1328
|
await writeMaybeEncryptedFile(
|
|
850
1329
|
this.filePath,
|
|
851
1330
|
[...this.hashes].join("\n") + "\n",
|
|
@@ -1708,9 +2187,10 @@ var StorageManager = class _StorageManager {
|
|
|
1708
2187
|
baseDir;
|
|
1709
2188
|
entitySchemas;
|
|
1710
2189
|
knowledgeIndexCache = null;
|
|
2190
|
+
artifactIndexCache = null;
|
|
2191
|
+
/** Read by storage/entity-store.ts (decomposition), hence not `private`. */
|
|
1711
2192
|
static KNOWLEDGE_INDEX_CACHE_TTL_MS = 6e5;
|
|
1712
2193
|
// 10 minutes (entity mutations invalidate)
|
|
1713
|
-
artifactIndexCache = null;
|
|
1714
2194
|
static ARTIFACT_INDEX_CACHE_TTL_MS = 6e4;
|
|
1715
2195
|
// 1 minute
|
|
1716
2196
|
static artifactWriteVersionByDir = /* @__PURE__ */ new Map();
|
|
@@ -1905,7 +2385,7 @@ var StorageManager = class _StorageManager {
|
|
|
1905
2385
|
void 0,
|
|
1906
2386
|
this.baseDir
|
|
1907
2387
|
);
|
|
1908
|
-
const rel =
|
|
2388
|
+
const rel = path2.relative(this.baseDir, filePath).split(path2.sep).join("/");
|
|
1909
2389
|
return `${rel}:${version.versionId}`;
|
|
1910
2390
|
} catch (err) {
|
|
1911
2391
|
log.warn(
|
|
@@ -1915,18 +2395,28 @@ var StorageManager = class _StorageManager {
|
|
|
1915
2395
|
}
|
|
1916
2396
|
}
|
|
1917
2397
|
/** The root directory of this storage instance. */
|
|
2398
|
+
/** EntityStore (storage.ts decomposition). Lazy; selfDeps live wiring. */
|
|
2399
|
+
_entityStore;
|
|
2400
|
+
get entityStore() {
|
|
2401
|
+
if (!this._entityStore) {
|
|
2402
|
+
this._entityStore = new EntityStore(
|
|
2403
|
+
selfDeps(this)
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
return this._entityStore;
|
|
2407
|
+
}
|
|
1918
2408
|
get dir() {
|
|
1919
2409
|
return this.baseDir;
|
|
1920
2410
|
}
|
|
1921
2411
|
identityFilePath(workspaceDir, namespace) {
|
|
1922
2412
|
const rawNamespace = typeof namespace === "string" ? namespace.trim() : "";
|
|
1923
|
-
if (!rawNamespace) return
|
|
2413
|
+
if (!rawNamespace) return path2.join(workspaceDir, "IDENTITY.md");
|
|
1924
2414
|
const safeNamespace = rawNamespace.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
1925
|
-
return
|
|
2415
|
+
return path2.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
|
|
1926
2416
|
}
|
|
1927
2417
|
versionFilePath(kind) {
|
|
1928
2418
|
const fileName = kind === "memory-status" ? ".memory-status-version.log" : kind === "artifact-write" ? ".artifact-write-version.log" : ".cold-write-version.log";
|
|
1929
|
-
return
|
|
2419
|
+
return path2.join(this.stateDir, fileName);
|
|
1930
2420
|
}
|
|
1931
2421
|
bumpSharedVersion(kind, fallbackMap) {
|
|
1932
2422
|
const filePath = this.versionFilePath(kind);
|
|
@@ -1973,7 +2463,7 @@ var StorageManager = class _StorageManager {
|
|
|
1973
2463
|
// memories.
|
|
1974
2464
|
// -------------------------------------------------------------------------
|
|
1975
2465
|
get wearablesDir() {
|
|
1976
|
-
return
|
|
2466
|
+
return path2.join(this.baseDir, WEARABLES_DIR_NAME);
|
|
1977
2467
|
}
|
|
1978
2468
|
/**
|
|
1979
2469
|
* Resolve the on-disk path for a source/day transcript. Throws on
|
|
@@ -1991,7 +2481,7 @@ var StorageManager = class _StorageManager {
|
|
|
1991
2481
|
`invalid wearable transcript date '${String(date)}' \u2014 expected YYYY-MM-DD`
|
|
1992
2482
|
);
|
|
1993
2483
|
}
|
|
1994
|
-
return
|
|
2484
|
+
return path2.join(this.wearablesDir, sourceId, `${date}.md`);
|
|
1995
2485
|
}
|
|
1996
2486
|
async writeWearableDayTranscript(sourceId, date, serialized) {
|
|
1997
2487
|
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
@@ -2018,7 +2508,7 @@ var StorageManager = class _StorageManager {
|
|
|
2018
2508
|
sources = [sourceId];
|
|
2019
2509
|
} else {
|
|
2020
2510
|
try {
|
|
2021
|
-
const entries = await
|
|
2511
|
+
const entries = await readdir2(this.wearablesDir, { withFileTypes: true });
|
|
2022
2512
|
sources = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
2023
2513
|
} catch (err) {
|
|
2024
2514
|
if (err.code === "ENOENT") return [];
|
|
@@ -2029,7 +2519,7 @@ var StorageManager = class _StorageManager {
|
|
|
2029
2519
|
if (!/^[a-z][a-z0-9-]{0,63}$/.test(source)) continue;
|
|
2030
2520
|
let entries;
|
|
2031
2521
|
try {
|
|
2032
|
-
entries = await
|
|
2522
|
+
entries = await readdir2(path2.join(this.wearablesDir, source));
|
|
2033
2523
|
} catch (err) {
|
|
2034
2524
|
if (err.code === "ENOENT") continue;
|
|
2035
2525
|
throw err;
|
|
@@ -2116,25 +2606,25 @@ var StorageManager = class _StorageManager {
|
|
|
2116
2606
|
});
|
|
2117
2607
|
}
|
|
2118
2608
|
get factsDir() {
|
|
2119
|
-
return
|
|
2609
|
+
return path2.join(this.baseDir, "facts");
|
|
2120
2610
|
}
|
|
2121
2611
|
get correctionsDir() {
|
|
2122
|
-
return
|
|
2612
|
+
return path2.join(this.baseDir, "corrections");
|
|
2123
2613
|
}
|
|
2124
2614
|
get proceduresDir() {
|
|
2125
|
-
return
|
|
2615
|
+
return path2.join(this.baseDir, "procedures");
|
|
2126
2616
|
}
|
|
2127
2617
|
get reasoningTracesDir() {
|
|
2128
|
-
return
|
|
2618
|
+
return path2.join(this.baseDir, "reasoning-traces");
|
|
2129
2619
|
}
|
|
2130
2620
|
get entitiesDir() {
|
|
2131
|
-
return
|
|
2621
|
+
return path2.join(this.baseDir, "entities");
|
|
2132
2622
|
}
|
|
2133
2623
|
resolveEntityFilePath(name) {
|
|
2134
2624
|
if (typeof name !== "string") return null;
|
|
2135
|
-
const filePath =
|
|
2136
|
-
const relative =
|
|
2137
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
2625
|
+
const filePath = path2.resolve(this.entitiesDir, `${name}.md`);
|
|
2626
|
+
const relative = path2.relative(this.entitiesDir, filePath);
|
|
2627
|
+
if (relative === "" || relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
2138
2628
|
return null;
|
|
2139
2629
|
}
|
|
2140
2630
|
return filePath;
|
|
@@ -2149,10 +2639,10 @@ var StorageManager = class _StorageManager {
|
|
|
2149
2639
|
return writeMaybeEncryptedFileFromChunks(filePath, chunks, this.resolveWriteKey(), {}, this.baseDir).then(() => this.notifyCatalogWrite());
|
|
2150
2640
|
}
|
|
2151
2641
|
assertManagedStoragePath(filePath, method) {
|
|
2152
|
-
const resolved =
|
|
2153
|
-
const base =
|
|
2154
|
-
const rel =
|
|
2155
|
-
if (rel === "" || rel === ".." || rel.startsWith(`..${
|
|
2642
|
+
const resolved = path2.resolve(filePath);
|
|
2643
|
+
const base = path2.resolve(this.baseDir);
|
|
2644
|
+
const rel = path2.relative(base, resolved);
|
|
2645
|
+
if (rel === "" || rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel)) {
|
|
2156
2646
|
throw new Error(`${method}: file path escapes memory dir`);
|
|
2157
2647
|
}
|
|
2158
2648
|
return resolved;
|
|
@@ -2164,7 +2654,7 @@ var StorageManager = class _StorageManager {
|
|
|
2164
2654
|
async digestOfflineSyncFile(filePath) {
|
|
2165
2655
|
const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
|
|
2166
2656
|
const st = await stat(target);
|
|
2167
|
-
const relPath =
|
|
2657
|
+
const relPath = path2.relative(this.baseDir, target).split(path2.sep).join("/");
|
|
2168
2658
|
const cache = await this.loadOfflineSyncDigestCache();
|
|
2169
2659
|
const cached = cache.get(relPath);
|
|
2170
2660
|
if (cached && cached.statBytes === st.size && cached.mtimeMs === st.mtimeMs && cached.ctimeMs === st.ctimeMs && !cached.encrypted) {
|
|
@@ -2225,7 +2715,7 @@ var StorageManager = class _StorageManager {
|
|
|
2225
2715
|
const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
|
|
2226
2716
|
const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
|
|
2227
2717
|
const encrypted = record.encrypted === true;
|
|
2228
|
-
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") ||
|
|
2718
|
+
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path2.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
|
|
2229
2719
|
continue;
|
|
2230
2720
|
}
|
|
2231
2721
|
cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
|
|
@@ -2267,7 +2757,7 @@ var StorageManager = class _StorageManager {
|
|
|
2267
2757
|
const cache = this.offlineSyncDigestCache;
|
|
2268
2758
|
if (!cache) return;
|
|
2269
2759
|
const entries = [...cache.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
|
|
2270
|
-
await mkdir(
|
|
2760
|
+
await mkdir(path2.dirname(this.offlineSyncDigestCachePath), { recursive: true });
|
|
2271
2761
|
await writeFile(
|
|
2272
2762
|
this.offlineSyncDigestCachePath,
|
|
2273
2763
|
`${JSON.stringify({ version: 1, entries })}
|
|
@@ -2306,7 +2796,7 @@ var StorageManager = class _StorageManager {
|
|
|
2306
2796
|
}
|
|
2307
2797
|
async deleteOfflineSyncFile(filePath) {
|
|
2308
2798
|
const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
|
|
2309
|
-
await
|
|
2799
|
+
await unlink2(target).catch((error) => {
|
|
2310
2800
|
if (isErrnoCode(error, "ENOENT")) return;
|
|
2311
2801
|
throw error;
|
|
2312
2802
|
});
|
|
@@ -2316,14 +2806,14 @@ var StorageManager = class _StorageManager {
|
|
|
2316
2806
|
this.invalidateAllMemoriesCache();
|
|
2317
2807
|
this.invalidateKnowledgeIndexCache();
|
|
2318
2808
|
this.factHashIndexAuthoritative = false;
|
|
2319
|
-
await
|
|
2809
|
+
await unlink2(this.factHashIndexReadyPath).catch((error) => {
|
|
2320
2810
|
if (isErrnoCode(error, "ENOENT")) return;
|
|
2321
2811
|
throw error;
|
|
2322
2812
|
});
|
|
2323
|
-
if (filePath.includes(`${
|
|
2813
|
+
if (filePath.includes(`${path2.sep}cold${path2.sep}`)) {
|
|
2324
2814
|
this.invalidateColdMemoriesCache();
|
|
2325
2815
|
}
|
|
2326
|
-
if (filePath.includes(`${
|
|
2816
|
+
if (filePath.includes(`${path2.sep}artifacts${path2.sep}`)) {
|
|
2327
2817
|
this.bumpArtifactWriteVersion();
|
|
2328
2818
|
}
|
|
2329
2819
|
this.bumpMemoryStatusVersion();
|
|
@@ -2351,7 +2841,7 @@ var StorageManager = class _StorageManager {
|
|
|
2351
2841
|
}
|
|
2352
2842
|
async appendStorageSecureFileUnlocked(filePath, content) {
|
|
2353
2843
|
const writeKey = this.resolveWriteKey();
|
|
2354
|
-
await mkdir(
|
|
2844
|
+
await mkdir(path2.dirname(filePath), { recursive: true });
|
|
2355
2845
|
if (writeKey === null) {
|
|
2356
2846
|
try {
|
|
2357
2847
|
if (isEncryptedFile(await readFile(filePath))) {
|
|
@@ -2377,16 +2867,16 @@ var StorageManager = class _StorageManager {
|
|
|
2377
2867
|
this.notifyCatalogWrite();
|
|
2378
2868
|
}
|
|
2379
2869
|
get stateDir() {
|
|
2380
|
-
return
|
|
2870
|
+
return path2.join(this.baseDir, "state");
|
|
2381
2871
|
}
|
|
2382
2872
|
get offlineSyncDigestCachePath() {
|
|
2383
|
-
return
|
|
2873
|
+
return path2.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
|
|
2384
2874
|
}
|
|
2385
2875
|
get entitySynthesisQueuePath() {
|
|
2386
|
-
return
|
|
2876
|
+
return path2.join(this.stateDir, "entity-synthesis-queue.json");
|
|
2387
2877
|
}
|
|
2388
2878
|
get factHashIndexReadyPath() {
|
|
2389
|
-
return
|
|
2879
|
+
return path2.join(this.stateDir, "fact-hashes.ready");
|
|
2390
2880
|
}
|
|
2391
2881
|
// ── Tombstone store access (issue #1579) ─────────────────────────────────
|
|
2392
2882
|
/**
|
|
@@ -2396,7 +2886,7 @@ var StorageManager = class _StorageManager {
|
|
|
2396
2886
|
* helpers are injected into the store).
|
|
2397
2887
|
*/
|
|
2398
2888
|
get tombstonesPath() {
|
|
2399
|
-
return
|
|
2889
|
+
return path2.join(this.stateDir, "tombstones.jsonl");
|
|
2400
2890
|
}
|
|
2401
2891
|
/**
|
|
2402
2892
|
* Configure the tombstone invariant for this storage instance. Installed
|
|
@@ -2595,7 +3085,7 @@ var StorageManager = class _StorageManager {
|
|
|
2595
3085
|
);
|
|
2596
3086
|
}
|
|
2597
3087
|
await factHashIndex.save();
|
|
2598
|
-
await mkdir(
|
|
3088
|
+
await mkdir(path2.dirname(this.factHashIndexReadyPath), { recursive: true });
|
|
2599
3089
|
await writeFile(this.factHashIndexReadyPath, "v1\n", "utf-8");
|
|
2600
3090
|
this.factHashIndexAuthoritative = true;
|
|
2601
3091
|
})().finally(() => {
|
|
@@ -2604,55 +3094,55 @@ var StorageManager = class _StorageManager {
|
|
|
2604
3094
|
await this.factHashIndexAuthoritativePromise;
|
|
2605
3095
|
}
|
|
2606
3096
|
get questionsDir() {
|
|
2607
|
-
return
|
|
3097
|
+
return path2.join(this.baseDir, "questions");
|
|
2608
3098
|
}
|
|
2609
3099
|
get artifactsDir() {
|
|
2610
|
-
return
|
|
3100
|
+
return path2.join(this.baseDir, "artifacts");
|
|
2611
3101
|
}
|
|
2612
3102
|
get identityDir() {
|
|
2613
|
-
return
|
|
3103
|
+
return path2.join(this.baseDir, "identity");
|
|
2614
3104
|
}
|
|
2615
3105
|
get identityAnchorPath() {
|
|
2616
|
-
return
|
|
3106
|
+
return path2.join(this.identityDir, "identity-anchor.md");
|
|
2617
3107
|
}
|
|
2618
3108
|
get identityIncidentsDir() {
|
|
2619
|
-
return
|
|
3109
|
+
return path2.join(this.identityDir, "incidents");
|
|
2620
3110
|
}
|
|
2621
3111
|
get identityAuditsWeeklyDir() {
|
|
2622
|
-
return
|
|
3112
|
+
return path2.join(this.identityDir, "audits", "weekly");
|
|
2623
3113
|
}
|
|
2624
3114
|
get identityAuditsMonthlyDir() {
|
|
2625
|
-
return
|
|
3115
|
+
return path2.join(this.identityDir, "audits", "monthly");
|
|
2626
3116
|
}
|
|
2627
3117
|
get identityImprovementLoopsPath() {
|
|
2628
|
-
return
|
|
3118
|
+
return path2.join(this.identityDir, "improvement-loops.md");
|
|
2629
3119
|
}
|
|
2630
3120
|
get identityReflectionsPath() {
|
|
2631
|
-
return
|
|
3121
|
+
return path2.join(this.identityDir, "reflections.md");
|
|
2632
3122
|
}
|
|
2633
3123
|
get profilePath() {
|
|
2634
|
-
return
|
|
3124
|
+
return path2.join(this.baseDir, "profile.md");
|
|
2635
3125
|
}
|
|
2636
3126
|
get memoryActionsPath() {
|
|
2637
|
-
return
|
|
3127
|
+
return path2.join(this.stateDir, "memory-actions.jsonl");
|
|
2638
3128
|
}
|
|
2639
3129
|
get memoryLifecycleLedgerPath() {
|
|
2640
|
-
return
|
|
3130
|
+
return path2.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
|
|
2641
3131
|
}
|
|
2642
3132
|
get compressionGuidelinesPath() {
|
|
2643
|
-
return
|
|
3133
|
+
return path2.join(this.stateDir, "compression-guidelines.md");
|
|
2644
3134
|
}
|
|
2645
3135
|
get compressionGuidelineDraftPath() {
|
|
2646
|
-
return
|
|
3136
|
+
return path2.join(this.stateDir, "compression-guidelines.draft.md");
|
|
2647
3137
|
}
|
|
2648
3138
|
get compressionGuidelineStatePath() {
|
|
2649
|
-
return
|
|
3139
|
+
return path2.join(this.stateDir, "compression-guideline-state.json");
|
|
2650
3140
|
}
|
|
2651
3141
|
get compressionGuidelineDraftStatePath() {
|
|
2652
|
-
return
|
|
3142
|
+
return path2.join(this.stateDir, "compression-guideline-draft-state.json");
|
|
2653
3143
|
}
|
|
2654
3144
|
get behaviorSignalsPath() {
|
|
2655
|
-
return
|
|
3145
|
+
return path2.join(this.stateDir, "behavior-signals.jsonl");
|
|
2656
3146
|
}
|
|
2657
3147
|
/**
|
|
2658
3148
|
* Buffer surprise telemetry ledger (issue #563 PR 3).
|
|
@@ -2665,7 +3155,7 @@ var StorageManager = class _StorageManager {
|
|
|
2665
3155
|
* governance sweeps can treat it uniformly.
|
|
2666
3156
|
*/
|
|
2667
3157
|
get bufferSurpriseLedgerPath() {
|
|
2668
|
-
return
|
|
3158
|
+
return path2.join(this.stateDir, "buffer-surprise-ledger.jsonl");
|
|
2669
3159
|
}
|
|
2670
3160
|
/**
|
|
2671
3161
|
* Entity alias table loaded from THIS store's config/aliases.json.
|
|
@@ -2675,9 +3165,11 @@ var StorageManager = class _StorageManager {
|
|
|
2675
3165
|
* store loaded last rewrite every other store's canonical entity ids.
|
|
2676
3166
|
*/
|
|
2677
3167
|
userAliases = {};
|
|
2678
|
-
/** Normalize an entity name using this store's alias table. */
|
|
2679
3168
|
normalizeEntityName(raw, type) {
|
|
2680
|
-
return normalizeEntityName(
|
|
3169
|
+
return this.entityStore.normalizeEntityName(
|
|
3170
|
+
raw,
|
|
3171
|
+
type
|
|
3172
|
+
);
|
|
2681
3173
|
}
|
|
2682
3174
|
/**
|
|
2683
3175
|
* Read-only view of this store's user alias table, for call sites that
|
|
@@ -2697,7 +3189,7 @@ var StorageManager = class _StorageManager {
|
|
|
2697
3189
|
this.loadAliasesSync();
|
|
2698
3190
|
}
|
|
2699
3191
|
loadAliasesSync() {
|
|
2700
|
-
const aliasPath =
|
|
3192
|
+
const aliasPath = path2.join(this.baseDir, "config", "aliases.json");
|
|
2701
3193
|
this.userAliases = {};
|
|
2702
3194
|
try {
|
|
2703
3195
|
const raw = readFileSync(aliasPath, "utf-8");
|
|
@@ -2722,9 +3214,9 @@ var StorageManager = class _StorageManager {
|
|
|
2722
3214
|
}
|
|
2723
3215
|
async ensureDirectories() {
|
|
2724
3216
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2725
|
-
await mkdir(
|
|
2726
|
-
await mkdir(
|
|
2727
|
-
await mkdir(
|
|
3217
|
+
await mkdir(path2.join(this.factsDir, today), { recursive: true });
|
|
3218
|
+
await mkdir(path2.join(this.proceduresDir, today), { recursive: true });
|
|
3219
|
+
await mkdir(path2.join(this.reasoningTracesDir, today), { recursive: true });
|
|
2728
3220
|
await mkdir(this.correctionsDir, { recursive: true });
|
|
2729
3221
|
await mkdir(this.entitiesDir, { recursive: true });
|
|
2730
3222
|
await mkdir(this.stateDir, { recursive: true });
|
|
@@ -2734,7 +3226,7 @@ var StorageManager = class _StorageManager {
|
|
|
2734
3226
|
await mkdir(this.identityIncidentsDir, { recursive: true });
|
|
2735
3227
|
await mkdir(this.identityAuditsWeeklyDir, { recursive: true });
|
|
2736
3228
|
await mkdir(this.identityAuditsMonthlyDir, { recursive: true });
|
|
2737
|
-
await mkdir(
|
|
3229
|
+
await mkdir(path2.join(this.baseDir, "config"), { recursive: true });
|
|
2738
3230
|
}
|
|
2739
3231
|
/**
|
|
2740
3232
|
* Resolve the on-disk write path for a memory of the given category, creating
|
|
@@ -2751,11 +3243,11 @@ var StorageManager = class _StorageManager {
|
|
|
2751
3243
|
async resolveCategoryWritePath(category, id, today) {
|
|
2752
3244
|
if (category === "correction") {
|
|
2753
3245
|
await mkdir(this.correctionsDir, { recursive: true });
|
|
2754
|
-
return
|
|
3246
|
+
return path2.join(this.correctionsDir, `${id}.md`);
|
|
2755
3247
|
}
|
|
2756
|
-
const datedDir =
|
|
3248
|
+
const datedDir = path2.join(getCategoryDir(this.baseDir, category), today);
|
|
2757
3249
|
await mkdir(datedDir, { recursive: true });
|
|
2758
|
-
return
|
|
3250
|
+
return path2.join(datedDir, `${id}.md`);
|
|
2759
3251
|
}
|
|
2760
3252
|
async writeMemory(category, content, options = {}) {
|
|
2761
3253
|
await this.ensureDirectories();
|
|
@@ -3005,7 +3497,7 @@ ${sanitized.text}
|
|
|
3005
3497
|
await this.ensureDirectories();
|
|
3006
3498
|
const now = /* @__PURE__ */ new Date();
|
|
3007
3499
|
const day = now.toISOString().slice(0, 10);
|
|
3008
|
-
const dir =
|
|
3500
|
+
const dir = path2.join(this.artifactsDir, day);
|
|
3009
3501
|
await mkdir(dir, { recursive: true });
|
|
3010
3502
|
const id = `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
3011
3503
|
const fm = {
|
|
@@ -3029,7 +3521,7 @@ ${sanitized.text}
|
|
|
3029
3521
|
log.warn(`artifact content rejected for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
3030
3522
|
return "";
|
|
3031
3523
|
}
|
|
3032
|
-
const filePath =
|
|
3524
|
+
const filePath = path2.join(dir, `${id}.md`);
|
|
3033
3525
|
await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(fm)}
|
|
3034
3526
|
|
|
3035
3527
|
${sanitized.text}
|
|
@@ -3055,9 +3547,9 @@ ${sanitized.text}
|
|
|
3055
3547
|
const artifacts = [];
|
|
3056
3548
|
const readDir = async (dir) => {
|
|
3057
3549
|
try {
|
|
3058
|
-
const entries = await
|
|
3550
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
3059
3551
|
for (const entry of entries) {
|
|
3060
|
-
const fullPath =
|
|
3552
|
+
const fullPath = path2.join(dir, entry.name);
|
|
3061
3553
|
if (entry.isDirectory()) {
|
|
3062
3554
|
await readDir(fullPath);
|
|
3063
3555
|
continue;
|
|
@@ -3106,107 +3598,12 @@ ${sanitized.text}
|
|
|
3106
3598
|
return hits.slice(0, maxResults).map((h) => h.memory);
|
|
3107
3599
|
}
|
|
3108
3600
|
async writeEntity(name, type, facts, options = {}) {
|
|
3109
|
-
|
|
3110
|
-
if (typeof name !== "string" || !name.trim() || typeof type !== "string" || !type.trim()) {
|
|
3111
|
-
log.warn("writeEntity: invalid entity payload, skipping", {
|
|
3112
|
-
nameType: typeof name,
|
|
3113
|
-
typeType: typeof type
|
|
3114
|
-
});
|
|
3115
|
-
return "";
|
|
3116
|
-
}
|
|
3117
|
-
const safeFacts = Array.isArray(facts) ? [...new Set(
|
|
3118
|
-
facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0)
|
|
3119
|
-
)] : [];
|
|
3120
|
-
let normalized = this.normalizeEntityName(name, type);
|
|
3121
|
-
const match = await this.findMatchingEntity(name, type);
|
|
3122
|
-
if (match && match !== normalized) {
|
|
3123
|
-
log.debug(`fuzzy match: "${normalized}" \u2192 existing "${match}"`);
|
|
3124
|
-
normalized = match;
|
|
3125
|
-
}
|
|
3126
|
-
const filePath = path.join(this.entitiesDir, `${normalized}.md`);
|
|
3127
|
-
let entity = {
|
|
3601
|
+
return this.entityStore.writeEntity(
|
|
3128
3602
|
name,
|
|
3129
3603
|
type,
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
facts: [],
|
|
3133
|
-
summary: void 0,
|
|
3134
|
-
synthesis: void 0,
|
|
3135
|
-
synthesisUpdatedAt: void 0,
|
|
3136
|
-
synthesisVersion: void 0,
|
|
3137
|
-
synthesisStructuredFactCount: void 0,
|
|
3138
|
-
synthesisStructuredFactDigest: void 0,
|
|
3139
|
-
timeline: [],
|
|
3140
|
-
relationships: [],
|
|
3141
|
-
activity: [],
|
|
3142
|
-
aliases: []
|
|
3143
|
-
};
|
|
3144
|
-
try {
|
|
3145
|
-
const existing = await this.readStorageSecureFile(filePath);
|
|
3146
|
-
entity = parseEntityFile(existing, this.entitySchemas);
|
|
3147
|
-
} catch (err) {
|
|
3148
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
3149
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
3150
|
-
}
|
|
3151
|
-
const timestamp = options.timestamp?.trim() || (/* @__PURE__ */ new Date()).toISOString();
|
|
3152
|
-
const source = options.source?.trim() || void 0;
|
|
3153
|
-
const sessionKey = options.sessionKey?.trim() || void 0;
|
|
3154
|
-
const principal = options.principal?.trim() || void 0;
|
|
3155
|
-
const structuredSectionMap = new Map(
|
|
3156
|
-
(entity.structuredSections ?? []).map((section) => [section.key, {
|
|
3157
|
-
...section,
|
|
3158
|
-
facts: [...section.facts]
|
|
3159
|
-
}])
|
|
3160
|
-
);
|
|
3161
|
-
for (const section of options.structuredSections ?? []) {
|
|
3162
|
-
const normalizedSection = normalizeEntityStructuredSection(type, section, this.entitySchemas);
|
|
3163
|
-
const normalizedFacts = normalizeStructuredSectionFacts(section.facts);
|
|
3164
|
-
if (normalizedFacts.length === 0) continue;
|
|
3165
|
-
const existingSection = structuredSectionMap.get(normalizedSection.key);
|
|
3166
|
-
if (!existingSection) {
|
|
3167
|
-
structuredSectionMap.set(normalizedSection.key, {
|
|
3168
|
-
key: normalizedSection.key,
|
|
3169
|
-
title: normalizedSection.title,
|
|
3170
|
-
facts: normalizedFacts
|
|
3171
|
-
});
|
|
3172
|
-
continue;
|
|
3173
|
-
}
|
|
3174
|
-
existingSection.facts = normalizeStructuredSectionFacts([...existingSection.facts, ...normalizedFacts]);
|
|
3175
|
-
if (!existingSection.title.trim() && normalizedSection.title.trim()) {
|
|
3176
|
-
existingSection.title = normalizedSection.title;
|
|
3177
|
-
}
|
|
3178
|
-
}
|
|
3179
|
-
for (const fact of safeFacts) {
|
|
3180
|
-
const nextEntry = {
|
|
3181
|
-
timestamp,
|
|
3182
|
-
text: fact,
|
|
3183
|
-
...source ? { source } : {},
|
|
3184
|
-
...sessionKey ? { sessionKey } : {},
|
|
3185
|
-
...principal ? { principal } : {}
|
|
3186
|
-
};
|
|
3187
|
-
const alreadyPresent = entity.timeline.some(
|
|
3188
|
-
(entry) => entry.timestamp === nextEntry.timestamp && entry.text === nextEntry.text && entry.source === nextEntry.source && entry.sessionKey === nextEntry.sessionKey && entry.principal === nextEntry.principal
|
|
3189
|
-
);
|
|
3190
|
-
if (alreadyPresent) continue;
|
|
3191
|
-
entity.timeline.push(nextEntry);
|
|
3192
|
-
}
|
|
3193
|
-
entity.structuredSections = sortStructuredSectionsBySchema(
|
|
3194
|
-
type,
|
|
3195
|
-
Array.from(structuredSectionMap.values()).filter((section) => section.facts.length > 0),
|
|
3196
|
-
this.entitySchemas
|
|
3604
|
+
facts,
|
|
3605
|
+
options
|
|
3197
3606
|
);
|
|
3198
|
-
entity.facts = compileEntityFacts(entity.timeline, entity.structuredSections);
|
|
3199
|
-
entity.summary = entity.synthesis || entity.summary;
|
|
3200
|
-
entity.name = name;
|
|
3201
|
-
entity.type = type;
|
|
3202
|
-
entity.created = entity.created || timestamp;
|
|
3203
|
-
entity.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
3204
|
-
await this.snapshotBeforeWrite(filePath, "write");
|
|
3205
|
-
await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
|
|
3206
|
-
this.invalidateKnowledgeIndexCache();
|
|
3207
|
-
this.bumpMemoryStatusVersion();
|
|
3208
|
-
log.debug(`wrote entity ${normalized}`);
|
|
3209
|
-
return normalized;
|
|
3210
3607
|
}
|
|
3211
3608
|
async readProfile() {
|
|
3212
3609
|
try {
|
|
@@ -3364,7 +3761,7 @@ ${sanitized.text}
|
|
|
3364
3761
|
* per-process in-memory cache safe across process boundaries.
|
|
3365
3762
|
*/
|
|
3366
3763
|
invalidateColdMemoriesCache() {
|
|
3367
|
-
const coldRoot =
|
|
3764
|
+
const coldRoot = path2.join(this.baseDir, "cold");
|
|
3368
3765
|
_StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
3369
3766
|
this.bumpColdWriteVersion();
|
|
3370
3767
|
invalidateAllForDir(this.baseDir);
|
|
@@ -3400,14 +3797,14 @@ ${sanitized.text}
|
|
|
3400
3797
|
const dirStat = await lstat(dir);
|
|
3401
3798
|
if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) return;
|
|
3402
3799
|
assertPathInsideRoot(memoryRootReal, await realpath(dir), dir);
|
|
3403
|
-
entries = await
|
|
3800
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
3404
3801
|
} catch {
|
|
3405
3802
|
return;
|
|
3406
3803
|
}
|
|
3407
3804
|
const subdirs = [];
|
|
3408
3805
|
for (const entry of entries) {
|
|
3409
3806
|
if (entry.isSymbolicLink()) continue;
|
|
3410
|
-
const fullPath =
|
|
3807
|
+
const fullPath = path2.join(dir, entry.name);
|
|
3411
3808
|
if (entry.isDirectory()) {
|
|
3412
3809
|
subdirs.push(fullPath);
|
|
3413
3810
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -3423,7 +3820,7 @@ ${sanitized.text}
|
|
|
3423
3820
|
}
|
|
3424
3821
|
};
|
|
3425
3822
|
for (const dir of RECALL_FALLBACK_DIRS) {
|
|
3426
|
-
await collectPaths(
|
|
3823
|
+
await collectPaths(path2.join(this.baseDir, dir));
|
|
3427
3824
|
}
|
|
3428
3825
|
return filePaths;
|
|
3429
3826
|
}
|
|
@@ -3492,7 +3889,7 @@ ${sanitized.text}
|
|
|
3492
3889
|
const correctionPaths = [];
|
|
3493
3890
|
const factPaths = [];
|
|
3494
3891
|
for (const filePath of filePaths) {
|
|
3495
|
-
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${
|
|
3892
|
+
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path2.sep}`)) {
|
|
3496
3893
|
correctionPaths.push(filePath);
|
|
3497
3894
|
} else {
|
|
3498
3895
|
factPaths.push(filePath);
|
|
@@ -3610,10 +4007,10 @@ ${sanitized.text}
|
|
|
3610
4007
|
const filePaths = [];
|
|
3611
4008
|
const collectPaths = async (dir) => {
|
|
3612
4009
|
try {
|
|
3613
|
-
const entries = await
|
|
4010
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
3614
4011
|
const subdirs = [];
|
|
3615
4012
|
for (const entry of entries) {
|
|
3616
|
-
const fullPath =
|
|
4013
|
+
const fullPath = path2.join(dir, entry.name);
|
|
3617
4014
|
if (entry.isDirectory()) {
|
|
3618
4015
|
subdirs.push(fullPath);
|
|
3619
4016
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -3640,9 +4037,9 @@ ${sanitized.text}
|
|
|
3640
4037
|
const root = this.archiveDir;
|
|
3641
4038
|
const readDir = async (dir) => {
|
|
3642
4039
|
try {
|
|
3643
|
-
const entries = await
|
|
4040
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
3644
4041
|
for (const entry of entries) {
|
|
3645
|
-
const fullPath =
|
|
4042
|
+
const fullPath = path2.join(dir, entry.name);
|
|
3646
4043
|
if (entry.isDirectory()) {
|
|
3647
4044
|
await readDir(fullPath);
|
|
3648
4045
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -3687,11 +4084,11 @@ ${sanitized.text}
|
|
|
3687
4084
|
content: parsed.content
|
|
3688
4085
|
};
|
|
3689
4086
|
}
|
|
3690
|
-
const normalizedPath = filePath.split(
|
|
4087
|
+
const normalizedPath = filePath.split(path2.sep).join("/");
|
|
3691
4088
|
if (normalizedPath.includes("/entities/") && filePath.endsWith(".md")) {
|
|
3692
4089
|
const entity = parseEntityFile(raw, this.entitySchemas);
|
|
3693
4090
|
if (!entity.name) return null;
|
|
3694
|
-
const nameWithoutExt =
|
|
4091
|
+
const nameWithoutExt = path2.basename(filePath, ".md");
|
|
3695
4092
|
const fileMtime = entity.updated || await stat(filePath).then((s) => s.mtime.toISOString()).catch(() => (/* @__PURE__ */ new Date(0)).toISOString());
|
|
3696
4093
|
return {
|
|
3697
4094
|
path: filePath,
|
|
@@ -3715,7 +4112,7 @@ ${sanitized.text}
|
|
|
3715
4112
|
}
|
|
3716
4113
|
}
|
|
3717
4114
|
resolveTierRootDir(tier) {
|
|
3718
|
-
return tier === "cold" ?
|
|
4115
|
+
return tier === "cold" ? path2.join(this.baseDir, "cold") : this.baseDir;
|
|
3719
4116
|
}
|
|
3720
4117
|
resolveMemoryDateDir(memory) {
|
|
3721
4118
|
const preferred = memory.frontmatter.created || memory.frontmatter.updated;
|
|
@@ -3730,13 +4127,13 @@ ${sanitized.text}
|
|
|
3730
4127
|
buildTierMemoryPath(memory, tier) {
|
|
3731
4128
|
const root = this.resolveTierRootDir(tier);
|
|
3732
4129
|
if (this.isArtifactMemory(memory)) {
|
|
3733
|
-
return
|
|
4130
|
+
return path2.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
3734
4131
|
}
|
|
3735
4132
|
if (memory.frontmatter.category === "correction") {
|
|
3736
|
-
return
|
|
4133
|
+
return path2.join(root, "corrections", `${memory.frontmatter.id}.md`);
|
|
3737
4134
|
}
|
|
3738
4135
|
const dir = categoryDirName(memory.frontmatter.category);
|
|
3739
|
-
return
|
|
4136
|
+
return path2.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
3740
4137
|
}
|
|
3741
4138
|
async writeMemoryFileAtomic(targetPath, memory) {
|
|
3742
4139
|
const fileContent = `${serializeFrontmatter(memory.frontmatter)}
|
|
@@ -3749,11 +4146,11 @@ ${memory.content}
|
|
|
3749
4146
|
}
|
|
3750
4147
|
async moveMemoryToPath(memory, targetPath) {
|
|
3751
4148
|
await this.writeMemoryFileAtomic(targetPath, memory);
|
|
3752
|
-
const sourcePath =
|
|
3753
|
-
const destPath =
|
|
4149
|
+
const sourcePath = path2.resolve(memory.path);
|
|
4150
|
+
const destPath = path2.resolve(targetPath);
|
|
3754
4151
|
if (sourcePath !== destPath) {
|
|
3755
4152
|
try {
|
|
3756
|
-
await
|
|
4153
|
+
await unlink2(memory.path);
|
|
3757
4154
|
} catch (err) {
|
|
3758
4155
|
const message = err instanceof Error ? err.message : String(err);
|
|
3759
4156
|
if (!message.includes("ENOENT")) {
|
|
@@ -3765,15 +4162,15 @@ ${memory.content}
|
|
|
3765
4162
|
}
|
|
3766
4163
|
async migrateMemoryToTier(memory, targetTier) {
|
|
3767
4164
|
const targetPath = this.buildTierMemoryPath(memory, targetTier);
|
|
3768
|
-
const sourcePath =
|
|
3769
|
-
const destPath =
|
|
4165
|
+
const sourcePath = path2.resolve(memory.path);
|
|
4166
|
+
const destPath = path2.resolve(targetPath);
|
|
3770
4167
|
if (sourcePath === destPath) {
|
|
3771
4168
|
return { changed: false, targetPath };
|
|
3772
4169
|
}
|
|
3773
4170
|
const existing = await this.readMemoryByPath(targetPath);
|
|
3774
4171
|
if (existing?.frontmatter.id === memory.frontmatter.id) {
|
|
3775
4172
|
try {
|
|
3776
|
-
await
|
|
4173
|
+
await unlink2(memory.path);
|
|
3777
4174
|
} catch (err) {
|
|
3778
4175
|
const message = err instanceof Error ? err.message : String(err);
|
|
3779
4176
|
if (!message.includes("ENOENT")) {
|
|
@@ -3792,7 +4189,7 @@ ${memory.content}
|
|
|
3792
4189
|
return { changed: true, targetPath };
|
|
3793
4190
|
}
|
|
3794
4191
|
get archiveDir() {
|
|
3795
|
-
return
|
|
4192
|
+
return path2.join(this.baseDir, "archive");
|
|
3796
4193
|
}
|
|
3797
4194
|
/**
|
|
3798
4195
|
* Archive a memory by moving it from facts/ to archive/YYYY-MM-DD/.
|
|
@@ -3803,7 +4200,7 @@ ${memory.content}
|
|
|
3803
4200
|
try {
|
|
3804
4201
|
const now = lifecycle?.at ?? /* @__PURE__ */ new Date();
|
|
3805
4202
|
const today = now.toISOString().slice(0, 10);
|
|
3806
|
-
const destDir =
|
|
4203
|
+
const destDir = path2.join(this.archiveDir, today);
|
|
3807
4204
|
await mkdir(destDir, { recursive: true });
|
|
3808
4205
|
const updatedFm = {
|
|
3809
4206
|
...memory.frontmatter,
|
|
@@ -3815,9 +4212,9 @@ ${memory.content}
|
|
|
3815
4212
|
|
|
3816
4213
|
${memory.content}
|
|
3817
4214
|
`;
|
|
3818
|
-
const destPath =
|
|
4215
|
+
const destPath = path2.join(destDir, path2.basename(memory.path));
|
|
3819
4216
|
await this.writeStorageSecureFile(destPath, fileContent);
|
|
3820
|
-
await
|
|
4217
|
+
await unlink2(memory.path);
|
|
3821
4218
|
this.invalidateAllMemoriesCache();
|
|
3822
4219
|
await this.appendGeneratedMemoryLifecycleEventFailOpen(
|
|
3823
4220
|
"storage.archiveMemory",
|
|
@@ -3844,33 +4241,19 @@ ${memory.content}
|
|
|
3844
4241
|
}
|
|
3845
4242
|
async readEntities() {
|
|
3846
4243
|
try {
|
|
3847
|
-
const entries = await
|
|
4244
|
+
const entries = await readdir2(this.entitiesDir);
|
|
3848
4245
|
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
3849
4246
|
} catch {
|
|
3850
4247
|
return [];
|
|
3851
4248
|
}
|
|
3852
4249
|
}
|
|
3853
4250
|
async readEntity(name) {
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
}
|
|
3858
|
-
try {
|
|
3859
|
-
return await this.readStorageSecureFile(filePath);
|
|
3860
|
-
} catch (err) {
|
|
3861
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
3862
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
3863
|
-
return "";
|
|
3864
|
-
}
|
|
4251
|
+
return this.entityStore.readEntity(
|
|
4252
|
+
name
|
|
4253
|
+
);
|
|
3865
4254
|
}
|
|
3866
|
-
/** Return sorted list of entity filenames (without .md extension) */
|
|
3867
4255
|
async listEntityNames() {
|
|
3868
|
-
|
|
3869
|
-
const entries = await readdir(this.entitiesDir);
|
|
3870
|
-
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", "")).sort();
|
|
3871
|
-
} catch {
|
|
3872
|
-
return [];
|
|
3873
|
-
}
|
|
4256
|
+
return this.entityStore.listEntityNames();
|
|
3874
4257
|
}
|
|
3875
4258
|
/**
|
|
3876
4259
|
* Find an existing entity that fuzzy-matches the proposed name.
|
|
@@ -3912,7 +4295,7 @@ ${memory.content}
|
|
|
3912
4295
|
const memory = memories.find((m) => m.frontmatter.id === id);
|
|
3913
4296
|
if (!memory) return false;
|
|
3914
4297
|
try {
|
|
3915
|
-
await
|
|
4298
|
+
await unlink2(memory.path);
|
|
3916
4299
|
this.invalidateAllMemoriesCache();
|
|
3917
4300
|
this.bumpMemoryStatusVersion();
|
|
3918
4301
|
log.debug(`invalidated memory ${id}`);
|
|
@@ -3977,7 +4360,7 @@ ${memory.content}
|
|
|
3977
4360
|
`;
|
|
3978
4361
|
await this.writeStorageSecureFile(memory.path, fileContent);
|
|
3979
4362
|
this.invalidateAllMemoriesCache();
|
|
3980
|
-
if (memory.path.includes(`${
|
|
4363
|
+
if (memory.path.includes(`${path2.sep}cold${path2.sep}`)) {
|
|
3981
4364
|
this.invalidateColdMemoriesCache();
|
|
3982
4365
|
}
|
|
3983
4366
|
try {
|
|
@@ -4035,7 +4418,7 @@ ${memory.content}
|
|
|
4035
4418
|
const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
|
|
4036
4419
|
if (expiresAt < now) {
|
|
4037
4420
|
try {
|
|
4038
|
-
await
|
|
4421
|
+
await unlink2(m.path);
|
|
4039
4422
|
deleted.push(m);
|
|
4040
4423
|
log.debug(`cleaned expired memory ${m.frontmatter.id} (TTL expired)`);
|
|
4041
4424
|
} catch {
|
|
@@ -4049,7 +4432,7 @@ ${memory.content}
|
|
|
4049
4432
|
return deleted;
|
|
4050
4433
|
}
|
|
4051
4434
|
async loadBuffer() {
|
|
4052
|
-
const bufferPath =
|
|
4435
|
+
const bufferPath = path2.join(this.stateDir, "buffer.json");
|
|
4053
4436
|
try {
|
|
4054
4437
|
const raw = await this.readStorageSecureFile(bufferPath);
|
|
4055
4438
|
return JSON.parse(raw);
|
|
@@ -4061,11 +4444,11 @@ ${memory.content}
|
|
|
4061
4444
|
}
|
|
4062
4445
|
async saveBuffer(state) {
|
|
4063
4446
|
await this.ensureDirectories();
|
|
4064
|
-
const bufferPath =
|
|
4447
|
+
const bufferPath = path2.join(this.stateDir, "buffer.json");
|
|
4065
4448
|
await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
|
|
4066
4449
|
}
|
|
4067
4450
|
async loadMeta() {
|
|
4068
|
-
const metaPath =
|
|
4451
|
+
const metaPath = path2.join(this.stateDir, "meta.json");
|
|
4069
4452
|
try {
|
|
4070
4453
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
4071
4454
|
const parsed = JSON.parse(raw);
|
|
@@ -4099,7 +4482,7 @@ ${memory.content}
|
|
|
4099
4482
|
}
|
|
4100
4483
|
async saveMeta(state) {
|
|
4101
4484
|
await this.ensureDirectories();
|
|
4102
|
-
const metaPath =
|
|
4485
|
+
const metaPath = path2.join(this.stateDir, "meta.json");
|
|
4103
4486
|
await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
|
|
4104
4487
|
}
|
|
4105
4488
|
async appendMemoryActionEvents(events) {
|
|
@@ -4269,7 +4652,7 @@ ${memory.content}
|
|
|
4269
4652
|
async appendReextractJobs(events) {
|
|
4270
4653
|
if (events.length === 0) return 0;
|
|
4271
4654
|
await this.ensureDirectories();
|
|
4272
|
-
const filePath =
|
|
4655
|
+
const filePath = path2.join(this.stateDir, "reextract-jobs.jsonl");
|
|
4273
4656
|
const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
|
|
4274
4657
|
try {
|
|
4275
4658
|
await this.appendStorageSecureFile(filePath, lines);
|
|
@@ -4282,7 +4665,7 @@ ${memory.content}
|
|
|
4282
4665
|
}
|
|
4283
4666
|
async readReextractJobs(limit = 200) {
|
|
4284
4667
|
const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
|
|
4285
|
-
const filePath =
|
|
4668
|
+
const filePath = path2.join(this.stateDir, "reextract-jobs.jsonl");
|
|
4286
4669
|
try {
|
|
4287
4670
|
const raw = await this.readStorageSecureFile(filePath);
|
|
4288
4671
|
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
@@ -4463,8 +4846,8 @@ ${memory.content}
|
|
|
4463
4846
|
activationState: "active"
|
|
4464
4847
|
});
|
|
4465
4848
|
await Promise.all([
|
|
4466
|
-
|
|
4467
|
-
|
|
4849
|
+
unlink2(this.compressionGuidelineDraftPath).catch(() => void 0),
|
|
4850
|
+
unlink2(this.compressionGuidelineDraftStatePath).catch(() => void 0)
|
|
4468
4851
|
]);
|
|
4469
4852
|
return true;
|
|
4470
4853
|
}
|
|
@@ -4536,7 +4919,7 @@ ${memory.content}
|
|
|
4536
4919
|
const date = nowIso.slice(0, 10);
|
|
4537
4920
|
const id = this.generateId("incident");
|
|
4538
4921
|
const incident = createContinuityIncidentRecord(id, input, nowIso);
|
|
4539
|
-
const filePath =
|
|
4922
|
+
const filePath = path2.join(this.identityIncidentsDir, `${date}-${id}.md`);
|
|
4540
4923
|
await this.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
|
|
4541
4924
|
return { ...incident, filePath };
|
|
4542
4925
|
}
|
|
@@ -4549,7 +4932,7 @@ ${memory.content}
|
|
|
4549
4932
|
const incidents = [];
|
|
4550
4933
|
for (const file of candidates) {
|
|
4551
4934
|
if (incidents.length >= cappedLimit) break;
|
|
4552
|
-
const filePath =
|
|
4935
|
+
const filePath = path2.join(this.identityIncidentsDir, file);
|
|
4553
4936
|
try {
|
|
4554
4937
|
const raw = await this.readStorageSecureFile(filePath);
|
|
4555
4938
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -4580,7 +4963,7 @@ ${memory.content}
|
|
|
4580
4963
|
await this.ensureDirectories();
|
|
4581
4964
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
4582
4965
|
const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
|
|
4583
|
-
const filePath =
|
|
4966
|
+
const filePath = path2.join(dir, `${safeKey}.md`);
|
|
4584
4967
|
await this.writeStorageSecureFile(filePath, content);
|
|
4585
4968
|
return filePath;
|
|
4586
4969
|
}
|
|
@@ -4588,7 +4971,7 @@ ${memory.content}
|
|
|
4588
4971
|
try {
|
|
4589
4972
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
4590
4973
|
const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
|
|
4591
|
-
return await this.readStorageSecureFile(
|
|
4974
|
+
return await this.readStorageSecureFile(path2.join(dir, `${safeKey}.md`));
|
|
4592
4975
|
} catch (err) {
|
|
4593
4976
|
if (err instanceof Error && err.message === "Invalid identity audit key") return null;
|
|
4594
4977
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
@@ -4635,7 +5018,7 @@ ${memory.content}
|
|
|
4635
5018
|
return `${prefix}-${ts}-${rand}`;
|
|
4636
5019
|
}
|
|
4637
5020
|
async readContinuityIncidentFileNames() {
|
|
4638
|
-
const files = await
|
|
5021
|
+
const files = await readdir2(this.identityIncidentsDir);
|
|
4639
5022
|
return files.filter((file) => file.endsWith(".md")).sort().reverse();
|
|
4640
5023
|
}
|
|
4641
5024
|
async readContinuityIncidentFile(filePath) {
|
|
@@ -4652,12 +5035,12 @@ ${memory.content}
|
|
|
4652
5035
|
const fileNames = await this.readContinuityIncidentFileNames();
|
|
4653
5036
|
const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
|
|
4654
5037
|
if (directMatch) {
|
|
4655
|
-
const directPath =
|
|
5038
|
+
const directPath = path2.join(this.identityIncidentsDir, directMatch);
|
|
4656
5039
|
const parsed = await this.readContinuityIncidentFile(directPath);
|
|
4657
5040
|
if (parsed?.id === id) return directPath;
|
|
4658
5041
|
}
|
|
4659
5042
|
for (const fileName of fileNames) {
|
|
4660
|
-
const filePath =
|
|
5043
|
+
const filePath = path2.join(this.identityIncidentsDir, fileName);
|
|
4661
5044
|
const parsed = await this.readContinuityIncidentFile(filePath);
|
|
4662
5045
|
if (parsed?.id === id) return filePath;
|
|
4663
5046
|
}
|
|
@@ -4687,7 +5070,7 @@ ${question}
|
|
|
4687
5070
|
|
|
4688
5071
|
**Context:** ${context}
|
|
4689
5072
|
`;
|
|
4690
|
-
const filePath =
|
|
5073
|
+
const filePath = path2.join(this.questionsDir, `${id}.md`);
|
|
4691
5074
|
await this.writeStorageSecureFile(filePath, content);
|
|
4692
5075
|
log.debug(`wrote question ${id} to ${filePath}`);
|
|
4693
5076
|
this.invalidateQuestionsCache();
|
|
@@ -4707,11 +5090,11 @@ ${question}
|
|
|
4707
5090
|
}
|
|
4708
5091
|
}
|
|
4709
5092
|
try {
|
|
4710
|
-
const files = await
|
|
5093
|
+
const files = await readdir2(this.questionsDir);
|
|
4711
5094
|
const questions = [];
|
|
4712
5095
|
for (const file of files) {
|
|
4713
5096
|
if (!file.endsWith(".md")) continue;
|
|
4714
|
-
const filePath =
|
|
5097
|
+
const filePath = path2.join(this.questionsDir, file);
|
|
4715
5098
|
const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
4716
5099
|
const parsed = this.parseQuestionFile(raw, filePath);
|
|
4717
5100
|
if (parsed) {
|
|
@@ -4734,7 +5117,7 @@ ${question}
|
|
|
4734
5117
|
if (!match) return null;
|
|
4735
5118
|
const frontmatterStr = match[1];
|
|
4736
5119
|
const body = match[2].trim();
|
|
4737
|
-
const id = this.extractFrontmatterValue(frontmatterStr, "id") ??
|
|
5120
|
+
const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path2.basename(filePath, ".md");
|
|
4738
5121
|
const created = this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
|
|
4739
5122
|
const priority = parseFloat(
|
|
4740
5123
|
this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5"
|
|
@@ -4796,11 +5179,11 @@ ${question}
|
|
|
4796
5179
|
} catch {
|
|
4797
5180
|
}
|
|
4798
5181
|
const hygiene = opts?.hygiene;
|
|
4799
|
-
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(
|
|
5182
|
+
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path2.basename(identityPath));
|
|
4800
5183
|
if (rotateEnabled) {
|
|
4801
5184
|
const maxBytes = hygiene.rotateMaxBytes;
|
|
4802
5185
|
if (existing.length > maxBytes) {
|
|
4803
|
-
const archiveDir =
|
|
5186
|
+
const archiveDir = path2.join(workspaceDir, hygiene.archiveDir);
|
|
4804
5187
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
4805
5188
|
filePath: identityPath,
|
|
4806
5189
|
archiveDir,
|
|
@@ -4894,7 +5277,7 @@ ${reflection}
|
|
|
4894
5277
|
* Deduplicates by target+label.
|
|
4895
5278
|
*/
|
|
4896
5279
|
async addEntityRelationship(name, rel) {
|
|
4897
|
-
const filePath =
|
|
5280
|
+
const filePath = path2.join(this.entitiesDir, `${name}.md`);
|
|
4898
5281
|
let entity;
|
|
4899
5282
|
try {
|
|
4900
5283
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -4919,7 +5302,7 @@ ${reflection}
|
|
|
4919
5302
|
* Prepends to the beginning, prunes oldest entries beyond maxEntries.
|
|
4920
5303
|
*/
|
|
4921
5304
|
async addEntityActivity(name, entry, maxEntries) {
|
|
4922
|
-
const filePath =
|
|
5305
|
+
const filePath = path2.join(this.entitiesDir, `${name}.md`);
|
|
4923
5306
|
let entity;
|
|
4924
5307
|
try {
|
|
4925
5308
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -4942,7 +5325,7 @@ ${reflection}
|
|
|
4942
5325
|
* Add an alias to an entity file. Deduplicates.
|
|
4943
5326
|
*/
|
|
4944
5327
|
async addEntityAlias(name, alias) {
|
|
4945
|
-
const filePath =
|
|
5328
|
+
const filePath = path2.join(this.entitiesDir, `${name}.md`);
|
|
4946
5329
|
let entity;
|
|
4947
5330
|
try {
|
|
4948
5331
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -4959,40 +5342,12 @@ ${reflection}
|
|
|
4959
5342
|
await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
|
|
4960
5343
|
this.invalidateKnowledgeIndexCache();
|
|
4961
5344
|
}
|
|
4962
|
-
/**
|
|
4963
|
-
* Set or rewrite the synthesis layer of an entity file.
|
|
4964
|
-
*/
|
|
4965
5345
|
async updateEntitySynthesis(name, synthesis, options = {}) {
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
} catch (err) {
|
|
4972
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
4973
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
4974
|
-
log.debug(`updateEntitySynthesis: entity file ${name}.md not found`);
|
|
4975
|
-
return;
|
|
4976
|
-
}
|
|
4977
|
-
const updatedAt = options.updatedAt?.trim() || entity.synthesisUpdatedAt?.trim() || void 0;
|
|
4978
|
-
const entityUpdatedAt = options.entityUpdatedAt?.trim() || updatedAt || entity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
4979
|
-
const synthesisTimelineCount = Number.isInteger(options.synthesisTimelineCount) && (options.synthesisTimelineCount ?? 0) >= 0 ? options.synthesisTimelineCount : void 0;
|
|
4980
|
-
const synthesisStructuredFactCount = Number.isInteger(options.synthesisStructuredFactCount) && (options.synthesisStructuredFactCount ?? 0) >= 0 ? options.synthesisStructuredFactCount : countEntityStructuredFacts(entity);
|
|
4981
|
-
const synthesisStructuredFactDigest = options.synthesisStructuredFactDigest?.trim() || fingerprintEntityStructuredFacts(entity);
|
|
4982
|
-
entity.synthesis = synthesis.trim();
|
|
4983
|
-
entity.summary = entity.synthesis;
|
|
4984
|
-
entity.synthesisUpdatedAt = updatedAt;
|
|
4985
|
-
entity.synthesisTimelineCount = synthesisTimelineCount;
|
|
4986
|
-
entity.synthesisStructuredFactCount = synthesisStructuredFactCount;
|
|
4987
|
-
entity.synthesisStructuredFactDigest = synthesisStructuredFactDigest;
|
|
4988
|
-
entity.synthesisVersion = Math.max(0, entity.synthesisVersion ?? 0) + (options.incrementVersion === false ? 0 : 1);
|
|
4989
|
-
entity.updated = entityUpdatedAt;
|
|
4990
|
-
await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
|
|
4991
|
-
await this.removeEntitySynthesisQueueEntries([
|
|
4992
|
-
.../* @__PURE__ */ new Set([name, this.normalizeEntityName(entity.name, entity.type)])
|
|
4993
|
-
]);
|
|
4994
|
-
this.invalidateKnowledgeIndexCache();
|
|
4995
|
-
this.bumpMemoryStatusVersion();
|
|
5346
|
+
return this.entityStore.updateEntitySynthesis(
|
|
5347
|
+
name,
|
|
5348
|
+
synthesis,
|
|
5349
|
+
options
|
|
5350
|
+
);
|
|
4996
5351
|
}
|
|
4997
5352
|
/**
|
|
4998
5353
|
* Backward-compatible alias for legacy callers/tests.
|
|
@@ -5001,7 +5356,7 @@ ${reflection}
|
|
|
5001
5356
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5002
5357
|
let synthesisTimelineCount;
|
|
5003
5358
|
try {
|
|
5004
|
-
const filePath =
|
|
5359
|
+
const filePath = path2.join(this.entitiesDir, `${name}.md`);
|
|
5005
5360
|
const content = await this.readStorageSecureFile(filePath);
|
|
5006
5361
|
synthesisTimelineCount = parseEntityFile(content, this.entitySchemas).timeline.length;
|
|
5007
5362
|
} catch (err) {
|
|
@@ -5084,7 +5439,7 @@ ${reflection}
|
|
|
5084
5439
|
if (!raw) continue;
|
|
5085
5440
|
const serialized = serializeEntityFile(parseEntityFile(raw, this.entitySchemas), this.entitySchemas);
|
|
5086
5441
|
if (raw.trimEnd() === serialized.trimEnd()) continue;
|
|
5087
|
-
await this.writeStorageSecureFile(
|
|
5442
|
+
await this.writeStorageSecureFile(path2.join(this.entitiesDir, `${entityName}.md`), serialized);
|
|
5088
5443
|
migrated += 1;
|
|
5089
5444
|
}
|
|
5090
5445
|
if (migrated > 0) {
|
|
@@ -5099,45 +5454,8 @@ ${reflection}
|
|
|
5099
5454
|
// ---------------------------------------------------------------------------
|
|
5100
5455
|
// Scoring + Knowledge Index (Knowledge Graph v7.0)
|
|
5101
5456
|
// ---------------------------------------------------------------------------
|
|
5102
|
-
/**
|
|
5103
|
-
* Read all entity files and return lightweight EntityFile objects.
|
|
5104
|
-
* Parsing is fast (~50-100ms for ~1,800 files) since entity files are small.
|
|
5105
|
-
*/
|
|
5106
5457
|
async readAllEntityFiles() {
|
|
5107
|
-
|
|
5108
|
-
const schemaCacheKey = buildEntitySchemaCacheKey(this.entitySchemas);
|
|
5109
|
-
const cacheKey = `${this.getEntityCacheSecureStoreKey()}\0${schemaCacheKey}`;
|
|
5110
|
-
const cached = getCachedEntities(this.baseDir, currentVersion, cacheKey);
|
|
5111
|
-
if (cached) return cached;
|
|
5112
|
-
try {
|
|
5113
|
-
const entries = await readdir(this.entitiesDir);
|
|
5114
|
-
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
5115
|
-
if (mdFiles.length === 0) return [];
|
|
5116
|
-
const BATCH_SIZE = 100;
|
|
5117
|
-
const entities = [];
|
|
5118
|
-
for (let i = 0; i < mdFiles.length; i += BATCH_SIZE) {
|
|
5119
|
-
const batch = mdFiles.slice(i, i + BATCH_SIZE);
|
|
5120
|
-
const results = await Promise.all(
|
|
5121
|
-
batch.map(async (entry) => {
|
|
5122
|
-
try {
|
|
5123
|
-
return await this.readStorageSecureFile(path.join(this.entitiesDir, entry));
|
|
5124
|
-
} catch (err) {
|
|
5125
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5126
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
5127
|
-
return null;
|
|
5128
|
-
}
|
|
5129
|
-
})
|
|
5130
|
-
);
|
|
5131
|
-
for (const content of results) {
|
|
5132
|
-
if (content !== null) entities.push(parseEntityFile(content, this.entitySchemas));
|
|
5133
|
-
}
|
|
5134
|
-
}
|
|
5135
|
-
setCachedEntities(this.baseDir, entities, currentVersion, cacheKey);
|
|
5136
|
-
return entities;
|
|
5137
|
-
} catch (err) {
|
|
5138
|
-
if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
|
|
5139
|
-
return [];
|
|
5140
|
-
}
|
|
5458
|
+
return this.entityStore.readAllEntityFiles();
|
|
5141
5459
|
}
|
|
5142
5460
|
/**
|
|
5143
5461
|
* Score an entity based on recency, frequency, activity, type priority,
|
|
@@ -5163,53 +5481,11 @@ ${reflection}
|
|
|
5163
5481
|
const relDensity = Math.min(entity.relationships.length / 8, 1);
|
|
5164
5482
|
return recency * 0.4 + frequency * 0.25 + activityScore * 0.15 + typePriority * 0.1 + relDensity * 0.1;
|
|
5165
5483
|
}
|
|
5166
|
-
/**
|
|
5167
|
-
* Build the Knowledge Index: a compact markdown table of top-scored entities.
|
|
5168
|
-
* Respects maxEntities and maxChars limits from config.
|
|
5169
|
-
*/
|
|
5170
5484
|
async buildKnowledgeIndex(config, overrides) {
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
const entities = await this.readAllEntityFiles();
|
|
5176
|
-
if (entities.length === 0) {
|
|
5177
|
-
if (useDefaultLimits) this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
5178
|
-
return { result: "", cached: false };
|
|
5179
|
-
}
|
|
5180
|
-
const now = /* @__PURE__ */ new Date();
|
|
5181
|
-
const scored = entities.map((e) => ({
|
|
5182
|
-
name: e.name,
|
|
5183
|
-
type: e.type,
|
|
5184
|
-
score: _StorageManager.scoreEntity(e, now),
|
|
5185
|
-
factCount: e.facts.length,
|
|
5186
|
-
summary: e.synthesis ?? e.summary,
|
|
5187
|
-
topRelationships: e.relationships.slice(0, 3).map((r) => r.target)
|
|
5188
|
-
}));
|
|
5189
|
-
scored.sort((a, b) => b.score - a.score);
|
|
5190
|
-
const maxEntities = typeof overrides?.maxEntities === "number" ? Math.max(0, Math.floor(overrides.maxEntities)) : config.knowledgeIndexMaxEntities;
|
|
5191
|
-
const topN = scored.slice(0, maxEntities);
|
|
5192
|
-
if (topN.length === 0) {
|
|
5193
|
-
if (useDefaultLimits) this.knowledgeIndexCache = { result: "", builtAt: Date.now() };
|
|
5194
|
-
return { result: "", cached: false };
|
|
5195
|
-
}
|
|
5196
|
-
const header = "## Knowledge Index\n\n| Entity | Type | Summary | Connected to |\n|--------|------|---------|-------------|";
|
|
5197
|
-
const rows = [];
|
|
5198
|
-
let totalChars = header.length;
|
|
5199
|
-
const maxChars = typeof overrides?.maxChars === "number" ? Math.max(0, Math.floor(overrides.maxChars)) : config.knowledgeIndexMaxChars;
|
|
5200
|
-
for (const entity of topN) {
|
|
5201
|
-
const summary = entity.summary || `${entity.factCount} facts`;
|
|
5202
|
-
const connected = entity.topRelationships.length > 0 ? entity.topRelationships.join(", ") : "\u2014";
|
|
5203
|
-
const row = `| ${entity.name} | ${entity.type} | ${summary} | ${connected} |`;
|
|
5204
|
-
if (totalChars + row.length + 1 > maxChars) break;
|
|
5205
|
-
rows.push(row);
|
|
5206
|
-
totalChars += row.length + 1;
|
|
5207
|
-
}
|
|
5208
|
-
const result = rows.length === 0 ? "" : `${header}
|
|
5209
|
-
${rows.join("\n")}
|
|
5210
|
-
`;
|
|
5211
|
-
if (useDefaultLimits) this.knowledgeIndexCache = { result, builtAt: Date.now() };
|
|
5212
|
-
return { result, cached: false };
|
|
5485
|
+
return this.entityStore.buildKnowledgeIndex(
|
|
5486
|
+
config,
|
|
5487
|
+
overrides
|
|
5488
|
+
);
|
|
5213
5489
|
}
|
|
5214
5490
|
/** Invalidate the Knowledge Index cache (call after entity mutations). */
|
|
5215
5491
|
invalidateKnowledgeIndexCache() {
|
|
@@ -5220,201 +5496,8 @@ ${rows.join("\n")}
|
|
|
5220
5496
|
// ---------------------------------------------------------------------------
|
|
5221
5497
|
/** Max lines for profile.md before LLM consolidation triggers */
|
|
5222
5498
|
static PROFILE_MAX_LINES = 300;
|
|
5223
|
-
/**
|
|
5224
|
-
* Merge fragmented entity files that resolve to the same canonical name.
|
|
5225
|
-
* Preserves relationships, activity, aliases, and summary from all fragments.
|
|
5226
|
-
* Returns count of files merged.
|
|
5227
|
-
*/
|
|
5228
5499
|
async mergeFragmentedEntities() {
|
|
5229
|
-
|
|
5230
|
-
try {
|
|
5231
|
-
const entries = await readdir(this.entitiesDir);
|
|
5232
|
-
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
5233
|
-
const groups = /* @__PURE__ */ new Map();
|
|
5234
|
-
for (const file of mdFiles) {
|
|
5235
|
-
const baseName = file.replace(".md", "");
|
|
5236
|
-
const dashIdx = baseName.indexOf("-");
|
|
5237
|
-
if (dashIdx === -1) continue;
|
|
5238
|
-
const type = baseName.slice(0, dashIdx);
|
|
5239
|
-
const restOfName = baseName.slice(dashIdx + 1);
|
|
5240
|
-
const canonical = this.normalizeEntityName(restOfName, type);
|
|
5241
|
-
if (!groups.has(canonical)) groups.set(canonical, []);
|
|
5242
|
-
groups.get(canonical).push(file);
|
|
5243
|
-
}
|
|
5244
|
-
for (const [canonical, files] of groups) {
|
|
5245
|
-
if (files.length <= 1) continue;
|
|
5246
|
-
const mergedEntity = {
|
|
5247
|
-
name: "",
|
|
5248
|
-
type: "other",
|
|
5249
|
-
created: "",
|
|
5250
|
-
updated: "",
|
|
5251
|
-
extraFrontmatterLines: [],
|
|
5252
|
-
preSectionLines: [],
|
|
5253
|
-
facts: [],
|
|
5254
|
-
summary: void 0,
|
|
5255
|
-
synthesis: void 0,
|
|
5256
|
-
synthesisUpdatedAt: void 0,
|
|
5257
|
-
synthesisTimelineCount: void 0,
|
|
5258
|
-
synthesisStructuredFactCount: void 0,
|
|
5259
|
-
synthesisStructuredFactDigest: void 0,
|
|
5260
|
-
synthesisVersion: void 0,
|
|
5261
|
-
timeline: [],
|
|
5262
|
-
relationships: [],
|
|
5263
|
-
activity: [],
|
|
5264
|
-
aliases: [],
|
|
5265
|
-
structuredSections: [],
|
|
5266
|
-
extraSections: []
|
|
5267
|
-
};
|
|
5268
|
-
for (const file of files) {
|
|
5269
|
-
const filePath = path.join(this.entitiesDir, file);
|
|
5270
|
-
try {
|
|
5271
|
-
const content = await this.readStorageSecureFile(filePath);
|
|
5272
|
-
const parsed = parseEntityFile(content, this.entitySchemas);
|
|
5273
|
-
if (!mergedEntity.type || mergedEntity.type === "other") {
|
|
5274
|
-
mergedEntity.type = parsed.type;
|
|
5275
|
-
}
|
|
5276
|
-
if (!mergedEntity.updated || compareEntityTimestamps(parsed.updated, mergedEntity.updated) > 0) {
|
|
5277
|
-
mergedEntity.updated = parsed.updated;
|
|
5278
|
-
}
|
|
5279
|
-
const parsedCreated = parsed.created || parsed.updated;
|
|
5280
|
-
const mergedCreated = mergedEntity.created?.trim() || "";
|
|
5281
|
-
const parsedCreatedMs = parsedCreated ? Date.parse(parsedCreated) : Number.NaN;
|
|
5282
|
-
const mergedCreatedMs = mergedCreated ? Date.parse(mergedCreated) : Number.NaN;
|
|
5283
|
-
const parsedCreatedIsValid = Number.isFinite(parsedCreatedMs);
|
|
5284
|
-
const mergedCreatedIsValid = Number.isFinite(mergedCreatedMs);
|
|
5285
|
-
if (parsedCreated && (!mergedCreated || parsedCreatedIsValid && !mergedCreatedIsValid || parsedCreatedIsValid && mergedCreatedIsValid && parsedCreatedMs < mergedCreatedMs || !parsedCreatedIsValid && !mergedCreatedIsValid && compareEntityTimestamps(parsedCreated, mergedCreated) < 0)) {
|
|
5286
|
-
mergedEntity.created = parsedCreated;
|
|
5287
|
-
}
|
|
5288
|
-
if (parsed.name.length > mergedEntity.name.length) {
|
|
5289
|
-
mergedEntity.name = parsed.name;
|
|
5290
|
-
}
|
|
5291
|
-
const parsedSynthesisUpdatedAt = parsed.synthesisUpdatedAt?.trim() || void 0;
|
|
5292
|
-
const mergedSynthesisUpdatedAt = mergedEntity.synthesisUpdatedAt?.trim() || void 0;
|
|
5293
|
-
if (parsed.synthesis && (!mergedEntity.synthesis || !mergedSynthesisUpdatedAt && Boolean(parsedSynthesisUpdatedAt) || Boolean(mergedSynthesisUpdatedAt) && Boolean(parsedSynthesisUpdatedAt) && compareEntityTimestamps(parsedSynthesisUpdatedAt, mergedSynthesisUpdatedAt) > 0)) {
|
|
5294
|
-
mergedEntity.synthesis = parsed.synthesis;
|
|
5295
|
-
mergedEntity.summary = parsed.synthesis;
|
|
5296
|
-
mergedEntity.synthesisUpdatedAt = parsedSynthesisUpdatedAt;
|
|
5297
|
-
mergedEntity.synthesisTimelineCount = parsed.synthesisTimelineCount;
|
|
5298
|
-
mergedEntity.synthesisStructuredFactCount = parsed.synthesisStructuredFactCount;
|
|
5299
|
-
mergedEntity.synthesisStructuredFactDigest = parsed.synthesisStructuredFactDigest;
|
|
5300
|
-
mergedEntity.synthesisVersion = parsed.synthesisVersion;
|
|
5301
|
-
} else if (!mergedEntity.summary && parsed.summary) {
|
|
5302
|
-
mergedEntity.summary = parsed.summary;
|
|
5303
|
-
mergedEntity.synthesis = parsed.summary;
|
|
5304
|
-
mergedEntity.synthesisUpdatedAt = parsedSynthesisUpdatedAt;
|
|
5305
|
-
mergedEntity.synthesisTimelineCount = parsed.synthesisTimelineCount;
|
|
5306
|
-
mergedEntity.synthesisStructuredFactCount = parsed.synthesisStructuredFactCount;
|
|
5307
|
-
mergedEntity.synthesisStructuredFactDigest = parsed.synthesisStructuredFactDigest;
|
|
5308
|
-
mergedEntity.synthesisVersion = parsed.synthesisVersion;
|
|
5309
|
-
}
|
|
5310
|
-
mergedEntity.timeline.push(...parsed.timeline);
|
|
5311
|
-
mergedEntity.relationships.push(...parsed.relationships);
|
|
5312
|
-
mergedEntity.activity.push(...parsed.activity);
|
|
5313
|
-
mergedEntity.aliases.push(...parsed.aliases);
|
|
5314
|
-
const mergedStructuredSectionMap = new Map(
|
|
5315
|
-
(mergedEntity.structuredSections ?? []).map((section) => [section.key, {
|
|
5316
|
-
...section,
|
|
5317
|
-
facts: [...section.facts]
|
|
5318
|
-
}])
|
|
5319
|
-
);
|
|
5320
|
-
for (const section of parsed.structuredSections ?? []) {
|
|
5321
|
-
const existingSection = mergedStructuredSectionMap.get(section.key);
|
|
5322
|
-
if (!existingSection) {
|
|
5323
|
-
mergedStructuredSectionMap.set(section.key, {
|
|
5324
|
-
key: section.key,
|
|
5325
|
-
title: section.title,
|
|
5326
|
-
facts: [...new Set(section.facts.map((fact) => fact.trim()).filter((fact) => fact.length > 0))]
|
|
5327
|
-
});
|
|
5328
|
-
continue;
|
|
5329
|
-
}
|
|
5330
|
-
const mergedFacts = new Set(existingSection.facts.map((fact) => fact.trim()));
|
|
5331
|
-
for (const fact of section.facts) {
|
|
5332
|
-
const trimmed = fact.trim();
|
|
5333
|
-
if (!trimmed) continue;
|
|
5334
|
-
mergedFacts.add(trimmed);
|
|
5335
|
-
}
|
|
5336
|
-
existingSection.facts = Array.from(mergedFacts);
|
|
5337
|
-
if (!existingSection.title.trim() && section.title.trim()) {
|
|
5338
|
-
existingSection.title = section.title;
|
|
5339
|
-
}
|
|
5340
|
-
}
|
|
5341
|
-
mergedEntity.structuredSections = Array.from(mergedStructuredSectionMap.values());
|
|
5342
|
-
mergedEntity.extraFrontmatterLines.push(...parsed.extraFrontmatterLines ?? []);
|
|
5343
|
-
mergedEntity.preSectionLines.push(...parsed.preSectionLines ?? []);
|
|
5344
|
-
mergedEntity.extraSections.push(...(parsed.extraSections ?? []).map((section) => ({
|
|
5345
|
-
title: section.title,
|
|
5346
|
-
lines: [...section.lines]
|
|
5347
|
-
})));
|
|
5348
|
-
} catch (err) {
|
|
5349
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5350
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
5351
|
-
}
|
|
5352
|
-
}
|
|
5353
|
-
const timelineKeys = /* @__PURE__ */ new Set();
|
|
5354
|
-
mergedEntity.timeline = mergedEntity.timeline.filter((entry) => {
|
|
5355
|
-
const key = JSON.stringify([
|
|
5356
|
-
entry.timestamp,
|
|
5357
|
-
entry.source ?? "",
|
|
5358
|
-
entry.sessionKey ?? "",
|
|
5359
|
-
entry.principal ?? "",
|
|
5360
|
-
entry.text
|
|
5361
|
-
]);
|
|
5362
|
-
if (timelineKeys.has(key)) return false;
|
|
5363
|
-
timelineKeys.add(key);
|
|
5364
|
-
return true;
|
|
5365
|
-
});
|
|
5366
|
-
const relKeys = /* @__PURE__ */ new Set();
|
|
5367
|
-
mergedEntity.relationships = mergedEntity.relationships.filter((r) => {
|
|
5368
|
-
const key = `${r.target}::${r.label}`;
|
|
5369
|
-
if (relKeys.has(key)) return false;
|
|
5370
|
-
relKeys.add(key);
|
|
5371
|
-
return true;
|
|
5372
|
-
});
|
|
5373
|
-
const actKeys = /* @__PURE__ */ new Set();
|
|
5374
|
-
mergedEntity.activity = mergedEntity.activity.filter((a) => {
|
|
5375
|
-
const key = `${a.date}::${a.note}`;
|
|
5376
|
-
if (actKeys.has(key)) return false;
|
|
5377
|
-
actKeys.add(key);
|
|
5378
|
-
return true;
|
|
5379
|
-
}).sort((a, b) => b.date.localeCompare(a.date));
|
|
5380
|
-
mergedEntity.aliases = [...new Set(mergedEntity.aliases)];
|
|
5381
|
-
mergedEntity.structuredSections = sortStructuredSectionsBySchema(
|
|
5382
|
-
mergedEntity.type,
|
|
5383
|
-
mergedEntity.structuredSections ?? [],
|
|
5384
|
-
this.entitySchemas
|
|
5385
|
-
);
|
|
5386
|
-
mergedEntity.facts = compileEntityFacts(mergedEntity.timeline, mergedEntity.structuredSections);
|
|
5387
|
-
const extraSectionKeys = /* @__PURE__ */ new Set();
|
|
5388
|
-
mergedEntity.extraSections = (mergedEntity.extraSections ?? []).filter((section) => {
|
|
5389
|
-
const key = `${section.title}::${section.lines.join("\n")}`;
|
|
5390
|
-
if (extraSectionKeys.has(key)) return false;
|
|
5391
|
-
extraSectionKeys.add(key);
|
|
5392
|
-
return true;
|
|
5393
|
-
});
|
|
5394
|
-
if (!mergedEntity.name) {
|
|
5395
|
-
const dashIdx = canonical.indexOf("-");
|
|
5396
|
-
mergedEntity.name = dashIdx !== -1 ? canonical.slice(dashIdx + 1) : canonical;
|
|
5397
|
-
}
|
|
5398
|
-
mergedEntity.created = mergedEntity.created || mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
5399
|
-
mergedEntity.updated = mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
5400
|
-
const canonicalPath = path.join(this.entitiesDir, `${canonical}.md`);
|
|
5401
|
-
await this.writeStorageSecureFile(canonicalPath, serializeEntityFile(mergedEntity, this.entitySchemas));
|
|
5402
|
-
for (const file of files) {
|
|
5403
|
-
const filePath = path.join(this.entitiesDir, file);
|
|
5404
|
-
if (filePath !== canonicalPath) {
|
|
5405
|
-
try {
|
|
5406
|
-
await unlink(filePath);
|
|
5407
|
-
merged++;
|
|
5408
|
-
log.debug(`merged entity ${file} \u2192 ${canonical}.md`);
|
|
5409
|
-
} catch {
|
|
5410
|
-
}
|
|
5411
|
-
}
|
|
5412
|
-
}
|
|
5413
|
-
}
|
|
5414
|
-
} catch (err) {
|
|
5415
|
-
if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
|
|
5416
|
-
}
|
|
5417
|
-
return merged;
|
|
5500
|
+
return this.entityStore.mergeFragmentedEntities();
|
|
5418
5501
|
}
|
|
5419
5502
|
async cleanExpiredCommitments(decayDays) {
|
|
5420
5503
|
const memories = await this.readAllMemories();
|
|
@@ -5429,7 +5512,7 @@ ${rows.join("\n")}
|
|
|
5429
5512
|
const updatedAt = new Date(m.frontmatter.updated).getTime();
|
|
5430
5513
|
if (updatedAt < cutoff) {
|
|
5431
5514
|
try {
|
|
5432
|
-
await
|
|
5515
|
+
await unlink2(m.path);
|
|
5433
5516
|
deleted.push(m);
|
|
5434
5517
|
log.debug(`cleaned expired commitment ${m.frontmatter.id}`);
|
|
5435
5518
|
} catch {
|
|
@@ -5499,7 +5582,7 @@ ${memory.content}
|
|
|
5499
5582
|
const filePaths = await this.collectActiveMemoryPaths();
|
|
5500
5583
|
const foundIds = /* @__PURE__ */ new Set();
|
|
5501
5584
|
for (const filePath of filePaths) {
|
|
5502
|
-
const basename =
|
|
5585
|
+
const basename = path2.basename(filePath, ".md");
|
|
5503
5586
|
if (wantedIds.has(basename)) {
|
|
5504
5587
|
foundIds.add(basename);
|
|
5505
5588
|
if (foundIds.size === wantedIds.size) break;
|
|
@@ -5689,14 +5772,14 @@ Reason: ${reason}`, {
|
|
|
5689
5772
|
// Memory Summarization (Phase 4A)
|
|
5690
5773
|
// ---------------------------------------------------------------------------
|
|
5691
5774
|
get summariesDir() {
|
|
5692
|
-
return
|
|
5775
|
+
return path2.join(this.baseDir, "summaries");
|
|
5693
5776
|
}
|
|
5694
5777
|
/**
|
|
5695
5778
|
* Write a memory summary.
|
|
5696
5779
|
*/
|
|
5697
5780
|
async writeSummary(summary) {
|
|
5698
5781
|
await mkdir(this.summariesDir, { recursive: true });
|
|
5699
|
-
const filePath =
|
|
5782
|
+
const filePath = path2.join(this.summariesDir, `${summary.id}.json`);
|
|
5700
5783
|
await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
|
|
5701
5784
|
log.debug(`wrote summary ${summary.id}`);
|
|
5702
5785
|
}
|
|
@@ -5705,11 +5788,11 @@ Reason: ${reason}`, {
|
|
|
5705
5788
|
*/
|
|
5706
5789
|
async readSummaries() {
|
|
5707
5790
|
try {
|
|
5708
|
-
const files = await
|
|
5791
|
+
const files = await readdir2(this.summariesDir);
|
|
5709
5792
|
const summaries = [];
|
|
5710
5793
|
for (const file of files) {
|
|
5711
5794
|
if (!file.endsWith(".json")) continue;
|
|
5712
|
-
const filePath =
|
|
5795
|
+
const filePath = path2.join(this.summariesDir, file);
|
|
5713
5796
|
const raw = await this.readStorageSecureFile(filePath);
|
|
5714
5797
|
summaries.push(JSON.parse(raw));
|
|
5715
5798
|
}
|
|
@@ -5770,7 +5853,7 @@ ${memory.content}
|
|
|
5770
5853
|
* Save topic scores to meta.json.
|
|
5771
5854
|
*/
|
|
5772
5855
|
async saveTopics(topics) {
|
|
5773
|
-
const metaPath =
|
|
5856
|
+
const metaPath = path2.join(this.stateDir, "topics.json");
|
|
5774
5857
|
await mkdir(this.stateDir, { recursive: true });
|
|
5775
5858
|
await this.writeStorageSecureFile(metaPath, JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
5776
5859
|
log.debug(`saved ${topics.length} topic scores`);
|
|
@@ -5779,7 +5862,7 @@ ${memory.content}
|
|
|
5779
5862
|
* Load topic scores from meta.json.
|
|
5780
5863
|
*/
|
|
5781
5864
|
async loadTopics() {
|
|
5782
|
-
const metaPath =
|
|
5865
|
+
const metaPath = path2.join(this.stateDir, "topics.json");
|
|
5783
5866
|
try {
|
|
5784
5867
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
5785
5868
|
return JSON.parse(raw);
|
|
@@ -5857,14 +5940,19 @@ ${memory.content}
|
|
|
5857
5940
|
};
|
|
5858
5941
|
|
|
5859
5942
|
export {
|
|
5943
|
+
selfDeps,
|
|
5860
5944
|
normalizeEntityName,
|
|
5861
5945
|
ContentHashIndex,
|
|
5862
5946
|
normalizeAttributePairs,
|
|
5947
|
+
normalizeStructuredSectionFacts,
|
|
5948
|
+
compileEntityFacts,
|
|
5863
5949
|
compareEntityTimestamps,
|
|
5950
|
+
countEntityStructuredFacts,
|
|
5864
5951
|
fingerprintEntityStructuredFacts,
|
|
5865
5952
|
isEntitySynthesisStale,
|
|
5866
5953
|
parseEntityFile,
|
|
5867
5954
|
serializeEntityFile,
|
|
5955
|
+
buildEntitySchemaCacheKey,
|
|
5868
5956
|
StorageManager
|
|
5869
5957
|
};
|
|
5870
|
-
//# sourceMappingURL=chunk-
|
|
5958
|
+
//# sourceMappingURL=chunk-XBM5DG4O.js.map
|