@remnic/core 9.51.0 → 9.52.0
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 +6 -5
- package/dist/access-admin-ops-surface.js +16 -16
- package/dist/access-authorization-probe.d.ts +6 -5
- package/dist/access-authorization-probe.js +17 -17
- package/dist/access-boundary.d.ts +6 -5
- package/dist/access-boundary.js +16 -16
- package/dist/access-cli.js +39 -39
- package/dist/access-extraction-force-flush.d.ts +6 -5
- package/dist/access-extraction-force-flush.js +16 -16
- package/dist/access-http-lcm-compaction.d.ts +6 -5
- package/dist/access-http-lifecycle-flush.d.ts +6 -5
- package/dist/access-http-offline-stream.d.ts +6 -5
- package/dist/access-http-query.js +16 -16
- package/dist/access-http.d.ts +6 -5
- package/dist/access-http.js +22 -22
- package/dist/access-identity-continuity-surface.d.ts +5 -4
- package/dist/access-identity-continuity-surface.js +16 -16
- package/dist/access-lcm-surface.d.ts +6 -5
- package/dist/access-lcm-surface.js +16 -16
- package/dist/access-mcp.d.ts +6 -5
- package/dist/access-mcp.js +20 -20
- package/dist/access-namespace-preflight.js +16 -16
- package/dist/access-observe-write-surface.d.ts +6 -5
- package/dist/access-observe-write-surface.js +16 -16
- package/dist/access-offline-manifest.d.ts +5 -4
- package/dist/access-operations-batch.js +17 -17
- package/dist/access-operations.d.ts +9 -8
- package/dist/access-operations.js +19 -19
- package/dist/access-recall-concurrency.d.ts +6 -5
- package/dist/access-recall-concurrency.js +16 -16
- package/dist/access-recall-response.d.ts +6 -5
- package/dist/access-recall-response.js +16 -16
- package/dist/access-recall-surface.d.ts +6 -5
- package/dist/access-recall-surface.js +16 -16
- package/dist/access-schema.d.ts +88 -88
- package/dist/{access-service-BY0KJ44c.d.ts → access-service-Cbk3bNbD.d.ts} +3 -3
- package/dist/access-service-helpers.d.ts +6 -5
- package/dist/access-service.d.ts +6 -5
- package/dist/access-service.js +16 -16
- package/dist/access-surface-catalog.d.ts +6 -5
- package/dist/access-wearables-meetings-surface.d.ts +2 -2
- package/dist/bootstrap.d.ts +5 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -7
- package/dist/buffer.d.ts +1 -1
- package/dist/{capsule-merge-XMFBXVDP.js → capsule-merge-HCJJMLPS.js} +4 -4
- package/dist/causal-consolidation.js +8 -8
- package/dist/{chunk-PWN32TAG.js → chunk-2ASAS3CQ.js} +2 -2
- package/dist/{chunk-WQM326GS.js → chunk-2LY6GIIE.js} +2 -2
- package/dist/{chunk-JL43BVDE.js → chunk-2OIQDTDC.js} +2 -2
- package/dist/{chunk-33SV3E43.js → chunk-3OY7KXHM.js} +3 -3
- package/dist/{chunk-AIUBBHSF.js → chunk-5UZ6UCOZ.js} +3 -3
- package/dist/chunk-5WITETWU.js +591 -0
- package/dist/chunk-5WITETWU.js.map +1 -0
- package/dist/{chunk-UT5BV5OG.js → chunk-7HXZJAID.js} +2 -2
- package/dist/{chunk-M7BXDKPR.js → chunk-7M6NYQWB.js} +3 -3
- package/dist/{chunk-TPTECFWR.js → chunk-B6LZ4IBH.js} +3 -3
- package/dist/{chunk-J5LLX6IL.js → chunk-BF6SXNQJ.js} +10 -10
- package/dist/{chunk-4PC4PRJE.js → chunk-BKIFTXHR.js} +2 -2
- package/dist/{chunk-VVG2BOKA.js → chunk-BMEYMKWX.js} +2 -2
- package/dist/{chunk-DCZBSE4F.js → chunk-D5YOZ66N.js} +18 -18
- package/dist/{chunk-BHXYAVUM.js → chunk-DLG5V6ND.js} +2 -2
- package/dist/{chunk-52UKFTUK.js → chunk-F4EU6VUC.js} +2 -2
- package/dist/{chunk-M4A44LUA.js → chunk-GSTIBXBW.js} +2 -2
- package/dist/{chunk-W5XVQFLM.js → chunk-GXFSQ6SD.js} +2 -2
- package/dist/{chunk-XADV5IZ3.js → chunk-HDIUYMSB.js} +5 -5
- package/dist/{chunk-FXTYIA46.js → chunk-J272WKX2.js} +2 -2
- package/dist/{chunk-ENTCVTQR.js → chunk-JJRVPWFY.js} +11 -1
- package/dist/chunk-JJRVPWFY.js.map +1 -0
- package/dist/{chunk-JYL3BVVZ.js → chunk-JSEJH2YQ.js} +2 -2
- package/dist/{chunk-5UDMFE3Q.js → chunk-MLXZD4WM.js} +2 -2
- package/dist/{chunk-PUSLMZBQ.js → chunk-NOEMGE56.js} +2 -2
- package/dist/{chunk-C5QLI33R.js → chunk-QINJITDS.js} +2 -2
- package/dist/{chunk-45BRYON6.js → chunk-QPOHMO6M.js} +2 -2
- package/dist/{chunk-ZQOCRIGA.js → chunk-QYN5PXDZ.js} +3 -3
- package/dist/{chunk-PA45XCUD.js → chunk-RANKJI5T.js} +2 -2
- package/dist/{chunk-ZHY5TMJG.js → chunk-RMHMUUBW.js} +2392 -628
- package/dist/chunk-RMHMUUBW.js.map +1 -0
- package/dist/{chunk-37ZO6LLJ.js → chunk-RYLEDKQ2.js} +2 -2
- package/dist/{chunk-CV6BBVYV.js → chunk-TEMSWHQD.js} +2 -2
- package/dist/{chunk-QGR6OVGT.js → chunk-VFSAWRMZ.js} +2 -2
- package/dist/{chunk-NLK2VZYZ.js → chunk-VXCNQYTC.js} +2 -2
- package/dist/{chunk-AZ2QBGYB.js → chunk-W7Y5JDVB.js} +1 -1
- package/dist/{chunk-VBHPOWBJ.js → chunk-WD5B5AIE.js} +1051 -591
- package/dist/chunk-WD5B5AIE.js.map +1 -0
- package/dist/{chunk-OYESWJ43.js → chunk-WLRD2TXF.js} +15 -10
- package/dist/chunk-WLRD2TXF.js.map +1 -0
- package/dist/{chunk-4F6X3RXW.js → chunk-X57M2Q4I.js} +2 -2
- package/dist/{chunk-6MTSFX7A.js → chunk-X7XEDOSL.js} +8 -8
- package/dist/{chunk-UEVLYVNW.js → chunk-YXKVLYXU.js} +2 -2
- package/dist/{chunk-OQAL6SIR.js → chunk-ZMSZVISA.js} +2 -2
- package/dist/{cli-CnOkH9hA.d.ts → cli-DMAFI0qt.d.ts} +3 -3
- package/dist/cli.d.ts +7 -6
- package/dist/cli.js +33 -33
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +7 -7
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/consolidation-undo.js +3 -3
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +1 -1
- package/dist/convergence-refresh.js +9 -9
- package/dist/corpus-watermark.js +9 -9
- package/dist/dependency-propagation-delivery-D63r24pH.d.ts +41 -0
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +7 -7
- package/dist/explicit-capture.d.ts +5 -4
- package/dist/explicit-capture.js +4 -4
- package/dist/external-wiki-access.d.ts +6 -5
- package/dist/external-wiki-access.js +17 -17
- package/dist/external-wiki-mcp-tools.d.ts +6 -5
- package/dist/extraction.js +7 -7
- package/dist/{forget-UJDJHG6R.js → forget-HDS4N6CG.js} +3 -3
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +687 -686
- package/dist/index.js +39 -39
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- package/dist/{maintenance-CKwxpdRx.d.ts → maintenance-kBII2qUI.d.ts} +3 -2
- package/dist/mcp-memory-inspector-app.d.ts +6 -5
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +1 -1
- package/dist/namespaces/migrate.js +8 -8
- package/dist/namespaces/storage.d.ts +1 -1
- package/dist/namespaces/storage.js +7 -7
- package/dist/operator-doctor-corpus.js +10 -10
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +15 -15
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +12 -12
- package/dist/{orchestrator-BshGam-o.d.ts → orchestrator-DHy5LtTy.d.ts} +20 -35
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +39 -39
- package/dist/page-versioning.js +3 -3
- package/dist/schemas.d.ts +138 -138
- package/dist/semantic-consolidation.js +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.js +7 -7
- package/dist/{service-BIgVM7fL.d.ts → service-PT2_z7xm.d.ts} +1 -1
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/source-agent-qualifier.js +7 -7
- package/dist/{storage-CVfGWqCI.d.ts → storage-BmVudhk4.d.ts} +46 -27
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +6 -6
- package/dist/temporal-supersession.d.ts +51 -26
- package/dist/temporal-supersession.js +3 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/types.d.ts +66 -66
- package/dist/verified-recall.js +7 -7
- package/package.json +2 -2
- package/src/lifecycle/tombstones.ts +30 -0
- package/src/orchestration/consolidation-run.ts +324 -106
- package/src/orchestration/contradiction-linking-coordinator.ts +237 -64
- package/src/orchestration/dependency-propagation-delivery.test.ts +2353 -0
- package/src/orchestration/dependency-propagation-delivery.ts +1143 -0
- package/src/orchestration/dependency-propagation-queue-state.ts +220 -0
- package/src/orchestration/dependency-propagation-recovery-replay.ts +237 -0
- package/src/orchestration/dependency-propagation-replay-reconciliation.ts +73 -0
- package/src/orchestration/dependency-propagation-triggers.test.ts +1069 -11
- package/src/orchestration/dependency-propagation.test.ts +82 -31
- package/src/orchestration/dependency-propagation.ts +55 -17
- package/src/orchestration/extraction-persist-deps.ts +2 -2
- package/src/orchestration/extraction-persist.ts +14 -15
- package/src/orchestration/extraction-queue-coordinator.ts +23 -1
- package/src/orchestration/extraction-shutdown.ts +29 -0
- package/src/orchestration/maintenance.ts +13 -7
- package/src/orchestration/turn-ingestion.ts +5 -1
- package/src/orchestrator-background-writes.test.ts +265 -1
- package/src/orchestrator-extraction-queue.test.ts +39 -0
- package/src/orchestrator.ts +59 -58
- package/src/storage/committed-invalidation.ts +34 -0
- package/src/storage/deletion-revision-store.ts +529 -0
- package/src/storage/supersession-audit.ts +41 -0
- package/src/storage/supersession-side-effects.ts +52 -25
- package/src/storage-contract/failure-semantics.test.ts +12 -0
- package/src/storage-contract/round-trip.test.ts +184 -2
- package/src/storage-deletion-revisions.test.ts +213 -1
- package/src/storage-frontmatter-cas.test.ts +141 -0
- package/src/storage-memory-lifecycle-ledger.test.ts +372 -0
- package/src/storage-replay.test.ts +149 -0
- package/src/storage.ts +345 -358
- package/src/temporal-supersession.test.ts +431 -2
- package/src/temporal-supersession.ts +567 -107
- package/src/testing/subjects/dependency-propagation.test.ts +179 -38
- package/dist/chunk-ENTCVTQR.js.map +0 -1
- package/dist/chunk-EWMTOVMY.js +0 -484
- package/dist/chunk-EWMTOVMY.js.map +0 -1
- package/dist/chunk-OYESWJ43.js.map +0 -1
- package/dist/chunk-VBHPOWBJ.js.map +0 -1
- package/dist/chunk-ZHY5TMJG.js.map +0 -1
- /package/dist/{capsule-merge-XMFBXVDP.js.map → capsule-merge-HCJJMLPS.js.map} +0 -0
- /package/dist/{chunk-PWN32TAG.js.map → chunk-2ASAS3CQ.js.map} +0 -0
- /package/dist/{chunk-WQM326GS.js.map → chunk-2LY6GIIE.js.map} +0 -0
- /package/dist/{chunk-JL43BVDE.js.map → chunk-2OIQDTDC.js.map} +0 -0
- /package/dist/{chunk-33SV3E43.js.map → chunk-3OY7KXHM.js.map} +0 -0
- /package/dist/{chunk-AIUBBHSF.js.map → chunk-5UZ6UCOZ.js.map} +0 -0
- /package/dist/{chunk-UT5BV5OG.js.map → chunk-7HXZJAID.js.map} +0 -0
- /package/dist/{chunk-M7BXDKPR.js.map → chunk-7M6NYQWB.js.map} +0 -0
- /package/dist/{chunk-TPTECFWR.js.map → chunk-B6LZ4IBH.js.map} +0 -0
- /package/dist/{chunk-J5LLX6IL.js.map → chunk-BF6SXNQJ.js.map} +0 -0
- /package/dist/{chunk-4PC4PRJE.js.map → chunk-BKIFTXHR.js.map} +0 -0
- /package/dist/{chunk-VVG2BOKA.js.map → chunk-BMEYMKWX.js.map} +0 -0
- /package/dist/{chunk-DCZBSE4F.js.map → chunk-D5YOZ66N.js.map} +0 -0
- /package/dist/{chunk-BHXYAVUM.js.map → chunk-DLG5V6ND.js.map} +0 -0
- /package/dist/{chunk-52UKFTUK.js.map → chunk-F4EU6VUC.js.map} +0 -0
- /package/dist/{chunk-M4A44LUA.js.map → chunk-GSTIBXBW.js.map} +0 -0
- /package/dist/{chunk-W5XVQFLM.js.map → chunk-GXFSQ6SD.js.map} +0 -0
- /package/dist/{chunk-XADV5IZ3.js.map → chunk-HDIUYMSB.js.map} +0 -0
- /package/dist/{chunk-FXTYIA46.js.map → chunk-J272WKX2.js.map} +0 -0
- /package/dist/{chunk-JYL3BVVZ.js.map → chunk-JSEJH2YQ.js.map} +0 -0
- /package/dist/{chunk-5UDMFE3Q.js.map → chunk-MLXZD4WM.js.map} +0 -0
- /package/dist/{chunk-PUSLMZBQ.js.map → chunk-NOEMGE56.js.map} +0 -0
- /package/dist/{chunk-C5QLI33R.js.map → chunk-QINJITDS.js.map} +0 -0
- /package/dist/{chunk-45BRYON6.js.map → chunk-QPOHMO6M.js.map} +0 -0
- /package/dist/{chunk-ZQOCRIGA.js.map → chunk-QYN5PXDZ.js.map} +0 -0
- /package/dist/{chunk-PA45XCUD.js.map → chunk-RANKJI5T.js.map} +0 -0
- /package/dist/{chunk-37ZO6LLJ.js.map → chunk-RYLEDKQ2.js.map} +0 -0
- /package/dist/{chunk-CV6BBVYV.js.map → chunk-TEMSWHQD.js.map} +0 -0
- /package/dist/{chunk-QGR6OVGT.js.map → chunk-VFSAWRMZ.js.map} +0 -0
- /package/dist/{chunk-NLK2VZYZ.js.map → chunk-VXCNQYTC.js.map} +0 -0
- /package/dist/{chunk-AZ2QBGYB.js.map → chunk-W7Y5JDVB.js.map} +0 -0
- /package/dist/{chunk-4F6X3RXW.js.map → chunk-X57M2Q4I.js.map} +0 -0
- /package/dist/{chunk-6MTSFX7A.js.map → chunk-X7XEDOSL.js.map} +0 -0
- /package/dist/{chunk-UEVLYVNW.js.map → chunk-YXKVLYXU.js.map} +0 -0
- /package/dist/{chunk-OQAL6SIR.js.map → chunk-ZMSZVISA.js.map} +0 -0
- /package/dist/{forget-UJDJHG6R.js.map → forget-HDS4N6CG.js.map} +0 -0
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
ActivityStore,
|
|
15
15
|
MaintenanceScheduler
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WLRD2TXF.js";
|
|
17
17
|
import {
|
|
18
18
|
runPeerProfileReasoner
|
|
19
19
|
} from "./chunk-I74SUMNI.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import {
|
|
33
33
|
CompoundingEngine,
|
|
34
34
|
defaultTierMigrationCycleBudget
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-7HXZJAID.js";
|
|
36
36
|
import {
|
|
37
37
|
SharedContextManager
|
|
38
38
|
} from "./chunk-M23RF3LJ.js";
|
|
@@ -190,7 +190,7 @@ import {
|
|
|
190
190
|
buildEntityRecallSection,
|
|
191
191
|
entityRecentTranscriptLookbackHours,
|
|
192
192
|
readRecentEntityTranscriptEntries
|
|
193
|
-
} from "./chunk-
|
|
193
|
+
} from "./chunk-TEMSWHQD.js";
|
|
194
194
|
import {
|
|
195
195
|
buildEventOrderRecallSection,
|
|
196
196
|
shouldRecallEventOrderEvidence
|
|
@@ -207,7 +207,7 @@ import {
|
|
|
207
207
|
} from "./chunk-YK6BDDHP.js";
|
|
208
208
|
import {
|
|
209
209
|
refreshConvergedNamespaces
|
|
210
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-GXFSQ6SD.js";
|
|
211
211
|
import {
|
|
212
212
|
formatDaySummaryMemories
|
|
213
213
|
} from "./chunk-RPSYDM6C.js";
|
|
@@ -216,7 +216,7 @@ import {
|
|
|
216
216
|
ensureBuiltInWearableConnectors,
|
|
217
217
|
getWearableConnector,
|
|
218
218
|
listWearableConnectors
|
|
219
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-D5YOZ66N.js";
|
|
220
220
|
import {
|
|
221
221
|
normalizeReplaySessionKey
|
|
222
222
|
} from "./chunk-AJVLDJU6.js";
|
|
@@ -226,14 +226,14 @@ import {
|
|
|
226
226
|
import {
|
|
227
227
|
compareVerifiedEpisodeResults,
|
|
228
228
|
searchVerifiedEpisodes
|
|
229
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-J272WKX2.js";
|
|
230
230
|
import {
|
|
231
231
|
ThreadingManager
|
|
232
232
|
} from "./chunk-OJP3N7UK.js";
|
|
233
233
|
import {
|
|
234
234
|
compareVerifiedSemanticRuleResults,
|
|
235
235
|
searchVerifiedSemanticRules
|
|
236
|
-
} from "./chunk-
|
|
236
|
+
} from "./chunk-VFSAWRMZ.js";
|
|
237
237
|
import {
|
|
238
238
|
searchWorkProductLedgerEntries
|
|
239
239
|
} from "./chunk-WT6UL3UA.js";
|
|
@@ -246,14 +246,14 @@ import {
|
|
|
246
246
|
import {
|
|
247
247
|
runNamespaceMaintenanceFanout,
|
|
248
248
|
summarizeNamespaceMaintenanceHealth
|
|
249
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-X7XEDOSL.js";
|
|
250
250
|
import {
|
|
251
251
|
NamespaceCatalog,
|
|
252
252
|
planNamespaceMaintenance
|
|
253
|
-
} from "./chunk-
|
|
253
|
+
} from "./chunk-BKIFTXHR.js";
|
|
254
254
|
import {
|
|
255
255
|
searchHarmonicRetrieval
|
|
256
|
-
} from "./chunk-
|
|
256
|
+
} from "./chunk-W7Y5JDVB.js";
|
|
257
257
|
import {
|
|
258
258
|
isAboveImportanceThreshold,
|
|
259
259
|
scoreImportance
|
|
@@ -285,7 +285,7 @@ import {
|
|
|
285
285
|
materializeAfterSemanticConsolidation,
|
|
286
286
|
parseConsolidationResponse,
|
|
287
287
|
parseOperatorAwareConsolidationResponse
|
|
288
|
-
} from "./chunk-
|
|
288
|
+
} from "./chunk-BMEYMKWX.js";
|
|
289
289
|
import {
|
|
290
290
|
BootstrapEngine
|
|
291
291
|
} from "./chunk-MYVPLJOO.js";
|
|
@@ -331,7 +331,7 @@ import {
|
|
|
331
331
|
} from "./chunk-KD3QD3A5.js";
|
|
332
332
|
import {
|
|
333
333
|
MeetingsInputError
|
|
334
|
-
} from "./chunk-
|
|
334
|
+
} from "./chunk-HDIUYMSB.js";
|
|
335
335
|
import {
|
|
336
336
|
WearablesInputError,
|
|
337
337
|
describeErrorForOperator
|
|
@@ -352,10 +352,10 @@ import {
|
|
|
352
352
|
filterRecallCandidates,
|
|
353
353
|
isGenericRecallExcludedPath,
|
|
354
354
|
isTopLevelArchivePath
|
|
355
|
-
} from "./chunk-
|
|
355
|
+
} from "./chunk-BF6SXNQJ.js";
|
|
356
356
|
import {
|
|
357
357
|
importCapsule
|
|
358
|
-
} from "./chunk-
|
|
358
|
+
} from "./chunk-QYN5PXDZ.js";
|
|
359
359
|
import {
|
|
360
360
|
isCodingGraphInstalled
|
|
361
361
|
} from "./chunk-VQ7B2Q6M.js";
|
|
@@ -396,7 +396,7 @@ import {
|
|
|
396
396
|
} from "./chunk-QDW3E4RD.js";
|
|
397
397
|
import {
|
|
398
398
|
shouldSkipImplicitExtraction
|
|
399
|
-
} from "./chunk-
|
|
399
|
+
} from "./chunk-RYLEDKQ2.js";
|
|
400
400
|
import {
|
|
401
401
|
NamespaceSearchRouter,
|
|
402
402
|
namespaceCollectionName
|
|
@@ -411,7 +411,7 @@ import {
|
|
|
411
411
|
} from "./chunk-CYEPCZN5.js";
|
|
412
412
|
import {
|
|
413
413
|
NamespaceStorageRouter
|
|
414
|
-
} from "./chunk-
|
|
414
|
+
} from "./chunk-2LY6GIIE.js";
|
|
415
415
|
import {
|
|
416
416
|
writeConversationChunks
|
|
417
417
|
} from "./chunk-72A4L5JJ.js";
|
|
@@ -423,7 +423,7 @@ import {
|
|
|
423
423
|
} from "./chunk-BIJAWZYW.js";
|
|
424
424
|
import {
|
|
425
425
|
buildChainFollowupGenerator
|
|
426
|
-
} from "./chunk-
|
|
426
|
+
} from "./chunk-F4EU6VUC.js";
|
|
427
427
|
import {
|
|
428
428
|
QmdResultResolver,
|
|
429
429
|
StorageManager,
|
|
@@ -442,7 +442,7 @@ import {
|
|
|
442
442
|
reconstructFusionInputs,
|
|
443
443
|
selfDeps,
|
|
444
444
|
serializeFusionDay
|
|
445
|
-
} from "./chunk-
|
|
445
|
+
} from "./chunk-WD5B5AIE.js";
|
|
446
446
|
import {
|
|
447
447
|
bodyIsEscaped,
|
|
448
448
|
decodeTranscriptBody,
|
|
@@ -455,7 +455,7 @@ import {
|
|
|
455
455
|
} from "./chunk-UHVXE24Y.js";
|
|
456
456
|
import {
|
|
457
457
|
buildRetiredFactTombstoneInputs
|
|
458
|
-
} from "./chunk-
|
|
458
|
+
} from "./chunk-JJRVPWFY.js";
|
|
459
459
|
import {
|
|
460
460
|
canRecallToolScopedMemory,
|
|
461
461
|
shouldPromoteGlobalFactToShared,
|
|
@@ -506,15 +506,15 @@ import {
|
|
|
506
506
|
requestEntityCanonicalIdReconcile,
|
|
507
507
|
requestEntityCanonicalIdReconcileSync,
|
|
508
508
|
withRawEntityPageMutation
|
|
509
|
-
} from "./chunk-
|
|
509
|
+
} from "./chunk-GSTIBXBW.js";
|
|
510
510
|
import {
|
|
511
511
|
applyTemporalSupersession,
|
|
512
|
+
applyTemporalSupersessionPrimaryMutation,
|
|
512
513
|
lookupAttributeByNormalizedKey,
|
|
513
514
|
normalizeSupersessionKey,
|
|
514
|
-
propagateInvalidation,
|
|
515
515
|
shouldFilterSupersededFromRecall,
|
|
516
516
|
supersessionKeysForFact
|
|
517
|
-
} from "./chunk-
|
|
517
|
+
} from "./chunk-5WITETWU.js";
|
|
518
518
|
import {
|
|
519
519
|
isValidAsOf,
|
|
520
520
|
isValidityExpiredNow
|
|
@@ -681,7 +681,16 @@ import {
|
|
|
681
681
|
throwIfAborted
|
|
682
682
|
} from "./chunk-BTTPKDIL.js";
|
|
683
683
|
import {
|
|
684
|
-
|
|
684
|
+
isRecord
|
|
685
|
+
} from "./chunk-A2IYSXDQ.js";
|
|
686
|
+
import {
|
|
687
|
+
listJsonFilesStrict,
|
|
688
|
+
withJsonStoreMutationLock,
|
|
689
|
+
writeJsonFileAtomic
|
|
690
|
+
} from "./chunk-P77QX3ID.js";
|
|
691
|
+
import {
|
|
692
|
+
isPathInsideStorageRoot,
|
|
693
|
+
resolveSafeStoragePath
|
|
685
694
|
} from "./chunk-FF4KLI5W.js";
|
|
686
695
|
import {
|
|
687
696
|
SecureStoreLockedError
|
|
@@ -700,8 +709,8 @@ import {
|
|
|
700
709
|
} from "./chunk-3WCASNQZ.js";
|
|
701
710
|
|
|
702
711
|
// src/orchestrator.ts
|
|
703
|
-
import
|
|
704
|
-
import { createHash as
|
|
712
|
+
import path20 from "path";
|
|
713
|
+
import { createHash as createHash8 } from "crypto";
|
|
705
714
|
|
|
706
715
|
// src/orchestration/tier-migration-coordinator.ts
|
|
707
716
|
import path from "path";
|
|
@@ -898,6 +907,7 @@ var ExtractionQueueCoordinator = class {
|
|
|
898
907
|
queue = [];
|
|
899
908
|
/** Whether the serial drain loop is currently running. */
|
|
900
909
|
processing = false;
|
|
910
|
+
accepting = true;
|
|
901
911
|
/** Current queue depth (idle-wait + test seam). */
|
|
902
912
|
get length() {
|
|
903
913
|
return this.queue.length;
|
|
@@ -912,6 +922,7 @@ var ExtractionQueueCoordinator = class {
|
|
|
912
922
|
* builds each task closure and hands it here.
|
|
913
923
|
*/
|
|
914
924
|
enqueue(task) {
|
|
925
|
+
if (!this.accepting) return false;
|
|
915
926
|
this.queue.push(task);
|
|
916
927
|
if (!this.processing) {
|
|
917
928
|
this.processing = true;
|
|
@@ -920,6 +931,23 @@ var ExtractionQueueCoordinator = class {
|
|
|
920
931
|
this.processing = false;
|
|
921
932
|
});
|
|
922
933
|
}
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
/** Stop new producers from entering the queue. */
|
|
937
|
+
stopAccepting() {
|
|
938
|
+
this.accepting = false;
|
|
939
|
+
}
|
|
940
|
+
/** Resume producer acceptance after an orchestrator re-initializes. */
|
|
941
|
+
resumeAccepting() {
|
|
942
|
+
this.accepting = true;
|
|
943
|
+
}
|
|
944
|
+
/** Stop new producers and wait for all accepted work to finish. */
|
|
945
|
+
async pauseAndDrain(timeoutMs = 6e4) {
|
|
946
|
+
this.stopAccepting();
|
|
947
|
+
if (await this.waitForIdle(timeoutMs)) return true;
|
|
948
|
+
log.warn("extraction queue drain exceeded timeout; waiting for accepted work to settle");
|
|
949
|
+
await this.waitForIdle(Number.POSITIVE_INFINITY);
|
|
950
|
+
return true;
|
|
923
951
|
}
|
|
924
952
|
/**
|
|
925
953
|
* Background serial queue processor.
|
|
@@ -2195,7 +2223,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
|
|
|
2195
2223
|
namespaces,
|
|
2196
2224
|
{
|
|
2197
2225
|
readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
|
|
2198
|
-
readMemoryFrontmatter: async (
|
|
2226
|
+
readMemoryFrontmatter: async (path35, preferredNamespace) => {
|
|
2199
2227
|
if (!fallbackReader) {
|
|
2200
2228
|
for (const ns of namespaces) {
|
|
2201
2229
|
try {
|
|
@@ -2207,7 +2235,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
|
|
|
2207
2235
|
}
|
|
2208
2236
|
if (!fallbackReader) return null;
|
|
2209
2237
|
const memory = await this.readQmdResultMemory(
|
|
2210
|
-
|
|
2238
|
+
path35,
|
|
2211
2239
|
fallbackReader,
|
|
2212
2240
|
namespaces,
|
|
2213
2241
|
preferredNamespace
|
|
@@ -2819,6 +2847,214 @@ async function localizeUpdateCandidates(deps, anchor, newContent, options) {
|
|
|
2819
2847
|
return merged;
|
|
2820
2848
|
}
|
|
2821
2849
|
|
|
2850
|
+
// src/orchestration/dependency-propagation.ts
|
|
2851
|
+
function isDependencyPropagationEnabled(config) {
|
|
2852
|
+
const parsed = propagationConfig(config);
|
|
2853
|
+
return isEnabled(parsed.enabled) && parsed.maxDependents > 0;
|
|
2854
|
+
}
|
|
2855
|
+
function propagationConfig(config) {
|
|
2856
|
+
return config.dependencyPropagation;
|
|
2857
|
+
}
|
|
2858
|
+
function isEnabled(value) {
|
|
2859
|
+
if (value === true) return true;
|
|
2860
|
+
if (typeof value !== "string") return false;
|
|
2861
|
+
return ["true", "1", "yes", "on"].includes(value.trim().toLowerCase());
|
|
2862
|
+
}
|
|
2863
|
+
var ACTIVE_STATUS = "active";
|
|
2864
|
+
function findDependents(memories, oldMemory, linkTypes = ["supports", "follows"]) {
|
|
2865
|
+
const allowed = new Set(linkTypes);
|
|
2866
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2867
|
+
const byId = new Map(memories.map((memory) => [memory.frontmatter.id, memory]));
|
|
2868
|
+
for (const link of oldMemory.frontmatter.links ?? []) {
|
|
2869
|
+
if (link.linkType !== "supports" && link.linkType !== "references") continue;
|
|
2870
|
+
if (!allowed.has(link.linkType)) continue;
|
|
2871
|
+
const dependent = byId.get(link.targetId);
|
|
2872
|
+
if (dependent && (dependent.frontmatter.status ?? ACTIVE_STATUS) === ACTIVE_STATUS) {
|
|
2873
|
+
ids.add(dependent.frontmatter.id);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
const oldId = oldMemory.frontmatter.id;
|
|
2877
|
+
for (const memory of memories) {
|
|
2878
|
+
if ((memory.frontmatter.status ?? ACTIVE_STATUS) !== ACTIVE_STATUS) continue;
|
|
2879
|
+
if (memory.frontmatter.id === oldId) continue;
|
|
2880
|
+
for (const link of memory.frontmatter.links ?? []) {
|
|
2881
|
+
if (link.targetId !== oldId) continue;
|
|
2882
|
+
if (link.linkType !== "follows" && link.linkType !== "references") continue;
|
|
2883
|
+
if (!allowed.has(link.linkType)) continue;
|
|
2884
|
+
ids.add(memory.frontmatter.id);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return Array.from(ids).map((id) => byId.get(id)).filter((memory) => memory !== void 0).sort((a, b) => {
|
|
2888
|
+
const left = a.frontmatter.id;
|
|
2889
|
+
const right = b.frontmatter.id;
|
|
2890
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
function emptyResult(skipped, durationMs, route = null, dependentsFound = 0) {
|
|
2894
|
+
return { dependentsFound, invalidated: 0, stillValid: 0, uncertain: 0, skipped, route, durationMs };
|
|
2895
|
+
}
|
|
2896
|
+
function logResult(event, result) {
|
|
2897
|
+
const oldId = event.oldMemory.frontmatter.id;
|
|
2898
|
+
log.info(
|
|
2899
|
+
`dependency-propagation ${JSON.stringify({
|
|
2900
|
+
cause: event.cause,
|
|
2901
|
+
oldId,
|
|
2902
|
+
dependentsFound: result.dependentsFound,
|
|
2903
|
+
invalidated: result.invalidated,
|
|
2904
|
+
stillValid: result.stillValid,
|
|
2905
|
+
uncertain: result.uncertain,
|
|
2906
|
+
skipped: result.skipped,
|
|
2907
|
+
route: result.route,
|
|
2908
|
+
durationMs: result.durationMs
|
|
2909
|
+
})}`
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
var PropagationTimeoutError = class extends Error {
|
|
2913
|
+
constructor() {
|
|
2914
|
+
super("dependency propagation timed out");
|
|
2915
|
+
this.name = "PropagationTimeoutError";
|
|
2916
|
+
}
|
|
2917
|
+
};
|
|
2918
|
+
function isTimeoutError(error, signal) {
|
|
2919
|
+
return signal.aborted || error instanceof PropagationTimeoutError || error instanceof Error && (error.name === "AbortError" || /timeout|timed out/i.test(error.message));
|
|
2920
|
+
}
|
|
2921
|
+
async function propagateInvalidation(deps, event) {
|
|
2922
|
+
const startedAt = Date.now();
|
|
2923
|
+
const config = propagationConfig(deps.config);
|
|
2924
|
+
if (!isEnabled(config.enabled)) {
|
|
2925
|
+
const result2 = emptyResult("disabled", Date.now() - startedAt);
|
|
2926
|
+
logResult(event, result2);
|
|
2927
|
+
return result2;
|
|
2928
|
+
}
|
|
2929
|
+
const maxDependents = config.maxDependents;
|
|
2930
|
+
if (maxDependents <= 0) {
|
|
2931
|
+
const result2 = emptyResult("no_dependents", Date.now() - startedAt);
|
|
2932
|
+
logResult(event, result2);
|
|
2933
|
+
return result2;
|
|
2934
|
+
}
|
|
2935
|
+
let memories;
|
|
2936
|
+
try {
|
|
2937
|
+
memories = await deps.storage.readAllMemories();
|
|
2938
|
+
} catch (error) {
|
|
2939
|
+
log.warn(`dependency propagation discovery failed for ${event.oldMemory.frontmatter.id}: ${error}`);
|
|
2940
|
+
const result2 = emptyResult("llm_error", Date.now() - startedAt);
|
|
2941
|
+
logResult(event, result2);
|
|
2942
|
+
return result2;
|
|
2943
|
+
}
|
|
2944
|
+
const discovered = findDependents(
|
|
2945
|
+
memories,
|
|
2946
|
+
event.oldMemory,
|
|
2947
|
+
config.linkTypes
|
|
2948
|
+
).filter((memory) => memory.frontmatter.id !== event.replacementId);
|
|
2949
|
+
const dependents = discovered.slice(0, Math.max(0, Math.floor(maxDependents)));
|
|
2950
|
+
if (dependents.length === 0) {
|
|
2951
|
+
const result2 = emptyResult("no_dependents", Date.now() - startedAt);
|
|
2952
|
+
logResult(event, result2);
|
|
2953
|
+
return result2;
|
|
2954
|
+
}
|
|
2955
|
+
const controller = new AbortController();
|
|
2956
|
+
const timeoutMs = config.timeoutMs;
|
|
2957
|
+
let timedOut = false;
|
|
2958
|
+
let timeout;
|
|
2959
|
+
const deadline = Number.isFinite(timeoutMs) && timeoutMs > 0 ? new Promise((_, reject) => {
|
|
2960
|
+
timeout = setTimeout(() => {
|
|
2961
|
+
timedOut = true;
|
|
2962
|
+
const error = new PropagationTimeoutError();
|
|
2963
|
+
controller.abort(error);
|
|
2964
|
+
reject(error);
|
|
2965
|
+
}, timeoutMs);
|
|
2966
|
+
}) : void 0;
|
|
2967
|
+
const revalidation = Promise.resolve().then(() => deps.extraction.revalidateDependents(
|
|
2968
|
+
{ id: event.oldMemory.frontmatter.id, content: event.oldMemory.content },
|
|
2969
|
+
event.replacementId && event.replacementContent !== null ? { id: event.replacementId, content: event.replacementContent } : null,
|
|
2970
|
+
dependents.map((memory) => ({
|
|
2971
|
+
id: memory.frontmatter.id,
|
|
2972
|
+
category: memory.frontmatter.category,
|
|
2973
|
+
content: memory.content
|
|
2974
|
+
})),
|
|
2975
|
+
controller.signal
|
|
2976
|
+
));
|
|
2977
|
+
let verdicts;
|
|
2978
|
+
try {
|
|
2979
|
+
const response = deadline ? await Promise.race([revalidation, deadline]) : await revalidation;
|
|
2980
|
+
if (timedOut || controller.signal.aborted) {
|
|
2981
|
+
const result2 = emptyResult(
|
|
2982
|
+
"timeout",
|
|
2983
|
+
Date.now() - startedAt,
|
|
2984
|
+
"fast-completion",
|
|
2985
|
+
dependents.length
|
|
2986
|
+
);
|
|
2987
|
+
logResult(event, result2);
|
|
2988
|
+
return result2;
|
|
2989
|
+
}
|
|
2990
|
+
verdicts = response.verdicts;
|
|
2991
|
+
} catch (error) {
|
|
2992
|
+
const result2 = emptyResult(
|
|
2993
|
+
timedOut || isTimeoutError(error, controller.signal) ? "timeout" : "llm_error",
|
|
2994
|
+
Date.now() - startedAt,
|
|
2995
|
+
"fast-completion",
|
|
2996
|
+
dependents.length
|
|
2997
|
+
);
|
|
2998
|
+
log.warn(`dependency propagation revalidation failed for ${event.oldMemory.frontmatter.id}: ${error}`);
|
|
2999
|
+
logResult(event, result2);
|
|
3000
|
+
return result2;
|
|
3001
|
+
} finally {
|
|
3002
|
+
clearTimeout(timeout);
|
|
3003
|
+
}
|
|
3004
|
+
let invalidated = 0;
|
|
3005
|
+
let stillValid = 0;
|
|
3006
|
+
let uncertain = 0;
|
|
3007
|
+
for (const dependent of dependents) {
|
|
3008
|
+
const verdict = verdicts.find((candidate) => candidate.memoryId === dependent.frontmatter.id);
|
|
3009
|
+
if (verdict?.verdict === "invalidated") {
|
|
3010
|
+
if (config.dryRun) {
|
|
3011
|
+
invalidated += 1;
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
try {
|
|
3015
|
+
const replacementId = event.replacementId ?? event.oldMemory.frontmatter.id;
|
|
3016
|
+
const write = () => deps.storage.supersedeMemory(
|
|
3017
|
+
dependent.frontmatter.id,
|
|
3018
|
+
replacementId,
|
|
3019
|
+
`dependency_propagation:${event.cause}`,
|
|
3020
|
+
{
|
|
3021
|
+
supersessionCause: "dependency",
|
|
3022
|
+
invalidatedBy: event.oldMemory.frontmatter.id
|
|
3023
|
+
},
|
|
3024
|
+
{
|
|
3025
|
+
requireActive: true,
|
|
3026
|
+
acceptExactReplay: true,
|
|
3027
|
+
expectedSnapshot: dependent
|
|
3028
|
+
}
|
|
3029
|
+
);
|
|
3030
|
+
const superseded = deps.writeFence ? await deps.writeFence(write) : await write();
|
|
3031
|
+
if (superseded !== true) continue;
|
|
3032
|
+
invalidated += 1;
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
log.warn(`dependency propagation write failed for ${dependent.frontmatter.id}: ${error}`);
|
|
3035
|
+
}
|
|
3036
|
+
continue;
|
|
3037
|
+
}
|
|
3038
|
+
if (verdict?.verdict === "still_valid") {
|
|
3039
|
+
stillValid += 1;
|
|
3040
|
+
} else {
|
|
3041
|
+
uncertain += 1;
|
|
3042
|
+
log.info(`dependency propagation uncertain for ${dependent.frontmatter.id}`);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
const result = {
|
|
3046
|
+
dependentsFound: dependents.length,
|
|
3047
|
+
invalidated,
|
|
3048
|
+
stillValid,
|
|
3049
|
+
uncertain,
|
|
3050
|
+
skipped: null,
|
|
3051
|
+
route: "fast-completion",
|
|
3052
|
+
durationMs: Date.now() - startedAt
|
|
3053
|
+
};
|
|
3054
|
+
logResult(event, result);
|
|
3055
|
+
return result;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2822
3058
|
// src/orchestration/contradiction-linking-coordinator.ts
|
|
2823
3059
|
var ContradictionLinkingCoordinator = class {
|
|
2824
3060
|
getConfig;
|
|
@@ -2828,6 +3064,8 @@ var ContradictionLinkingCoordinator = class {
|
|
|
2828
3064
|
namespaceFromPath;
|
|
2829
3065
|
storageForNamespace;
|
|
2830
3066
|
getExtraction;
|
|
3067
|
+
storageDirNamespace;
|
|
3068
|
+
getDependencyPropagationDelivery;
|
|
2831
3069
|
constructor(options) {
|
|
2832
3070
|
this.getConfig = options.getConfig;
|
|
2833
3071
|
this.isSearchAvailable = options.isSearchAvailable;
|
|
@@ -2836,6 +3074,10 @@ var ContradictionLinkingCoordinator = class {
|
|
|
2836
3074
|
this.namespaceFromPath = options.namespaceFromPath;
|
|
2837
3075
|
this.storageForNamespace = options.storageForNamespace;
|
|
2838
3076
|
this.getExtraction = options.getExtraction;
|
|
3077
|
+
this.storageDirNamespace = options.storageDirNamespace ?? (() => "default");
|
|
3078
|
+
this.getDependencyPropagationDelivery = options.getDependencyPropagationDelivery ?? (() => {
|
|
3079
|
+
throw new Error("dependency propagation delivery is unavailable");
|
|
3080
|
+
});
|
|
2839
3081
|
}
|
|
2840
3082
|
/**
|
|
2841
3083
|
* Check if a new memory contradicts an existing one.
|
|
@@ -3017,28 +3259,165 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3017
3259
|
return postWriteGuard ? "blocked" : "not_attempted";
|
|
3018
3260
|
}
|
|
3019
3261
|
let oldMemory = null;
|
|
3020
|
-
|
|
3262
|
+
let replacementContent = null;
|
|
3263
|
+
let propagationEvent = null;
|
|
3264
|
+
try {
|
|
3265
|
+
const [hot, cold] = await Promise.all([
|
|
3266
|
+
typeof storage.readAllMemories === "function" ? storage.readAllMemories() : Promise.resolve([]),
|
|
3267
|
+
typeof storage.readAllColdMemories === "function" ? storage.readAllColdMemories() : Promise.resolve([])
|
|
3268
|
+
]);
|
|
3269
|
+
const memories = mergeMemorySnapshots(hot, cold, storage.dir);
|
|
3270
|
+
const captured = memories.find(
|
|
3271
|
+
(memory) => memory.frontmatter.id === contradiction.supersededId && inferLocalizationMemoryStatus(memory, storage.dir) === "active"
|
|
3272
|
+
);
|
|
3273
|
+
if (captured) {
|
|
3274
|
+
oldMemory = {
|
|
3275
|
+
content: captured.content,
|
|
3276
|
+
frontmatter: {
|
|
3277
|
+
...captured.frontmatter,
|
|
3278
|
+
...captured.frontmatter.tags ? { tags: [...captured.frontmatter.tags] } : {},
|
|
3279
|
+
...captured.frontmatter.links ? { links: captured.frontmatter.links.map((link) => ({ ...link })) } : {}
|
|
3280
|
+
},
|
|
3281
|
+
path: captured.path
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
replacementContent = memories.find((memory) => memory.frontmatter.id === newMemoryId)?.content ?? (await storage.getMemoryById(newMemoryId))?.content ?? null;
|
|
3285
|
+
} catch (err) {
|
|
3286
|
+
log.warn(
|
|
3287
|
+
`contradiction propagation snapshot failed for ${contradiction.supersededId}: ${err}`
|
|
3288
|
+
);
|
|
3289
|
+
}
|
|
3290
|
+
if (oldMemory && replacementContent !== null) {
|
|
3021
3291
|
try {
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3292
|
+
propagationEvent = {
|
|
3293
|
+
oldMemory,
|
|
3294
|
+
replacementId: newMemoryId,
|
|
3295
|
+
replacementContent,
|
|
3296
|
+
cause: "contradiction",
|
|
3297
|
+
namespaceScope: this.storageDirNamespace(storage.dir)
|
|
3298
|
+
};
|
|
3299
|
+
} catch (err) {
|
|
3300
|
+
log.warn(
|
|
3301
|
+
`contradiction dependency propagation event failed for ${contradiction.supersededId}: ${err}`
|
|
3302
|
+
);
|
|
3303
|
+
}
|
|
3304
|
+
} else if (oldMemory) {
|
|
3305
|
+
log.warn(
|
|
3306
|
+
`contradiction dependency propagation skipped for ${contradiction.supersededId}: replacement content was not captured`
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
const propagationEnabled = isDependencyPropagationEnabled(config);
|
|
3310
|
+
let delivery = null;
|
|
3311
|
+
if (propagationEvent && propagationEnabled) {
|
|
3312
|
+
try {
|
|
3313
|
+
delivery = this.getDependencyPropagationDelivery();
|
|
3314
|
+
} catch (err) {
|
|
3315
|
+
log.warn(
|
|
3316
|
+
`contradiction dependency propagation delivery unavailable for ${contradiction.supersededId}: ${err}`
|
|
3317
|
+
);
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
let preparation = null;
|
|
3321
|
+
if (delivery && propagationEvent) {
|
|
3322
|
+
try {
|
|
3323
|
+
preparation = await delivery.prepare(propagationEvent);
|
|
3031
3324
|
} catch (err) {
|
|
3032
|
-
log.warn(
|
|
3325
|
+
log.warn(
|
|
3326
|
+
`contradiction dependency propagation prepare failed for ${contradiction.supersededId}: ${err}`
|
|
3327
|
+
);
|
|
3033
3328
|
}
|
|
3034
3329
|
}
|
|
3330
|
+
const supersessionCommitted = async () => {
|
|
3331
|
+
try {
|
|
3332
|
+
const current = await storage.getMemoryById(contradiction.supersededId);
|
|
3333
|
+
if (!current) return null;
|
|
3334
|
+
return current.frontmatter.status === "superseded" && current.frontmatter.supersededBy === newMemoryId;
|
|
3335
|
+
} catch (err) {
|
|
3336
|
+
log.warn(
|
|
3337
|
+
`contradiction supersession state read failed for ${contradiction.supersededId}: ${err}`
|
|
3338
|
+
);
|
|
3339
|
+
return null;
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
const settleFailedPropagation = async (commitState2) => {
|
|
3343
|
+
let token = preparation;
|
|
3344
|
+
if (commitState2 === false) {
|
|
3345
|
+
if (token !== null) {
|
|
3346
|
+
try {
|
|
3347
|
+
await delivery?.cancel(token);
|
|
3348
|
+
} catch (err) {
|
|
3349
|
+
log.warn(
|
|
3350
|
+
`contradiction dependency propagation cancel failed for ${contradiction.supersededId}: ${err}`
|
|
3351
|
+
);
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
if (commitState2 === null && token === null && delivery && propagationEvent) {
|
|
3357
|
+
try {
|
|
3358
|
+
token = await delivery.prepare(propagationEvent);
|
|
3359
|
+
} catch (err) {
|
|
3360
|
+
log.warn(
|
|
3361
|
+
`contradiction dependency propagation recovery prepare failed for ${contradiction.supersededId}: ${err}`
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
if (token !== null) {
|
|
3366
|
+
try {
|
|
3367
|
+
await delivery?.deferPrepared(token);
|
|
3368
|
+
} catch (err) {
|
|
3369
|
+
log.warn(
|
|
3370
|
+
`contradiction dependency propagation defer failed for ${contradiction.supersededId}: ${err}`
|
|
3371
|
+
);
|
|
3372
|
+
}
|
|
3373
|
+
return;
|
|
3374
|
+
}
|
|
3375
|
+
if (commitState2 === true && delivery && propagationEvent) {
|
|
3376
|
+
try {
|
|
3377
|
+
await delivery.afterMutation(null, propagationEvent);
|
|
3378
|
+
} catch (err) {
|
|
3379
|
+
log.warn(
|
|
3380
|
+
`contradiction dependency propagation fallback failed for ${contradiction.supersededId}: ${err}`
|
|
3381
|
+
);
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
};
|
|
3385
|
+
const readyPreparedPropagation = async () => {
|
|
3386
|
+
if (!propagationEvent || !propagationEnabled) return;
|
|
3387
|
+
if (!delivery) {
|
|
3388
|
+
await propagateInvalidation(
|
|
3389
|
+
{ storage, extraction: this.getExtraction(), config },
|
|
3390
|
+
propagationEvent
|
|
3391
|
+
);
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
let token = preparation;
|
|
3395
|
+
if (token === null) {
|
|
3396
|
+
try {
|
|
3397
|
+
token = await delivery.prepare(propagationEvent);
|
|
3398
|
+
} catch (err) {
|
|
3399
|
+
log.warn(
|
|
3400
|
+
`contradiction dependency propagation recovery prepare failed for ${contradiction.supersededId}: ${err}`
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
try {
|
|
3405
|
+
await delivery.afterMutation(token, propagationEvent);
|
|
3406
|
+
} catch (err) {
|
|
3407
|
+
log.warn(
|
|
3408
|
+
`contradiction dependency propagation failed for ${contradiction.supersededId}: ${err}`
|
|
3409
|
+
);
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
let commitState = false;
|
|
3035
3413
|
try {
|
|
3036
|
-
const
|
|
3414
|
+
const committed = await storage.supersedeMemory(
|
|
3037
3415
|
contradiction.supersededId,
|
|
3038
3416
|
newMemoryId,
|
|
3039
3417
|
contradiction.reason
|
|
3040
3418
|
);
|
|
3041
|
-
|
|
3419
|
+
commitState = committed ? true : await supersessionCommitted();
|
|
3420
|
+
if (!committed || commitState !== true) {
|
|
3042
3421
|
let mergedSnapshot;
|
|
3043
3422
|
try {
|
|
3044
3423
|
const [hot, cold] = await Promise.all([
|
|
@@ -3055,72 +3434,74 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3055
3434
|
log.warn(
|
|
3056
3435
|
`contradiction auto-resolve race check failed for ${contradiction.supersededId}: ${err}`
|
|
3057
3436
|
);
|
|
3437
|
+
await settleFailedPropagation(commitState);
|
|
3058
3438
|
return "supersede_failed";
|
|
3059
3439
|
}
|
|
3060
3440
|
const target = mergedSnapshot.find(
|
|
3061
3441
|
(memory) => memory.frontmatter.id === contradiction.supersededId
|
|
3062
3442
|
);
|
|
3063
|
-
if (!target)
|
|
3443
|
+
if (!target) {
|
|
3444
|
+
await settleFailedPropagation(commitState);
|
|
3445
|
+
return "supersede_failed";
|
|
3446
|
+
}
|
|
3064
3447
|
const losingMemory = mergedSnapshot.find(
|
|
3065
3448
|
(memory) => memory.frontmatter.id === newMemoryId && inferLocalizationMemoryStatus(memory, storage.dir) === "active"
|
|
3066
3449
|
);
|
|
3067
|
-
if (!losingMemory)
|
|
3450
|
+
if (!losingMemory) {
|
|
3451
|
+
await settleFailedPropagation(commitState);
|
|
3452
|
+
return "supersede_failed";
|
|
3453
|
+
}
|
|
3068
3454
|
try {
|
|
3069
3455
|
if (losingMemory.frontmatter.supersedes === contradiction.supersededId) {
|
|
3070
3456
|
const cleared = await storage.writeMemoryFrontmatter(losingMemory, {
|
|
3071
3457
|
supersedes: void 0
|
|
3072
3458
|
});
|
|
3073
|
-
if (cleared === false)
|
|
3459
|
+
if (cleared === false) {
|
|
3460
|
+
await settleFailedPropagation(commitState);
|
|
3461
|
+
return "supersedes_clear_failed";
|
|
3462
|
+
}
|
|
3074
3463
|
}
|
|
3075
3464
|
} catch (err) {
|
|
3076
3465
|
log.warn(
|
|
3077
3466
|
`contradiction auto-resolve losing supersedes clear failed for ${newMemoryId}: ${err}`
|
|
3078
3467
|
);
|
|
3468
|
+
await settleFailedPropagation(commitState);
|
|
3079
3469
|
return "supersedes_clear_failed";
|
|
3080
3470
|
}
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
await
|
|
3086
|
-
{
|
|
3087
|
-
storage,
|
|
3088
|
-
extraction: this.getExtraction(),
|
|
3089
|
-
config
|
|
3090
|
-
},
|
|
3091
|
-
{
|
|
3092
|
-
oldMemory,
|
|
3093
|
-
replacementId: newMemoryId,
|
|
3094
|
-
replacementContent: (await storage.getMemoryById(newMemoryId))?.content ?? null,
|
|
3095
|
-
cause: "contradiction",
|
|
3096
|
-
namespaceScope: this.namespaceFromPath(storage.dir)
|
|
3097
|
-
}
|
|
3098
|
-
);
|
|
3099
|
-
} catch (err) {
|
|
3100
|
-
log.warn(`contradiction dependency propagation failed for ${contradiction.supersededId}: ${err}`);
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
if (resolveIndexingCapabilities(config).queryAwareIndexing && contradiction.supersededPath) {
|
|
3104
|
-
try {
|
|
3105
|
-
await deindexMemoryAsync(
|
|
3106
|
-
config.memoryDir,
|
|
3107
|
-
contradiction.supersededPath,
|
|
3108
|
-
contradiction.supersededCreated,
|
|
3109
|
-
contradiction.supersededTags
|
|
3110
|
-
);
|
|
3111
|
-
} catch (err) {
|
|
3112
|
-
log.warn(
|
|
3113
|
-
`contradiction auto-resolve deindex failed for ${contradiction.supersededId}: ${err}`
|
|
3114
|
-
);
|
|
3471
|
+
const outcome = inferLocalizationMemoryStatus(target, storage.dir) === "active" ? "supersede_failed" : "lost_race";
|
|
3472
|
+
if (commitState === true) {
|
|
3473
|
+
await readyPreparedPropagation();
|
|
3474
|
+
} else {
|
|
3475
|
+
await settleFailedPropagation(commitState);
|
|
3115
3476
|
}
|
|
3477
|
+
return outcome;
|
|
3116
3478
|
}
|
|
3117
|
-
return "resolved";
|
|
3118
3479
|
} catch (err) {
|
|
3480
|
+
commitState = await supersessionCommitted();
|
|
3119
3481
|
log.warn(
|
|
3120
3482
|
`contradiction auto-resolve supersede failed for ${contradiction.supersededId}: ${err}`
|
|
3121
3483
|
);
|
|
3122
|
-
|
|
3484
|
+
if (commitState !== true) {
|
|
3485
|
+
await settleFailedPropagation(commitState);
|
|
3486
|
+
return "supersede_failed";
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
await readyPreparedPropagation();
|
|
3490
|
+
if (resolveIndexingCapabilities(config).queryAwareIndexing && contradiction.supersededPath) {
|
|
3491
|
+
try {
|
|
3492
|
+
await deindexMemoryAsync(
|
|
3493
|
+
config.memoryDir,
|
|
3494
|
+
contradiction.supersededPath,
|
|
3495
|
+
contradiction.supersededCreated,
|
|
3496
|
+
contradiction.supersededTags
|
|
3497
|
+
);
|
|
3498
|
+
} catch (err) {
|
|
3499
|
+
log.warn(
|
|
3500
|
+
`contradiction auto-resolve deindex failed for ${contradiction.supersededId}: ${err}`
|
|
3501
|
+
);
|
|
3502
|
+
}
|
|
3123
3503
|
}
|
|
3504
|
+
return "resolved";
|
|
3124
3505
|
}
|
|
3125
3506
|
// ---------------------------------------------------------------------------
|
|
3126
3507
|
// Memory Linking (Phase 3A)
|
|
@@ -3172,46 +3553,326 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3172
3553
|
// src/orchestration/consolidation-run.ts
|
|
3173
3554
|
import path4 from "path";
|
|
3174
3555
|
import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
|
|
3556
|
+
|
|
3557
|
+
// src/orchestration/dependency-propagation-queue-state.ts
|
|
3558
|
+
import { createHash as createHash2 } from "crypto";
|
|
3559
|
+
var JOB_STATES = [
|
|
3560
|
+
"prepared",
|
|
3561
|
+
"ready",
|
|
3562
|
+
"leased",
|
|
3563
|
+
"retryable",
|
|
3564
|
+
"completed",
|
|
3565
|
+
"dead_letter",
|
|
3566
|
+
"canceled"
|
|
3567
|
+
];
|
|
3568
|
+
var CAUSES = {
|
|
3569
|
+
contradiction: true,
|
|
3570
|
+
temporal_supersession: true,
|
|
3571
|
+
consolidation_invalidate: true,
|
|
3572
|
+
consolidation_merge: true
|
|
3573
|
+
};
|
|
3574
|
+
function isFiniteNumber(value) {
|
|
3575
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
3576
|
+
}
|
|
3577
|
+
function isPropagationEvent(value) {
|
|
3578
|
+
if (!isRecord(value) || !isRecord(value.oldMemory)) return false;
|
|
3579
|
+
const oldMemory = value.oldMemory;
|
|
3580
|
+
if (!isRecord(oldMemory.frontmatter)) return false;
|
|
3581
|
+
const frontmatter = oldMemory.frontmatter;
|
|
3582
|
+
if (typeof oldMemory.content !== "string" || typeof frontmatter.id !== "string") return false;
|
|
3583
|
+
if (!(value.replacementId === null || typeof value.replacementId === "string") || !(value.replacementContent === null || typeof value.replacementContent === "string") || typeof value.namespaceScope !== "string" || value.namespaceScope.length === 0 || typeof value.cause !== "string" || !Object.hasOwn(CAUSES, value.cause)) {
|
|
3584
|
+
return false;
|
|
3585
|
+
}
|
|
3586
|
+
const temporalMutation = value.temporalMutation;
|
|
3587
|
+
if (temporalMutation !== void 0 && (!isRecord(temporalMutation) || typeof temporalMutation.supersededAt !== "string" || temporalMutation.invalidAt !== void 0 && typeof temporalMutation.invalidAt !== "string" || !Array.isArray(temporalMutation.matchedKeys) || temporalMutation.matchedKeys.some((key) => typeof key !== "string"))) {
|
|
3588
|
+
return false;
|
|
3589
|
+
}
|
|
3590
|
+
if (frontmatter.links !== void 0) {
|
|
3591
|
+
if (!Array.isArray(frontmatter.links)) return false;
|
|
3592
|
+
for (const link of frontmatter.links) {
|
|
3593
|
+
if (!isRecord(link) || typeof link.targetId !== "string" || typeof link.linkType !== "string") return false;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
return true;
|
|
3597
|
+
}
|
|
3598
|
+
function validateJob(value, expectedState) {
|
|
3599
|
+
if (!isRecord(value)) return null;
|
|
3600
|
+
const status = value.status;
|
|
3601
|
+
const event = value.event;
|
|
3602
|
+
const attempts = value.attempts;
|
|
3603
|
+
const createdAt = value.createdAt;
|
|
3604
|
+
const updatedAt = value.updatedAt;
|
|
3605
|
+
if (typeof value.jobId !== "string" || value.jobId.length === 0 || typeof value.namespace !== "string" || value.namespace.length === 0 || typeof value.sourceId !== "string" || value.sourceId.length === 0 || !isPropagationEvent(event) || value.jobId !== eventJobId(event) || value.namespace !== event.namespaceScope || value.sourceId !== event.oldMemory.frontmatter.id || typeof status !== "string" || !JOB_STATES.includes(status) || expectedState !== void 0 && status !== expectedState || typeof attempts !== "number" || !Number.isInteger(attempts) || attempts < 0 || !isFiniteNumber(createdAt) || !isFiniteNumber(updatedAt)) {
|
|
3606
|
+
return null;
|
|
3607
|
+
}
|
|
3608
|
+
const revision = value.revision === void 0 ? 0 : value.revision;
|
|
3609
|
+
if (typeof revision !== "number" || !Number.isInteger(revision) || revision < 0) return null;
|
|
3610
|
+
const reservationIdsValue = value.reservationIds;
|
|
3611
|
+
if (!Array.isArray(reservationIdsValue)) return null;
|
|
3612
|
+
const reservationIds = reservationIdsValue;
|
|
3613
|
+
if (reservationIds.some(
|
|
3614
|
+
(reservationId) => typeof reservationId !== "string" || reservationId.trim().length === 0
|
|
3615
|
+
) || new Set(reservationIds).size !== reservationIds.length) {
|
|
3616
|
+
return null;
|
|
3617
|
+
}
|
|
3618
|
+
const reservations = value.reservations;
|
|
3619
|
+
if (typeof reservations !== "number" || !Number.isInteger(reservations) || reservations < 0 || reservations !== reservationIds.length) {
|
|
3620
|
+
return null;
|
|
3621
|
+
}
|
|
3622
|
+
const nextAttemptAt = value.nextAttemptAt;
|
|
3623
|
+
const leaseOwner = value.leaseOwner;
|
|
3624
|
+
const leaseExpiresAt = value.leaseExpiresAt;
|
|
3625
|
+
const lastError = value.lastError;
|
|
3626
|
+
const preparedReplacementFingerprint = value.preparedReplacementFingerprint;
|
|
3627
|
+
if (nextAttemptAt !== void 0 && !isFiniteNumber(nextAttemptAt)) return null;
|
|
3628
|
+
if (leaseOwner !== void 0 && typeof leaseOwner !== "string") return null;
|
|
3629
|
+
if (leaseExpiresAt !== void 0 && !isFiniteNumber(leaseExpiresAt)) return null;
|
|
3630
|
+
if (lastError !== void 0 && typeof lastError !== "string") return null;
|
|
3631
|
+
if (preparedReplacementFingerprint !== void 0 && (typeof preparedReplacementFingerprint !== "string" || preparedReplacementFingerprint.length === 0)) {
|
|
3632
|
+
return null;
|
|
3633
|
+
}
|
|
3634
|
+
if (status === "retryable" && nextAttemptAt === void 0) return null;
|
|
3635
|
+
if (status === "leased" && (typeof leaseOwner !== "string" || leaseExpiresAt === void 0)) return null;
|
|
3636
|
+
return {
|
|
3637
|
+
jobId: value.jobId,
|
|
3638
|
+
namespace: value.namespace,
|
|
3639
|
+
sourceId: value.sourceId,
|
|
3640
|
+
event,
|
|
3641
|
+
status,
|
|
3642
|
+
attempts,
|
|
3643
|
+
createdAt,
|
|
3644
|
+
updatedAt,
|
|
3645
|
+
reservations,
|
|
3646
|
+
reservationIds: [...reservationIds],
|
|
3647
|
+
revision,
|
|
3648
|
+
...nextAttemptAt === void 0 ? {} : { nextAttemptAt },
|
|
3649
|
+
...leaseOwner === void 0 ? {} : { leaseOwner },
|
|
3650
|
+
...leaseExpiresAt === void 0 ? {} : { leaseExpiresAt },
|
|
3651
|
+
...lastError === void 0 ? {} : { lastError },
|
|
3652
|
+
...preparedReplacementFingerprint === void 0 ? {} : { preparedReplacementFingerprint }
|
|
3653
|
+
};
|
|
3654
|
+
}
|
|
3655
|
+
function canonicalize(value) {
|
|
3656
|
+
if (Array.isArray(value)) return value.map((item) => canonicalize(item));
|
|
3657
|
+
if (isRecord(value)) {
|
|
3658
|
+
const result = {};
|
|
3659
|
+
for (const key of Object.keys(value).sort()) {
|
|
3660
|
+
if (value[key] === void 0) continue;
|
|
3661
|
+
result[key] = canonicalize(value[key]);
|
|
3662
|
+
}
|
|
3663
|
+
return result;
|
|
3664
|
+
}
|
|
3665
|
+
return value === void 0 ? null : value;
|
|
3666
|
+
}
|
|
3667
|
+
function compareByteStable(left, right) {
|
|
3668
|
+
return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
|
|
3669
|
+
}
|
|
3670
|
+
function canonicalEvent(event) {
|
|
3671
|
+
return canonicalize(event);
|
|
3672
|
+
}
|
|
3673
|
+
function stableLinks(links) {
|
|
3674
|
+
if (!Array.isArray(links)) return links;
|
|
3675
|
+
return [...links].sort((left, right) => {
|
|
3676
|
+
const leftKey = JSON.stringify(canonicalize(left));
|
|
3677
|
+
const rightKey = JSON.stringify(canonicalize(right));
|
|
3678
|
+
return compareByteStable(leftKey, rightKey);
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
function matchesPreparedSource(current, prepared) {
|
|
3682
|
+
const { accessCount: _currentAccessCount, lastAccessed: _currentLastAccessed, ...currentSemantic } = current.frontmatter;
|
|
3683
|
+
const { accessCount: _preparedAccessCount, lastAccessed: _preparedLastAccessed, ...preparedSemantic } = prepared.frontmatter;
|
|
3684
|
+
const normalizedCurrent = { ...currentSemantic, links: stableLinks(currentSemantic.links) };
|
|
3685
|
+
const normalizedPrepared = { ...preparedSemantic, links: stableLinks(preparedSemantic.links) };
|
|
3686
|
+
return current.content === prepared.content && JSON.stringify(canonicalize(normalizedCurrent)) === JSON.stringify(canonicalize(normalizedPrepared));
|
|
3687
|
+
}
|
|
3688
|
+
function eventJobId(event) {
|
|
3689
|
+
const { accessCount: _accessCount, lastAccessed: _lastAccessed, links, ...semanticFrontmatter } = event.oldMemory.frontmatter;
|
|
3690
|
+
const semanticEvent = {
|
|
3691
|
+
...event,
|
|
3692
|
+
oldMemory: {
|
|
3693
|
+
...event.oldMemory,
|
|
3694
|
+
frontmatter: { ...semanticFrontmatter, links: stableLinks(links) }
|
|
3695
|
+
}
|
|
3696
|
+
};
|
|
3697
|
+
return createHash2("sha256").update(JSON.stringify(canonicalize(semanticEvent))).digest("hex");
|
|
3698
|
+
}
|
|
3699
|
+
function isNotFound(error) {
|
|
3700
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
// src/orchestration/consolidation-run.ts
|
|
3175
3704
|
var MAX_HARMONIC_NAMESPACE_STORES = 50;
|
|
3176
3705
|
var HARMONIC_CATALOG_CURSOR_FILE = "harmonic-catalog-cursor.json";
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3706
|
+
async function prepareDependencyPropagation(getDelivery, event, enabled) {
|
|
3707
|
+
if (!enabled) return null;
|
|
3708
|
+
let delivery;
|
|
3709
|
+
try {
|
|
3710
|
+
delivery = getDelivery();
|
|
3711
|
+
} catch (error) {
|
|
3712
|
+
log.warn(`consolidation dependency propagation getter failed: ${error}`);
|
|
3713
|
+
return null;
|
|
3180
3714
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3715
|
+
let preparation = null;
|
|
3716
|
+
try {
|
|
3717
|
+
preparation = await delivery.prepare(event);
|
|
3718
|
+
} catch (error) {
|
|
3719
|
+
log.warn(`consolidation dependency propagation preparation failed: ${error}`);
|
|
3720
|
+
}
|
|
3721
|
+
return { delivery, event, preparation };
|
|
3722
|
+
}
|
|
3723
|
+
function clonePropagationMemory(memory) {
|
|
3724
|
+
return structuredClone(memory);
|
|
3725
|
+
}
|
|
3726
|
+
function sameMemorySnapshot(a, b) {
|
|
3727
|
+
return a.content === b.content && JSON.stringify(canonicalize(a.frontmatter)) === JSON.stringify(canonicalize(b.frontmatter));
|
|
3728
|
+
}
|
|
3729
|
+
async function prepareCurrentDependencyPropagation(storage, getDelivery, sourceId, buildEvent, enabled) {
|
|
3730
|
+
const loaded = await storage.getMemoryById(sourceId);
|
|
3731
|
+
if (!loaded) return null;
|
|
3732
|
+
let source = clonePropagationMemory(loaded);
|
|
3733
|
+
let pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
|
|
3734
|
+
if (!pending) return { source, pending: null };
|
|
3735
|
+
let latest;
|
|
3736
|
+
try {
|
|
3737
|
+
latest = await storage.getMemoryById(sourceId);
|
|
3738
|
+
} catch (error) {
|
|
3739
|
+
await cancelDependencyPropagation(pending);
|
|
3740
|
+
throw error;
|
|
3741
|
+
}
|
|
3742
|
+
if (!latest) {
|
|
3743
|
+
await cancelDependencyPropagation(pending);
|
|
3744
|
+
return null;
|
|
3745
|
+
}
|
|
3746
|
+
if (!sameMemorySnapshot(source, latest)) {
|
|
3747
|
+
await cancelDependencyPropagation(pending);
|
|
3748
|
+
source = clonePropagationMemory(latest);
|
|
3749
|
+
pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
|
|
3750
|
+
}
|
|
3751
|
+
return { source, pending };
|
|
3752
|
+
}
|
|
3753
|
+
async function cancelDependencyPropagation(pending) {
|
|
3754
|
+
if (!pending.preparation?.ownsPreparedJob) return;
|
|
3755
|
+
try {
|
|
3756
|
+
await pending.delivery.cancel(pending.preparation);
|
|
3757
|
+
} catch (error) {
|
|
3758
|
+
log.warn(`consolidation dependency propagation cancellation failed: ${error}`);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
async function deferDependencyPropagation(pending) {
|
|
3762
|
+
if (!pending) return;
|
|
3763
|
+
let token = pending.preparation;
|
|
3764
|
+
if (token === null) {
|
|
3184
3765
|
try {
|
|
3185
|
-
|
|
3186
|
-
const parsed = JSON.parse(raw);
|
|
3187
|
-
return typeof parsed.nextStorageDir === "string" && parsed.nextStorageDir.length > 0 ? path4.resolve(parsed.nextStorageDir) : void 0;
|
|
3766
|
+
token = await pending.delivery.prepare(pending.event);
|
|
3188
3767
|
} catch (error) {
|
|
3189
|
-
|
|
3190
|
-
log.warn(
|
|
3191
|
-
`harmonic namespace catalog cursor read failed open: ${error instanceof Error ? error.message : String(error)}`
|
|
3192
|
-
);
|
|
3193
|
-
}
|
|
3194
|
-
return void 0;
|
|
3768
|
+
log.warn(`consolidation dependency propagation recovery preparation failed: ${error}`);
|
|
3195
3769
|
}
|
|
3196
3770
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
await
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
JSON.stringify({ nextStorageDir }, null, 2),
|
|
3203
|
-
"utf-8"
|
|
3204
|
-
);
|
|
3771
|
+
if (token === null) return;
|
|
3772
|
+
try {
|
|
3773
|
+
await pending.delivery.deferPrepared(token);
|
|
3774
|
+
} catch (error) {
|
|
3775
|
+
log.warn(`consolidation dependency propagation defer failed: ${error}`);
|
|
3205
3776
|
}
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3777
|
+
}
|
|
3778
|
+
async function afterDependencyPropagationMutation(pending) {
|
|
3779
|
+
try {
|
|
3780
|
+
await pending.delivery.afterMutation(pending.preparation, pending.event);
|
|
3781
|
+
} catch (error) {
|
|
3782
|
+
log.warn(`consolidation dependency propagation delivery failed: ${error}`);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
async function classifyInvalidation(storage, memoryId, expectedSnapshot, options = {}) {
|
|
3786
|
+
if (options.checkInvalidationProof !== false && expectedSnapshot) {
|
|
3787
|
+
if (!storage.hasCommittedInvalidation) {
|
|
3788
|
+
log.warn(`consolidation invalidation proof capability missing for ${memoryId}`);
|
|
3789
|
+
} else {
|
|
3790
|
+
try {
|
|
3791
|
+
if (await storage.hasCommittedInvalidation(expectedSnapshot)) return "committed";
|
|
3792
|
+
} catch (error) {
|
|
3793
|
+
log.warn(`consolidation invalidation proof read failed for ${memoryId}: ${error}`);
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
try {
|
|
3798
|
+
const current = await storage.getMemoryById(memoryId);
|
|
3799
|
+
if (!current) {
|
|
3800
|
+
if (options.missingMeansCommitted === false) return "not-committed";
|
|
3801
|
+
if (expectedSnapshot && !storage.hasCommittedInvalidation) return "unknown";
|
|
3802
|
+
return "committed";
|
|
3803
|
+
}
|
|
3804
|
+
if (options.matchesCommitted?.(current)) return "committed";
|
|
3805
|
+
if (expectedSnapshot && sameMemorySnapshot(current, expectedSnapshot)) {
|
|
3806
|
+
return "not-committed";
|
|
3807
|
+
}
|
|
3808
|
+
return "unknown";
|
|
3809
|
+
} catch (error) {
|
|
3810
|
+
log.warn(`consolidation invalidation state read failed for ${memoryId}: ${error}`);
|
|
3811
|
+
return "unknown";
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
async function settleFailedConsolidationPropagation(pending, commitState, memoryId) {
|
|
3815
|
+
if (!pending) return;
|
|
3816
|
+
let token = pending.preparation;
|
|
3817
|
+
if (commitState === "not-committed") {
|
|
3818
|
+
if (token !== null) await cancelDependencyPropagation({ ...pending, preparation: token });
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3821
|
+
if (token === null && (commitState === "committed" || commitState === "unknown")) {
|
|
3822
|
+
try {
|
|
3823
|
+
token = await pending.delivery.prepare(pending.event);
|
|
3824
|
+
} catch (error) {
|
|
3825
|
+
log.warn(
|
|
3826
|
+
`consolidation dependency propagation recovery preparation failed for ${memoryId}: ${error}`
|
|
3827
|
+
);
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
if (token !== null) {
|
|
3831
|
+
try {
|
|
3832
|
+
await pending.delivery.deferPrepared(token);
|
|
3833
|
+
} catch (error) {
|
|
3834
|
+
log.warn(`consolidation dependency propagation defer failed for ${memoryId}: ${error}`);
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
var ConsolidationRunCoordinator = class {
|
|
3839
|
+
constructor(deps) {
|
|
3840
|
+
this.deps = deps;
|
|
3841
|
+
}
|
|
3842
|
+
deps;
|
|
3843
|
+
async readHarmonicCatalogCursor(defaultDir) {
|
|
3844
|
+
const cursorPath = path4.join(defaultDir, "state", HARMONIC_CATALOG_CURSOR_FILE);
|
|
3845
|
+
try {
|
|
3846
|
+
const raw = await readFile(cursorPath, "utf-8");
|
|
3847
|
+
const parsed = JSON.parse(raw);
|
|
3848
|
+
return typeof parsed.nextStorageDir === "string" && parsed.nextStorageDir.length > 0 ? path4.resolve(parsed.nextStorageDir) : void 0;
|
|
3849
|
+
} catch (error) {
|
|
3850
|
+
if (error.code !== "ENOENT") {
|
|
3851
|
+
log.warn(
|
|
3852
|
+
`harmonic namespace catalog cursor read failed open: ${error instanceof Error ? error.message : String(error)}`
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3855
|
+
return void 0;
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
async writeHarmonicCatalogCursor(defaultDir, nextStorageDir) {
|
|
3859
|
+
const stateDir = path4.join(defaultDir, "state");
|
|
3860
|
+
await mkdir2(stateDir, { recursive: true });
|
|
3861
|
+
await writeFile2(
|
|
3862
|
+
path4.join(stateDir, HARMONIC_CATALOG_CURSOR_FILE),
|
|
3863
|
+
JSON.stringify({ nextStorageDir }, null, 2),
|
|
3864
|
+
"utf-8"
|
|
3865
|
+
);
|
|
3866
|
+
}
|
|
3867
|
+
async pruneHarmonicStores(storage) {
|
|
3868
|
+
const config = this.deps.config;
|
|
3869
|
+
if (!resolveCapabilities(config).harmonicRetrieval || !resolveConsolidationCapabilities(config).abstractionAnchors) {
|
|
3870
|
+
return;
|
|
3871
|
+
}
|
|
3872
|
+
const stores = /* @__PURE__ */ new Map();
|
|
3873
|
+
const defaultDir = path4.resolve(storage.dir);
|
|
3874
|
+
stores.set(defaultDir, {
|
|
3875
|
+
memoryDir: storage.dir,
|
|
3215
3876
|
abstractionNodeStoreDir: config.abstractionNodeStoreDir
|
|
3216
3877
|
});
|
|
3217
3878
|
try {
|
|
@@ -3297,49 +3958,65 @@ var ConsolidationRunCoordinator = class {
|
|
|
3297
3958
|
);
|
|
3298
3959
|
const profile = await storage.readProfile();
|
|
3299
3960
|
const result = await this.deps.getExtraction().consolidate(recent, older, profile);
|
|
3300
|
-
const memoryLookup = resolveIndexingCapabilities(config).queryAwareIndexing ? new Map(allMemories.map((m) => [m.frontmatter.id, m])) : null;
|
|
3301
3961
|
const itemsDeindexBatch = [];
|
|
3302
3962
|
try {
|
|
3303
3963
|
for (const item of result.items) {
|
|
3304
3964
|
switch (item.action) {
|
|
3305
3965
|
case "INVALIDATE": {
|
|
3306
|
-
const
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3966
|
+
const propagationEnabled = config.dependencyPropagation.enabled && config.dependencyPropagation.maxDependents > 0;
|
|
3967
|
+
const prepared = await prepareCurrentDependencyPropagation(
|
|
3968
|
+
storage,
|
|
3969
|
+
this.deps.getDependencyPropagationDelivery,
|
|
3970
|
+
item.existingId,
|
|
3971
|
+
(propagationOld2) => ({
|
|
3972
|
+
oldMemory: propagationOld2,
|
|
3973
|
+
replacementId: null,
|
|
3974
|
+
replacementContent: null,
|
|
3975
|
+
cause: "consolidation_invalidate",
|
|
3976
|
+
namespaceScope: this.deps.storageDirNamespace(storage.dir)
|
|
3977
|
+
}),
|
|
3978
|
+
propagationEnabled
|
|
3309
3979
|
);
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3980
|
+
const propagationOld = prepared?.source ?? null;
|
|
3981
|
+
const pending = prepared?.pending ?? null;
|
|
3982
|
+
const expectedSnapshot = pending ? propagationOld : null;
|
|
3983
|
+
const toInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? propagationOld : null;
|
|
3984
|
+
const recordCommitProof = pending?.preparation?.ownsPreparedJob === true;
|
|
3985
|
+
let didInvalidate;
|
|
3986
|
+
try {
|
|
3987
|
+
didInvalidate = await storage.invalidateMemory(
|
|
3988
|
+
item.existingId,
|
|
3989
|
+
expectedSnapshot ?? void 0,
|
|
3990
|
+
{ recordCommitProof }
|
|
3991
|
+
);
|
|
3992
|
+
} catch (error) {
|
|
3993
|
+
const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
|
|
3994
|
+
await settleFailedConsolidationPropagation(
|
|
3995
|
+
pending,
|
|
3996
|
+
commitState,
|
|
3997
|
+
item.existingId
|
|
3998
|
+
);
|
|
3999
|
+
throw error;
|
|
4000
|
+
}
|
|
4001
|
+
if (!didInvalidate) {
|
|
4002
|
+
const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
|
|
4003
|
+
await settleFailedConsolidationPropagation(
|
|
4004
|
+
pending,
|
|
4005
|
+
commitState,
|
|
4006
|
+
item.existingId
|
|
4007
|
+
);
|
|
4008
|
+
break;
|
|
4009
|
+
}
|
|
4010
|
+
invalidated += 1;
|
|
4011
|
+
memoryItemMutated = true;
|
|
4012
|
+
if (pending) await afterDependencyPropagationMutation(pending);
|
|
4013
|
+
await this.deps.embeddingFallback.removeFromIndex(item.existingId);
|
|
4014
|
+
if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
|
|
4015
|
+
itemsDeindexBatch.push({
|
|
4016
|
+
path: toInvalidate.path,
|
|
4017
|
+
createdAt: toInvalidate.frontmatter.created,
|
|
4018
|
+
tags: toInvalidate.frontmatter.tags ?? []
|
|
4019
|
+
});
|
|
3343
4020
|
}
|
|
3344
4021
|
break;
|
|
3345
4022
|
}
|
|
@@ -3358,53 +4035,81 @@ var ConsolidationRunCoordinator = class {
|
|
|
3358
4035
|
break;
|
|
3359
4036
|
case "MERGE":
|
|
3360
4037
|
if (item.updatedContent && item.mergeWith) {
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
4038
|
+
const updatedContent = item.updatedContent;
|
|
4039
|
+
const mergeWith = item.mergeWith;
|
|
4040
|
+
const propagationEnabled = config.dependencyPropagation.enabled && config.dependencyPropagation.maxDependents > 0;
|
|
4041
|
+
const prepared = await prepareCurrentDependencyPropagation(
|
|
4042
|
+
storage,
|
|
4043
|
+
this.deps.getDependencyPropagationDelivery,
|
|
4044
|
+
item.mergeWith,
|
|
4045
|
+
(propagationOld2) => ({
|
|
4046
|
+
oldMemory: propagationOld2,
|
|
4047
|
+
replacementId: item.existingId,
|
|
4048
|
+
replacementContent: updatedContent,
|
|
4049
|
+
cause: "consolidation_merge",
|
|
4050
|
+
namespaceScope: this.deps.storageDirNamespace(storage.dir)
|
|
4051
|
+
}),
|
|
4052
|
+
propagationEnabled
|
|
3368
4053
|
);
|
|
4054
|
+
const propagationOld = prepared?.source ?? null;
|
|
4055
|
+
const pending = prepared?.pending ?? null;
|
|
4056
|
+
const expectedSnapshot = pending ? propagationOld : null;
|
|
4057
|
+
const toMergeInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? propagationOld : null;
|
|
4058
|
+
let didUpdate;
|
|
4059
|
+
try {
|
|
4060
|
+
didUpdate = await storage.updateMemory(
|
|
4061
|
+
item.existingId,
|
|
4062
|
+
updatedContent,
|
|
4063
|
+
{
|
|
4064
|
+
supersedes: item.mergeWith,
|
|
4065
|
+
lineage: [item.existingId, item.mergeWith]
|
|
4066
|
+
}
|
|
4067
|
+
);
|
|
4068
|
+
} catch (error) {
|
|
4069
|
+
await deferDependencyPropagation(pending);
|
|
4070
|
+
throw error;
|
|
4071
|
+
}
|
|
4072
|
+
if (!didUpdate) {
|
|
4073
|
+
if (pending) await cancelDependencyPropagation(pending);
|
|
4074
|
+
break;
|
|
4075
|
+
}
|
|
3369
4076
|
memoryItemMutated = true;
|
|
3370
4077
|
await this.deps.indexPersistedMemory(storage, item.existingId);
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
});
|
|
3407
|
-
}
|
|
4078
|
+
let didInvalidate;
|
|
4079
|
+
try {
|
|
4080
|
+
didInvalidate = await storage.invalidateMemory(
|
|
4081
|
+
item.mergeWith,
|
|
4082
|
+
expectedSnapshot ?? void 0,
|
|
4083
|
+
{ recordCommitProof: pending?.preparation?.ownsPreparedJob === true }
|
|
4084
|
+
);
|
|
4085
|
+
} catch (error) {
|
|
4086
|
+
const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
|
|
4087
|
+
await settleFailedConsolidationPropagation(
|
|
4088
|
+
pending,
|
|
4089
|
+
commitState,
|
|
4090
|
+
item.mergeWith
|
|
4091
|
+
);
|
|
4092
|
+
throw error;
|
|
4093
|
+
}
|
|
4094
|
+
if (!didInvalidate) {
|
|
4095
|
+
const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
|
|
4096
|
+
await settleFailedConsolidationPropagation(
|
|
4097
|
+
pending,
|
|
4098
|
+
commitState,
|
|
4099
|
+
item.mergeWith
|
|
4100
|
+
);
|
|
4101
|
+
break;
|
|
4102
|
+
}
|
|
4103
|
+
invalidated += 1;
|
|
4104
|
+
merged += 1;
|
|
4105
|
+
if (pending) await afterDependencyPropagationMutation(pending);
|
|
4106
|
+
await this.deps.embeddingFallback.removeFromIndex(item.mergeWith);
|
|
4107
|
+
if (toMergeInvalidate?.path && toMergeInvalidate.frontmatter?.created) {
|
|
4108
|
+
itemsDeindexBatch.push({
|
|
4109
|
+
path: toMergeInvalidate.path,
|
|
4110
|
+
createdAt: toMergeInvalidate.frontmatter.created,
|
|
4111
|
+
tags: toMergeInvalidate.frontmatter.tags ?? []
|
|
4112
|
+
});
|
|
3408
4113
|
}
|
|
3409
4114
|
}
|
|
3410
4115
|
break;
|
|
@@ -4028,6 +4733,10 @@ var ExtractionPersistCoordinator = class {
|
|
|
4028
4733
|
get config() {
|
|
4029
4734
|
return this.deps.config;
|
|
4030
4735
|
}
|
|
4736
|
+
get dependencyPropagationDelivery() {
|
|
4737
|
+
if (!isDependencyPropagationEnabled(this.config)) return void 0;
|
|
4738
|
+
return this.deps.getDependencyPropagationDelivery();
|
|
4739
|
+
}
|
|
4031
4740
|
async persistExtraction(result, storage, threadIdForExtraction, sourceContext, baseNamespace, scopeProfileWritePlan, sourceText, graphCaps = resolveGraphConstructionCapabilities(this.deps.config), lifecycleCaps = resolveMemoryLifecycleCapabilities(this.deps.config)) {
|
|
4032
4741
|
const citationEnabled = resolvePipelineProcessingCapabilities(this.deps.config).inlineSourceAttribution === true;
|
|
4033
4742
|
const citationTemplate = this.deps.config.inlineSourceAttributionFormat;
|
|
@@ -4238,8 +4947,7 @@ var ExtractionPersistCoordinator = class {
|
|
|
4238
4947
|
structuredAttributes: { ...targetSurvivingAttrs },
|
|
4239
4948
|
createdAt: supersessionOrderingAt(options.validAt),
|
|
4240
4949
|
enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
|
|
4241
|
-
|
|
4242
|
-
config: this.config,
|
|
4950
|
+
dependencyPropagationDelivery: this.dependencyPropagationDelivery,
|
|
4243
4951
|
namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
|
|
4244
4952
|
});
|
|
4245
4953
|
} catch (profileSupersessionErr) {
|
|
@@ -4380,10 +5088,9 @@ var ExtractionPersistCoordinator = class {
|
|
|
4380
5088
|
entityRef: sharedPromotionEnvelope.entityRef,
|
|
4381
5089
|
structuredAttributes: { ...sharedSurvivingAttrs },
|
|
4382
5090
|
createdAt: supersessionOrderingAt(options.validAt),
|
|
4383
|
-
enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
|
|
4384
5091
|
useCallerTimestamp: true,
|
|
4385
|
-
|
|
4386
|
-
|
|
5092
|
+
enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
|
|
5093
|
+
dependencyPropagationDelivery: this.dependencyPropagationDelivery,
|
|
4387
5094
|
namespaceScope: this.deps.storageDirNamespace(sharedStorage.dir)
|
|
4388
5095
|
});
|
|
4389
5096
|
if (hashDedupSupersession.supersededIds.length > 0) {
|
|
@@ -4455,8 +5162,7 @@ var ExtractionPersistCoordinator = class {
|
|
|
4455
5162
|
structuredAttributes: { ...sharedSurvivingAttrs },
|
|
4456
5163
|
createdAt: supersessionOrderingAt(options.validAt),
|
|
4457
5164
|
enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
|
|
4458
|
-
|
|
4459
|
-
config: this.config,
|
|
5165
|
+
dependencyPropagationDelivery: this.dependencyPropagationDelivery,
|
|
4460
5166
|
namespaceScope: this.deps.storageDirNamespace(sharedStorage.dir)
|
|
4461
5167
|
});
|
|
4462
5168
|
} catch (sharedSupersessionErr) {
|
|
@@ -5280,13 +5986,12 @@ var ExtractionPersistCoordinator = class {
|
|
|
5280
5986
|
newMemoryId: parentId,
|
|
5281
5987
|
entityRef: parentWriteEnvelope.entityRef,
|
|
5282
5988
|
structuredAttributes: parentWriteEnvelope.rawStructuredAttributes ? { ...parentWriteEnvelope.rawStructuredAttributes } : void 0,
|
|
5283
|
-
createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
|
|
5284
5989
|
// #1578 r3: an extracted end-only bound (validFrom absent) is
|
|
5285
5990
|
// historical, not a new authoritative state — never let it
|
|
5286
5991
|
// supersede a later active fact (codex P1 on :15534).
|
|
5992
|
+
createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
|
|
5287
5993
|
enabled: lifecycleCaps.temporalSupersession && !(biTemporal && !biTemporal.validFrom),
|
|
5288
|
-
|
|
5289
|
-
config: this.config,
|
|
5994
|
+
dependencyPropagationDelivery: this.dependencyPropagationDelivery,
|
|
5290
5995
|
namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
|
|
5291
5996
|
});
|
|
5292
5997
|
for (const supersededId of temporalSupersession.supersededIds) {
|
|
@@ -5486,8 +6191,7 @@ var ExtractionPersistCoordinator = class {
|
|
|
5486
6191
|
structuredAttributes: factWriteEnvelope.rawStructuredAttributes ? { ...factWriteEnvelope.rawStructuredAttributes } : void 0,
|
|
5487
6192
|
createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
|
|
5488
6193
|
enabled: lifecycleCaps.temporalSupersession && !(biTemporal && !biTemporal.validFrom),
|
|
5489
|
-
|
|
5490
|
-
config: this.config,
|
|
6194
|
+
dependencyPropagationDelivery: this.dependencyPropagationDelivery,
|
|
5491
6195
|
namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
|
|
5492
6196
|
});
|
|
5493
6197
|
for (const supersededId of temporalSupersession.supersededIds) {
|
|
@@ -5685,84 +6389,1127 @@ var ExtractionPersistCoordinator = class {
|
|
|
5685
6389
|
}
|
|
5686
6390
|
}
|
|
5687
6391
|
}
|
|
5688
|
-
if (profileUpdates.length > 0) {
|
|
5689
|
-
await storage.appendToProfile(profileUpdates);
|
|
5690
|
-
recordDurableNonFactWrite();
|
|
6392
|
+
if (profileUpdates.length > 0) {
|
|
6393
|
+
await storage.appendToProfile(profileUpdates);
|
|
6394
|
+
recordDurableNonFactWrite();
|
|
6395
|
+
}
|
|
6396
|
+
for (const q of questions) {
|
|
6397
|
+
const id = await storage.writeQuestion(q.question, q.context, q.priority);
|
|
6398
|
+
if (id) {
|
|
6399
|
+
trackPersistedId(storage, id);
|
|
6400
|
+
recordDurableNonFactWrite();
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
if (resolveRecallEnhancementCapabilities(this.deps.config).identity && result.identityReflection) {
|
|
6404
|
+
try {
|
|
6405
|
+
await storage.appendIdentityReflection(result.identityReflection);
|
|
6406
|
+
recordDurableNonFactWrite();
|
|
6407
|
+
} catch (err) {
|
|
6408
|
+
log.debug(`identity reflection write failed: ${err}`);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
if (durableNonFactWritten) {
|
|
6412
|
+
touchBaseNonFactNamespace();
|
|
6413
|
+
}
|
|
6414
|
+
await this.deps.saveContentHashIndexes().catch((err) => {
|
|
6415
|
+
log.warn(`content-hash index save failed: ${err}`);
|
|
6416
|
+
});
|
|
6417
|
+
for (const {
|
|
6418
|
+
storage: targetStorage,
|
|
6419
|
+
events
|
|
6420
|
+
} of behaviorSignalsByStorage.values()) {
|
|
6421
|
+
const dedupedSignals = dedupeBehaviorSignalsByMemoryAndHash(events);
|
|
6422
|
+
if (dedupedSignals.length === 0) continue;
|
|
6423
|
+
await targetStorage.appendBehaviorSignals(dedupedSignals).catch(
|
|
6424
|
+
(err) => log.warn(`appendBehaviorSignals failed (non-fatal): ${err}`)
|
|
6425
|
+
);
|
|
6426
|
+
}
|
|
6427
|
+
const dedupSuffix = dedupedCount > 0 ? ` (${dedupedCount} deduped)` : "";
|
|
6428
|
+
const gatedSuffix = importanceGatedCount > 0 ? ` (${importanceGatedCount} gated)` : "";
|
|
6429
|
+
const judgeSuffix = judgeGatedCount > 0 ? ` (${judgeGatedCount} judge-rejected)` : "";
|
|
6430
|
+
const redactionSuffix = redactionGatedCount > 0 ? ` (${redactionGatedCount} redacted)` : "";
|
|
6431
|
+
log.info(
|
|
6432
|
+
`persisted: ${facts.length - dedupedCount - importanceGatedCount - judgeGatedCount - redactionGatedCount} facts${dedupSuffix}${gatedSuffix}${judgeSuffix}${redactionSuffix}, ${entities.length} entities, ${questions.length} questions, ${profileUpdates.length} profile updates`
|
|
6433
|
+
);
|
|
6434
|
+
if (harmonicConstructionEnabled && harmonicFactsByStorage.size > 0) {
|
|
6435
|
+
await persistConstructedHarmonicRecords({
|
|
6436
|
+
entries: harmonicFactsByStorage.values(),
|
|
6437
|
+
baseStorageDir: storage.dir,
|
|
6438
|
+
abstractionNodeStoreDir: !scopeProfileWritePlan && harmonicBaseNamespace === this.deps.config.defaultNamespace ? this.deps.config.abstractionNodeStoreDir : void 0,
|
|
6439
|
+
sessionKey: sourceContext?.sessionKey,
|
|
6440
|
+
validAt: sourceContext?.validAt,
|
|
6441
|
+
episodeTitle: result.episodeTitle,
|
|
6442
|
+
anchorsEnabled: harmonicAnchorsEnabled,
|
|
6443
|
+
entityMentions: entities
|
|
6444
|
+
});
|
|
6445
|
+
}
|
|
6446
|
+
void (async () => {
|
|
6447
|
+
if (persistedIdsByStorage.size === 0) {
|
|
6448
|
+
await this.deps.updateTemporalTagIndexes(storage, []);
|
|
6449
|
+
return;
|
|
6450
|
+
}
|
|
6451
|
+
for (const entry of persistedIdsByStorage.values()) {
|
|
6452
|
+
await this.deps.updateTemporalTagIndexes(entry.storage, entry.ids);
|
|
6453
|
+
}
|
|
6454
|
+
})().catch(
|
|
6455
|
+
(err) => log.debug(`temporal-index update error (non-fatal): ${err}`)
|
|
6456
|
+
);
|
|
6457
|
+
this.deps.setLastPersistExtractionPendingReviewIds(pendingReviewPersistedIds);
|
|
6458
|
+
return { persistedIds, memoryPathById };
|
|
6459
|
+
}
|
|
6460
|
+
};
|
|
6461
|
+
|
|
6462
|
+
// src/orchestration/extraction-shutdown.ts
|
|
6463
|
+
async function drainExtractionAndShutdownDependencyPropagation(extractionQueue, dependencyPropagationDelivery) {
|
|
6464
|
+
let extractionDrainError;
|
|
6465
|
+
try {
|
|
6466
|
+
if (!await extractionQueue.pauseAndDrain()) {
|
|
6467
|
+
extractionDrainError = new Error("orchestrator.destroy: extraction queue did not drain before teardown");
|
|
6468
|
+
}
|
|
6469
|
+
} catch (error) {
|
|
6470
|
+
extractionDrainError = error;
|
|
6471
|
+
}
|
|
6472
|
+
try {
|
|
6473
|
+
await dependencyPropagationDelivery?.shutdown();
|
|
6474
|
+
} catch (error) {
|
|
6475
|
+
log.warn(`orchestrator.destroy: dependency propagation shutdown failed: ${error}`);
|
|
6476
|
+
}
|
|
6477
|
+
return extractionDrainError;
|
|
6478
|
+
}
|
|
6479
|
+
|
|
6480
|
+
// src/orchestration/dependency-propagation-delivery.ts
|
|
6481
|
+
import { randomUUID } from "crypto";
|
|
6482
|
+
import { readFile as readFile2, unlink } from "fs/promises";
|
|
6483
|
+
import path6 from "path";
|
|
6484
|
+
|
|
6485
|
+
// src/orchestration/dependency-propagation-recovery-replay.ts
|
|
6486
|
+
import { createHash as createHash3 } from "crypto";
|
|
6487
|
+
function memoryFingerprint(memory) {
|
|
6488
|
+
const { accessCount: _accessCount, lastAccessed: _lastAccessed, ...frontmatter } = memory.frontmatter;
|
|
6489
|
+
return createHash3("sha256").update(JSON.stringify(canonicalize({ content: memory.content, frontmatter }))).digest("hex");
|
|
6490
|
+
}
|
|
6491
|
+
async function findRecoverySource(storage, sourceId) {
|
|
6492
|
+
const hot = await storage.getMemoryById(sourceId);
|
|
6493
|
+
if (hot?.frontmatter.id === sourceId) return hot;
|
|
6494
|
+
if (storage.readAllColdMemories) {
|
|
6495
|
+
const cold = await storage.readAllColdMemories();
|
|
6496
|
+
const coldMatch = cold.find((memory) => memory.frontmatter.id === sourceId);
|
|
6497
|
+
if (coldMatch) return coldMatch;
|
|
6498
|
+
}
|
|
6499
|
+
if (storage.readArchivedMemories) {
|
|
6500
|
+
const archived = await storage.readArchivedMemories();
|
|
6501
|
+
const archivedMatch = archived.find((memory) => memory.frontmatter.id === sourceId);
|
|
6502
|
+
if (archivedMatch) return archivedMatch;
|
|
6503
|
+
}
|
|
6504
|
+
return null;
|
|
6505
|
+
}
|
|
6506
|
+
async function readCurrentReplacementEvent(storage, event) {
|
|
6507
|
+
if (event.replacementId === null) return event;
|
|
6508
|
+
const replacement = await findRecoverySource(
|
|
6509
|
+
storage,
|
|
6510
|
+
event.replacementId
|
|
6511
|
+
);
|
|
6512
|
+
if (!replacement) return null;
|
|
6513
|
+
return {
|
|
6514
|
+
...event,
|
|
6515
|
+
replacementContent: replacement.content
|
|
6516
|
+
};
|
|
6517
|
+
}
|
|
6518
|
+
async function replayPrimaryMutation(sourceStorage, event, preparedReplacementFingerprint) {
|
|
6519
|
+
const storage = sourceStorage;
|
|
6520
|
+
const source = await findRecoverySource(storage, event.oldMemory.frontmatter.id);
|
|
6521
|
+
const primaryApplied = source?.frontmatter.status === "superseded" && source.frontmatter.supersededBy === event.replacementId;
|
|
6522
|
+
if (event.cause === "temporal_supersession") {
|
|
6523
|
+
if (!event.replacementId || !event.temporalMutation || !source) return false;
|
|
6524
|
+
const temporalPrimaryApplied = primaryApplied && source.frontmatter.supersededAt === event.temporalMutation.supersededAt && (event.temporalMutation.invalidAt === void 0 || source.frontmatter.invalid_at === event.temporalMutation.invalidAt);
|
|
6525
|
+
if (!temporalPrimaryApplied) {
|
|
6526
|
+
const replacement2 = await findRecoverySource(storage, event.replacementId);
|
|
6527
|
+
if (event.replacementContent === null || !replacement2 || replacement2.frontmatter.id !== event.replacementId || replacement2.content !== event.replacementContent)
|
|
6528
|
+
return false;
|
|
6529
|
+
if (!matchesPreparedSource(source, event.oldMemory)) return false;
|
|
6530
|
+
}
|
|
6531
|
+
return await applyTemporalSupersessionPrimaryMutation({
|
|
6532
|
+
storage: sourceStorage,
|
|
6533
|
+
oldMemory: source,
|
|
6534
|
+
replacementId: event.replacementId,
|
|
6535
|
+
mutation: event.temporalMutation
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
if (event.cause === "contradiction") {
|
|
6539
|
+
if (!event.replacementId || !source) return false;
|
|
6540
|
+
if (primaryApplied) return true;
|
|
6541
|
+
const replacement2 = await findRecoverySource(storage, event.replacementId);
|
|
6542
|
+
if (event.replacementContent === null || !replacement2 || replacement2.frontmatter.id !== event.replacementId || replacement2.content !== event.replacementContent)
|
|
6543
|
+
return false;
|
|
6544
|
+
if (!matchesPreparedSource(source, event.oldMemory)) return false;
|
|
6545
|
+
return await storage.supersedeMemory(
|
|
6546
|
+
event.oldMemory.frontmatter.id,
|
|
6547
|
+
event.replacementId,
|
|
6548
|
+
`dependency_propagation:${event.cause}`,
|
|
6549
|
+
void 0,
|
|
6550
|
+
{ requireActive: true, acceptExactReplay: true, expectedSnapshot: source }
|
|
6551
|
+
);
|
|
6552
|
+
}
|
|
6553
|
+
if (event.cause === "consolidation_invalidate") {
|
|
6554
|
+
if (!source) {
|
|
6555
|
+
return storage.hasCommittedInvalidation ? await storage.hasCommittedInvalidation(event.oldMemory) : false;
|
|
6556
|
+
}
|
|
6557
|
+
if (!storage.invalidateMemory || !matchesPreparedSource(source, event.oldMemory)) return false;
|
|
6558
|
+
return await storage.invalidateMemory(source.frontmatter.id, source, {
|
|
6559
|
+
recordCommitProof: true
|
|
6560
|
+
});
|
|
6561
|
+
}
|
|
6562
|
+
if (!event.replacementId || event.replacementContent === null || !storage.updateMemoryIfUnchanged) {
|
|
6563
|
+
return false;
|
|
6564
|
+
}
|
|
6565
|
+
const replacement = await findRecoverySource(storage, event.replacementId);
|
|
6566
|
+
const persistedReplacementContent = sanitizeMemoryContent(event.replacementContent).text;
|
|
6567
|
+
if (replacement && replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id && replacement.content !== persistedReplacementContent) {
|
|
6568
|
+
return false;
|
|
6569
|
+
}
|
|
6570
|
+
const mergeAlreadyApplied = replacement !== null && replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id && replacement.content === persistedReplacementContent;
|
|
6571
|
+
if (!source) {
|
|
6572
|
+
if (!storage.hasCommittedInvalidation) return false;
|
|
6573
|
+
return await storage.hasCommittedInvalidation(event.oldMemory);
|
|
6574
|
+
}
|
|
6575
|
+
if (preparedReplacementFingerprint && source && !mergeAlreadyApplied && (!replacement || memoryFingerprint(replacement) !== preparedReplacementFingerprint)) {
|
|
6576
|
+
return false;
|
|
6577
|
+
}
|
|
6578
|
+
if (!replacement || !matchesPreparedSource(source, event.oldMemory)) return false;
|
|
6579
|
+
if (!mergeAlreadyApplied) {
|
|
6580
|
+
const updated = await storage.updateMemoryIfUnchanged(
|
|
6581
|
+
replacement,
|
|
6582
|
+
event.replacementContent,
|
|
6583
|
+
{
|
|
6584
|
+
supersedes: event.oldMemory.frontmatter.id,
|
|
6585
|
+
lineage: [event.replacementId, event.oldMemory.frontmatter.id]
|
|
6586
|
+
}
|
|
6587
|
+
);
|
|
6588
|
+
if (!updated) return false;
|
|
6589
|
+
}
|
|
6590
|
+
if (!storage.invalidateMemory) return false;
|
|
6591
|
+
return await storage.invalidateMemory(source.frontmatter.id, source, {
|
|
6592
|
+
recordCommitProof: true
|
|
6593
|
+
});
|
|
6594
|
+
}
|
|
6595
|
+
|
|
6596
|
+
// src/orchestration/dependency-propagation-delivery.ts
|
|
6597
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
6598
|
+
var DEFAULT_LEASE_MS = 6e4;
|
|
6599
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
6600
|
+
var TERMINAL_RETENTION_COUNT = 32;
|
|
6601
|
+
var MAX_SERIALIZED_QUEUE_JOB_BYTES = 64 * 1024;
|
|
6602
|
+
var MAX_STORED_ERROR_LENGTH = 1024;
|
|
6603
|
+
var QueueJobTooLargeError = class extends Error {
|
|
6604
|
+
constructor(bytes) {
|
|
6605
|
+
super(`dependency propagation queue job exceeds ${MAX_SERIALIZED_QUEUE_JOB_BYTES} bytes (${bytes})`);
|
|
6606
|
+
this.name = "QueueJobTooLargeError";
|
|
6607
|
+
}
|
|
6608
|
+
};
|
|
6609
|
+
function formatStoredError(error) {
|
|
6610
|
+
const text = String(error);
|
|
6611
|
+
return text.length > MAX_STORED_ERROR_LENGTH ? text.slice(0, MAX_STORED_ERROR_LENGTH) : text;
|
|
6612
|
+
}
|
|
6613
|
+
function chooseEntry(entries) {
|
|
6614
|
+
return [...entries].sort((left, right) => {
|
|
6615
|
+
if (left.job.revision !== right.job.revision) return right.job.revision - left.job.revision;
|
|
6616
|
+
if (left.job.updatedAt !== right.job.updatedAt) return right.job.updatedAt - left.job.updatedAt;
|
|
6617
|
+
return left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0;
|
|
6618
|
+
})[0];
|
|
6619
|
+
}
|
|
6620
|
+
var DependencyPropagationDelivery = class {
|
|
6621
|
+
queueRoot;
|
|
6622
|
+
getStorage;
|
|
6623
|
+
config;
|
|
6624
|
+
extraction;
|
|
6625
|
+
workerId;
|
|
6626
|
+
clock;
|
|
6627
|
+
retryDelayMs;
|
|
6628
|
+
leaseMs;
|
|
6629
|
+
maxAttempts;
|
|
6630
|
+
autoStart;
|
|
6631
|
+
schedule;
|
|
6632
|
+
readQueueFile;
|
|
6633
|
+
writeQueueFile;
|
|
6634
|
+
replayReconciliation;
|
|
6635
|
+
backgroundScheduled = false;
|
|
6636
|
+
recoveryScheduled = false;
|
|
6637
|
+
backgroundRun = null;
|
|
6638
|
+
activeRuns = /* @__PURE__ */ new Set();
|
|
6639
|
+
stopped = false;
|
|
6640
|
+
constructor(options) {
|
|
6641
|
+
this.queueRoot = options.queueRoot;
|
|
6642
|
+
this.config = options.config;
|
|
6643
|
+
this.extraction = options.extraction;
|
|
6644
|
+
this.getStorage = options.getStorage;
|
|
6645
|
+
this.workerId = options.workerId ?? `worker-${process.pid}-${randomUUID()}`;
|
|
6646
|
+
this.clock = options.clock ?? Date.now;
|
|
6647
|
+
this.retryDelayMs = Math.max(0, options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS);
|
|
6648
|
+
this.leaseMs = Math.max(1, options.leaseMs ?? DEFAULT_LEASE_MS);
|
|
6649
|
+
this.maxAttempts = Math.max(1, Math.floor(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS));
|
|
6650
|
+
this.autoStart = options.autoStart ?? false;
|
|
6651
|
+
this.schedule = options.schedule ?? ((run, delayMs) => {
|
|
6652
|
+
const timer = setTimeout(() => {
|
|
6653
|
+
void run().catch((error) => {
|
|
6654
|
+
log.warn(`dependency propagation scheduled run failed: ${error}`);
|
|
6655
|
+
});
|
|
6656
|
+
}, delayMs);
|
|
6657
|
+
timer.unref();
|
|
6658
|
+
});
|
|
6659
|
+
this.readQueueFile = options.readQueueFile ?? ((filePath) => readFile2(filePath, "utf8"));
|
|
6660
|
+
this.writeQueueFile = options.writeQueueFile ?? ((filePath, content) => writeJsonFileAtomic(filePath, JSON.parse(content)));
|
|
6661
|
+
this.replayReconciliation = options.replayReconciliation;
|
|
6662
|
+
}
|
|
6663
|
+
async prepare(event) {
|
|
6664
|
+
if (this.stopped) return null;
|
|
6665
|
+
if (!isDependencyPropagationEnabled(this.config)) return null;
|
|
6666
|
+
const reservationId = randomUUID();
|
|
6667
|
+
try {
|
|
6668
|
+
if (!isPropagationEvent(event)) throw new Error("invalid dependency propagation event");
|
|
6669
|
+
const snapshot = canonicalEvent(event);
|
|
6670
|
+
const jobId = eventJobId(snapshot);
|
|
6671
|
+
let preparedReplacementFingerprint;
|
|
6672
|
+
if (snapshot.cause === "consolidation_merge" && snapshot.replacementId !== null) {
|
|
6673
|
+
try {
|
|
6674
|
+
const storage = await this.getStorage(snapshot.namespaceScope);
|
|
6675
|
+
const replacement = await storage.getMemoryById(snapshot.replacementId);
|
|
6676
|
+
if (!replacement) {
|
|
6677
|
+
log.warn(
|
|
6678
|
+
`dependency propagation survivor lookup returned no record for ${snapshot.replacementId}`
|
|
6679
|
+
);
|
|
6680
|
+
return null;
|
|
6681
|
+
}
|
|
6682
|
+
preparedReplacementFingerprint = memoryFingerprint(replacement);
|
|
6683
|
+
} catch (error) {
|
|
6684
|
+
log.warn(`dependency propagation survivor fingerprint capture failed: ${error}`);
|
|
6685
|
+
return null;
|
|
6686
|
+
}
|
|
6687
|
+
}
|
|
6688
|
+
let token = null;
|
|
6689
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6690
|
+
const entries = await this.readEntries();
|
|
6691
|
+
const existing = chooseEntry(entries.filter((entry) => entry.job.jobId === jobId));
|
|
6692
|
+
if (existing && existing.job.status === "canceled") {
|
|
6693
|
+
const reopened = {
|
|
6694
|
+
...existing.job,
|
|
6695
|
+
status: "prepared",
|
|
6696
|
+
attempts: 0,
|
|
6697
|
+
reservations: 1,
|
|
6698
|
+
reservationIds: [reservationId],
|
|
6699
|
+
nextAttemptAt: void 0,
|
|
6700
|
+
leaseOwner: void 0,
|
|
6701
|
+
leaseExpiresAt: void 0,
|
|
6702
|
+
lastError: void 0,
|
|
6703
|
+
updatedAt: this.clock(),
|
|
6704
|
+
revision: existing.job.revision + 1
|
|
6705
|
+
};
|
|
6706
|
+
await this.writeQueueJob(await this.jobPath("prepared", jobId), reopened);
|
|
6707
|
+
await unlink(existing.filePath).catch((error) => {
|
|
6708
|
+
if (!isNotFound(error)) throw error;
|
|
6709
|
+
});
|
|
6710
|
+
token = {
|
|
6711
|
+
jobId,
|
|
6712
|
+
revision: reopened.revision,
|
|
6713
|
+
ownsPreparedJob: true,
|
|
6714
|
+
reservationId
|
|
6715
|
+
};
|
|
6716
|
+
return;
|
|
6717
|
+
}
|
|
6718
|
+
if (existing && existing.job.status === "prepared") {
|
|
6719
|
+
const reservationIds = existing.job.reservationIds ?? [];
|
|
6720
|
+
const next = {
|
|
6721
|
+
...existing.job,
|
|
6722
|
+
reservations: reservationIds.length + 1,
|
|
6723
|
+
reservationIds: [...reservationIds, reservationId],
|
|
6724
|
+
updatedAt: this.clock()
|
|
6725
|
+
};
|
|
6726
|
+
await this.writeQueueJob(existing.filePath, next);
|
|
6727
|
+
token = {
|
|
6728
|
+
jobId,
|
|
6729
|
+
revision: existing.job.revision,
|
|
6730
|
+
ownsPreparedJob: true,
|
|
6731
|
+
reservationId
|
|
6732
|
+
};
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6735
|
+
if (existing) {
|
|
6736
|
+
token = {
|
|
6737
|
+
jobId,
|
|
6738
|
+
revision: existing.job.revision,
|
|
6739
|
+
ownsPreparedJob: false,
|
|
6740
|
+
reservationId
|
|
6741
|
+
};
|
|
6742
|
+
return;
|
|
6743
|
+
}
|
|
6744
|
+
const now = this.clock();
|
|
6745
|
+
const job = {
|
|
6746
|
+
jobId,
|
|
6747
|
+
namespace: snapshot.namespaceScope,
|
|
6748
|
+
sourceId: snapshot.oldMemory.frontmatter.id,
|
|
6749
|
+
event: snapshot,
|
|
6750
|
+
status: "prepared",
|
|
6751
|
+
attempts: 0,
|
|
6752
|
+
createdAt: now,
|
|
6753
|
+
updatedAt: now,
|
|
6754
|
+
reservations: 1,
|
|
6755
|
+
reservationIds: [reservationId],
|
|
6756
|
+
revision: 0,
|
|
6757
|
+
...preparedReplacementFingerprint ? { preparedReplacementFingerprint } : {}
|
|
6758
|
+
};
|
|
6759
|
+
await this.writeQueueJob(await this.jobPath(job.status, job.jobId), job);
|
|
6760
|
+
token = {
|
|
6761
|
+
jobId,
|
|
6762
|
+
revision: job.revision,
|
|
6763
|
+
ownsPreparedJob: true,
|
|
6764
|
+
reservationId
|
|
6765
|
+
};
|
|
6766
|
+
});
|
|
6767
|
+
return token;
|
|
6768
|
+
} catch (error) {
|
|
6769
|
+
log.warn(`dependency propagation queue preparation failed: ${error}`);
|
|
6770
|
+
if (error instanceof QueueJobTooLargeError) return null;
|
|
6771
|
+
try {
|
|
6772
|
+
if (isPropagationEvent(event)) {
|
|
6773
|
+
const snapshot = canonicalEvent(event);
|
|
6774
|
+
const jobId = eventJobId(snapshot);
|
|
6775
|
+
return await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6776
|
+
const existing = chooseEntry(
|
|
6777
|
+
(await this.readEntries()).filter((entry) => entry.job.jobId === jobId)
|
|
6778
|
+
);
|
|
6779
|
+
if (!existing || existing.job.status !== "prepared") return null;
|
|
6780
|
+
const reservationIds = existing.job.reservationIds ?? [];
|
|
6781
|
+
if (!reservationIds.includes(reservationId)) {
|
|
6782
|
+
reservationIds.push(reservationId);
|
|
6783
|
+
await this.writeQueueJob(existing.filePath, {
|
|
6784
|
+
...existing.job,
|
|
6785
|
+
reservationIds,
|
|
6786
|
+
reservations: reservationIds.length,
|
|
6787
|
+
updatedAt: this.clock()
|
|
6788
|
+
});
|
|
6789
|
+
}
|
|
6790
|
+
return {
|
|
6791
|
+
jobId,
|
|
6792
|
+
revision: existing.job.revision,
|
|
6793
|
+
ownsPreparedJob: true,
|
|
6794
|
+
reservationId
|
|
6795
|
+
};
|
|
6796
|
+
});
|
|
6797
|
+
}
|
|
6798
|
+
} catch (readbackError) {
|
|
6799
|
+
log.warn(`dependency propagation queue preparation readback failed: ${readbackError}`);
|
|
6800
|
+
}
|
|
6801
|
+
return null;
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
6804
|
+
async afterMutation(token, event) {
|
|
6805
|
+
if (this.stopped) return;
|
|
6806
|
+
if (token !== null) {
|
|
6807
|
+
try {
|
|
6808
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6809
|
+
const entries = await this.readEntries();
|
|
6810
|
+
const matching = entries.filter((entry) => entry.job.jobId === token.jobId);
|
|
6811
|
+
const current = chooseEntry(matching);
|
|
6812
|
+
if (!current) throw new Error(`dependency propagation job ${token.jobId} is missing`);
|
|
6813
|
+
if (current.job.status === "prepared" && current.job.revision === token.revision) {
|
|
6814
|
+
const reservationIds = current.job.reservationIds;
|
|
6815
|
+
if (reservationIds && !reservationIds.includes(token.reservationId)) return;
|
|
6816
|
+
await this.transitionUnlocked(matching, current.job, "ready", {
|
|
6817
|
+
...reservationIds ? {
|
|
6818
|
+
reservationIds: reservationIds.filter((id) => id !== token.reservationId),
|
|
6819
|
+
reservations: Math.max(0, reservationIds.length - 1)
|
|
6820
|
+
} : {}
|
|
6821
|
+
});
|
|
6822
|
+
}
|
|
6823
|
+
});
|
|
6824
|
+
this.scheduleBackgroundRun();
|
|
6825
|
+
return;
|
|
6826
|
+
} catch (error) {
|
|
6827
|
+
log.warn(`dependency propagation ready transition failed: ${error}`);
|
|
6828
|
+
this.scheduleBackgroundRecovery();
|
|
6829
|
+
return;
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
await this.propagateDirect(event);
|
|
6833
|
+
}
|
|
6834
|
+
async cancel(token) {
|
|
6835
|
+
if (token === null) return;
|
|
6836
|
+
try {
|
|
6837
|
+
let terminalJob = null;
|
|
6838
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6839
|
+
const entries = await this.readEntries();
|
|
6840
|
+
const matching = entries.filter((entry) => entry.job.jobId === token.jobId);
|
|
6841
|
+
const current = chooseEntry(matching);
|
|
6842
|
+
if (!current || current.job.status !== "prepared" || current.job.revision !== token.revision) {
|
|
6843
|
+
return;
|
|
6844
|
+
}
|
|
6845
|
+
const reservationIds = current.job.reservationIds;
|
|
6846
|
+
if (reservationIds && !reservationIds.includes(token.reservationId)) return;
|
|
6847
|
+
if (reservationIds && reservationIds.length > 1) {
|
|
6848
|
+
const remaining = reservationIds.filter((id) => id !== token.reservationId);
|
|
6849
|
+
await this.writeQueueJob(current.filePath, {
|
|
6850
|
+
...current.job,
|
|
6851
|
+
reservationIds: remaining,
|
|
6852
|
+
reservations: remaining.length,
|
|
6853
|
+
updatedAt: this.clock()
|
|
6854
|
+
});
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
terminalJob = await this.transitionUnlocked(matching, current.job, "canceled", {
|
|
6858
|
+
reservationIds: [],
|
|
6859
|
+
reservations: 0,
|
|
6860
|
+
lastError: void 0
|
|
6861
|
+
});
|
|
6862
|
+
});
|
|
6863
|
+
if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
|
|
6864
|
+
this.scheduleBackgroundRecovery();
|
|
6865
|
+
}
|
|
6866
|
+
} catch (error) {
|
|
6867
|
+
log.warn(`dependency propagation cancellation failed for ${token.jobId}: ${error}`);
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
async deferPrepared(token) {
|
|
6871
|
+
if (this.stopped || token === null) return;
|
|
6872
|
+
try {
|
|
6873
|
+
const shouldSchedule = await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6874
|
+
const entries = await this.readEntries();
|
|
6875
|
+
const current = chooseEntry(
|
|
6876
|
+
entries.filter((entry) => entry.job.jobId === token.jobId)
|
|
6877
|
+
);
|
|
6878
|
+
if (!current || current.job.status !== "prepared" || current.job.revision !== token.revision) {
|
|
6879
|
+
return false;
|
|
6880
|
+
}
|
|
6881
|
+
const reservationIds = current.job.reservationIds;
|
|
6882
|
+
return !reservationIds || reservationIds.includes(token.reservationId);
|
|
6883
|
+
});
|
|
6884
|
+
if (shouldSchedule) this.scheduleBackgroundRecovery();
|
|
6885
|
+
} catch (error) {
|
|
6886
|
+
log.warn(`dependency propagation prepared-job deferral failed for ${token.jobId}: ${error}`);
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
trackRun(run) {
|
|
6890
|
+
this.activeRuns.add(run);
|
|
6891
|
+
void run.finally(() => this.activeRuns.delete(run)).catch(() => void 0);
|
|
6892
|
+
return run;
|
|
6893
|
+
}
|
|
6894
|
+
recover() {
|
|
6895
|
+
return this.trackRun(this.recoverInternal());
|
|
6896
|
+
}
|
|
6897
|
+
async recoverInternal() {
|
|
6898
|
+
let jobs;
|
|
6899
|
+
try {
|
|
6900
|
+
jobs = await this.listJobs();
|
|
6901
|
+
} catch (error) {
|
|
6902
|
+
log.warn(`dependency propagation recovery scan failed: ${error}`);
|
|
6903
|
+
this.scheduleBackgroundRecovery();
|
|
6904
|
+
return;
|
|
6905
|
+
}
|
|
6906
|
+
let retryPreparedRecovery = false;
|
|
6907
|
+
for (const job of jobs) {
|
|
6908
|
+
if (job.status === "completed" || job.status === "dead_letter" || job.status === "canceled") {
|
|
6909
|
+
retryPreparedRecovery ||= !await this.finalizeTerminalJob(job);
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
const now = this.clock();
|
|
6913
|
+
for (const job of jobs) {
|
|
6914
|
+
if (job.status !== "leased" || job.leaseOwner === void 0 || !job.leaseOwner.includes(":recovery:") || (job.leaseExpiresAt ?? Number.POSITIVE_INFINITY) > now)
|
|
6915
|
+
continue;
|
|
6916
|
+
let claimed;
|
|
6917
|
+
try {
|
|
6918
|
+
claimed = await this.claimExpiredRecoveryLease(job);
|
|
6919
|
+
} catch (error) {
|
|
6920
|
+
retryPreparedRecovery = true;
|
|
6921
|
+
log.warn(`dependency propagation expired recovery lease claim failed: ${error}`);
|
|
6922
|
+
continue;
|
|
6923
|
+
}
|
|
6924
|
+
if (!claimed) continue;
|
|
6925
|
+
const outcome = await this.recoverPrimaryMutation(claimed);
|
|
6926
|
+
retryPreparedRecovery ||= outcome.retry;
|
|
6927
|
+
}
|
|
6928
|
+
for (const job of jobs) {
|
|
6929
|
+
if (job.status !== "prepared") continue;
|
|
6930
|
+
let claimed;
|
|
6931
|
+
try {
|
|
6932
|
+
claimed = await this.claimPrepared(job);
|
|
6933
|
+
} catch (error) {
|
|
6934
|
+
retryPreparedRecovery = true;
|
|
6935
|
+
log.warn(`dependency propagation prepared-job claim failed: ${error}`);
|
|
6936
|
+
continue;
|
|
6937
|
+
}
|
|
6938
|
+
if (!claimed) continue;
|
|
6939
|
+
const outcome = await this.recoverPrimaryMutation(claimed);
|
|
6940
|
+
retryPreparedRecovery ||= outcome.retry;
|
|
6941
|
+
}
|
|
6942
|
+
if (retryPreparedRecovery) this.scheduleBackgroundRecovery();
|
|
6943
|
+
await this.scheduleNextPendingRun();
|
|
6944
|
+
}
|
|
6945
|
+
async claimPrepared(job) {
|
|
6946
|
+
return withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6947
|
+
const entries = await this.readEntries();
|
|
6948
|
+
const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
|
|
6949
|
+
const current = chooseEntry(matching);
|
|
6950
|
+
if (!current || current.job.status !== "prepared" || current.job.revision !== job.revision) {
|
|
6951
|
+
return null;
|
|
6952
|
+
}
|
|
6953
|
+
return this.transitionUnlocked(matching, current.job, "leased", {
|
|
6954
|
+
leaseOwner: `${this.workerId}:recovery:${randomUUID()}`,
|
|
6955
|
+
leaseExpiresAt: this.clock() + this.leaseMs,
|
|
6956
|
+
nextAttemptAt: void 0,
|
|
6957
|
+
lastError: void 0
|
|
6958
|
+
});
|
|
6959
|
+
});
|
|
6960
|
+
}
|
|
6961
|
+
async claimExpiredRecoveryLease(job) {
|
|
6962
|
+
return withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
6963
|
+
const entries = await this.readEntries();
|
|
6964
|
+
const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
|
|
6965
|
+
const current = chooseEntry(matching);
|
|
6966
|
+
if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner) {
|
|
6967
|
+
return null;
|
|
6968
|
+
}
|
|
6969
|
+
if ((current.job.leaseExpiresAt ?? Number.POSITIVE_INFINITY) > this.clock()) {
|
|
6970
|
+
return null;
|
|
6971
|
+
}
|
|
6972
|
+
return this.transitionUnlocked(matching, current.job, "leased", {
|
|
6973
|
+
leaseOwner: `${this.workerId}:recovery:${randomUUID()}`,
|
|
6974
|
+
leaseExpiresAt: this.clock() + this.leaseMs,
|
|
6975
|
+
nextAttemptAt: void 0,
|
|
6976
|
+
lastError: void 0
|
|
6977
|
+
});
|
|
6978
|
+
});
|
|
6979
|
+
}
|
|
6980
|
+
async recoverPrimaryMutation(claimed) {
|
|
6981
|
+
try {
|
|
6982
|
+
const storage = await this.getStorage(claimed.event.namespaceScope);
|
|
6983
|
+
const replayed = await this.withCurrentLease(
|
|
6984
|
+
claimed,
|
|
6985
|
+
() => replayPrimaryMutation(
|
|
6986
|
+
storage,
|
|
6987
|
+
claimed.event,
|
|
6988
|
+
claimed.preparedReplacementFingerprint
|
|
6989
|
+
)
|
|
6990
|
+
);
|
|
6991
|
+
if (replayed !== true) {
|
|
6992
|
+
return {
|
|
6993
|
+
retry: await this.retainPreparedJob(
|
|
6994
|
+
claimed,
|
|
6995
|
+
new Error("primary mutation replay incomplete")
|
|
6996
|
+
)
|
|
6997
|
+
};
|
|
6998
|
+
}
|
|
6999
|
+
if (this.replayReconciliation) {
|
|
7000
|
+
if (claimed.event.cause === "consolidation_merge" && claimed.event.replacementId) {
|
|
7001
|
+
await this.replayReconciliation.reindex(
|
|
7002
|
+
claimed.event,
|
|
7003
|
+
claimed.event.replacementId
|
|
7004
|
+
);
|
|
7005
|
+
}
|
|
7006
|
+
await this.replayReconciliation.deindex(
|
|
7007
|
+
claimed.event,
|
|
7008
|
+
[claimed.event.oldMemory.frontmatter.id]
|
|
7009
|
+
);
|
|
7010
|
+
}
|
|
7011
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7012
|
+
const entries = await this.readEntries();
|
|
7013
|
+
const matching = entries.filter((entry) => entry.job.jobId === claimed.jobId);
|
|
7014
|
+
const current = chooseEntry(matching);
|
|
7015
|
+
if (current && current.job.status === "leased" && current.job.leaseOwner === claimed.leaseOwner && (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) > this.clock()) {
|
|
7016
|
+
await this.transitionUnlocked(matching, current.job, "ready", {
|
|
7017
|
+
leaseOwner: void 0,
|
|
7018
|
+
leaseExpiresAt: void 0
|
|
7019
|
+
});
|
|
7020
|
+
}
|
|
7021
|
+
});
|
|
7022
|
+
return { retry: false };
|
|
7023
|
+
} catch (error) {
|
|
7024
|
+
log.warn(`dependency propagation prepared-job recovery failed: ${error}`);
|
|
7025
|
+
return { retry: await this.retainPreparedJob(claimed, error) };
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
runUntilIdle() {
|
|
7029
|
+
return this.trackRun(this.runUntilIdleInternal());
|
|
7030
|
+
}
|
|
7031
|
+
async runUntilIdleInternal() {
|
|
7032
|
+
let processed = 0;
|
|
7033
|
+
while (true) {
|
|
7034
|
+
let claimed;
|
|
7035
|
+
try {
|
|
7036
|
+
claimed = await this.claimNext();
|
|
7037
|
+
} catch (error) {
|
|
7038
|
+
log.warn(`dependency propagation claim failed: ${error}`);
|
|
7039
|
+
this.scheduleBackgroundRecovery();
|
|
7040
|
+
break;
|
|
7041
|
+
}
|
|
7042
|
+
if (!claimed) break;
|
|
7043
|
+
processed += 1;
|
|
7044
|
+
await this.processClaimed(claimed);
|
|
7045
|
+
}
|
|
7046
|
+
return processed;
|
|
7047
|
+
}
|
|
7048
|
+
scheduleBackgroundRun(delayMs = 0) {
|
|
7049
|
+
if (!this.autoStart || this.stopped || this.backgroundScheduled) return;
|
|
7050
|
+
this.backgroundScheduled = true;
|
|
7051
|
+
this.schedule(async () => {
|
|
7052
|
+
this.backgroundScheduled = false;
|
|
7053
|
+
if (this.stopped) return;
|
|
7054
|
+
if (this.backgroundRun !== null) {
|
|
7055
|
+
await this.backgroundRun.catch(() => void 0);
|
|
7056
|
+
this.scheduleBackgroundRun();
|
|
7057
|
+
return;
|
|
7058
|
+
}
|
|
7059
|
+
const run = this.runUntilIdle();
|
|
7060
|
+
this.backgroundRun = run;
|
|
7061
|
+
try {
|
|
7062
|
+
await run;
|
|
7063
|
+
} finally {
|
|
7064
|
+
this.backgroundRun = null;
|
|
7065
|
+
}
|
|
7066
|
+
await this.scheduleNextPendingRun();
|
|
7067
|
+
}, delayMs);
|
|
7068
|
+
}
|
|
7069
|
+
scheduleBackgroundRecovery(delayMs = this.retryDelayMs) {
|
|
7070
|
+
if (!this.autoStart || this.stopped || this.recoveryScheduled) return;
|
|
7071
|
+
this.recoveryScheduled = true;
|
|
7072
|
+
this.schedule(async () => {
|
|
7073
|
+
this.recoveryScheduled = false;
|
|
7074
|
+
if (this.stopped) return;
|
|
7075
|
+
await this.recover();
|
|
7076
|
+
}, delayMs);
|
|
7077
|
+
}
|
|
7078
|
+
async scheduleNextPendingRun() {
|
|
7079
|
+
if (!this.autoStart || this.stopped || this.backgroundScheduled) return;
|
|
7080
|
+
let jobs;
|
|
7081
|
+
try {
|
|
7082
|
+
jobs = await this.listJobs();
|
|
7083
|
+
} catch (error) {
|
|
7084
|
+
log.warn(`dependency propagation scheduling scan failed: ${error}`);
|
|
7085
|
+
this.scheduleBackgroundRecovery();
|
|
7086
|
+
return;
|
|
7087
|
+
}
|
|
7088
|
+
const now = this.clock();
|
|
7089
|
+
let nextDelayMs = Number.POSITIVE_INFINITY;
|
|
7090
|
+
for (const job of jobs) {
|
|
7091
|
+
if (job.status === "ready") {
|
|
7092
|
+
nextDelayMs = 0;
|
|
7093
|
+
break;
|
|
7094
|
+
}
|
|
7095
|
+
if (job.status === "retryable" && job.nextAttemptAt !== void 0) {
|
|
7096
|
+
nextDelayMs = Math.min(nextDelayMs, Math.max(0, job.nextAttemptAt - now));
|
|
7097
|
+
}
|
|
7098
|
+
if (job.status === "leased" && job.leaseExpiresAt !== void 0) {
|
|
7099
|
+
nextDelayMs = Math.min(nextDelayMs, Math.max(0, job.leaseExpiresAt - now));
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
if (Number.isFinite(nextDelayMs)) this.scheduleBackgroundRun(nextDelayMs);
|
|
7103
|
+
}
|
|
7104
|
+
async listJobs() {
|
|
7105
|
+
const entries = await this.readEntries();
|
|
7106
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7107
|
+
for (const entry of entries) {
|
|
7108
|
+
const group = grouped.get(entry.job.jobId) ?? [];
|
|
7109
|
+
group.push(entry);
|
|
7110
|
+
grouped.set(entry.job.jobId, group);
|
|
7111
|
+
}
|
|
7112
|
+
return [...grouped.values()].map((group) => chooseEntry(group)?.job).filter((job) => job !== void 0).sort((left, right) => compareByteStable(left.jobId, right.jobId));
|
|
7113
|
+
}
|
|
7114
|
+
async shutdown() {
|
|
7115
|
+
this.stopped = true;
|
|
7116
|
+
while (true) {
|
|
7117
|
+
const activeRuns = [...this.activeRuns];
|
|
7118
|
+
if (activeRuns.length > 0) {
|
|
7119
|
+
await Promise.all(activeRuns.map((run) => run.catch(() => 0)));
|
|
7120
|
+
continue;
|
|
7121
|
+
}
|
|
7122
|
+
if (this.backgroundScheduled) {
|
|
7123
|
+
this.backgroundScheduled = false;
|
|
7124
|
+
await this.runUntilIdle();
|
|
7125
|
+
continue;
|
|
7126
|
+
}
|
|
7127
|
+
break;
|
|
7128
|
+
}
|
|
7129
|
+
for (let pass = 0; pass <= this.maxAttempts; pass += 1) {
|
|
7130
|
+
await this.recover();
|
|
7131
|
+
await this.runUntilIdle();
|
|
7132
|
+
const jobs = await this.listJobs();
|
|
7133
|
+
const now = this.clock();
|
|
7134
|
+
const actionable = jobs.some(
|
|
7135
|
+
(job) => job.status === "prepared" || job.status === "ready" || job.status === "retryable" && (job.nextAttemptAt === void 0 || job.nextAttemptAt <= now) || job.status === "leased" && job.leaseExpiresAt !== void 0 && job.leaseExpiresAt <= now
|
|
7136
|
+
);
|
|
7137
|
+
if (!actionable) return;
|
|
7138
|
+
}
|
|
7139
|
+
}
|
|
7140
|
+
async stateDir(status) {
|
|
7141
|
+
return resolveSafeStoragePath(this.queueRoot, status);
|
|
7142
|
+
}
|
|
7143
|
+
async jobPath(status, jobId) {
|
|
7144
|
+
return resolveSafeStoragePath(this.queueRoot, status, `${jobId}.json`);
|
|
7145
|
+
}
|
|
7146
|
+
async writeQueueJob(filePath, job) {
|
|
7147
|
+
const serialized = JSON.stringify(job, null, 2);
|
|
7148
|
+
const bytes = Buffer.byteLength(serialized, "utf8");
|
|
7149
|
+
if (bytes > MAX_SERIALIZED_QUEUE_JOB_BYTES) throw new QueueJobTooLargeError(bytes);
|
|
7150
|
+
await this.writeQueueFile(filePath, serialized);
|
|
7151
|
+
}
|
|
7152
|
+
async readEntries() {
|
|
7153
|
+
const entries = [];
|
|
7154
|
+
for (const status of JOB_STATES) {
|
|
7155
|
+
let files;
|
|
7156
|
+
try {
|
|
7157
|
+
files = await listJsonFilesStrict(await this.stateDir(status), { allowMissingDirectory: true });
|
|
7158
|
+
} catch (error) {
|
|
7159
|
+
log.warn(`dependency propagation state scan failed for ${status}: ${error}`);
|
|
7160
|
+
throw error;
|
|
7161
|
+
}
|
|
7162
|
+
for (const filePath of files) {
|
|
7163
|
+
try {
|
|
7164
|
+
const job = validateJob(JSON.parse(await this.readQueueFile(filePath)), status);
|
|
7165
|
+
if (!job) {
|
|
7166
|
+
log.warn(`dependency propagation ignored invalid job file ${path6.basename(filePath)}`);
|
|
7167
|
+
continue;
|
|
7168
|
+
}
|
|
7169
|
+
entries.push({ job, filePath });
|
|
7170
|
+
} catch (error) {
|
|
7171
|
+
if (error instanceof SyntaxError || isNotFound(error)) {
|
|
7172
|
+
log.warn(`dependency propagation ignored unreadable job file ${path6.basename(filePath)}: ${error}`);
|
|
7173
|
+
continue;
|
|
7174
|
+
}
|
|
7175
|
+
throw error;
|
|
7176
|
+
}
|
|
7177
|
+
}
|
|
7178
|
+
}
|
|
7179
|
+
return entries;
|
|
7180
|
+
}
|
|
7181
|
+
async clearCommittedInvalidationProof(job) {
|
|
7182
|
+
if (job.event.cause !== "consolidation_invalidate" && job.event.cause !== "consolidation_merge") {
|
|
7183
|
+
return true;
|
|
5691
7184
|
}
|
|
5692
|
-
|
|
5693
|
-
const
|
|
5694
|
-
if (
|
|
5695
|
-
|
|
5696
|
-
|
|
7185
|
+
try {
|
|
7186
|
+
const storage = await this.getStorage(job.event.namespaceScope);
|
|
7187
|
+
if (!storage.clearCommittedInvalidation) {
|
|
7188
|
+
log.warn("dependency propagation invalidation proof cleanup capability missing");
|
|
7189
|
+
return false;
|
|
5697
7190
|
}
|
|
7191
|
+
await storage.clearCommittedInvalidation(job.event.oldMemory);
|
|
7192
|
+
return true;
|
|
7193
|
+
} catch (error) {
|
|
7194
|
+
log.warn(`dependency propagation invalidation proof cleanup failed: ${error}`);
|
|
7195
|
+
return false;
|
|
5698
7196
|
}
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
7197
|
+
}
|
|
7198
|
+
async retainPreparedJob(job, error) {
|
|
7199
|
+
try {
|
|
7200
|
+
let terminalJob = null;
|
|
7201
|
+
const retained = await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7202
|
+
const entries = await this.readEntries();
|
|
7203
|
+
const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
|
|
7204
|
+
const current = chooseEntry(matching);
|
|
7205
|
+
if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner) {
|
|
7206
|
+
return false;
|
|
7207
|
+
}
|
|
7208
|
+
const attempts = current.job.attempts + 1;
|
|
7209
|
+
if (attempts >= this.maxAttempts) {
|
|
7210
|
+
terminalJob = await this.transitionUnlocked(matching, current.job, "dead_letter", {
|
|
7211
|
+
attempts,
|
|
7212
|
+
lastError: formatStoredError(error)
|
|
7213
|
+
});
|
|
7214
|
+
return false;
|
|
7215
|
+
}
|
|
7216
|
+
await this.transitionUnlocked(matching, current.job, "prepared", {
|
|
7217
|
+
attempts,
|
|
7218
|
+
lastError: formatStoredError(error)
|
|
7219
|
+
});
|
|
7220
|
+
return true;
|
|
7221
|
+
});
|
|
7222
|
+
if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
|
|
7223
|
+
this.scheduleBackgroundRecovery();
|
|
5705
7224
|
}
|
|
7225
|
+
return retained;
|
|
7226
|
+
} catch (retainError) {
|
|
7227
|
+
log.warn(`dependency propagation prepared-job retention failed: ${retainError}`);
|
|
7228
|
+
return true;
|
|
5706
7229
|
}
|
|
5707
|
-
|
|
5708
|
-
|
|
7230
|
+
}
|
|
7231
|
+
async transitionUnlocked(entries, job, status, patch = {}) {
|
|
7232
|
+
const next = {
|
|
7233
|
+
...job,
|
|
7234
|
+
...patch,
|
|
7235
|
+
status,
|
|
7236
|
+
updatedAt: this.clock(),
|
|
7237
|
+
revision: job.revision + 1
|
|
7238
|
+
};
|
|
7239
|
+
const targetPath = await this.jobPath(status, next.jobId);
|
|
7240
|
+
await this.writeQueueJob(targetPath, next);
|
|
7241
|
+
const terminal = status === "completed" || status === "dead_letter" || status === "canceled";
|
|
7242
|
+
for (const entry of entries) {
|
|
7243
|
+
if (path6.resolve(entry.filePath) === path6.resolve(targetPath)) continue;
|
|
7244
|
+
await unlink(entry.filePath).catch((error) => {
|
|
7245
|
+
if (isNotFound(error)) return;
|
|
7246
|
+
if (terminal) {
|
|
7247
|
+
log.warn(`dependency propagation old terminal state unlink failed: ${error}`);
|
|
7248
|
+
return;
|
|
7249
|
+
}
|
|
7250
|
+
throw error;
|
|
7251
|
+
});
|
|
5709
7252
|
}
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
if (dedupedSignals.length === 0) continue;
|
|
5719
|
-
await targetStorage.appendBehaviorSignals(dedupedSignals).catch(
|
|
5720
|
-
(err) => log.warn(`appendBehaviorSignals failed (non-fatal): ${err}`)
|
|
7253
|
+
return next;
|
|
7254
|
+
}
|
|
7255
|
+
async finalizeTerminalJob(job) {
|
|
7256
|
+
if (!await this.clearCommittedInvalidationProof(job)) return false;
|
|
7257
|
+
try {
|
|
7258
|
+
return await withJsonStoreMutationLock(
|
|
7259
|
+
this.queueRoot,
|
|
7260
|
+
() => this.pruneTerminalStatusUnlocked(job.status)
|
|
5721
7261
|
);
|
|
7262
|
+
} catch (error) {
|
|
7263
|
+
log.warn(`dependency propagation terminal finalization failed: ${error}`);
|
|
7264
|
+
return false;
|
|
5722
7265
|
}
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
7266
|
+
}
|
|
7267
|
+
async pruneTerminalStatusUnlocked(status) {
|
|
7268
|
+
try {
|
|
7269
|
+
const latestByJobId = /* @__PURE__ */ new Map();
|
|
7270
|
+
for (const entry of (await this.readEntries()).filter((item) => item.job.status === status)) {
|
|
7271
|
+
const current = latestByJobId.get(entry.job.jobId);
|
|
7272
|
+
if (!current || chooseEntry([current, entry]) === entry) {
|
|
7273
|
+
latestByJobId.set(entry.job.jobId, entry);
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
const entries = [...latestByJobId.values()].sort((left, right) => {
|
|
7277
|
+
if (left.job.updatedAt !== right.job.updatedAt) {
|
|
7278
|
+
return right.job.updatedAt > left.job.updatedAt ? 1 : -1;
|
|
7279
|
+
}
|
|
7280
|
+
if (left.job.revision !== right.job.revision) {
|
|
7281
|
+
return right.job.revision > left.job.revision ? 1 : -1;
|
|
7282
|
+
}
|
|
7283
|
+
return compareByteStable(left.filePath, right.filePath);
|
|
5740
7284
|
});
|
|
7285
|
+
const oldJobs = entries.slice(TERMINAL_RETENTION_COUNT);
|
|
7286
|
+
for (const entry of oldJobs) {
|
|
7287
|
+
if (!await this.clearCommittedInvalidationProof(entry.job)) return false;
|
|
7288
|
+
}
|
|
7289
|
+
let removed = true;
|
|
7290
|
+
for (const entry of oldJobs) {
|
|
7291
|
+
const jobRemoved = await this.removeAllQueueFilesForJobUnlocked(entry.job.jobId);
|
|
7292
|
+
removed = jobRemoved && removed;
|
|
7293
|
+
}
|
|
7294
|
+
return removed;
|
|
7295
|
+
} catch (error) {
|
|
7296
|
+
log.warn(`dependency propagation terminal retention failed: ${error}`);
|
|
7297
|
+
return false;
|
|
5741
7298
|
}
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
7299
|
+
}
|
|
7300
|
+
async removeAllQueueFilesForJobUnlocked(jobId) {
|
|
7301
|
+
let removed = true;
|
|
7302
|
+
for (const status of JOB_STATES) {
|
|
7303
|
+
const files = await listJsonFilesStrict(await this.stateDir(status), {
|
|
7304
|
+
allowMissingDirectory: true
|
|
7305
|
+
});
|
|
7306
|
+
for (const filePath of files) {
|
|
7307
|
+
if (path6.basename(filePath, ".json") !== jobId) continue;
|
|
7308
|
+
try {
|
|
7309
|
+
await unlink(filePath);
|
|
7310
|
+
} catch (error) {
|
|
7311
|
+
if (!isNotFound(error)) {
|
|
7312
|
+
removed = false;
|
|
7313
|
+
log.warn(`dependency propagation queue cleanup unlink failed: ${error}`);
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
return removed;
|
|
7319
|
+
}
|
|
7320
|
+
async claimNext() {
|
|
7321
|
+
return withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7322
|
+
const now = this.clock();
|
|
7323
|
+
const entries = await this.readEntries();
|
|
7324
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
7325
|
+
for (const entry of entries) {
|
|
7326
|
+
const group = candidates.get(entry.job.jobId) ?? [];
|
|
7327
|
+
group.push(entry);
|
|
7328
|
+
candidates.set(entry.job.jobId, group);
|
|
7329
|
+
}
|
|
7330
|
+
const jobs = [...candidates.values()].map((group) => ({ group, current: chooseEntry(group) })).filter((item) => item.current !== void 0).sort((left, right) => compareByteStable(left.current.job.jobId, right.current.job.jobId));
|
|
7331
|
+
for (const item of jobs) {
|
|
7332
|
+
const current = item.current.job;
|
|
7333
|
+
const expiredLease = current.status === "leased" && (current.leaseExpiresAt ?? Number.POSITIVE_INFINITY) <= now;
|
|
7334
|
+
const ready = current.status === "ready";
|
|
7335
|
+
const retryable = current.status === "retryable" && (current.nextAttemptAt ?? Number.POSITIVE_INFINITY) <= now;
|
|
7336
|
+
if (!ready && !retryable && !expiredLease) continue;
|
|
7337
|
+
return this.transitionUnlocked(item.group, current, "leased", {
|
|
7338
|
+
attempts: current.attempts + 1,
|
|
7339
|
+
leaseOwner: `${this.workerId}:${randomUUID()}`,
|
|
7340
|
+
leaseExpiresAt: now + this.leaseMs,
|
|
7341
|
+
nextAttemptAt: void 0,
|
|
7342
|
+
lastError: void 0
|
|
7343
|
+
});
|
|
7344
|
+
}
|
|
7345
|
+
return null;
|
|
7346
|
+
});
|
|
7347
|
+
}
|
|
7348
|
+
async processClaimed(job) {
|
|
7349
|
+
let renewal = Promise.resolve();
|
|
7350
|
+
const heartbeatMs = Math.max(1, Math.floor(this.leaseMs / 3));
|
|
7351
|
+
const heartbeat = setInterval(() => {
|
|
7352
|
+
renewal = renewal.then(() => this.renewLease(job)).catch((error) => {
|
|
7353
|
+
log.warn(`dependency propagation lease renewal failed: ${error}`);
|
|
7354
|
+
});
|
|
7355
|
+
}, heartbeatMs);
|
|
7356
|
+
heartbeat.unref();
|
|
7357
|
+
try {
|
|
7358
|
+
const storage = await this.getStorage(job.event.namespaceScope);
|
|
7359
|
+
const currentEvent = await readCurrentReplacementEvent(storage, job.event);
|
|
7360
|
+
if (currentEvent === null) {
|
|
7361
|
+
await this.finishAttempt(job, new Error("persisted replacement is missing"));
|
|
5745
7362
|
return;
|
|
5746
7363
|
}
|
|
5747
|
-
|
|
5748
|
-
|
|
7364
|
+
const result = await propagateInvalidation(
|
|
7365
|
+
{
|
|
7366
|
+
storage,
|
|
7367
|
+
extraction: this.extraction,
|
|
7368
|
+
config: this.config,
|
|
7369
|
+
writeFence: (write) => this.withCurrentLease(job, write)
|
|
7370
|
+
},
|
|
7371
|
+
currentEvent
|
|
7372
|
+
);
|
|
7373
|
+
const accountedDependents = result.invalidated + result.stillValid + result.uncertain;
|
|
7374
|
+
if (result.skipped === "timeout" || result.skipped === "llm_error") {
|
|
7375
|
+
await this.finishAttempt(job, new Error(`propagation skipped: ${result.skipped}`));
|
|
7376
|
+
} else if (accountedDependents < result.dependentsFound) {
|
|
7377
|
+
await this.finishAttempt(job, new Error("one or more dependent writes did not commit"));
|
|
7378
|
+
} else {
|
|
7379
|
+
await this.finishAttempt(job, null);
|
|
5749
7380
|
}
|
|
5750
|
-
}
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
7381
|
+
} catch (error) {
|
|
7382
|
+
await this.finishAttempt(job, error);
|
|
7383
|
+
} finally {
|
|
7384
|
+
clearInterval(heartbeat);
|
|
7385
|
+
await renewal;
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
async withCurrentLease(job, write) {
|
|
7389
|
+
return withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7390
|
+
const entries = await this.readEntries();
|
|
7391
|
+
const current = chooseEntry(entries.filter((entry) => entry.job.jobId === job.jobId));
|
|
7392
|
+
if (current?.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
|
|
7393
|
+
return void 0;
|
|
7394
|
+
}
|
|
7395
|
+
const result = await write();
|
|
7396
|
+
const renewedEntries = await this.readEntries();
|
|
7397
|
+
const renewedMatching = renewedEntries.filter((entry) => entry.job.jobId === job.jobId);
|
|
7398
|
+
const renewedCurrent = chooseEntry(renewedMatching);
|
|
7399
|
+
if (renewedCurrent && renewedCurrent.job.status === "leased" && renewedCurrent.job.leaseOwner === job.leaseOwner) {
|
|
7400
|
+
await this.transitionUnlocked(renewedMatching, renewedCurrent.job, "leased", {
|
|
7401
|
+
leaseExpiresAt: this.clock() + this.leaseMs
|
|
7402
|
+
});
|
|
7403
|
+
}
|
|
7404
|
+
return result;
|
|
7405
|
+
});
|
|
7406
|
+
}
|
|
7407
|
+
async renewLease(job) {
|
|
7408
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7409
|
+
const entries = await this.readEntries();
|
|
7410
|
+
const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
|
|
7411
|
+
const current = chooseEntry(matching);
|
|
7412
|
+
if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
|
|
7413
|
+
return;
|
|
7414
|
+
}
|
|
7415
|
+
await this.transitionUnlocked(matching, current.job, "leased", {
|
|
7416
|
+
leaseExpiresAt: this.clock() + this.leaseMs
|
|
7417
|
+
});
|
|
7418
|
+
});
|
|
7419
|
+
}
|
|
7420
|
+
async finishAttempt(job, error) {
|
|
7421
|
+
let retryDelayMs = null;
|
|
7422
|
+
let terminalJob = null;
|
|
7423
|
+
try {
|
|
7424
|
+
await withJsonStoreMutationLock(this.queueRoot, async () => {
|
|
7425
|
+
const entries = await this.readEntries();
|
|
7426
|
+
const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
|
|
7427
|
+
const current = chooseEntry(matching);
|
|
7428
|
+
if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
|
|
7429
|
+
return;
|
|
7430
|
+
}
|
|
7431
|
+
if (error === null) {
|
|
7432
|
+
terminalJob = await this.transitionUnlocked(matching, current.job, "completed", {
|
|
7433
|
+
leaseOwner: void 0,
|
|
7434
|
+
leaseExpiresAt: void 0,
|
|
7435
|
+
nextAttemptAt: void 0,
|
|
7436
|
+
lastError: void 0
|
|
7437
|
+
});
|
|
7438
|
+
return;
|
|
7439
|
+
}
|
|
7440
|
+
if (current.job.attempts >= this.maxAttempts) {
|
|
7441
|
+
terminalJob = await this.transitionUnlocked(matching, current.job, "dead_letter", {
|
|
7442
|
+
leaseOwner: void 0,
|
|
7443
|
+
leaseExpiresAt: void 0,
|
|
7444
|
+
nextAttemptAt: void 0,
|
|
7445
|
+
lastError: formatStoredError(error)
|
|
7446
|
+
});
|
|
7447
|
+
return;
|
|
7448
|
+
}
|
|
7449
|
+
retryDelayMs = this.retryDelayMs * 2 ** Math.max(0, current.job.attempts - 1);
|
|
7450
|
+
await this.transitionUnlocked(matching, current.job, "retryable", {
|
|
7451
|
+
leaseOwner: void 0,
|
|
7452
|
+
leaseExpiresAt: void 0,
|
|
7453
|
+
nextAttemptAt: this.clock() + retryDelayMs,
|
|
7454
|
+
lastError: formatStoredError(error)
|
|
7455
|
+
});
|
|
7456
|
+
});
|
|
7457
|
+
if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
|
|
7458
|
+
this.scheduleBackgroundRecovery();
|
|
7459
|
+
}
|
|
7460
|
+
if (retryDelayMs !== null) this.scheduleBackgroundRun(retryDelayMs);
|
|
7461
|
+
} catch (finishError) {
|
|
7462
|
+
log.warn(`dependency propagation attempt finalization failed: ${finishError}`);
|
|
7463
|
+
}
|
|
7464
|
+
}
|
|
7465
|
+
async propagateDirect(event) {
|
|
7466
|
+
try {
|
|
7467
|
+
const storage = await this.getStorage(event.namespaceScope);
|
|
7468
|
+
await propagateInvalidation(
|
|
7469
|
+
{ storage, extraction: this.extraction, config: this.config },
|
|
7470
|
+
event
|
|
7471
|
+
);
|
|
7472
|
+
} catch (error) {
|
|
7473
|
+
log.warn(`dependency propagation direct fallback failed: ${error}`);
|
|
7474
|
+
}
|
|
5755
7475
|
}
|
|
5756
7476
|
};
|
|
5757
7477
|
|
|
7478
|
+
// src/orchestration/dependency-propagation-replay-reconciliation.ts
|
|
7479
|
+
function createDependencyPropagationReplayReconciliation(host) {
|
|
7480
|
+
return {
|
|
7481
|
+
reindex: async (event, survivorId) => {
|
|
7482
|
+
if (event.cause !== "consolidation_merge") return;
|
|
7483
|
+
const storage = await host.getStorage(event.namespaceScope);
|
|
7484
|
+
await host.indexPersistedMemory(storage, survivorId);
|
|
7485
|
+
},
|
|
7486
|
+
deindex: async (event, memoryIds) => {
|
|
7487
|
+
if (event.cause !== "consolidation_merge" && event.cause !== "consolidation_invalidate") {
|
|
7488
|
+
return;
|
|
7489
|
+
}
|
|
7490
|
+
await Promise.all(
|
|
7491
|
+
memoryIds.map((memoryId) => host.removeFromIndex(memoryId))
|
|
7492
|
+
);
|
|
7493
|
+
if (!resolveIndexingCapabilities(host.config).queryAwareIndexing) return;
|
|
7494
|
+
if (!event.oldMemory.path || !event.oldMemory.frontmatter.created) return;
|
|
7495
|
+
const storage = await host.getStorage(event.namespaceScope);
|
|
7496
|
+
await deindexMemoriesBatchAsync(storage.dir, [{
|
|
7497
|
+
path: event.oldMemory.path,
|
|
7498
|
+
createdAt: event.oldMemory.frontmatter.created,
|
|
7499
|
+
tags: event.oldMemory.frontmatter.tags ?? []
|
|
7500
|
+
}]);
|
|
7501
|
+
}
|
|
7502
|
+
};
|
|
7503
|
+
}
|
|
7504
|
+
|
|
5758
7505
|
// src/orchestration/recall-internal.ts
|
|
5759
|
-
import { createHash as
|
|
5760
|
-
import { readFile as
|
|
5761
|
-
import
|
|
7506
|
+
import { createHash as createHash5 } from "crypto";
|
|
7507
|
+
import { readFile as readFile3, stat, unlink as unlink2 } from "fs/promises";
|
|
7508
|
+
import path10 from "path";
|
|
5762
7509
|
|
|
5763
7510
|
// src/orchestration/graph-path-state-loader.ts
|
|
5764
7511
|
import { lstat, opendir, realpath } from "fs/promises";
|
|
5765
|
-
import
|
|
7512
|
+
import path7 from "path";
|
|
5766
7513
|
function isArchivePathIndexUnavailable(index) {
|
|
5767
7514
|
return "unavailable" in index;
|
|
5768
7515
|
}
|
|
@@ -5781,11 +7528,11 @@ var GraphPathStateLoader = class {
|
|
|
5781
7528
|
openArchiveDirectory = opendir;
|
|
5782
7529
|
async readNode(storage, nodeId, deadlineAtMs, allowArchiveLookup) {
|
|
5783
7530
|
if (typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) return null;
|
|
5784
|
-
const configuredStorageRoot =
|
|
7531
|
+
const configuredStorageRoot = path7.resolve(storage.dir);
|
|
5785
7532
|
const storageRoot = await realpath(configuredStorageRoot).catch(() => null);
|
|
5786
7533
|
if (!storageRoot || storageRoot !== configuredStorageRoot) return null;
|
|
5787
|
-
const logicalId =
|
|
5788
|
-
const directRelativePaths = [nodeId,
|
|
7534
|
+
const logicalId = path7.basename(nodeId, path7.extname(nodeId));
|
|
7535
|
+
const directRelativePaths = [nodeId, path7.join("cold", nodeId)];
|
|
5789
7536
|
let inactiveDirectMemory = null;
|
|
5790
7537
|
for (const relativePath of directRelativePaths) {
|
|
5791
7538
|
const safePath = await this.resolveContainedPath(storageRoot, relativePath);
|
|
@@ -5808,7 +7555,7 @@ var GraphPathStateLoader = class {
|
|
|
5808
7555
|
if (!archiveIndex || isArchivePathIndexUnavailable(archiveIndex) || typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) {
|
|
5809
7556
|
return null;
|
|
5810
7557
|
}
|
|
5811
|
-
const archivePaths = archiveIndex.pathsByBasename.get(
|
|
7558
|
+
const archivePaths = archiveIndex.pathsByBasename.get(path7.basename(nodeId)) ?? [];
|
|
5812
7559
|
for (const archivePath of archivePaths) {
|
|
5813
7560
|
if (typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) return null;
|
|
5814
7561
|
const memory = await storage.readMemoryByPath(archivePath);
|
|
@@ -5818,17 +7565,17 @@ var GraphPathStateLoader = class {
|
|
|
5818
7565
|
return null;
|
|
5819
7566
|
}
|
|
5820
7567
|
async resolveContainedPath(storageRoot, relativePath) {
|
|
5821
|
-
if (
|
|
7568
|
+
if (path7.isAbsolute(relativePath)) return null;
|
|
5822
7569
|
const rawSegments = relativePath.replace(/\\/g, "/").split("/");
|
|
5823
7570
|
if (rawSegments.includes("..")) return null;
|
|
5824
|
-
const candidate =
|
|
5825
|
-
const lexicalRelative =
|
|
5826
|
-
if (lexicalRelative.length > 0 && (lexicalRelative.startsWith("..") ||
|
|
7571
|
+
const candidate = path7.resolve(storageRoot, relativePath);
|
|
7572
|
+
const lexicalRelative = path7.relative(storageRoot, candidate);
|
|
7573
|
+
if (lexicalRelative.length > 0 && (lexicalRelative.startsWith("..") || path7.isAbsolute(lexicalRelative))) {
|
|
5827
7574
|
return null;
|
|
5828
7575
|
}
|
|
5829
7576
|
const canonical = await realpath(candidate).catch(() => candidate);
|
|
5830
|
-
const canonicalRelative =
|
|
5831
|
-
if (canonicalRelative.length > 0 && (canonicalRelative.startsWith("..") ||
|
|
7577
|
+
const canonicalRelative = path7.relative(storageRoot, canonical);
|
|
7578
|
+
if (canonicalRelative.length > 0 && (canonicalRelative.startsWith("..") || path7.isAbsolute(canonicalRelative))) {
|
|
5832
7579
|
return null;
|
|
5833
7580
|
}
|
|
5834
7581
|
return canonical;
|
|
@@ -5901,7 +7648,7 @@ var GraphPathStateLoader = class {
|
|
|
5901
7648
|
}
|
|
5902
7649
|
async buildArchivePathIndex(storageRoot, version) {
|
|
5903
7650
|
const pathsByBasename = /* @__PURE__ */ new Map();
|
|
5904
|
-
const archiveRoot =
|
|
7651
|
+
const archiveRoot = path7.join(storageRoot, "archive");
|
|
5905
7652
|
let archiveRootStatus;
|
|
5906
7653
|
try {
|
|
5907
7654
|
archiveRootStatus = await this.archiveLstat(archiveRoot);
|
|
@@ -5919,8 +7666,8 @@ var GraphPathStateLoader = class {
|
|
|
5919
7666
|
if (isArchiveScanAbsenceError(error)) return { version, pathsByBasename };
|
|
5920
7667
|
return null;
|
|
5921
7668
|
}
|
|
5922
|
-
const archiveRelative =
|
|
5923
|
-
if (archiveRelative.length > 0 && (archiveRelative.startsWith("..") ||
|
|
7669
|
+
const archiveRelative = path7.relative(storageRoot, canonicalArchiveRoot);
|
|
7670
|
+
if (archiveRelative.length > 0 && (archiveRelative.startsWith("..") || path7.isAbsolute(archiveRelative))) {
|
|
5924
7671
|
return { version, pathsByBasename };
|
|
5925
7672
|
}
|
|
5926
7673
|
const pending = [canonicalArchiveRoot];
|
|
@@ -5946,8 +7693,8 @@ var GraphPathStateLoader = class {
|
|
|
5946
7693
|
if (isArchiveScanAbsenceError(error)) continue;
|
|
5947
7694
|
return null;
|
|
5948
7695
|
}
|
|
5949
|
-
const currentRelative =
|
|
5950
|
-
if (currentRelative.length > 0 && (currentRelative.startsWith("..") ||
|
|
7696
|
+
const currentRelative = path7.relative(storageRoot, canonicalCurrent);
|
|
7697
|
+
if (currentRelative.length > 0 && (currentRelative.startsWith("..") || path7.isAbsolute(currentRelative))) {
|
|
5951
7698
|
continue;
|
|
5952
7699
|
}
|
|
5953
7700
|
let directory;
|
|
@@ -5964,7 +7711,7 @@ var GraphPathStateLoader = class {
|
|
|
5964
7711
|
return { version, unavailable: "oversized" };
|
|
5965
7712
|
}
|
|
5966
7713
|
if (entry.isSymbolicLink()) continue;
|
|
5967
|
-
const entryPath =
|
|
7714
|
+
const entryPath = path7.join(canonicalCurrent, entry.name);
|
|
5968
7715
|
if (entry.isDirectory()) {
|
|
5969
7716
|
pending.push(entryPath);
|
|
5970
7717
|
continue;
|
|
@@ -5980,8 +7727,8 @@ var GraphPathStateLoader = class {
|
|
|
5980
7727
|
if (isArchiveScanAbsenceError(error)) continue;
|
|
5981
7728
|
return null;
|
|
5982
7729
|
}
|
|
5983
|
-
const relative =
|
|
5984
|
-
if (relative.length > 0 && (relative.startsWith("..") ||
|
|
7730
|
+
const relative = path7.relative(storageRoot, canonical);
|
|
7731
|
+
if (relative.length > 0 && (relative.startsWith("..") || path7.isAbsolute(relative))) {
|
|
5985
7732
|
continue;
|
|
5986
7733
|
}
|
|
5987
7734
|
const paths = pathsByBasename.get(entry.name);
|
|
@@ -6005,9 +7752,9 @@ var GraphPathStateLoader = class {
|
|
|
6005
7752
|
};
|
|
6006
7753
|
|
|
6007
7754
|
// src/orchestration/graph-recall-coordinator.ts
|
|
6008
|
-
import { createHash as
|
|
7755
|
+
import { createHash as createHash4 } from "crypto";
|
|
6009
7756
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
6010
|
-
import
|
|
7757
|
+
import path8 from "path";
|
|
6011
7758
|
function mergeGraphExpandedResults(primary, expanded) {
|
|
6012
7759
|
const mergedByNamespaceAndPath = /* @__PURE__ */ new Map();
|
|
6013
7760
|
for (const item of [...primary, ...expanded]) {
|
|
@@ -6024,11 +7771,11 @@ function mergeGraphExpandedResults(primary, expanded) {
|
|
|
6024
7771
|
return Array.from(mergedByNamespaceAndPath.values());
|
|
6025
7772
|
}
|
|
6026
7773
|
function graphPathRelativeToStorage(storageDir, candidatePath) {
|
|
6027
|
-
const absolutePath =
|
|
6028
|
-
const rel =
|
|
7774
|
+
const absolutePath = path8.isAbsolute(candidatePath) ? candidatePath : path8.resolve(storageDir, candidatePath);
|
|
7775
|
+
const rel = path8.relative(storageDir, absolutePath);
|
|
6029
7776
|
if (!rel || rel === ".") return null;
|
|
6030
7777
|
if (rel.startsWith("..")) return null;
|
|
6031
|
-
return rel.split(
|
|
7778
|
+
return rel.split(path8.sep).join("/");
|
|
6032
7779
|
}
|
|
6033
7780
|
function normalizeGraphActivationScore(score) {
|
|
6034
7781
|
const bounded = Number.isFinite(score) && score > 0 ? score : 0;
|
|
@@ -6128,7 +7875,7 @@ var GraphRecallCoordinator = class {
|
|
|
6128
7875
|
resolvedPath = resolvedCold.result.path;
|
|
6129
7876
|
resolvedResult = resolvedCold.result;
|
|
6130
7877
|
}
|
|
6131
|
-
if (!
|
|
7878
|
+
if (!path8.isAbsolute(resolvedPath)) {
|
|
6132
7879
|
resolvedAmbiguousSeeds.set(result.path, null);
|
|
6133
7880
|
return null;
|
|
6134
7881
|
}
|
|
@@ -6153,7 +7900,7 @@ var GraphRecallCoordinator = class {
|
|
|
6153
7900
|
}
|
|
6154
7901
|
continue;
|
|
6155
7902
|
}
|
|
6156
|
-
if (
|
|
7903
|
+
if (path8.isAbsolute(result.path)) {
|
|
6157
7904
|
const resolved = await resolveAmbiguousSeedOwner(result, null);
|
|
6158
7905
|
if (resolved) {
|
|
6159
7906
|
addResultForNamespace(resolved.namespace, resolved.result);
|
|
@@ -6199,7 +7946,7 @@ var GraphRecallCoordinator = class {
|
|
|
6199
7946
|
0
|
|
6200
7947
|
);
|
|
6201
7948
|
seedPaths.push(
|
|
6202
|
-
...seedRelativePaths.map((rel) =>
|
|
7949
|
+
...seedRelativePaths.map((rel) => path8.join(storage.dir, rel))
|
|
6203
7950
|
);
|
|
6204
7951
|
const seedSet = new Set(seedRelativePaths);
|
|
6205
7952
|
const nodeMemoryCache = /* @__PURE__ */ new Map();
|
|
@@ -6253,7 +8000,7 @@ var GraphRecallCoordinator = class {
|
|
|
6253
8000
|
path: memory.path,
|
|
6254
8001
|
score,
|
|
6255
8002
|
namespace,
|
|
6256
|
-
seed:
|
|
8003
|
+
seed: path8.resolve(storage.dir, candidate.seed),
|
|
6257
8004
|
hopDepth: candidate.hopDepth,
|
|
6258
8005
|
decayedWeight: candidate.decayedWeight,
|
|
6259
8006
|
graphType: candidate.graphType,
|
|
@@ -6285,7 +8032,7 @@ var GraphRecallCoordinator = class {
|
|
|
6285
8032
|
add(memory.frontmatter.id);
|
|
6286
8033
|
if (relativePath) {
|
|
6287
8034
|
add(relativePath);
|
|
6288
|
-
if (relativePath.endsWith(".md")) add(
|
|
8035
|
+
if (relativePath.endsWith(".md")) add(path8.basename(relativePath, ".md"));
|
|
6289
8036
|
}
|
|
6290
8037
|
return state;
|
|
6291
8038
|
};
|
|
@@ -6332,7 +8079,7 @@ var GraphRecallCoordinator = class {
|
|
|
6332
8079
|
path: memory.path,
|
|
6333
8080
|
score,
|
|
6334
8081
|
namespace,
|
|
6335
|
-
seed:
|
|
8082
|
+
seed: path8.resolve(storage.dir, candidate.seed),
|
|
6336
8083
|
hopDepth: candidate.hopDepth,
|
|
6337
8084
|
decayedWeight: candidate.decayedWeight,
|
|
6338
8085
|
graphType: candidate.graphType,
|
|
@@ -6374,12 +8121,12 @@ var GraphRecallCoordinator = class {
|
|
|
6374
8121
|
}
|
|
6375
8122
|
async recordLastGraphRecallSnapshot(options) {
|
|
6376
8123
|
try {
|
|
6377
|
-
const snapshotPath =
|
|
8124
|
+
const snapshotPath = path8.join(
|
|
6378
8125
|
options.storage.dir,
|
|
6379
8126
|
"state",
|
|
6380
8127
|
"last_graph_recall.json"
|
|
6381
8128
|
);
|
|
6382
|
-
await mkdir3(
|
|
8129
|
+
await mkdir3(path8.dirname(snapshotPath), { recursive: true });
|
|
6383
8130
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6384
8131
|
const totalSeedCount = options.seedPaths.length;
|
|
6385
8132
|
const totalExpandedCount = options.expandedPaths.length;
|
|
@@ -6391,7 +8138,7 @@ var GraphRecallCoordinator = class {
|
|
|
6391
8138
|
const payload = {
|
|
6392
8139
|
recordedAt: now,
|
|
6393
8140
|
mode: options.recallMode,
|
|
6394
|
-
queryHash:
|
|
8141
|
+
queryHash: createHash4("sha256").update(options.prompt).digest("hex"),
|
|
6395
8142
|
queryLength: options.prompt.length,
|
|
6396
8143
|
namespaces: options.recallNamespaces,
|
|
6397
8144
|
seedCount: totalSeedCount,
|
|
@@ -6494,11 +8241,11 @@ ${blocks.join("\n\n")}`;
|
|
|
6494
8241
|
}
|
|
6495
8242
|
|
|
6496
8243
|
// src/orchestration/recall-profile-storage.ts
|
|
6497
|
-
import
|
|
8244
|
+
import path9 from "path";
|
|
6498
8245
|
function resolveCompositeProfileStorage(options) {
|
|
6499
8246
|
const { profileStorages, memoryDir } = options;
|
|
6500
8247
|
const emptyProfileStorage = new Proxy(
|
|
6501
|
-
{ dir:
|
|
8248
|
+
{ dir: path9.join(memoryDir, ".empty-scope-profile") },
|
|
6502
8249
|
{
|
|
6503
8250
|
get(target, prop) {
|
|
6504
8251
|
if (prop in target) return Reflect.get(target, prop);
|
|
@@ -6724,8 +8471,8 @@ var RecallInternalCoordinator = class {
|
|
|
6724
8471
|
record: recordRecallSectionMetric,
|
|
6725
8472
|
logger: log
|
|
6726
8473
|
});
|
|
6727
|
-
const promptHash =
|
|
6728
|
-
const traceId =
|
|
8474
|
+
const promptHash = createHash5("sha256").update(prompt).digest("hex");
|
|
8475
|
+
const traceId = createHash5("sha256").update(`${sessionKey ?? "default"}:${recallStart}:${promptHash}`).digest("hex").slice(0, 16);
|
|
6729
8476
|
const sectionBuckets = /* @__PURE__ */ new Map();
|
|
6730
8477
|
const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
|
|
6731
8478
|
cronRecallPolicyEnabled: resolveRecallAuxiliaryCapabilities(this.deps.config).cronRecallPolicy,
|
|
@@ -6734,7 +8481,7 @@ var RecallInternalCoordinator = class {
|
|
|
6734
8481
|
cronConversationRecallMode: this.deps.config.cronConversationRecallMode
|
|
6735
8482
|
});
|
|
6736
8483
|
const retrievalQuery = queryPolicy.retrievalQuery || prompt;
|
|
6737
|
-
const retrievalQueryHash =
|
|
8484
|
+
const retrievalQueryHash = createHash5("sha256").update(retrievalQuery).digest("hex");
|
|
6738
8485
|
const policyVersion = this.deps.currentPolicyVersion();
|
|
6739
8486
|
let recallSource = "none";
|
|
6740
8487
|
let recalledMemoryCount = 0;
|
|
@@ -6875,7 +8622,7 @@ var RecallInternalCoordinator = class {
|
|
|
6875
8622
|
const graphExpandedResultPaths = /* @__PURE__ */ new Set();
|
|
6876
8623
|
const graphSourceLabelsForPath = (resultPath) => {
|
|
6877
8624
|
const labels = [];
|
|
6878
|
-
const normalizedPath = resultPath.split(
|
|
8625
|
+
const normalizedPath = resultPath.split(path10.sep).join("/");
|
|
6879
8626
|
const isEntityPath = normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/");
|
|
6880
8627
|
if (graphBaselinePaths.has(resultPath)) labels.push("baseline");
|
|
6881
8628
|
if (graphExpandedResultPaths.has(resultPath))
|
|
@@ -8392,16 +10139,16 @@ ${formatted}`;
|
|
|
8392
10139
|
if (!resolveRecallAuxiliaryCapabilities(this.deps.config).compactionReset) return null;
|
|
8393
10140
|
const workspaceDir = compactionWorkspaceDir || this.deps.config.workspaceDir || defaultWorkspaceDir();
|
|
8394
10141
|
const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
|
|
8395
|
-
const signalPath =
|
|
10142
|
+
const signalPath = path10.join(
|
|
8396
10143
|
workspaceDir,
|
|
8397
10144
|
`.compaction-reset-signal-${safeSessionKey}`
|
|
8398
10145
|
);
|
|
8399
|
-
const bootPath =
|
|
10146
|
+
const bootPath = path10.join(workspaceDir, "BOOT.md");
|
|
8400
10147
|
try {
|
|
8401
10148
|
const signalStat = await stat(signalPath).catch(() => null);
|
|
8402
10149
|
if (!signalStat) return null;
|
|
8403
10150
|
const signalAge = Date.now() - signalStat.mtimeMs;
|
|
8404
|
-
const signalData = JSON.parse(await
|
|
10151
|
+
const signalData = JSON.parse(await readFile3(signalPath, "utf-8"));
|
|
8405
10152
|
if (signalData.sessionKey !== effectiveSessionKey) {
|
|
8406
10153
|
log.debug(
|
|
8407
10154
|
`recall: compaction signal is for ${signalData.sessionKey}, not ${effectiveSessionKey} \u2014 skipping`
|
|
@@ -8412,7 +10159,7 @@ ${formatted}`;
|
|
|
8412
10159
|
log.debug(
|
|
8413
10160
|
`recall: stale compaction signal (${Math.round(signalAge / 1e3)}s old), skipping`
|
|
8414
10161
|
);
|
|
8415
|
-
await
|
|
10162
|
+
await unlink2(signalPath).catch(() => {
|
|
8416
10163
|
});
|
|
8417
10164
|
return null;
|
|
8418
10165
|
}
|
|
@@ -8421,7 +10168,7 @@ ${formatted}`;
|
|
|
8421
10168
|
|
|
8422
10169
|
`;
|
|
8423
10170
|
try {
|
|
8424
|
-
const bootContent = await
|
|
10171
|
+
const bootContent = await readFile3(bootPath, "utf-8");
|
|
8425
10172
|
section += "### BOOT.md (working state before compaction)\n\n";
|
|
8426
10173
|
section += bootContent + "\n";
|
|
8427
10174
|
} catch {
|
|
@@ -8432,12 +10179,12 @@ ${formatted}`;
|
|
|
8432
10179
|
log.info(
|
|
8433
10180
|
`recall: injected compaction reset context for ${effectiveSessionKey}`
|
|
8434
10181
|
);
|
|
8435
|
-
await
|
|
10182
|
+
await unlink2(signalPath).catch(() => {
|
|
8436
10183
|
});
|
|
8437
10184
|
return section;
|
|
8438
10185
|
} catch (err) {
|
|
8439
10186
|
log.debug("recall: compaction signal check failed:", err);
|
|
8440
|
-
await
|
|
10187
|
+
await unlink2(signalPath).catch(() => {
|
|
8441
10188
|
});
|
|
8442
10189
|
return null;
|
|
8443
10190
|
}
|
|
@@ -10630,11 +12377,11 @@ ${tmtNode.summary}`
|
|
|
10630
12377
|
};
|
|
10631
12378
|
|
|
10632
12379
|
// src/orchestration/recall-search-pipeline.ts
|
|
10633
|
-
import { createHash as
|
|
10634
|
-
import
|
|
12380
|
+
import { createHash as createHash6 } from "crypto";
|
|
12381
|
+
import path12 from "path";
|
|
10635
12382
|
|
|
10636
12383
|
// src/orchestration/recall-search-prefilter.ts
|
|
10637
|
-
import
|
|
12384
|
+
import path11 from "path";
|
|
10638
12385
|
async function fetchActiveArtifactsForNamespace(deps, namespace, prompt, targetCount, options = {}) {
|
|
10639
12386
|
const storage = await deps.storageRouter.storageFor(namespace);
|
|
10640
12387
|
let fetchLimit = computeArtifactCandidateFetchLimit(targetCount);
|
|
@@ -10763,7 +12510,7 @@ async function searchEmbeddingFallback(deps, query, limit) {
|
|
|
10763
12510
|
if (hits.length === 0) return [];
|
|
10764
12511
|
const results = [];
|
|
10765
12512
|
for (const hit of hits) {
|
|
10766
|
-
const fullPath =
|
|
12513
|
+
const fullPath = path11.isAbsolute(hit.path) ? hit.path : path11.join(deps.config.memoryDir, hit.path);
|
|
10767
12514
|
const memory = await deps.storage.readMemoryByPath(fullPath);
|
|
10768
12515
|
if (!memory) continue;
|
|
10769
12516
|
results.push({
|
|
@@ -10851,7 +12598,7 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
10851
12598
|
if (!options.onDebugSnapshot) return;
|
|
10852
12599
|
await options.onDebugSnapshot({
|
|
10853
12600
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10854
|
-
queryHash:
|
|
12601
|
+
queryHash: createHash6("sha256").update(prompt).digest("hex"),
|
|
10855
12602
|
queryLength: prompt.length,
|
|
10856
12603
|
collection: options.collection,
|
|
10857
12604
|
namespaces: options.recallNamespaces,
|
|
@@ -11131,7 +12878,7 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
11131
12878
|
const storage = await this.deps.storageRouter.storageFor(namespace);
|
|
11132
12879
|
const storageDir = typeof storage.dir === "string" && storage.dir ? storage.dir : null;
|
|
11133
12880
|
if (!storageDir) continue;
|
|
11134
|
-
const recallRoot =
|
|
12881
|
+
const recallRoot = path12.resolve(storageDir);
|
|
11135
12882
|
if (seenRecallRoots.has(recallRoot)) continue;
|
|
11136
12883
|
seenRecallRoots.add(recallRoot);
|
|
11137
12884
|
recallRoots.push(recallRoot);
|
|
@@ -11155,8 +12902,8 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
11155
12902
|
if (resolvedCold) scopedResults.push(resolvedCold.result);
|
|
11156
12903
|
continue;
|
|
11157
12904
|
}
|
|
11158
|
-
if (
|
|
11159
|
-
if (recallRoots.some((recallRoot) => isPathInsideStorageRoot(recallRoot,
|
|
12905
|
+
if (path12.isAbsolute(result.path)) {
|
|
12906
|
+
if (recallRoots.some((recallRoot) => isPathInsideStorageRoot(recallRoot, path12.resolve(result.path)))) {
|
|
11160
12907
|
scopedResults.push(result);
|
|
11161
12908
|
}
|
|
11162
12909
|
continue;
|
|
@@ -11719,7 +13466,7 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
11719
13466
|
};
|
|
11720
13467
|
|
|
11721
13468
|
// src/orchestration/turn-ingestion.ts
|
|
11722
|
-
import { createHash as
|
|
13469
|
+
import { createHash as createHash7, randomBytes } from "crypto";
|
|
11723
13470
|
var TurnIngestionCoordinator = class {
|
|
11724
13471
|
constructor(deps) {
|
|
11725
13472
|
this.deps = deps;
|
|
@@ -11904,7 +13651,7 @@ var TurnIngestionCoordinator = class {
|
|
|
11904
13651
|
const shouldUseStableBatchKey = turns.some(
|
|
11905
13652
|
(turn) => turn.persistProcessedFingerprint === true || typeof turn.turnFingerprint === "string" && turn.turnFingerprint.length > 0
|
|
11906
13653
|
);
|
|
11907
|
-
const stableBatchFingerprint = shouldUseStableBatchKey ?
|
|
13654
|
+
const stableBatchFingerprint = shouldUseStableBatchKey ? createHash7("sha256").update(
|
|
11908
13655
|
turns.map(
|
|
11909
13656
|
(turn) => [
|
|
11910
13657
|
turn.role,
|
|
@@ -12142,7 +13889,7 @@ var TurnIngestionCoordinator = class {
|
|
|
12142
13889
|
};
|
|
12143
13890
|
scheduleQueueWaitTimeout();
|
|
12144
13891
|
}
|
|
12145
|
-
this.deps.extractionQueueCoordinator.enqueue(async () => {
|
|
13892
|
+
const accepted = this.deps.extractionQueueCoordinator.enqueue(async () => {
|
|
12146
13893
|
if (settled) return;
|
|
12147
13894
|
if (typeof extractionDeadlineMs === "number" && extractionDeadlineMs <= Date.now()) {
|
|
12148
13895
|
settleTask(new ExtractionDeadlineError("queue_wait"));
|
|
@@ -12172,6 +13919,10 @@ var TurnIngestionCoordinator = class {
|
|
|
12172
13919
|
}
|
|
12173
13920
|
}
|
|
12174
13921
|
});
|
|
13922
|
+
if (!accepted) {
|
|
13923
|
+
settleTask(new Error("extraction queue is stopped"));
|
|
13924
|
+
return;
|
|
13925
|
+
}
|
|
12175
13926
|
log.debug(`queued extraction from ${reason}`);
|
|
12176
13927
|
}
|
|
12177
13928
|
/**
|
|
@@ -12245,8 +13996,8 @@ var TurnIngestionCoordinator = class {
|
|
|
12245
13996
|
};
|
|
12246
13997
|
|
|
12247
13998
|
// src/orchestration/recall-introspection.ts
|
|
12248
|
-
import { mkdir as mkdir5, readFile as
|
|
12249
|
-
import
|
|
13999
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
14000
|
+
import path14 from "path";
|
|
12250
14001
|
|
|
12251
14002
|
// src/taxonomy/default-taxonomy.ts
|
|
12252
14003
|
var DEFAULT_TAXONOMY = {
|
|
@@ -12368,8 +14119,8 @@ function generateResolverDocument(taxonomy) {
|
|
|
12368
14119
|
}
|
|
12369
14120
|
|
|
12370
14121
|
// src/taxonomy/taxonomy-loader.ts
|
|
12371
|
-
import { readFile as
|
|
12372
|
-
import
|
|
14122
|
+
import { readFile as readFile4, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
14123
|
+
import path13 from "path";
|
|
12373
14124
|
var TAXONOMY_DIR = ".taxonomy";
|
|
12374
14125
|
var TAXONOMY_FILE = "taxonomy.json";
|
|
12375
14126
|
var MAX_SLUG_LENGTH = 32;
|
|
@@ -12467,10 +14218,10 @@ function validateTaxonomy(taxonomy) {
|
|
|
12467
14218
|
}
|
|
12468
14219
|
}
|
|
12469
14220
|
async function loadTaxonomy(memoryDir) {
|
|
12470
|
-
const taxonomyPath =
|
|
14221
|
+
const taxonomyPath = path13.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
|
|
12471
14222
|
let raw;
|
|
12472
14223
|
try {
|
|
12473
|
-
raw = await
|
|
14224
|
+
raw = await readFile4(taxonomyPath, "utf-8");
|
|
12474
14225
|
} catch (err) {
|
|
12475
14226
|
if (err instanceof Error && err.code === "ENOENT") {
|
|
12476
14227
|
return structuredClone(DEFAULT_TAXONOMY);
|
|
@@ -12517,16 +14268,16 @@ async function loadTaxonomy(memoryDir) {
|
|
|
12517
14268
|
}
|
|
12518
14269
|
async function saveTaxonomy(memoryDir, taxonomy) {
|
|
12519
14270
|
validateTaxonomy(taxonomy);
|
|
12520
|
-
const dir =
|
|
14271
|
+
const dir = path13.join(memoryDir, TAXONOMY_DIR);
|
|
12521
14272
|
await mkdir4(dir, { recursive: true });
|
|
12522
|
-
const filePath =
|
|
14273
|
+
const filePath = path13.join(dir, TAXONOMY_FILE);
|
|
12523
14274
|
await writeFile4(filePath, JSON.stringify(taxonomy, null, 2) + "\n", "utf-8");
|
|
12524
14275
|
}
|
|
12525
14276
|
function getTaxonomyDir(memoryDir) {
|
|
12526
|
-
return
|
|
14277
|
+
return path13.join(memoryDir, TAXONOMY_DIR);
|
|
12527
14278
|
}
|
|
12528
14279
|
function getTaxonomyFilePath(memoryDir) {
|
|
12529
|
-
return
|
|
14280
|
+
return path13.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
|
|
12530
14281
|
}
|
|
12531
14282
|
|
|
12532
14283
|
// src/orchestration/recall-introspection.ts
|
|
@@ -12550,13 +14301,13 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12550
14301
|
}
|
|
12551
14302
|
async getLastGraphRecallSnapshot(namespace) {
|
|
12552
14303
|
const storage = await this.deps.getStorage(namespace);
|
|
12553
|
-
const snapshotPath =
|
|
14304
|
+
const snapshotPath = path14.join(
|
|
12554
14305
|
storage.dir,
|
|
12555
14306
|
"state",
|
|
12556
14307
|
"last_graph_recall.json"
|
|
12557
14308
|
);
|
|
12558
14309
|
try {
|
|
12559
|
-
const raw = await
|
|
14310
|
+
const raw = await readFile5(snapshotPath, "utf-8");
|
|
12560
14311
|
const parsed = JSON.parse(raw);
|
|
12561
14312
|
if (!parsed || typeof parsed !== "object") return null;
|
|
12562
14313
|
return {
|
|
@@ -12589,9 +14340,9 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12589
14340
|
}
|
|
12590
14341
|
async getLastIntentSnapshot(namespace) {
|
|
12591
14342
|
const storage = await this.deps.getStorage(namespace);
|
|
12592
|
-
const snapshotPath =
|
|
14343
|
+
const snapshotPath = path14.join(storage.dir, "state", "last_intent.json");
|
|
12593
14344
|
try {
|
|
12594
|
-
const raw = await
|
|
14345
|
+
const raw = await readFile5(snapshotPath, "utf-8");
|
|
12595
14346
|
const parsed = JSON.parse(raw);
|
|
12596
14347
|
if (!parsed || typeof parsed !== "object") return null;
|
|
12597
14348
|
const graphDecision = parsed.graphDecision && typeof parsed.graphDecision === "object" ? parsed.graphDecision : void 0;
|
|
@@ -12622,13 +14373,13 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12622
14373
|
}
|
|
12623
14374
|
async getLastQmdRecallSnapshot(namespace) {
|
|
12624
14375
|
const storage = await this.deps.getStorage(namespace);
|
|
12625
|
-
const snapshotPath =
|
|
14376
|
+
const snapshotPath = path14.join(
|
|
12626
14377
|
storage.dir,
|
|
12627
14378
|
"state",
|
|
12628
14379
|
"last_qmd_recall.json"
|
|
12629
14380
|
);
|
|
12630
14381
|
try {
|
|
12631
|
-
const raw = await
|
|
14382
|
+
const raw = await readFile5(snapshotPath, "utf-8");
|
|
12632
14383
|
const parsed = JSON.parse(raw);
|
|
12633
14384
|
if (!parsed || typeof parsed !== "object") return null;
|
|
12634
14385
|
return {
|
|
@@ -12914,12 +14665,12 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12914
14665
|
}
|
|
12915
14666
|
async recordLastIntentSnapshot(options) {
|
|
12916
14667
|
try {
|
|
12917
|
-
const snapshotPath =
|
|
14668
|
+
const snapshotPath = path14.join(
|
|
12918
14669
|
options.storage.dir,
|
|
12919
14670
|
"state",
|
|
12920
14671
|
"last_intent.json"
|
|
12921
14672
|
);
|
|
12922
|
-
await mkdir5(
|
|
14673
|
+
await mkdir5(path14.dirname(snapshotPath), { recursive: true });
|
|
12923
14674
|
await writeFile5(
|
|
12924
14675
|
snapshotPath,
|
|
12925
14676
|
JSON.stringify(options.snapshot, null, 2),
|
|
@@ -12931,12 +14682,12 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12931
14682
|
}
|
|
12932
14683
|
async recordLastQmdRecallSnapshot(options) {
|
|
12933
14684
|
try {
|
|
12934
|
-
const snapshotPath =
|
|
14685
|
+
const snapshotPath = path14.join(
|
|
12935
14686
|
options.storage.dir,
|
|
12936
14687
|
"state",
|
|
12937
14688
|
"last_qmd_recall.json"
|
|
12938
14689
|
);
|
|
12939
|
-
await mkdir5(
|
|
14690
|
+
await mkdir5(path14.dirname(snapshotPath), { recursive: true });
|
|
12940
14691
|
await writeFile5(
|
|
12941
14692
|
snapshotPath,
|
|
12942
14693
|
JSON.stringify(options.snapshot, null, 2),
|
|
@@ -12951,8 +14702,8 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12951
14702
|
const stateDir = await this.deps.resolveStateDirForNamespace(
|
|
12952
14703
|
options.namespace
|
|
12953
14704
|
);
|
|
12954
|
-
const snapshotPath =
|
|
12955
|
-
await mkdir5(
|
|
14705
|
+
const snapshotPath = path14.join(stateDir, "last_intent.json");
|
|
14706
|
+
await mkdir5(path14.dirname(snapshotPath), { recursive: true });
|
|
12956
14707
|
await writeFile5(
|
|
12957
14708
|
snapshotPath,
|
|
12958
14709
|
JSON.stringify(options.snapshot, null, 2),
|
|
@@ -12965,8 +14716,8 @@ var RecallIntrospectionCoordinator = class {
|
|
|
12965
14716
|
};
|
|
12966
14717
|
|
|
12967
14718
|
// src/orchestration/orchestrator-init.ts
|
|
12968
|
-
import { readdir as readdir2, stat as stat2, unlink as
|
|
12969
|
-
import
|
|
14719
|
+
import { readdir as readdir2, stat as stat2, unlink as unlink3 } from "fs/promises";
|
|
14720
|
+
import path15 from "path";
|
|
12970
14721
|
var OrchestratorInitCoordinator = class {
|
|
12971
14722
|
constructor(deps) {
|
|
12972
14723
|
this.deps = deps;
|
|
@@ -13070,10 +14821,10 @@ var OrchestratorInitCoordinator = class {
|
|
|
13070
14821
|
const files = await readdir2(wsDir).catch(() => []);
|
|
13071
14822
|
for (const f of files) {
|
|
13072
14823
|
if (!f.startsWith(".compaction-reset-signal-")) continue;
|
|
13073
|
-
const fp =
|
|
14824
|
+
const fp = path15.join(wsDir, f);
|
|
13074
14825
|
const s = await stat2(fp).catch(() => null);
|
|
13075
14826
|
if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
|
|
13076
|
-
await
|
|
14827
|
+
await unlink3(fp).catch(() => {
|
|
13077
14828
|
});
|
|
13078
14829
|
log.debug(`initialize: removed stale compaction signal ${f}`);
|
|
13079
14830
|
}
|
|
@@ -13422,7 +15173,7 @@ var OrchestratorInitCoordinator = class {
|
|
|
13422
15173
|
};
|
|
13423
15174
|
|
|
13424
15175
|
// src/orchestration/persistence-index.ts
|
|
13425
|
-
import
|
|
15176
|
+
import path16 from "path";
|
|
13426
15177
|
var PersistenceIndexCoordinator = class {
|
|
13427
15178
|
constructor(deps) {
|
|
13428
15179
|
this.deps = deps;
|
|
@@ -13608,7 +15359,7 @@ var PersistenceIndexCoordinator = class {
|
|
|
13608
15359
|
const allMems = allMemsForGraph ?? [];
|
|
13609
15360
|
for (const m of allMems) {
|
|
13610
15361
|
if (m.frontmatter.entityRef === entityRef) {
|
|
13611
|
-
const rel =
|
|
15362
|
+
const rel = path16.relative(storage.dir, m.path);
|
|
13612
15363
|
if (rel !== memoryRelPath) entitySiblings.push(rel);
|
|
13613
15364
|
}
|
|
13614
15365
|
}
|
|
@@ -13756,8 +15507,8 @@ var PersistenceIndexCoordinator = class {
|
|
|
13756
15507
|
};
|
|
13757
15508
|
|
|
13758
15509
|
// src/orchestration/workspace-ops.ts
|
|
13759
|
-
import { lstat as lstat2, mkdir as mkdir6, readFile as
|
|
13760
|
-
import
|
|
15510
|
+
import { lstat as lstat2, mkdir as mkdir6, readFile as readFile6, readdir as readdir3, realpath as realpath2, writeFile as writeFile6 } from "fs/promises";
|
|
15511
|
+
import path17 from "path";
|
|
13761
15512
|
|
|
13762
15513
|
// src/maintenance/pattern-reinforcement.ts
|
|
13763
15514
|
function patternReinforcementKey(content) {
|
|
@@ -13797,22 +15548,22 @@ function memoryInstant(memory) {
|
|
|
13797
15548
|
const parsed = Date.parse(stamp);
|
|
13798
15549
|
return Number.isFinite(parsed) ? parsed : null;
|
|
13799
15550
|
}
|
|
13800
|
-
var
|
|
15551
|
+
var ACTIVE_STATUS2 = "active";
|
|
13801
15552
|
async function runPatternReinforcement(storage, options) {
|
|
13802
15553
|
const minCount = Math.max(2, Math.floor(options.minCount));
|
|
13803
15554
|
const targetCategories = new Set(options.categories);
|
|
13804
15555
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
13805
15556
|
const nowIso = now().toISOString();
|
|
13806
15557
|
if (targetCategories.size === 0) {
|
|
13807
|
-
return
|
|
15558
|
+
return emptyResult2();
|
|
13808
15559
|
}
|
|
13809
15560
|
const memories = await storage.readAllMemories();
|
|
13810
15561
|
const eligible = memories.filter((m) => {
|
|
13811
15562
|
if (!targetCategories.has(m.frontmatter.category)) return false;
|
|
13812
|
-
const status = m.frontmatter.status ??
|
|
13813
|
-
return status ===
|
|
15563
|
+
const status = m.frontmatter.status ?? ACTIVE_STATUS2;
|
|
15564
|
+
return status === ACTIVE_STATUS2 || status === "superseded";
|
|
13814
15565
|
});
|
|
13815
|
-
if (eligible.length === 0) return
|
|
15566
|
+
if (eligible.length === 0) return emptyResult2();
|
|
13816
15567
|
const clusters = clusterByKey(
|
|
13817
15568
|
eligible,
|
|
13818
15569
|
(m) => patternReinforcementClusterKey(m.frontmatter.category, m.content)
|
|
@@ -13826,8 +15577,8 @@ async function runPatternReinforcement(storage, options) {
|
|
|
13826
15577
|
for (const cluster of clusters.values()) {
|
|
13827
15578
|
if (cluster.length < minCount) continue;
|
|
13828
15579
|
const activeMembers = cluster.filter((m) => {
|
|
13829
|
-
const status = m.frontmatter.status ??
|
|
13830
|
-
return status ===
|
|
15580
|
+
const status = m.frontmatter.status ?? ACTIVE_STATUS2;
|
|
15581
|
+
return status === ACTIVE_STATUS2;
|
|
13831
15582
|
});
|
|
13832
15583
|
if (activeMembers.length === 0) continue;
|
|
13833
15584
|
result.clustersFound += 1;
|
|
@@ -13898,7 +15649,7 @@ async function runPatternReinforcement(storage, options) {
|
|
|
13898
15649
|
}
|
|
13899
15650
|
return result;
|
|
13900
15651
|
}
|
|
13901
|
-
function
|
|
15652
|
+
function emptyResult2() {
|
|
13902
15653
|
return {
|
|
13903
15654
|
clustersFound: 0,
|
|
13904
15655
|
canonicalsUpdated: 0,
|
|
@@ -15664,8 +17415,8 @@ var MeetingsService = class {
|
|
|
15664
17415
|
// src/orchestration/workspace-ops.ts
|
|
15665
17416
|
var MEETINGS_BUILD_DEBOUNCE_MS = 5e3;
|
|
15666
17417
|
function matchesMemoryPath(candidatePath, requestedPath, memoryDir) {
|
|
15667
|
-
const normalizedCandidate =
|
|
15668
|
-
return normalizedCandidate ===
|
|
17418
|
+
const normalizedCandidate = path17.resolve(candidatePath);
|
|
17419
|
+
return normalizedCandidate === path17.resolve(requestedPath) || normalizedCandidate === path17.resolve(memoryDir, requestedPath);
|
|
15669
17420
|
}
|
|
15670
17421
|
function accessTrackingPathCandidates(storageDir, memoryPath) {
|
|
15671
17422
|
const candidates = qmdResultPathCandidates(storageDir, memoryPath);
|
|
@@ -15673,12 +17424,12 @@ function accessTrackingPathCandidates(storageDir, memoryPath) {
|
|
|
15673
17424
|
if (!parts) return candidates;
|
|
15674
17425
|
candidates.push(
|
|
15675
17426
|
...qmdResultPathCandidates(storageDir, parts.relativePath),
|
|
15676
|
-
...qmdResultPathCandidates(
|
|
17427
|
+
...qmdResultPathCandidates(path17.join(storageDir, "cold"), parts.relativePath)
|
|
15677
17428
|
);
|
|
15678
17429
|
return [...new Set(candidates)];
|
|
15679
17430
|
}
|
|
15680
17431
|
function canonicalMemoryPath(memoryPath, memoryDir) {
|
|
15681
|
-
return
|
|
17432
|
+
return path17.resolve(path17.isAbsolute(memoryPath) ? memoryPath : path17.resolve(memoryDir, memoryPath));
|
|
15682
17433
|
}
|
|
15683
17434
|
var WorkspaceOpsCoordinator = class {
|
|
15684
17435
|
constructor(deps) {
|
|
@@ -15760,15 +17511,15 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15760
17511
|
this.deps.lastFileHygieneRunAtMs = now;
|
|
15761
17512
|
if (hygiene.rotateEnabled) {
|
|
15762
17513
|
for (const rel of hygiene.rotatePaths) {
|
|
15763
|
-
const abs =
|
|
17514
|
+
const abs = path17.isAbsolute(rel) ? rel : path17.join(this.deps.config.workspaceDir, rel);
|
|
15764
17515
|
try {
|
|
15765
|
-
const raw = await
|
|
17516
|
+
const raw = await readFile6(abs, "utf-8");
|
|
15766
17517
|
if (raw.length > hygiene.rotateMaxBytes) {
|
|
15767
|
-
const archiveDir =
|
|
17518
|
+
const archiveDir = path17.join(
|
|
15768
17519
|
this.deps.config.workspaceDir,
|
|
15769
17520
|
hygiene.archiveDir
|
|
15770
17521
|
);
|
|
15771
|
-
const base =
|
|
17522
|
+
const base = path17.basename(abs);
|
|
15772
17523
|
const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
|
|
15773
17524
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
15774
17525
|
filePath: abs,
|
|
@@ -15793,8 +17544,8 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15793
17544
|
log.warn(w.message);
|
|
15794
17545
|
}
|
|
15795
17546
|
if (hygiene.warningsLogEnabled && warnings.length > 0) {
|
|
15796
|
-
const fp =
|
|
15797
|
-
await mkdir6(
|
|
17547
|
+
const fp = path17.join(this.deps.config.memoryDir, hygiene.warningsLogPath);
|
|
17548
|
+
await mkdir6(path17.dirname(fp), { recursive: true });
|
|
15798
17549
|
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
15799
17550
|
const block = `
|
|
15800
17551
|
|
|
@@ -15803,7 +17554,7 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15803
17554
|
` + warnings.map((w) => `- ${w.message}`).join("\n") + "\n";
|
|
15804
17555
|
let existing = "";
|
|
15805
17556
|
try {
|
|
15806
|
-
existing = await
|
|
17557
|
+
existing = await readFile6(fp, "utf-8");
|
|
15807
17558
|
} catch {
|
|
15808
17559
|
existing = "# Engram File Hygiene Warnings\n";
|
|
15809
17560
|
}
|
|
@@ -15834,7 +17585,7 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15834
17585
|
for (const categoryDir of RECALL_FALLBACK_DIRS) {
|
|
15835
17586
|
if (memoryRootReal === null) break;
|
|
15836
17587
|
for (const date of datesToScan) {
|
|
15837
|
-
const dateDir =
|
|
17588
|
+
const dateDir = path17.join(storage.dir, categoryDir, date);
|
|
15838
17589
|
try {
|
|
15839
17590
|
const dirStat = await lstat2(dateDir);
|
|
15840
17591
|
if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) continue;
|
|
@@ -15843,10 +17594,10 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15843
17594
|
for (const entry of entries) {
|
|
15844
17595
|
if (entry.isSymbolicLink()) continue;
|
|
15845
17596
|
if (!entry.name.endsWith(".md")) continue;
|
|
15846
|
-
const fullPath =
|
|
17597
|
+
const fullPath = path17.join(dateDir, entry.name);
|
|
15847
17598
|
try {
|
|
15848
17599
|
assertPathInsideRoot(memoryRootReal, await realpath2(fullPath), fullPath);
|
|
15849
|
-
const raw = await
|
|
17600
|
+
const raw = await readFile6(fullPath, "utf-8");
|
|
15850
17601
|
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
15851
17602
|
if (!fmMatch) continue;
|
|
15852
17603
|
const fmBlock = fmMatch[1];
|
|
@@ -15865,7 +17616,7 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15865
17616
|
facts.push({
|
|
15866
17617
|
path: fullPath,
|
|
15867
17618
|
frontmatter: {
|
|
15868
|
-
id: fm.id ||
|
|
17619
|
+
id: fm.id || path17.basename(entry.name, ".md"),
|
|
15869
17620
|
category: fm.category || "fact",
|
|
15870
17621
|
created,
|
|
15871
17622
|
updated: fm.updated || created,
|
|
@@ -15888,15 +17639,15 @@ var WorkspaceOpsCoordinator = class {
|
|
|
15888
17639
|
return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
|
|
15889
17640
|
});
|
|
15890
17641
|
const hourlySummaries = [];
|
|
15891
|
-
const hourlyBaseDir =
|
|
17642
|
+
const hourlyBaseDir = path17.join(storage.dir, "summaries", "hourly");
|
|
15892
17643
|
try {
|
|
15893
17644
|
const sessionKeys = await readdir3(hourlyBaseDir, { withFileTypes: true });
|
|
15894
17645
|
for (const sk of sessionKeys) {
|
|
15895
17646
|
if (!sk.isDirectory()) continue;
|
|
15896
17647
|
for (const date of datesToScan) {
|
|
15897
|
-
const summaryFile =
|
|
17648
|
+
const summaryFile = path17.join(hourlyBaseDir, sk.name, `${date}.md`);
|
|
15898
17649
|
try {
|
|
15899
|
-
const raw = await
|
|
17650
|
+
const raw = await readFile6(summaryFile, "utf-8");
|
|
15900
17651
|
const filtered = filterHourlySummaryMarkdownForLocalDay(
|
|
15901
17652
|
raw,
|
|
15902
17653
|
date,
|
|
@@ -16358,18 +18109,18 @@ ${reflectionsContent.trim()}
|
|
|
16358
18109
|
// src/orchestration/namespace-read-fanout.ts
|
|
16359
18110
|
import { existsSync, readFileSync } from "fs";
|
|
16360
18111
|
import { stat as stat3 } from "fs/promises";
|
|
16361
|
-
import
|
|
18112
|
+
import path19 from "path";
|
|
16362
18113
|
|
|
16363
18114
|
// src/orchestration/orchestrator-namespace-scope.ts
|
|
16364
|
-
import
|
|
18115
|
+
import path18 from "path";
|
|
16365
18116
|
function computeSemanticDedupScope(targetStorage, config) {
|
|
16366
18117
|
if (!resolveNamespaceCapabilities(config).namespaces) return {};
|
|
16367
|
-
const memoryDir =
|
|
16368
|
-
const storageDir =
|
|
18118
|
+
const memoryDir = path18.resolve(config.memoryDir);
|
|
18119
|
+
const storageDir = path18.resolve(targetStorage.dir);
|
|
16369
18120
|
if (storageDir === memoryDir) {
|
|
16370
18121
|
return { pathExcludePrefixes: ["namespaces/"] };
|
|
16371
18122
|
}
|
|
16372
|
-
let rel =
|
|
18123
|
+
let rel = path18.relative(memoryDir, storageDir);
|
|
16373
18124
|
if (!rel || rel.startsWith("..")) {
|
|
16374
18125
|
log.debug(
|
|
16375
18126
|
`semantic dedup: target storage dir ${storageDir} is outside memoryDir ${memoryDir}; scoping lookup to absolute path prefix`
|
|
@@ -16422,7 +18173,7 @@ var NamespaceReadFanoutCoordinator = class {
|
|
|
16422
18173
|
loadNamespaceStorageDirHintsFromCatalog() {
|
|
16423
18174
|
if (this.deps.namespaceStorageDirHintsLoaded || !this.deps.namespaceCatalog.enabled) return;
|
|
16424
18175
|
this.deps.namespaceStorageDirHintsLoaded = true;
|
|
16425
|
-
const catalogPath =
|
|
18176
|
+
const catalogPath = path19.join(this.deps.config.memoryDir, "state", "namespaces.jsonl");
|
|
16426
18177
|
if (!existsSync(catalogPath)) return;
|
|
16427
18178
|
let body;
|
|
16428
18179
|
try {
|
|
@@ -16459,7 +18210,7 @@ var NamespaceReadFanoutCoordinator = class {
|
|
|
16459
18210
|
if (!this.deps.storageDirMatchesNamespaceHint(record.namespace, record.storageDir)) {
|
|
16460
18211
|
continue;
|
|
16461
18212
|
}
|
|
16462
|
-
const resolvedStorageDir =
|
|
18213
|
+
const resolvedStorageDir = path19.resolve(record.storageDir);
|
|
16463
18214
|
const current = preferredByStorageDir.get(resolvedStorageDir);
|
|
16464
18215
|
preferredByStorageDir.set(
|
|
16465
18216
|
resolvedStorageDir,
|
|
@@ -16643,12 +18394,12 @@ var NamespaceReadFanoutCoordinator = class {
|
|
|
16643
18394
|
}
|
|
16644
18395
|
async resolveStateDirForNamespace(namespace) {
|
|
16645
18396
|
if (!resolveNamespaceCapabilities(this.deps.config).namespaces) {
|
|
16646
|
-
return
|
|
18397
|
+
return path19.join(this.deps.config.memoryDir, "state");
|
|
16647
18398
|
}
|
|
16648
18399
|
if (namespace !== this.deps.config.defaultNamespace) {
|
|
16649
|
-
return
|
|
18400
|
+
return path19.join(this.deps.config.memoryDir, "namespaces", namespace, "state");
|
|
16650
18401
|
}
|
|
16651
|
-
const candidate =
|
|
18402
|
+
const candidate = path19.join(
|
|
16652
18403
|
this.deps.config.memoryDir,
|
|
16653
18404
|
"namespaces",
|
|
16654
18405
|
this.deps.config.defaultNamespace
|
|
@@ -16656,11 +18407,11 @@ var NamespaceReadFanoutCoordinator = class {
|
|
|
16656
18407
|
try {
|
|
16657
18408
|
const candidateStat = await stat3(candidate);
|
|
16658
18409
|
if (candidateStat.isDirectory()) {
|
|
16659
|
-
return
|
|
18410
|
+
return path19.join(candidate, "state");
|
|
16660
18411
|
}
|
|
16661
18412
|
} catch {
|
|
16662
18413
|
}
|
|
16663
|
-
return
|
|
18414
|
+
return path19.join(this.deps.config.memoryDir, "state");
|
|
16664
18415
|
}
|
|
16665
18416
|
namespaceFromPath(p) {
|
|
16666
18417
|
if (!resolveNamespaceCapabilities(this.deps.config).namespaces) return this.deps.config.defaultNamespace;
|
|
@@ -17097,25 +18848,27 @@ var Orchestrator = class {
|
|
|
17097
18848
|
passiveCorrectionDedup = /* @__PURE__ */ new Set();
|
|
17098
18849
|
_passiveCorrectionService = null;
|
|
17099
18850
|
passiveCorrectionTelemetry = { detected: 0, queued: 0, autoApplied: 0, suppressedReasonCounts: {} };
|
|
17100
|
-
|
|
17101
|
-
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
|
|
17105
|
-
|
|
17106
|
-
|
|
18851
|
+
_dependencyPropagationDelivery;
|
|
18852
|
+
_dependencyPropagationRecovery = null;
|
|
18853
|
+
destroyed = false;
|
|
18854
|
+
get dependencyPropagationDelivery() {
|
|
18855
|
+
if (this.destroyed) throw new Error("orchestrator has been destroyed");
|
|
18856
|
+
if (!this._dependencyPropagationDelivery) {
|
|
18857
|
+
this._dependencyPropagationDelivery = new DependencyPropagationDelivery({
|
|
18858
|
+
queueRoot: path20.join(this.config.memoryDir, "state", "dependency-propagation"),
|
|
18859
|
+
config: this.config,
|
|
18860
|
+
extraction: this.extraction,
|
|
18861
|
+
getStorage: (namespace) => this.getStorage(namespace),
|
|
18862
|
+
readQueueFile: (filePath) => this.storage.readDependencyPropagationQueueFile(filePath),
|
|
18863
|
+
writeQueueFile: (filePath, content) => this.storage.writeDependencyPropagationQueueFile(filePath, content),
|
|
18864
|
+
autoStart: true,
|
|
18865
|
+
replayReconciliation: createDependencyPropagationReplayReconciliation({ getStorage: (namespace) => this.getStorage(namespace), indexPersistedMemory: (storage, memoryId) => this.indexPersistedMemory(storage, memoryId), removeFromIndex: (memoryId) => this.embeddingFallback.removeFromIndex(memoryId), config: this.config })
|
|
18866
|
+
});
|
|
18867
|
+
}
|
|
18868
|
+
return this._dependencyPropagationDelivery;
|
|
18869
|
+
}
|
|
17107
18870
|
extractionQueueCoordinator;
|
|
17108
|
-
/**
|
|
17109
|
-
* Extraction-run pipeline coordinator (issue #1526 seam 15). Owns
|
|
17110
|
-
* `runExtraction` + dedupe fingerprint helpers + processed-fingerprint
|
|
17111
|
-
* recording. The orchestrator delegates and injects its own methods.
|
|
17112
|
-
*/
|
|
17113
18871
|
_extractionRunCoordinator;
|
|
17114
|
-
/**
|
|
17115
|
-
* Lazy getter: creates the ExtractionRunCoordinator on first access using
|
|
17116
|
-
* the orchestrator's live field references. Supports Object.create(prototype)
|
|
17117
|
-
* tests that set fields post-construction without invoking the constructor.
|
|
17118
|
-
*/
|
|
17119
18872
|
get extractionRunCoordinator() {
|
|
17120
18873
|
if (!this._extractionRunCoordinator) {
|
|
17121
18874
|
this._extractionRunCoordinator = new ExtractionRunCoordinator({
|
|
@@ -17140,15 +18893,6 @@ var Orchestrator = class {
|
|
|
17140
18893
|
}
|
|
17141
18894
|
return this._extractionRunCoordinator;
|
|
17142
18895
|
}
|
|
17143
|
-
/**
|
|
17144
|
-
* Consolidation-run coordinator (issue #1526 seam 17). Owns
|
|
17145
|
-
* the full consolidation maintenance pass (LLM merge/invalidate/update,
|
|
17146
|
-
* entity merge, commitment/TTL cleanup, lifecycle policy, compression
|
|
17147
|
-
* guideline learning, tier migration, fact archival, semantic consolidation,
|
|
17148
|
-
* identity consolidation, profile consolidation, summarization, topic
|
|
17149
|
-
* extraction, TMT rebuild). The orchestrator delegates and injects
|
|
17150
|
-
* its own methods + coordinators.
|
|
17151
|
-
*/
|
|
17152
18896
|
_consolidationRunCoordinator;
|
|
17153
18897
|
get consolidationRunCoordinator() {
|
|
17154
18898
|
if (!this._consolidationRunCoordinator) {
|
|
@@ -17158,6 +18902,8 @@ var Orchestrator = class {
|
|
|
17158
18902
|
getStorageRouter: () => this.storageRouter,
|
|
17159
18903
|
getNamespaceCatalog: () => this.namespaceCatalog,
|
|
17160
18904
|
getExtraction: () => this.extraction,
|
|
18905
|
+
storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
|
|
18906
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
|
|
17161
18907
|
embeddingFallback: this.embeddingFallback,
|
|
17162
18908
|
tmtBuilder: this.tmtBuilder,
|
|
17163
18909
|
consolidationObservers: this.consolidationObservers,
|
|
@@ -17176,11 +18922,6 @@ var Orchestrator = class {
|
|
|
17176
18922
|
}
|
|
17177
18923
|
return this._consolidationRunCoordinator;
|
|
17178
18924
|
}
|
|
17179
|
-
/**
|
|
17180
|
-
* Extraction-persist coordinator (issue #1526 seam 16). Owns the
|
|
17181
|
-
* `persistExtraction` pipeline. Lazy: created on first access so
|
|
17182
|
-
* Object.create(prototype) tests that set fields post-construction work.
|
|
17183
|
-
*/
|
|
17184
18925
|
_extractionPersistCoordinator;
|
|
17185
18926
|
get extractionPersistCoordinator() {
|
|
17186
18927
|
if (!this._extractionPersistCoordinator) {
|
|
@@ -17188,8 +18929,8 @@ var Orchestrator = class {
|
|
|
17188
18929
|
config: this.config,
|
|
17189
18930
|
getStorageRouter: () => this.storageRouter,
|
|
17190
18931
|
getThreading: () => this.threading,
|
|
18932
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
|
|
17191
18933
|
getLocalLlm: () => this.localLlm,
|
|
17192
|
-
getExtraction: () => this.extraction,
|
|
17193
18934
|
getQmd: () => this.qmd,
|
|
17194
18935
|
getJudgeVerdictCache: () => this.judgeVerdictCache,
|
|
17195
18936
|
getJudgeDeferCounts: () => this.judgeDeferCounts,
|
|
@@ -17309,12 +19050,21 @@ var Orchestrator = class {
|
|
|
17309
19050
|
*/
|
|
17310
19051
|
async destroy() {
|
|
17311
19052
|
this.abortDeferredInit();
|
|
19053
|
+
this.extractionQueueCoordinator.stopAccepting();
|
|
17312
19054
|
if (this.wearablesAutoSyncHandle) {
|
|
17313
19055
|
await this.wearablesAutoSyncHandle.stop();
|
|
17314
19056
|
this.wearablesAutoSyncHandle = null;
|
|
17315
19057
|
}
|
|
17316
19058
|
for (const svc of this.meetingsServiceByNamespace.values()) svc.dispose();
|
|
17317
19059
|
await this.maintenanceScheduler.dispose();
|
|
19060
|
+
await this._dependencyPropagationRecovery;
|
|
19061
|
+
this._dependencyPropagationRecovery = null;
|
|
19062
|
+
const extractionDrainError = await drainExtractionAndShutdownDependencyPropagation(
|
|
19063
|
+
this.extractionQueueCoordinator,
|
|
19064
|
+
this._dependencyPropagationDelivery
|
|
19065
|
+
);
|
|
19066
|
+
this.destroyed = true;
|
|
19067
|
+
this._dependencyPropagationDelivery = void 0;
|
|
17318
19068
|
await drainRecallWrites(this);
|
|
17319
19069
|
await this.persistenceIndexCoordinator.drainContentHashReconcileRetries().catch((err) => log.warn(`content-hash reconcile drain failed during destroy: ${err}`));
|
|
17320
19070
|
let bufferFlushError;
|
|
@@ -17338,6 +19088,12 @@ var Orchestrator = class {
|
|
|
17338
19088
|
);
|
|
17339
19089
|
throw bufferFlushError;
|
|
17340
19090
|
}
|
|
19091
|
+
if (extractionDrainError !== void 0) {
|
|
19092
|
+
log.warn(
|
|
19093
|
+
`orchestrator.destroy: extraction queue did not drain before teardown: ${String(extractionDrainError)}`
|
|
19094
|
+
);
|
|
19095
|
+
throw extractionDrainError;
|
|
19096
|
+
}
|
|
17341
19097
|
}
|
|
17342
19098
|
}
|
|
17343
19099
|
/** Set per-session workspace for the next recall() call (compaction reset). @internal */
|
|
@@ -17441,7 +19197,7 @@ var Orchestrator = class {
|
|
|
17441
19197
|
const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
|
|
17442
19198
|
if (ns !== defaultNs && !isSafeRouteNamespace(ns)) return;
|
|
17443
19199
|
if (!this.storageDirMatchesNamespaceHint(ns, storageDir)) return;
|
|
17444
|
-
const resolvedStorageDir =
|
|
19200
|
+
const resolvedStorageDir = path20.resolve(storageDir);
|
|
17445
19201
|
let hints = this.namespaceStorageDirHints.get(resolvedStorageDir);
|
|
17446
19202
|
if (!hints) {
|
|
17447
19203
|
hints = /* @__PURE__ */ new Set();
|
|
@@ -17452,21 +19208,21 @@ var Orchestrator = class {
|
|
|
17452
19208
|
storageDirMatchesNamespaceHint(namespace, storageDir) {
|
|
17453
19209
|
const ns = normalizeNamespaceIdentity(namespace);
|
|
17454
19210
|
if (!ns) return false;
|
|
17455
|
-
const resolvedStorageDir =
|
|
17456
|
-
const resolvedMemoryDir =
|
|
19211
|
+
const resolvedStorageDir = path20.resolve(storageDir);
|
|
19212
|
+
const resolvedMemoryDir = path20.resolve(this.config.memoryDir);
|
|
17457
19213
|
const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
|
|
17458
19214
|
if (resolvedStorageDir === resolvedMemoryDir) return ns === defaultNs;
|
|
17459
|
-
const resolvedNamespacesDir =
|
|
19215
|
+
const resolvedNamespacesDir = path20.join(resolvedMemoryDir, "namespaces");
|
|
17460
19216
|
if (!isPathInsideStorageRoot(resolvedNamespacesDir, resolvedStorageDir)) return false;
|
|
17461
|
-
const rawRoot =
|
|
17462
|
-
const tokenRoot =
|
|
19217
|
+
const rawRoot = path20.resolve(resolvedNamespacesDir, ns);
|
|
19218
|
+
const tokenRoot = path20.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
|
|
17463
19219
|
return resolvedStorageDir === rawRoot || resolvedStorageDir === tokenRoot;
|
|
17464
19220
|
}
|
|
17465
19221
|
namespaceStorageDirHintOwnershipRank(record, resolvedStorageDir, configured) {
|
|
17466
|
-
if (resolvedStorageDir ===
|
|
19222
|
+
if (resolvedStorageDir === path20.resolve(this.config.memoryDir)) {
|
|
17467
19223
|
return record.namespace === normalizeNamespaceIdentity(this.config.defaultNamespace) ? 0 : 3;
|
|
17468
19224
|
}
|
|
17469
|
-
const leaf =
|
|
19225
|
+
const leaf = path20.basename(resolvedStorageDir);
|
|
17470
19226
|
const tokenOwnsRoot = namespaceIdentityToken(record.namespace) === leaf;
|
|
17471
19227
|
if (tokenOwnsRoot && configured.has(record.namespace)) return 0;
|
|
17472
19228
|
if (record.namespace === leaf) return 1;
|
|
@@ -17606,7 +19362,7 @@ var Orchestrator = class {
|
|
|
17606
19362
|
StorageManager.setScopedCacheInvalidationDefault(config.memoryDir, scopedCacheInvalidationEnabled);
|
|
17607
19363
|
this.profiler = new ProfilingCollector({
|
|
17608
19364
|
enabled: resolvePipelineProcessingCapabilities(this.config).profiling,
|
|
17609
|
-
storageDir: config.profilingStorageDir ||
|
|
19365
|
+
storageDir: config.profilingStorageDir || path20.join(config.memoryDir, "profiling"),
|
|
17610
19366
|
maxTraces: config.profilingMaxTraces
|
|
17611
19367
|
});
|
|
17612
19368
|
this.namespaceCatalog = new NamespaceCatalog(config);
|
|
@@ -17693,7 +19449,7 @@ var Orchestrator = class {
|
|
|
17693
19449
|
this.compounding = resolveConsolidationCapabilities(config).compounding ? new CompoundingEngine(config, this.storage) : void 0;
|
|
17694
19450
|
this.buffer = new SmartBuffer(config, this.storage);
|
|
17695
19451
|
this.transcript = new TranscriptManager(config);
|
|
17696
|
-
this.conversationIndexDir =
|
|
19452
|
+
this.conversationIndexDir = path20.join(
|
|
17697
19453
|
config.memoryDir,
|
|
17698
19454
|
"conversation-index",
|
|
17699
19455
|
"chunks"
|
|
@@ -17720,7 +19476,9 @@ var Orchestrator = class {
|
|
|
17720
19476
|
extractMemoryIdsFromResults: (results) => this.extractMemoryIdsFromResults(results),
|
|
17721
19477
|
namespaceFromPath: (p) => this.namespaceFromPath(p),
|
|
17722
19478
|
storageForNamespace: (namespace) => this.storageRouter.storageFor(namespace),
|
|
17723
|
-
getExtraction: () => this.extraction
|
|
19479
|
+
getExtraction: () => this.extraction,
|
|
19480
|
+
storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
|
|
19481
|
+
getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery
|
|
17724
19482
|
});
|
|
17725
19483
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
17726
19484
|
this.graphRecallCoordinator = new GraphRecallCoordinator({
|
|
@@ -17841,7 +19599,7 @@ var Orchestrator = class {
|
|
|
17841
19599
|
saveContentHashIndexes: () => this.saveContentHashIndexes()
|
|
17842
19600
|
});
|
|
17843
19601
|
this.threading = new ThreadingManager(
|
|
17844
|
-
|
|
19602
|
+
path20.join(config.memoryDir, "threads"),
|
|
17845
19603
|
config.threadingGapMinutes
|
|
17846
19604
|
);
|
|
17847
19605
|
const lifecycleCaps = resolveMemoryLifecycleCapabilities(config);
|
|
@@ -17921,7 +19679,7 @@ var Orchestrator = class {
|
|
|
17921
19679
|
utilityPromoteThresholdDelta: this.utilityRuntimeValues?.promoteThresholdDelta ?? 0,
|
|
17922
19680
|
utilityDemoteThresholdDelta: this.utilityRuntimeValues?.demoteThresholdDelta ?? 0
|
|
17923
19681
|
};
|
|
17924
|
-
return
|
|
19682
|
+
return createHash8("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 12);
|
|
17925
19683
|
}
|
|
17926
19684
|
effectiveLifecycleThresholds() {
|
|
17927
19685
|
const archiveDecayThreshold = this.config.lifecycleArchiveDecayThreshold;
|
|
@@ -18022,7 +19780,13 @@ var Orchestrator = class {
|
|
|
18022
19780
|
return buildChainFollowupGenerator(this.fastLlmForRerank);
|
|
18023
19781
|
}
|
|
18024
19782
|
async initialize() {
|
|
18025
|
-
|
|
19783
|
+
this.extractionQueueCoordinator.resumeAccepting();
|
|
19784
|
+
await this.orchestratorInitCoordinator.initialize();
|
|
19785
|
+
if (isDependencyPropagationEnabled(this.config)) {
|
|
19786
|
+
this._dependencyPropagationRecovery = this.dependencyPropagationDelivery.recover().catch((err) => {
|
|
19787
|
+
log.warn(`dependency propagation startup recovery failed: ${err}`);
|
|
19788
|
+
});
|
|
19789
|
+
}
|
|
18026
19790
|
}
|
|
18027
19791
|
async deferredInitialize(signal) {
|
|
18028
19792
|
return this.orchestratorInitCoordinator.deferredInitialize(
|
|
@@ -19945,9 +21709,9 @@ function readStructuralSymbol(entry) {
|
|
|
19945
21709
|
const rec = entry;
|
|
19946
21710
|
const symbol = typeof rec.symbol === "string" ? rec.symbol.trim() : "";
|
|
19947
21711
|
if (!symbol) return null;
|
|
19948
|
-
const
|
|
21712
|
+
const path35 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
|
|
19949
21713
|
const kind = typeof rec.kind === "string" && rec.kind.length > 0 ? rec.kind : void 0;
|
|
19950
|
-
const item =
|
|
21714
|
+
const item = path35 !== void 0 && kind !== void 0 ? { symbol, path: path35, kind } : path35 !== void 0 ? { symbol, path: path35 } : kind !== void 0 ? { symbol, kind } : { symbol };
|
|
19951
21715
|
return item;
|
|
19952
21716
|
}
|
|
19953
21717
|
function classifySpawnError(err) {
|
|
@@ -20021,7 +21785,7 @@ var DEFAULT_GRACE_PERIOD_DAYS = 7;
|
|
|
20021
21785
|
|
|
20022
21786
|
// src/binary-lifecycle/backend.ts
|
|
20023
21787
|
import fsp from "fs/promises";
|
|
20024
|
-
import
|
|
21788
|
+
import path21 from "path";
|
|
20025
21789
|
var FilesystemBackend = class {
|
|
20026
21790
|
type = "filesystem";
|
|
20027
21791
|
basePath;
|
|
@@ -20029,19 +21793,19 @@ var FilesystemBackend = class {
|
|
|
20029
21793
|
if (!basePath || basePath.trim().length === 0) {
|
|
20030
21794
|
throw new Error("FilesystemBackend requires a non-empty basePath");
|
|
20031
21795
|
}
|
|
20032
|
-
this.basePath =
|
|
21796
|
+
this.basePath = path21.resolve(basePath);
|
|
20033
21797
|
}
|
|
20034
21798
|
resolveRemotePath(remotePath) {
|
|
20035
|
-
const resolved =
|
|
20036
|
-
const relative =
|
|
20037
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
21799
|
+
const resolved = path21.isAbsolute(remotePath) ? path21.resolve(remotePath) : path21.resolve(this.basePath, remotePath);
|
|
21800
|
+
const relative = path21.relative(this.basePath, resolved);
|
|
21801
|
+
if (relative === ".." || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
|
|
20038
21802
|
throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
|
|
20039
21803
|
}
|
|
20040
21804
|
return resolved;
|
|
20041
21805
|
}
|
|
20042
21806
|
isInsideBase(candidate, realBase) {
|
|
20043
|
-
const relative =
|
|
20044
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
21807
|
+
const relative = path21.relative(realBase, candidate);
|
|
21808
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path21.sep}`) && !path21.isAbsolute(relative);
|
|
20045
21809
|
}
|
|
20046
21810
|
async realBasePathIfExists() {
|
|
20047
21811
|
try {
|
|
@@ -20070,13 +21834,13 @@ var FilesystemBackend = class {
|
|
|
20070
21834
|
}
|
|
20071
21835
|
async ensureSafeParentDirectory(dest) {
|
|
20072
21836
|
const realBase = await this.ensureBaseDirectory();
|
|
20073
|
-
const destDir =
|
|
20074
|
-
const relativeDir =
|
|
20075
|
-
const segments = relativeDir === "" ? [] : relativeDir.split(
|
|
21837
|
+
const destDir = path21.dirname(dest);
|
|
21838
|
+
const relativeDir = path21.relative(this.basePath, destDir);
|
|
21839
|
+
const segments = relativeDir === "" ? [] : relativeDir.split(path21.sep);
|
|
20076
21840
|
let current = this.basePath;
|
|
20077
21841
|
for (const segment of segments) {
|
|
20078
21842
|
if (segment === "." || segment === "") continue;
|
|
20079
|
-
current =
|
|
21843
|
+
current = path21.join(current, segment);
|
|
20080
21844
|
try {
|
|
20081
21845
|
const stat5 = await fsp.lstat(current);
|
|
20082
21846
|
if (stat5.isSymbolicLink()) {
|
|
@@ -20104,13 +21868,13 @@ var FilesystemBackend = class {
|
|
|
20104
21868
|
if (realBase === null) {
|
|
20105
21869
|
return null;
|
|
20106
21870
|
}
|
|
20107
|
-
const destDir =
|
|
20108
|
-
const relativeDir =
|
|
20109
|
-
const segments = relativeDir === "" ? [] : relativeDir.split(
|
|
21871
|
+
const destDir = path21.dirname(dest);
|
|
21872
|
+
const relativeDir = path21.relative(this.basePath, destDir);
|
|
21873
|
+
const segments = relativeDir === "" ? [] : relativeDir.split(path21.sep);
|
|
20110
21874
|
let current = this.basePath;
|
|
20111
21875
|
for (const segment of segments) {
|
|
20112
21876
|
if (segment === "." || segment === "") continue;
|
|
20113
|
-
current =
|
|
21877
|
+
current = path21.join(current, segment);
|
|
20114
21878
|
let stat5;
|
|
20115
21879
|
try {
|
|
20116
21880
|
stat5 = await fsp.lstat(current);
|
|
@@ -20158,7 +21922,7 @@ var FilesystemBackend = class {
|
|
|
20158
21922
|
return dest;
|
|
20159
21923
|
}
|
|
20160
21924
|
async upload(localPath, remotePath) {
|
|
20161
|
-
if (
|
|
21925
|
+
if (path21.isAbsolute(remotePath)) {
|
|
20162
21926
|
throw new Error(`FilesystemBackend upload remotePath must be relative: ${JSON.stringify(remotePath)}`);
|
|
20163
21927
|
}
|
|
20164
21928
|
const dest = this.resolveRemotePath(remotePath);
|
|
@@ -20231,7 +21995,7 @@ function createBackend(cfg) {
|
|
|
20231
21995
|
|
|
20232
21996
|
// src/binary-lifecycle/scanner.ts
|
|
20233
21997
|
import fsp2 from "fs/promises";
|
|
20234
|
-
import
|
|
21998
|
+
import path22 from "path";
|
|
20235
21999
|
import crypto from "crypto";
|
|
20236
22000
|
function matchesPatterns(filename, patterns) {
|
|
20237
22001
|
const lower = filename.toLowerCase();
|
|
@@ -20256,8 +22020,8 @@ async function scanForBinaries(memoryDir, config, manifest) {
|
|
|
20256
22020
|
return;
|
|
20257
22021
|
}
|
|
20258
22022
|
for (const entry of entries) {
|
|
20259
|
-
const fullPath =
|
|
20260
|
-
const relativePath =
|
|
22023
|
+
const fullPath = path22.join(dir, entry.name);
|
|
22024
|
+
const relativePath = path22.relative(memoryDir, fullPath).split(path22.sep).join("/");
|
|
20261
22025
|
if (entry.isDirectory()) {
|
|
20262
22026
|
if (entry.name === ".binary-lifecycle") continue;
|
|
20263
22027
|
await walk(fullPath);
|
|
@@ -20297,15 +22061,15 @@ async function hashFile(filePath) {
|
|
|
20297
22061
|
|
|
20298
22062
|
// src/binary-lifecycle/manifest.ts
|
|
20299
22063
|
import fsp3 from "fs/promises";
|
|
20300
|
-
import
|
|
22064
|
+
import path23 from "path";
|
|
20301
22065
|
import crypto2 from "crypto";
|
|
20302
22066
|
var MANIFEST_DIR = ".binary-lifecycle";
|
|
20303
22067
|
var MANIFEST_FILE = "manifest.json";
|
|
20304
22068
|
function manifestDir(memoryDir) {
|
|
20305
|
-
return
|
|
22069
|
+
return path23.join(memoryDir, MANIFEST_DIR);
|
|
20306
22070
|
}
|
|
20307
22071
|
function manifestPath(memoryDir) {
|
|
20308
|
-
return
|
|
22072
|
+
return path23.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
|
|
20309
22073
|
}
|
|
20310
22074
|
async function readManifest(memoryDir) {
|
|
20311
22075
|
const filePath = manifestPath(memoryDir);
|
|
@@ -20357,13 +22121,13 @@ function emptyManifest() {
|
|
|
20357
22121
|
|
|
20358
22122
|
// src/binary-lifecycle/pipeline.ts
|
|
20359
22123
|
import fsp4 from "fs/promises";
|
|
20360
|
-
import
|
|
22124
|
+
import path24 from "path";
|
|
20361
22125
|
import crypto3 from "crypto";
|
|
20362
22126
|
var historicalIdCache = new HistoricalEntityCanonicalIdCache();
|
|
20363
22127
|
function isRecallCorpusMarkdown(mdPath, memoryDir) {
|
|
20364
|
-
const rel =
|
|
20365
|
-
if (rel.startsWith("..") ||
|
|
20366
|
-
const top = rel.split(
|
|
22128
|
+
const rel = path24.relative(memoryDir, mdPath);
|
|
22129
|
+
if (rel.startsWith("..") || path24.isAbsolute(rel)) return false;
|
|
22130
|
+
const top = rel.split(path24.sep)[0];
|
|
20367
22131
|
return RECALL_FALLBACK_DIRS.includes(top);
|
|
20368
22132
|
}
|
|
20369
22133
|
async function hashFile2(filePath) {
|
|
@@ -20387,13 +22151,13 @@ function escapeRegex(s) {
|
|
|
20387
22151
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20388
22152
|
}
|
|
20389
22153
|
function resolveManifestAssetPath(memoryDir, originalPath) {
|
|
20390
|
-
if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") ||
|
|
22154
|
+
if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path24.isAbsolute(originalPath) || path24.win32.isAbsolute(originalPath)) {
|
|
20391
22155
|
return null;
|
|
20392
22156
|
}
|
|
20393
|
-
const memoryRoot =
|
|
20394
|
-
const fullPath =
|
|
20395
|
-
const relative =
|
|
20396
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
22157
|
+
const memoryRoot = path24.resolve(memoryDir);
|
|
22158
|
+
const fullPath = path24.resolve(memoryRoot, originalPath);
|
|
22159
|
+
const relative = path24.relative(memoryRoot, fullPath);
|
|
22160
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path24.sep}`) || path24.isAbsolute(relative)) {
|
|
20397
22161
|
return null;
|
|
20398
22162
|
}
|
|
20399
22163
|
return fullPath;
|
|
@@ -20404,7 +22168,7 @@ function validateBinaryLifecycleConfig(config) {
|
|
|
20404
22168
|
}
|
|
20405
22169
|
}
|
|
20406
22170
|
function remotePathForAsset(backend, relPath) {
|
|
20407
|
-
const normalized = relPath.split(
|
|
22171
|
+
const normalized = relPath.split(path24.sep).join("/");
|
|
20408
22172
|
if (backend.type === "filesystem") {
|
|
20409
22173
|
return `.binary-lifecycle/mirrors/${normalized}`;
|
|
20410
22174
|
}
|
|
@@ -20417,11 +22181,11 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
|
|
|
20417
22181
|
let mirrored = 0;
|
|
20418
22182
|
const errors = [];
|
|
20419
22183
|
for (const relPath of newPaths) {
|
|
20420
|
-
const fullPath =
|
|
22184
|
+
const fullPath = path24.join(memoryDir, relPath);
|
|
20421
22185
|
try {
|
|
20422
22186
|
const stat5 = await fsp4.stat(fullPath);
|
|
20423
22187
|
const contentHash = await hashFile2(fullPath);
|
|
20424
|
-
const ext =
|
|
22188
|
+
const ext = path24.extname(relPath);
|
|
20425
22189
|
const mimeType = guessMimeType(ext);
|
|
20426
22190
|
const remotePath = remotePathForAsset(backend, relPath);
|
|
20427
22191
|
let backendLocation = remotePath;
|
|
@@ -20559,7 +22323,7 @@ async function stageRedirect(memoryDir, assets, log2, dryRun, readMarkdownFile,
|
|
|
20559
22323
|
for (const update of updates) {
|
|
20560
22324
|
try {
|
|
20561
22325
|
await withRawEntityPageMutation(memoryDir, update.mdPath, async () => {
|
|
20562
|
-
const stateDir =
|
|
22326
|
+
const stateDir = path24.join(memoryDir, "state");
|
|
20563
22327
|
const kind = classifyEntityRefWritePath(memoryDir, update.mdPath);
|
|
20564
22328
|
if (kind === "memory") {
|
|
20565
22329
|
const idsAtWrite = historicalIdCache.get(stateDir);
|
|
@@ -20653,10 +22417,10 @@ async function countRemainingLocalReferences(memoryDir, asset, assetAbsolute, md
|
|
|
20653
22417
|
return { remaining, errors };
|
|
20654
22418
|
}
|
|
20655
22419
|
function markdownReferencePattern(asset, assetAbsolute, mdPath) {
|
|
20656
|
-
const mdDir =
|
|
22420
|
+
const mdDir = path24.dirname(mdPath);
|
|
20657
22421
|
const candidates = /* @__PURE__ */ new Set();
|
|
20658
22422
|
const addCandidate = (candidate) => {
|
|
20659
|
-
const normalized = candidate.split(
|
|
22423
|
+
const normalized = candidate.split(path24.sep).join("/");
|
|
20660
22424
|
if (normalized.length === 0) return;
|
|
20661
22425
|
candidates.add(normalized);
|
|
20662
22426
|
const isParentTraversal = normalized === ".." || normalized.startsWith("../");
|
|
@@ -20664,10 +22428,10 @@ function markdownReferencePattern(asset, assetAbsolute, mdPath) {
|
|
|
20664
22428
|
candidates.add(`./${normalized}`);
|
|
20665
22429
|
}
|
|
20666
22430
|
};
|
|
20667
|
-
addCandidate(
|
|
20668
|
-
const originalPath = asset.originalPath.split(
|
|
20669
|
-
const originalAsFileRelative =
|
|
20670
|
-
if (
|
|
22431
|
+
addCandidate(path24.relative(mdDir, assetAbsolute));
|
|
22432
|
+
const originalPath = asset.originalPath.split(path24.sep).join("/");
|
|
22433
|
+
const originalAsFileRelative = path24.resolve(mdDir, ...originalPath.split("/"));
|
|
22434
|
+
if (path24.resolve(originalAsFileRelative) === path24.resolve(assetAbsolute)) {
|
|
20671
22435
|
addCandidate(originalPath);
|
|
20672
22436
|
}
|
|
20673
22437
|
addCandidate(`/${originalPath}`);
|
|
@@ -20763,7 +22527,7 @@ async function findMarkdownFiles(dir) {
|
|
|
20763
22527
|
return;
|
|
20764
22528
|
}
|
|
20765
22529
|
for (const entry of entries) {
|
|
20766
|
-
const full =
|
|
22530
|
+
const full = path24.join(current, entry.name);
|
|
20767
22531
|
if (entry.isDirectory()) {
|
|
20768
22532
|
if (entry.name === ".binary-lifecycle") continue;
|
|
20769
22533
|
await walk(full);
|
|
@@ -20838,7 +22602,7 @@ async function runBinaryLifecyclePipeline(memoryDir, config, backend, log2, opts
|
|
|
20838
22602
|
|
|
20839
22603
|
// src/projection/index.ts
|
|
20840
22604
|
import fs from "fs";
|
|
20841
|
-
import
|
|
22605
|
+
import path25 from "path";
|
|
20842
22606
|
var VALID_PROJECTION_CATEGORIES = new Set(ALL_CATEGORY_KEYS);
|
|
20843
22607
|
async function generateContextTree(options) {
|
|
20844
22608
|
const startTime = Date.now();
|
|
@@ -20853,8 +22617,8 @@ async function generateContextTree(options) {
|
|
|
20853
22617
|
let nodesGenerated = 0;
|
|
20854
22618
|
let nodesSkipped = 0;
|
|
20855
22619
|
const categoryCounts = {};
|
|
20856
|
-
const resolvedMemoryDir =
|
|
20857
|
-
const resolvedOutputDir =
|
|
22620
|
+
const resolvedMemoryDir = path25.resolve(memoryDir);
|
|
22621
|
+
const resolvedOutputDir = path25.resolve(outputDir);
|
|
20858
22622
|
const requestedCategories = validateProjectionCategories(filterCategories);
|
|
20859
22623
|
const realMemoryDir = assertSafeMemoryRoot(resolvedMemoryDir);
|
|
20860
22624
|
assertNotSymlink(resolvedOutputDir, "context tree outputDir");
|
|
@@ -20892,7 +22656,7 @@ async function generateContextTree(options) {
|
|
|
20892
22656
|
}
|
|
20893
22657
|
}
|
|
20894
22658
|
if (includeEntities) {
|
|
20895
|
-
const entitiesDir =
|
|
22659
|
+
const entitiesDir = path25.join(memoryDir, "entities");
|
|
20896
22660
|
if (fs.existsSync(entitiesDir)) {
|
|
20897
22661
|
assertSafeInputRoot(realMemoryDir, entitiesDir, "entities root");
|
|
20898
22662
|
categoryCounts["entity"] = 0;
|
|
@@ -20904,7 +22668,7 @@ async function generateContextTree(options) {
|
|
|
20904
22668
|
continue;
|
|
20905
22669
|
}
|
|
20906
22670
|
const content = fs.readFileSync(filePath, "utf8");
|
|
20907
|
-
const fileName =
|
|
22671
|
+
const fileName = path25.basename(filePath, ".md");
|
|
20908
22672
|
const node = projectEntityNode(fileName, content);
|
|
20909
22673
|
const outputPath = resolveContainedOutputPath(realOutputDir, "entities", `${fileName}.md`);
|
|
20910
22674
|
writeProjectedContent(realOutputDir, outputPath, node.content);
|
|
@@ -20916,7 +22680,7 @@ async function generateContextTree(options) {
|
|
|
20916
22680
|
}
|
|
20917
22681
|
const shouldIncludeQuestions = includeQuestions && (requestedCategories === void 0 || requestedCategories.includes("question"));
|
|
20918
22682
|
if (shouldIncludeQuestions) {
|
|
20919
|
-
const questionsDir =
|
|
22683
|
+
const questionsDir = path25.join(memoryDir, "questions");
|
|
20920
22684
|
if (fs.existsSync(questionsDir)) {
|
|
20921
22685
|
assertSafeInputRoot(realMemoryDir, questionsDir, "questions root");
|
|
20922
22686
|
categoryCounts["question"] = 0;
|
|
@@ -20957,8 +22721,8 @@ async function generateContextTree(options) {
|
|
|
20957
22721
|
};
|
|
20958
22722
|
}
|
|
20959
22723
|
function isPathInside(root, candidate) {
|
|
20960
|
-
const relative =
|
|
20961
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
22724
|
+
const relative = path25.relative(root, candidate);
|
|
22725
|
+
return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
|
|
20962
22726
|
}
|
|
20963
22727
|
function assertNotSymlink(targetPath, label) {
|
|
20964
22728
|
try {
|
|
@@ -20995,12 +22759,12 @@ function assertSafeInputRoot(realMemoryDir, targetPath, label) {
|
|
|
20995
22759
|
}
|
|
20996
22760
|
function assertSafeOutputTarget(realOutputDir, outputPath) {
|
|
20997
22761
|
let current = realOutputDir;
|
|
20998
|
-
const relative =
|
|
20999
|
-
if (relative.startsWith("..") ||
|
|
22762
|
+
const relative = path25.relative(realOutputDir, outputPath);
|
|
22763
|
+
if (relative.startsWith("..") || path25.isAbsolute(relative)) {
|
|
21000
22764
|
throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
|
|
21001
22765
|
}
|
|
21002
|
-
for (const segment of relative.split(
|
|
21003
|
-
current =
|
|
22766
|
+
for (const segment of relative.split(path25.sep).filter(Boolean)) {
|
|
22767
|
+
current = path25.join(current, segment);
|
|
21004
22768
|
try {
|
|
21005
22769
|
const stat5 = fs.lstatSync(current);
|
|
21006
22770
|
if (stat5.isSymbolicLink()) {
|
|
@@ -21030,17 +22794,17 @@ function validateProjectionCategories(categories) {
|
|
|
21030
22794
|
return validated;
|
|
21031
22795
|
}
|
|
21032
22796
|
function resolveContainedOutputPath(outputRoot, ...segments) {
|
|
21033
|
-
const resolved =
|
|
21034
|
-
const relative =
|
|
21035
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
22797
|
+
const resolved = path25.resolve(outputRoot, ...segments);
|
|
22798
|
+
const relative = path25.relative(outputRoot, resolved);
|
|
22799
|
+
if (relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative)) {
|
|
21036
22800
|
return resolved;
|
|
21037
22801
|
}
|
|
21038
22802
|
throw new Error(`context tree output path escapes outputDir: ${segments.join("/")}`);
|
|
21039
22803
|
}
|
|
21040
22804
|
function writeProjectedContent(realOutputDir, outputPath, generatedContent) {
|
|
21041
22805
|
assertSafeOutputTarget(realOutputDir, outputPath);
|
|
21042
|
-
fs.mkdirSync(
|
|
21043
|
-
const realParent = fs.realpathSync(
|
|
22806
|
+
fs.mkdirSync(path25.dirname(outputPath), { recursive: true });
|
|
22807
|
+
const realParent = fs.realpathSync(path25.dirname(outputPath));
|
|
21044
22808
|
if (!isPathInside(realOutputDir, realParent)) {
|
|
21045
22809
|
throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
|
|
21046
22810
|
}
|
|
@@ -21079,7 +22843,7 @@ function walkR(dir, realMemoryDir) {
|
|
|
21079
22843
|
const results = [];
|
|
21080
22844
|
function walk(directory) {
|
|
21081
22845
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
21082
|
-
const fullPath =
|
|
22846
|
+
const fullPath = path25.join(directory, entry.name);
|
|
21083
22847
|
if (entry.isSymbolicLink()) {
|
|
21084
22848
|
throw new Error(`context tree input path contains symlink: ${fullPath}`);
|
|
21085
22849
|
}
|
|
@@ -21132,13 +22896,13 @@ function extractBody(content) {
|
|
|
21132
22896
|
}
|
|
21133
22897
|
function projectNode(filePath, category, fm, rawContent) {
|
|
21134
22898
|
const body = extractBody(rawContent);
|
|
21135
|
-
const fileName =
|
|
21136
|
-
const dateDir =
|
|
22899
|
+
const fileName = path25.basename(filePath, ".md");
|
|
22900
|
+
const dateDir = path25.basename(path25.dirname(filePath));
|
|
21137
22901
|
let relPath;
|
|
21138
22902
|
if (/^\d{4}-\d{2}-\d{2}$/.test(dateDir)) {
|
|
21139
|
-
relPath =
|
|
22903
|
+
relPath = path25.join(category, dateDir, `${fileName}.md`);
|
|
21140
22904
|
} else {
|
|
21141
|
-
relPath =
|
|
22905
|
+
relPath = path25.join(category, `${fileName}.md`);
|
|
21142
22906
|
}
|
|
21143
22907
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21144
22908
|
const md = `# ${fm.id}
|
|
@@ -21184,7 +22948,7 @@ function projectEntityNode(fileName, content) {
|
|
|
21184
22948
|
${content}
|
|
21185
22949
|
`;
|
|
21186
22950
|
return {
|
|
21187
|
-
path:
|
|
22951
|
+
path: path25.join("entities", `${fileName}.md`),
|
|
21188
22952
|
category: "entity",
|
|
21189
22953
|
title: fileName,
|
|
21190
22954
|
content: md,
|
|
@@ -21237,7 +23001,7 @@ function generateIndex(categoryCounts, outputDir) {
|
|
|
21237
23001
|
|
|
21238
23002
|
// src/onboarding/index.ts
|
|
21239
23003
|
import fs2 from "fs";
|
|
21240
|
-
import
|
|
23004
|
+
import path26 from "path";
|
|
21241
23005
|
var LANGUAGE_RULES = [
|
|
21242
23006
|
{
|
|
21243
23007
|
language: "TypeScript",
|
|
@@ -21339,7 +23103,7 @@ function onboard(options) {
|
|
|
21339
23103
|
maxDepth = 6,
|
|
21340
23104
|
excludeDirs = []
|
|
21341
23105
|
} = options;
|
|
21342
|
-
const directory =
|
|
23106
|
+
const directory = path26.resolve(options.directory ?? process.cwd());
|
|
21343
23107
|
let rootStat;
|
|
21344
23108
|
try {
|
|
21345
23109
|
rootStat = fs2.statSync(directory);
|
|
@@ -21381,7 +23145,7 @@ function walkDir(root, exclude, maxDepth) {
|
|
|
21381
23145
|
}
|
|
21382
23146
|
for (const entry of entries) {
|
|
21383
23147
|
if (exclude.has(entry.name)) continue;
|
|
21384
|
-
const fullPath =
|
|
23148
|
+
const fullPath = path26.join(dir, entry.name);
|
|
21385
23149
|
if (entry.isDirectory()) {
|
|
21386
23150
|
walk(fullPath, depth + 1);
|
|
21387
23151
|
} else if (entry.isFile()) {
|
|
@@ -21396,11 +23160,11 @@ function detectLanguages(files, root) {
|
|
|
21396
23160
|
const results = [];
|
|
21397
23161
|
const extCounts = /* @__PURE__ */ new Map();
|
|
21398
23162
|
for (const f of files) {
|
|
21399
|
-
const ext =
|
|
23163
|
+
const ext = path26.extname(f).toLowerCase();
|
|
21400
23164
|
if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
21401
23165
|
}
|
|
21402
23166
|
const rootFiles = new Set(
|
|
21403
|
-
files.filter((f) =>
|
|
23167
|
+
files.filter((f) => path26.dirname(f) === root).map((f) => path26.basename(f))
|
|
21404
23168
|
);
|
|
21405
23169
|
for (const rule of LANGUAGE_RULES) {
|
|
21406
23170
|
const evidence = [];
|
|
@@ -21444,7 +23208,7 @@ function detectLanguages(files, root) {
|
|
|
21444
23208
|
}
|
|
21445
23209
|
function detectShape(files, root) {
|
|
21446
23210
|
const rootFiles = new Set(
|
|
21447
|
-
files.filter((f) =>
|
|
23211
|
+
files.filter((f) => path26.dirname(f) === root).map((f) => path26.basename(f))
|
|
21448
23212
|
);
|
|
21449
23213
|
const rootDirs = /* @__PURE__ */ new Set();
|
|
21450
23214
|
try {
|
|
@@ -21455,7 +23219,7 @@ function detectShape(files, root) {
|
|
|
21455
23219
|
}
|
|
21456
23220
|
const evidence = [];
|
|
21457
23221
|
if (rootFiles.has("package.json")) {
|
|
21458
|
-
const pkg = readJsonSafe(
|
|
23222
|
+
const pkg = readJsonSafe(path26.join(root, "package.json"));
|
|
21459
23223
|
if (pkg?.workspaces) {
|
|
21460
23224
|
evidence.push("package.json has workspaces");
|
|
21461
23225
|
return { shape: "monorepo", evidence };
|
|
@@ -21469,13 +23233,13 @@ function detectShape(files, root) {
|
|
|
21469
23233
|
evidence.push("workspace manifest found");
|
|
21470
23234
|
return { shape: "workspace", evidence };
|
|
21471
23235
|
}
|
|
21472
|
-
const cargoToml = readTomlWorkspace(
|
|
23236
|
+
const cargoToml = readTomlWorkspace(path26.join(root, "Cargo.toml"));
|
|
21473
23237
|
if (cargoToml) {
|
|
21474
23238
|
evidence.push("Cargo.toml has workspace");
|
|
21475
23239
|
return { shape: "workspace", evidence };
|
|
21476
23240
|
}
|
|
21477
23241
|
if (rootFiles.has("package.json")) {
|
|
21478
|
-
const pkg = readJsonSafe(
|
|
23242
|
+
const pkg = readJsonSafe(path26.join(root, "package.json"));
|
|
21479
23243
|
if (pkg?.exports || pkg?.main) {
|
|
21480
23244
|
if (pkg?.bin) {
|
|
21481
23245
|
evidence.push("package.json has bin");
|
|
@@ -21509,8 +23273,8 @@ function discoverDocs(files, root) {
|
|
|
21509
23273
|
{ pattern: /^\.editorconfig$/i, kind: "config" }
|
|
21510
23274
|
];
|
|
21511
23275
|
for (const filePath of files) {
|
|
21512
|
-
const basename =
|
|
21513
|
-
const relPath =
|
|
23276
|
+
const basename = path26.basename(filePath).toLowerCase();
|
|
23277
|
+
const relPath = path26.relative(root, filePath);
|
|
21514
23278
|
let kind;
|
|
21515
23279
|
for (const { pattern, kind: k } of docPatterns) {
|
|
21516
23280
|
if (pattern.test(basename)) {
|
|
@@ -21522,7 +23286,7 @@ function discoverDocs(files, root) {
|
|
|
21522
23286
|
kind = "docs";
|
|
21523
23287
|
}
|
|
21524
23288
|
if (!kind && (basename.endsWith(".md") || basename.endsWith(".mdx"))) {
|
|
21525
|
-
if (
|
|
23289
|
+
if (path26.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
|
|
21526
23290
|
kind = "docs";
|
|
21527
23291
|
}
|
|
21528
23292
|
}
|
|
@@ -21543,7 +23307,7 @@ function discoverDocs(files, root) {
|
|
|
21543
23307
|
return docs;
|
|
21544
23308
|
}
|
|
21545
23309
|
function isUnderDocsDir(relPath) {
|
|
21546
|
-
const parts = relPath.split(
|
|
23310
|
+
const parts = relPath.split(path26.sep);
|
|
21547
23311
|
return parts[0] === "docs" || parts[0] === "doc" || parts[0] === "documentation";
|
|
21548
23312
|
}
|
|
21549
23313
|
function buildPlan(languages, shape, docs, _root) {
|
|
@@ -21587,7 +23351,7 @@ function readTomlWorkspace(filePath) {
|
|
|
21587
23351
|
|
|
21588
23352
|
// src/curation/index.ts
|
|
21589
23353
|
import fs3 from "fs";
|
|
21590
|
-
import
|
|
23354
|
+
import path27 from "path";
|
|
21591
23355
|
import crypto4 from "crypto";
|
|
21592
23356
|
async function curate(options) {
|
|
21593
23357
|
const startTime = Date.now();
|
|
@@ -21678,12 +23442,12 @@ function resolveTargets(targetPath) {
|
|
|
21678
23442
|
const extensions = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
|
|
21679
23443
|
function walk(dir) {
|
|
21680
23444
|
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
21681
|
-
const fullPath =
|
|
23445
|
+
const fullPath = path27.join(dir, entry.name);
|
|
21682
23446
|
if (entry.isDirectory()) {
|
|
21683
23447
|
if (entry.name !== "node_modules" && entry.name !== ".git") {
|
|
21684
23448
|
walk(fullPath);
|
|
21685
23449
|
}
|
|
21686
|
-
} else if (extensions.has(
|
|
23450
|
+
} else if (extensions.has(path27.extname(entry.name).toLowerCase())) {
|
|
21687
23451
|
results.push(fullPath);
|
|
21688
23452
|
}
|
|
21689
23453
|
}
|
|
@@ -21692,12 +23456,12 @@ function resolveTargets(targetPath) {
|
|
|
21692
23456
|
return results;
|
|
21693
23457
|
}
|
|
21694
23458
|
function resolveProvenanceRoot(targetPath) {
|
|
21695
|
-
const resolvedTarget =
|
|
23459
|
+
const resolvedTarget = path27.resolve(targetPath);
|
|
21696
23460
|
const stat5 = fs3.statSync(resolvedTarget);
|
|
21697
|
-
return stat5.isFile() ?
|
|
23461
|
+
return stat5.isFile() ? path27.dirname(resolvedTarget) : resolvedTarget;
|
|
21698
23462
|
}
|
|
21699
23463
|
function extractStatements(content, filePath, projectRoot, source, sourceFileHash, categoryOverride, confidence, entityRef, tags) {
|
|
21700
|
-
const relativePath =
|
|
23464
|
+
const relativePath = path27.relative(projectRoot, path27.resolve(filePath)) || path27.basename(filePath);
|
|
21701
23465
|
const statements = [];
|
|
21702
23466
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21703
23467
|
const paragraphs = content.split(/\n{2,}/).map((p) => p.trim()).filter((p) => p.length > 20 && p.length < 2e3);
|
|
@@ -21791,7 +23555,7 @@ function loadExistingMemories(memoryDir) {
|
|
|
21791
23555
|
if (!fs3.existsSync(memoryDir)) return result;
|
|
21792
23556
|
const dirs = ALL_CATEGORY_DIRS;
|
|
21793
23557
|
for (const dir of dirs) {
|
|
21794
|
-
const fullDir =
|
|
23558
|
+
const fullDir = path27.join(memoryDir, dir);
|
|
21795
23559
|
if (!fs3.existsSync(fullDir)) continue;
|
|
21796
23560
|
walkFiles(fullDir, (filePath) => {
|
|
21797
23561
|
const content = readFileSafe(filePath);
|
|
@@ -21814,11 +23578,11 @@ function writeStatement(stmt, memoryDir) {
|
|
|
21814
23578
|
const now = /* @__PURE__ */ new Date();
|
|
21815
23579
|
const dateDir = now.toISOString().split("T")[0];
|
|
21816
23580
|
const categoryDir = getCategoryDir(memoryDir, stmt.category);
|
|
21817
|
-
const dir =
|
|
23581
|
+
const dir = path27.join(categoryDir, dateDir);
|
|
21818
23582
|
fs3.mkdirSync(dir, { recursive: true });
|
|
21819
23583
|
const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;
|
|
21820
|
-
const filePath =
|
|
21821
|
-
const historicalIds = historicalIdCache2.get(
|
|
23584
|
+
const filePath = path27.join(dir, fileName);
|
|
23585
|
+
const historicalIds = historicalIdCache2.get(path27.join(memoryDir, "state"));
|
|
21822
23586
|
const rawBody = `${[
|
|
21823
23587
|
"---",
|
|
21824
23588
|
`id: ${stmt.id}`,
|
|
@@ -21841,7 +23605,7 @@ ${stmt.content}
|
|
|
21841
23605
|
fs3.writeFileSync(filePath, body);
|
|
21842
23606
|
try {
|
|
21843
23607
|
repairContentAfterJournalMoveSync({
|
|
21844
|
-
stateDir:
|
|
23608
|
+
stateDir: path27.join(memoryDir, "state"),
|
|
21845
23609
|
cache: historicalIdCache2,
|
|
21846
23610
|
idsAtWrite: historicalIds,
|
|
21847
23611
|
rawContent: rawBody,
|
|
@@ -21902,7 +23666,7 @@ function extractBody2(content) {
|
|
|
21902
23666
|
}
|
|
21903
23667
|
function walkFiles(dir, callback) {
|
|
21904
23668
|
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
21905
|
-
const fullPath =
|
|
23669
|
+
const fullPath = path27.join(dir, entry.name);
|
|
21906
23670
|
if (entry.isDirectory()) {
|
|
21907
23671
|
walkFiles(fullPath, callback);
|
|
21908
23672
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -21913,7 +23677,7 @@ function walkFiles(dir, callback) {
|
|
|
21913
23677
|
|
|
21914
23678
|
// src/dedup/index.ts
|
|
21915
23679
|
import fs4 from "fs";
|
|
21916
|
-
import
|
|
23680
|
+
import path28 from "path";
|
|
21917
23681
|
import crypto5 from "crypto";
|
|
21918
23682
|
var DEFAULT_DEDUP_THRESHOLD = 0.85;
|
|
21919
23683
|
var DEFAULT_MAX_LOAD = 1e4;
|
|
@@ -22055,7 +23819,7 @@ function loadMemories(memoryDir, categories, maxLoad = 1e4) {
|
|
|
22055
23819
|
const memoryRootReal = fs4.realpathSync(memoryDir);
|
|
22056
23820
|
for (const category of allCategories) {
|
|
22057
23821
|
if (result.length >= maxLoad) break;
|
|
22058
|
-
const dir =
|
|
23822
|
+
const dir = path28.join(memoryDir, category);
|
|
22059
23823
|
if (!fs4.existsSync(dir)) continue;
|
|
22060
23824
|
const categoryStat = fs4.lstatSync(dir);
|
|
22061
23825
|
if (categoryStat.isSymbolicLink()) {
|
|
@@ -22116,15 +23880,15 @@ function extractBody3(content) {
|
|
|
22116
23880
|
return match ? match[1].trim() : content.trim();
|
|
22117
23881
|
}
|
|
22118
23882
|
function assertPathInsideRoot2(rootReal, targetReal, sourcePath) {
|
|
22119
|
-
const rel =
|
|
22120
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
23883
|
+
const rel = path28.relative(rootReal, targetReal);
|
|
23884
|
+
if (rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel)) {
|
|
22121
23885
|
return;
|
|
22122
23886
|
}
|
|
22123
23887
|
throw new Error(`Refusing to scan memory path outside root: ${sourcePath}`);
|
|
22124
23888
|
}
|
|
22125
23889
|
function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
|
|
22126
23890
|
for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
22127
|
-
const fullPath =
|
|
23891
|
+
const fullPath = path28.join(dir, entry.name);
|
|
22128
23892
|
const entryStat = fs4.lstatSync(fullPath);
|
|
22129
23893
|
if (entryStat.isSymbolicLink()) {
|
|
22130
23894
|
throw new Error(`Refusing to scan symlinked memory path: ${fullPath}`);
|
|
@@ -22142,10 +23906,10 @@ function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
|
|
|
22142
23906
|
|
|
22143
23907
|
// src/review/index.ts
|
|
22144
23908
|
import fs5 from "fs";
|
|
22145
|
-
import
|
|
23909
|
+
import path29 from "path";
|
|
22146
23910
|
var historicalIdCache3 = new HistoricalEntityCanonicalIdCache();
|
|
22147
23911
|
function writeReviewFileCanonicalized(memoryDir, filePath, content) {
|
|
22148
|
-
const stateDir =
|
|
23912
|
+
const stateDir = path29.join(memoryDir, "state");
|
|
22149
23913
|
const idsAtWrite = historicalIdCache3.get(stateDir);
|
|
22150
23914
|
let originalBytes = null;
|
|
22151
23915
|
try {
|
|
@@ -22187,8 +23951,8 @@ function realMemoryRoot(memoryDir) {
|
|
|
22187
23951
|
}
|
|
22188
23952
|
}
|
|
22189
23953
|
function isPathInside2(rootReal, candidateReal) {
|
|
22190
|
-
const relative =
|
|
22191
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !
|
|
23954
|
+
const relative = path29.relative(rootReal, candidateReal);
|
|
23955
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path29.isAbsolute(relative);
|
|
22192
23956
|
}
|
|
22193
23957
|
function isSafeDirectory(rootReal, dir) {
|
|
22194
23958
|
try {
|
|
@@ -22231,7 +23995,7 @@ function listReviewItems(options) {
|
|
|
22231
23995
|
if (filterReason && item.reviewReason !== filterReason) return;
|
|
22232
23996
|
items.push(item);
|
|
22233
23997
|
};
|
|
22234
|
-
const suggestionsDir =
|
|
23998
|
+
const suggestionsDir = path29.join(memoryDir, "suggestions");
|
|
22235
23999
|
if (!isLimitReached() && fs5.existsSync(suggestionsDir) && isSafeDirectory(rootReal, suggestionsDir)) {
|
|
22236
24000
|
walkMd(rootReal, suggestionsDir, (filePath, content) => {
|
|
22237
24001
|
if (isLimitReached()) return true;
|
|
@@ -22252,7 +24016,7 @@ function listReviewItems(options) {
|
|
|
22252
24016
|
return isLimitReached();
|
|
22253
24017
|
});
|
|
22254
24018
|
}
|
|
22255
|
-
const reviewDir =
|
|
24019
|
+
const reviewDir = path29.join(memoryDir, "review");
|
|
22256
24020
|
if (!isLimitReached() && fs5.existsSync(reviewDir) && isSafeDirectory(rootReal, reviewDir)) {
|
|
22257
24021
|
walkMd(rootReal, reviewDir, (filePath, content) => {
|
|
22258
24022
|
if (isLimitReached()) return true;
|
|
@@ -22277,7 +24041,7 @@ function listReviewItems(options) {
|
|
|
22277
24041
|
const categories = ALL_CATEGORY_DIRS;
|
|
22278
24042
|
for (const category of categories) {
|
|
22279
24043
|
if (isLimitReached()) break;
|
|
22280
|
-
const dir =
|
|
24044
|
+
const dir = path29.join(memoryDir, category);
|
|
22281
24045
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
22282
24046
|
walkMd(rootReal, dir, (filePath, content) => {
|
|
22283
24047
|
if (isLimitReached()) return true;
|
|
@@ -22359,9 +24123,9 @@ function approveItem(memoryDir, itemId, options) {
|
|
|
22359
24123
|
const targetDir = getCategoryDir(memoryDir, category);
|
|
22360
24124
|
const dateDir = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
22361
24125
|
ensureSafeDirectory(rootReal, targetDir);
|
|
22362
|
-
const outputPath =
|
|
22363
|
-
ensureSafeDirectory(rootReal,
|
|
22364
|
-
const reviewStateDir =
|
|
24126
|
+
const outputPath = path29.join(targetDir, dateDir, path29.basename(found.filePath));
|
|
24127
|
+
ensureSafeDirectory(rootReal, path29.dirname(outputPath));
|
|
24128
|
+
const reviewStateDir = path29.join(memoryDir, "state");
|
|
22365
24129
|
const idsAtWrite = historicalIdCache3.get(reviewStateDir);
|
|
22366
24130
|
const promotedWritten = canonicalizeEntityRefFrontmatter(updatedContent, idsAtWrite);
|
|
22367
24131
|
const promotedPath = writeFileWithoutClobber(outputPath, promotedWritten, itemId);
|
|
@@ -22438,13 +24202,13 @@ function findReviewFileById(memoryDir, id, options = {}) {
|
|
|
22438
24202
|
const rootReal = realMemoryRoot(memoryDir);
|
|
22439
24203
|
if (!rootReal) return null;
|
|
22440
24204
|
for (const loc of ["suggestions", "review"]) {
|
|
22441
|
-
const dir =
|
|
24205
|
+
const dir = path29.join(memoryDir, loc);
|
|
22442
24206
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
22443
24207
|
const found = findFileById(rootReal, dir, id);
|
|
22444
24208
|
if (found) return { filePath: found, location: "queue" };
|
|
22445
24209
|
}
|
|
22446
24210
|
for (const category of ALL_CATEGORY_DIRS) {
|
|
22447
|
-
const dir =
|
|
24211
|
+
const dir = path29.join(memoryDir, category);
|
|
22448
24212
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
22449
24213
|
const found = findFileById(rootReal, dir, id, (fm) => isLowConfidenceReviewCandidate(fm, options));
|
|
22450
24214
|
if (found) return { filePath: found, location: "category" };
|
|
@@ -22520,10 +24284,10 @@ function readFileSafe3(filePath) {
|
|
|
22520
24284
|
}
|
|
22521
24285
|
}
|
|
22522
24286
|
function writeFileWithoutClobber(basePath, content, discriminator) {
|
|
22523
|
-
const parsed =
|
|
24287
|
+
const parsed = path29.parse(basePath);
|
|
22524
24288
|
const safeDiscriminator = sanitizeFilePart(discriminator);
|
|
22525
24289
|
for (let attempt = 0; attempt < 1e3; attempt++) {
|
|
22526
|
-
const candidate = attempt === 0 ? basePath :
|
|
24290
|
+
const candidate = attempt === 0 ? basePath : path29.join(
|
|
22527
24291
|
parsed.dir,
|
|
22528
24292
|
`${parsed.name}-${safeDiscriminator}${attempt === 1 ? "" : `-${attempt}`}${parsed.ext || ".md"}`
|
|
22529
24293
|
);
|
|
@@ -22579,7 +24343,7 @@ function extractBody4(content) {
|
|
|
22579
24343
|
function walkMd(rootReal, dir, callback) {
|
|
22580
24344
|
if (!isSafeDirectory(rootReal, dir)) return false;
|
|
22581
24345
|
for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
|
|
22582
|
-
const fullPath =
|
|
24346
|
+
const fullPath = path29.join(dir, entry.name);
|
|
22583
24347
|
if (entry.isSymbolicLink()) continue;
|
|
22584
24348
|
if (entry.isDirectory()) {
|
|
22585
24349
|
if (walkMd(rootReal, fullPath, callback)) return true;
|
|
@@ -22594,7 +24358,7 @@ function walkMdPaths(rootReal, dir) {
|
|
|
22594
24358
|
const results = [];
|
|
22595
24359
|
if (!isSafeDirectory(rootReal, dir)) return results;
|
|
22596
24360
|
for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
|
|
22597
|
-
const fullPath =
|
|
24361
|
+
const fullPath = path29.join(dir, entry.name);
|
|
22598
24362
|
if (entry.isSymbolicLink()) continue;
|
|
22599
24363
|
if (entry.isDirectory()) {
|
|
22600
24364
|
results.push(...walkMdPaths(rootReal, fullPath));
|
|
@@ -22607,7 +24371,7 @@ function walkMdPaths(rootReal, dir) {
|
|
|
22607
24371
|
|
|
22608
24372
|
// src/sync/index.ts
|
|
22609
24373
|
import fs6 from "fs";
|
|
22610
|
-
import
|
|
24374
|
+
import path30 from "path";
|
|
22611
24375
|
import crypto6 from "crypto";
|
|
22612
24376
|
var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
|
|
22613
24377
|
var DEFAULT_EXCLUDE2 = /* @__PURE__ */ new Set([
|
|
@@ -22629,7 +24393,7 @@ function syncChanges(options) {
|
|
|
22629
24393
|
} = options;
|
|
22630
24394
|
const extSet = new Set(extensions);
|
|
22631
24395
|
const excludeSet = /* @__PURE__ */ new Set([...DEFAULT_EXCLUDE2, ...excludeDirs]);
|
|
22632
|
-
const stateFilePath = options.stateFile ??
|
|
24396
|
+
const stateFilePath = options.stateFile ?? path30.join(memoryDir, ".sync-state.json");
|
|
22633
24397
|
const prevState = loadState(stateFilePath);
|
|
22634
24398
|
const currentFiles = scanFiles(sourceDir, extSet, excludeSet);
|
|
22635
24399
|
const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);
|
|
@@ -22645,7 +24409,7 @@ function syncChanges(options) {
|
|
|
22645
24409
|
for (const [relPath, hash] of Object.entries(currentFiles)) {
|
|
22646
24410
|
newState.fileHashes[relPath] = hash;
|
|
22647
24411
|
}
|
|
22648
|
-
fs6.mkdirSync(
|
|
24412
|
+
fs6.mkdirSync(path30.dirname(stateFilePath), { recursive: true });
|
|
22649
24413
|
fs6.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));
|
|
22650
24414
|
}
|
|
22651
24415
|
return {
|
|
@@ -22711,13 +24475,13 @@ function scanFiles(root, extensions, exclude) {
|
|
|
22711
24475
|
}
|
|
22712
24476
|
for (const entry of entries) {
|
|
22713
24477
|
if (exclude.has(entry.name)) continue;
|
|
22714
|
-
const fullPath =
|
|
24478
|
+
const fullPath = path30.join(dir, entry.name);
|
|
22715
24479
|
if (entry.isDirectory()) {
|
|
22716
24480
|
walk(fullPath);
|
|
22717
24481
|
} else if (entry.isFile()) {
|
|
22718
|
-
const ext =
|
|
24482
|
+
const ext = path30.extname(entry.name).toLowerCase();
|
|
22719
24483
|
if (!extensions.has(ext)) continue;
|
|
22720
|
-
const relPath =
|
|
24484
|
+
const relPath = path30.relative(root, fullPath);
|
|
22721
24485
|
try {
|
|
22722
24486
|
const content = fs6.readFileSync(fullPath, "utf8");
|
|
22723
24487
|
result[relPath] = hashContent3(content);
|
|
@@ -22732,7 +24496,7 @@ function scanFiles(root, extensions, exclude) {
|
|
|
22732
24496
|
function computeDiff(current, previous, sourceDir) {
|
|
22733
24497
|
const changes = [];
|
|
22734
24498
|
for (const [relPath, hash] of Object.entries(current)) {
|
|
22735
|
-
const fullPath =
|
|
24499
|
+
const fullPath = path30.join(sourceDir, relPath);
|
|
22736
24500
|
if (!(relPath in previous)) {
|
|
22737
24501
|
let size = 0;
|
|
22738
24502
|
try {
|
|
@@ -22765,7 +24529,7 @@ function computeDiff(current, previous, sourceDir) {
|
|
|
22765
24529
|
for (const relPath of Object.keys(previous)) {
|
|
22766
24530
|
if (!(relPath in current)) {
|
|
22767
24531
|
changes.push({
|
|
22768
|
-
filePath:
|
|
24532
|
+
filePath: path30.join(sourceDir, relPath),
|
|
22769
24533
|
relativePath: relPath,
|
|
22770
24534
|
type: "deleted",
|
|
22771
24535
|
currentHash: "",
|
|
@@ -22795,21 +24559,21 @@ function hashContent3(content) {
|
|
|
22795
24559
|
import { spawnSync } from "child_process";
|
|
22796
24560
|
import crypto7 from "crypto";
|
|
22797
24561
|
import fs7 from "fs";
|
|
22798
|
-
import
|
|
24562
|
+
import path31 from "path";
|
|
22799
24563
|
var historicalIdCache4 = new HistoricalEntityCanonicalIdCache();
|
|
22800
24564
|
var MANIFEST_VERSION = 1;
|
|
22801
24565
|
var MANIFEST_LOCK_STALE_MS = 3e4;
|
|
22802
24566
|
var MANIFEST_LOCK_TIMEOUT_MS = MANIFEST_LOCK_STALE_MS + 1e4;
|
|
22803
24567
|
var MANIFEST_LOCK_SLEEP_MS = 20;
|
|
22804
24568
|
function normalizeSpaceMemoryDir(memoryDir) {
|
|
22805
|
-
return
|
|
24569
|
+
return path31.resolve(memoryDir);
|
|
22806
24570
|
}
|
|
22807
24571
|
function getSpacesDir(baseDir) {
|
|
22808
24572
|
const homeDir = baseDir ?? resolveHomeDir();
|
|
22809
|
-
return
|
|
24573
|
+
return path31.join(homeDir, ".config", "engram", "spaces");
|
|
22810
24574
|
}
|
|
22811
24575
|
function getManifestPath(baseDir) {
|
|
22812
|
-
return
|
|
24576
|
+
return path31.join(getSpacesDir(baseDir), "manifest.json");
|
|
22813
24577
|
}
|
|
22814
24578
|
function loadManifest(baseDir, memoryDirOverride) {
|
|
22815
24579
|
if (fs7.existsSync(getManifestPath(baseDir))) {
|
|
@@ -22856,9 +24620,9 @@ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
|
|
|
22856
24620
|
}
|
|
22857
24621
|
function saveManifestUnlocked(manifest, baseDir) {
|
|
22858
24622
|
const manifestPath2 = getManifestPath(baseDir);
|
|
22859
|
-
const manifestDir2 =
|
|
24623
|
+
const manifestDir2 = path31.dirname(manifestPath2);
|
|
22860
24624
|
fs7.mkdirSync(manifestDir2, { recursive: true });
|
|
22861
|
-
const tempPath =
|
|
24625
|
+
const tempPath = path31.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
|
|
22862
24626
|
try {
|
|
22863
24627
|
fs7.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
|
|
22864
24628
|
`, { flag: "wx" });
|
|
@@ -22873,7 +24637,7 @@ function saveManifestUnlocked(manifest, baseDir) {
|
|
|
22873
24637
|
}
|
|
22874
24638
|
function withManifestLock(baseDir, operation) {
|
|
22875
24639
|
const lockDir = `${getManifestPath(baseDir)}.lock`;
|
|
22876
|
-
fs7.mkdirSync(
|
|
24640
|
+
fs7.mkdirSync(path31.dirname(lockDir), { recursive: true });
|
|
22877
24641
|
const lockOwner = acquireManifestLock(lockDir);
|
|
22878
24642
|
try {
|
|
22879
24643
|
return operation();
|
|
@@ -22899,7 +24663,7 @@ function acquireManifestLock(lockDir) {
|
|
|
22899
24663
|
try {
|
|
22900
24664
|
fs7.mkdirSync(lockDir, { recursive: false });
|
|
22901
24665
|
try {
|
|
22902
|
-
fs7.writeFileSync(
|
|
24666
|
+
fs7.writeFileSync(path31.join(lockDir, "owner"), `${owner}
|
|
22903
24667
|
`, { flag: "wx" });
|
|
22904
24668
|
} catch (error) {
|
|
22905
24669
|
fs7.rmSync(lockDir, { recursive: true, force: true });
|
|
@@ -22929,7 +24693,7 @@ function acquireManifestLock(lockDir) {
|
|
|
22929
24693
|
}
|
|
22930
24694
|
function releaseManifestLock(lockDir, owner) {
|
|
22931
24695
|
try {
|
|
22932
|
-
const ownerPath =
|
|
24696
|
+
const ownerPath = path31.join(lockDir, "owner");
|
|
22933
24697
|
if (fs7.readFileSync(ownerPath, "utf8").trim() === owner) {
|
|
22934
24698
|
fs7.rmSync(lockDir, { recursive: true, force: true });
|
|
22935
24699
|
}
|
|
@@ -22945,7 +24709,7 @@ function removeStaleManifestLock(lockDir) {
|
|
|
22945
24709
|
try {
|
|
22946
24710
|
fs7.mkdirSync(reclaimDir, { recursive: false });
|
|
22947
24711
|
try {
|
|
22948
|
-
fs7.writeFileSync(
|
|
24712
|
+
fs7.writeFileSync(path31.join(reclaimDir, "owner"), `${reclaimOwner}
|
|
22949
24713
|
`, { flag: "wx" });
|
|
22950
24714
|
} catch (error) {
|
|
22951
24715
|
fs7.rmSync(reclaimDir, { recursive: true, force: true });
|
|
@@ -23020,7 +24784,7 @@ function readManifestLockSnapshot(lockDir) {
|
|
|
23020
24784
|
throw error;
|
|
23021
24785
|
}
|
|
23022
24786
|
try {
|
|
23023
|
-
const owner = fs7.readFileSync(
|
|
24787
|
+
const owner = fs7.readFileSync(path31.join(lockDir, "owner"), "utf8").trim();
|
|
23024
24788
|
return { mtimeMs: stat5.mtimeMs, owner };
|
|
23025
24789
|
} catch (error) {
|
|
23026
24790
|
if (error.code === "ENOENT") {
|
|
@@ -23092,8 +24856,8 @@ function sleepSync(ms) {
|
|
|
23092
24856
|
}
|
|
23093
24857
|
function createPersonalSpace(baseDir, memoryDirOverride) {
|
|
23094
24858
|
const homeDir = baseDir ?? resolveHomeDir();
|
|
23095
|
-
const standalonePath =
|
|
23096
|
-
const openclawPath =
|
|
24859
|
+
const standalonePath = path31.join(homeDir, ".engram", "memory");
|
|
24860
|
+
const openclawPath = path31.join(homeDir, ".openclaw", "workspace", "memory", "local");
|
|
23097
24861
|
const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs7.existsSync(standalonePath) ? standalonePath : fs7.existsSync(openclawPath) ? openclawPath : standalonePath);
|
|
23098
24862
|
const normalizedMemoryDir = normalizeSpaceMemoryDir(memoryDir);
|
|
23099
24863
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -23122,7 +24886,7 @@ function createSpace(options) {
|
|
|
23122
24886
|
const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
23123
24887
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23124
24888
|
const memoryDir = normalizeSpaceMemoryDir(
|
|
23125
|
-
options.memoryDir ??
|
|
24889
|
+
options.memoryDir ?? path31.join(getSpacesDir(options.baseDir), id, "memory")
|
|
23126
24890
|
);
|
|
23127
24891
|
const space = updateManifest(options.baseDir, (manifest) => {
|
|
23128
24892
|
if (manifest.spaces.some((s) => s.id === id)) {
|
|
@@ -23343,14 +25107,14 @@ async function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
|
|
|
23343
25107
|
};
|
|
23344
25108
|
}
|
|
23345
25109
|
function getAuditLog(baseDir) {
|
|
23346
|
-
const auditPath =
|
|
25110
|
+
const auditPath = path31.join(getSpacesDir(baseDir), "audit.jsonl");
|
|
23347
25111
|
if (!fs7.existsSync(auditPath)) return [];
|
|
23348
25112
|
const lines = fs7.readFileSync(auditPath, "utf8").trim().split("\n");
|
|
23349
25113
|
return lines.filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
23350
25114
|
}
|
|
23351
25115
|
function appendAudit(entry, baseDir) {
|
|
23352
|
-
const auditPath =
|
|
23353
|
-
fs7.mkdirSync(
|
|
25116
|
+
const auditPath = path31.join(getSpacesDir(baseDir), "audit.jsonl");
|
|
25117
|
+
fs7.mkdirSync(path31.dirname(auditPath), { recursive: true });
|
|
23354
25118
|
const full = {
|
|
23355
25119
|
id: crypto7.randomUUID(),
|
|
23356
25120
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -23371,11 +25135,11 @@ async function copyMemories(sourceDir, targetDir, options) {
|
|
|
23371
25135
|
sourceFiles.filter((p) => {
|
|
23372
25136
|
const rp = safeRealpath(p);
|
|
23373
25137
|
if (!rp || !isPathInsideRoot(rp, sourceRoot)) return false;
|
|
23374
|
-
const rel =
|
|
23375
|
-
const mapped =
|
|
25138
|
+
const rel = path31.relative(sourceRoot, rp);
|
|
25139
|
+
const mapped = path31.resolve(targetRoot, rel);
|
|
23376
25140
|
if (!isPathInsideRoot(mapped, targetRoot)) return false;
|
|
23377
25141
|
return classifyEntityRefWritePath(targetRoot, mapped) === "entity";
|
|
23378
|
-
}).map((p) =>
|
|
25142
|
+
}).map((p) => path31.basename(p, ".md"))
|
|
23379
25143
|
);
|
|
23380
25144
|
const firstEntityId = sourceEntityIds.values().next().value;
|
|
23381
25145
|
if (firstEntityId === void 0) {
|
|
@@ -23383,7 +25147,7 @@ async function copyMemories(sourceDir, targetDir, options) {
|
|
|
23383
25147
|
}
|
|
23384
25148
|
return withRawEntityPageMutation(
|
|
23385
25149
|
targetRoot,
|
|
23386
|
-
|
|
25150
|
+
path31.join(targetRoot, "entities", `${firstEntityId}.md`),
|
|
23387
25151
|
async () => copyPreparedMemories(
|
|
23388
25152
|
sourceFiles,
|
|
23389
25153
|
sourceRoot,
|
|
@@ -23410,14 +25174,14 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
23410
25174
|
continue;
|
|
23411
25175
|
}
|
|
23412
25176
|
const content = fs7.readFileSync(sourcePath, "utf8");
|
|
23413
|
-
const relativePath =
|
|
23414
|
-
const targetPath =
|
|
25177
|
+
const relativePath = path31.relative(sourceRoot, sourceRealPath);
|
|
25178
|
+
const targetPath = path31.resolve(targetRoot, relativePath);
|
|
23415
25179
|
if (!isPathInsideRoot(targetPath, targetRoot)) {
|
|
23416
25180
|
skipped++;
|
|
23417
25181
|
continue;
|
|
23418
25182
|
}
|
|
23419
25183
|
const targetKind = classifyEntityRefWritePath(targetRoot, targetPath);
|
|
23420
|
-
const idsAtCompare = historicalIdCache4.get(
|
|
25184
|
+
const idsAtCompare = historicalIdCache4.get(path31.join(targetRoot, "state"));
|
|
23421
25185
|
const sourceRef = targetKind === "memory" ? readEntityRefFromFrontmatter(content) : null;
|
|
23422
25186
|
const refHasSourceClaimant = sourceRef !== null && sourceEntityIds.has(sourceRef);
|
|
23423
25187
|
const canonicalContent = targetKind === "memory" && !refHasSourceClaimant ? canonicalizeEntityRefFrontmatter(content, idsAtCompare) : content;
|
|
@@ -23444,7 +25208,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
23444
25208
|
if (fs7.existsSync(targetPath) && !options?.force) {
|
|
23445
25209
|
const targetContent = fs7.readFileSync(targetPath, "utf8");
|
|
23446
25210
|
const targetHash = hashContent4(targetContent);
|
|
23447
|
-
if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(
|
|
25211
|
+
if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(path31.join(targetRoot, "state")) === idsAtCompare) {
|
|
23448
25212
|
skipped++;
|
|
23449
25213
|
continue;
|
|
23450
25214
|
}
|
|
@@ -23460,13 +25224,13 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
23460
25224
|
continue;
|
|
23461
25225
|
}
|
|
23462
25226
|
}
|
|
23463
|
-
fs7.mkdirSync(
|
|
23464
|
-
const targetParentRealPath = safeRealpath(
|
|
25227
|
+
fs7.mkdirSync(path31.dirname(targetPath), { recursive: true });
|
|
25228
|
+
const targetParentRealPath = safeRealpath(path31.dirname(targetPath));
|
|
23465
25229
|
if (!targetParentRealPath || !isPathInsideRoot(targetParentRealPath, targetRoot)) {
|
|
23466
25230
|
skipped++;
|
|
23467
25231
|
continue;
|
|
23468
25232
|
}
|
|
23469
|
-
const stateDir =
|
|
25233
|
+
const stateDir = path31.join(targetRoot, "state");
|
|
23470
25234
|
if (targetKind === "memory" && !refHasSourceClaimant) {
|
|
23471
25235
|
const idsAtWrite = historicalIdCache4.get(stateDir);
|
|
23472
25236
|
const written = canonicalizeEntityRefFrontmatter(content, idsAtWrite);
|
|
@@ -23510,7 +25274,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
23510
25274
|
bumpMemoryCorpusVersionForDir(targetRoot);
|
|
23511
25275
|
}
|
|
23512
25276
|
if (reconcileNeeded) {
|
|
23513
|
-
requestEntityCanonicalIdReconcileSync(
|
|
25277
|
+
requestEntityCanonicalIdReconcileSync(path31.join(targetRoot, "state"));
|
|
23514
25278
|
}
|
|
23515
25279
|
return { merged, conflicts, skipped };
|
|
23516
25280
|
}
|
|
@@ -23521,7 +25285,7 @@ function walkMd2(dir) {
|
|
|
23521
25285
|
const results = [];
|
|
23522
25286
|
function walk(d) {
|
|
23523
25287
|
for (const entry of fs7.readdirSync(d, { withFileTypes: true })) {
|
|
23524
|
-
const fullPath =
|
|
25288
|
+
const fullPath = path31.join(d, entry.name);
|
|
23525
25289
|
if (entry.isSymbolicLink()) {
|
|
23526
25290
|
continue;
|
|
23527
25291
|
}
|
|
@@ -23550,8 +25314,8 @@ function safeRealpath(filePath) {
|
|
|
23550
25314
|
}
|
|
23551
25315
|
}
|
|
23552
25316
|
function isPathInsideRoot(candidatePath, rootPath) {
|
|
23553
|
-
const relative =
|
|
23554
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
25317
|
+
const relative = path31.relative(rootPath, candidatePath);
|
|
25318
|
+
return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
|
|
23555
25319
|
}
|
|
23556
25320
|
function parseSimpleFrontmatter(content) {
|
|
23557
25321
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -23672,7 +25436,7 @@ var REMNIC_RECALL_DECISION_RULES = `## When to Use Recall vs Direct Read
|
|
|
23672
25436
|
// src/memory-extension/codex-publisher.ts
|
|
23673
25437
|
import fs8 from "fs";
|
|
23674
25438
|
import os from "os";
|
|
23675
|
-
import
|
|
25439
|
+
import path32 from "path";
|
|
23676
25440
|
var REMNIC_EXTENSION_DIR_NAME = "remnic";
|
|
23677
25441
|
function resolveEnvHome(env) {
|
|
23678
25442
|
if (env === void 0) return resolveHomeDir();
|
|
@@ -23681,12 +25445,12 @@ function resolveEnvHome(env) {
|
|
|
23681
25445
|
function expandTildeWithHome(input, homeDir) {
|
|
23682
25446
|
if (input === "~") return homeDir;
|
|
23683
25447
|
if (input.startsWith("~/") || input.startsWith("~\\")) {
|
|
23684
|
-
return
|
|
25448
|
+
return path32.join(homeDir, input.slice(2));
|
|
23685
25449
|
}
|
|
23686
25450
|
return input;
|
|
23687
25451
|
}
|
|
23688
25452
|
function normalizeHostRoot(input, homeDir) {
|
|
23689
|
-
return
|
|
25453
|
+
return path32.resolve(expandTildeWithHome(input.trim(), homeDir));
|
|
23690
25454
|
}
|
|
23691
25455
|
var CodexMemoryExtensionPublisher = class {
|
|
23692
25456
|
hostId = "codex";
|
|
@@ -23700,12 +25464,12 @@ var CodexMemoryExtensionPublisher = class {
|
|
|
23700
25464
|
async resolveExtensionRoot(env) {
|
|
23701
25465
|
const homeDir = resolveEnvHome(env);
|
|
23702
25466
|
const codexHomeInput = env === void 0 ? readEnvVar("CODEX_HOME")?.trim() : env.CODEX_HOME?.trim();
|
|
23703
|
-
const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) :
|
|
23704
|
-
return
|
|
25467
|
+
const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path32.resolve(homeDir, ".codex");
|
|
25468
|
+
return path32.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
|
|
23705
25469
|
}
|
|
23706
25470
|
async isHostAvailable() {
|
|
23707
25471
|
try {
|
|
23708
|
-
const home = readEnvVar("CODEX_HOME")?.trim() ||
|
|
25472
|
+
const home = readEnvVar("CODEX_HOME")?.trim() || path32.join(resolveHomeDir(), ".codex");
|
|
23709
25473
|
return fs8.existsSync(home);
|
|
23710
25474
|
} catch {
|
|
23711
25475
|
return false;
|
|
@@ -23756,7 +25520,7 @@ When running inside the Codex phase-2 consolidation sandbox:
|
|
|
23756
25520
|
}
|
|
23757
25521
|
async publish(ctx) {
|
|
23758
25522
|
const extensionRoot = await this.resolveExtensionRoot();
|
|
23759
|
-
const instructionsPath =
|
|
25523
|
+
const instructionsPath = path32.join(extensionRoot, "instructions.md");
|
|
23760
25524
|
const filesWritten = [];
|
|
23761
25525
|
const skipped = [];
|
|
23762
25526
|
ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);
|
|
@@ -24130,9 +25894,9 @@ async function generateActivityMemories(date, digestBody, config, deps) {
|
|
|
24130
25894
|
}
|
|
24131
25895
|
|
|
24132
25896
|
// src/session-summaries/index.ts
|
|
24133
|
-
import { createHash as
|
|
24134
|
-
import { lstat as lstat3, mkdir as mkdir7, readFile as
|
|
24135
|
-
import
|
|
25897
|
+
import { createHash as createHash9 } from "crypto";
|
|
25898
|
+
import { lstat as lstat3, mkdir as mkdir7, readFile as readFile7, readdir as readdir4, rename, rm, stat as stat4, writeFile as writeFile7 } from "fs/promises";
|
|
25899
|
+
import path33 from "path";
|
|
24136
25900
|
|
|
24137
25901
|
// src/session-summaries/adapters.ts
|
|
24138
25902
|
var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "other"]);
|
|
@@ -24632,12 +26396,12 @@ var DEFAULT_MAX_FILES = 5e3;
|
|
|
24632
26396
|
var DEFAULT_MAX_SESSIONS = 500;
|
|
24633
26397
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".jsonl"]);
|
|
24634
26398
|
function shortHash(value, length = 16) {
|
|
24635
|
-
return
|
|
26399
|
+
return createHash9("sha256").update(value).digest("hex").slice(0, length);
|
|
24636
26400
|
}
|
|
24637
26401
|
async function listTranscriptFiles(root, maxFiles) {
|
|
24638
26402
|
const out = [];
|
|
24639
26403
|
let truncated = false;
|
|
24640
|
-
const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${
|
|
26404
|
+
const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path33.sep}` : entryName;
|
|
24641
26405
|
async function visit(dir) {
|
|
24642
26406
|
if (truncated) return;
|
|
24643
26407
|
const entries = (await readdir4(dir, { withFileTypes: true })).sort(
|
|
@@ -24647,13 +26411,13 @@ async function listTranscriptFiles(root, maxFiles) {
|
|
|
24647
26411
|
if (truncated) return;
|
|
24648
26412
|
if (entry.name.startsWith(".")) continue;
|
|
24649
26413
|
if (entry.isSymbolicLink()) continue;
|
|
24650
|
-
const fullPath =
|
|
26414
|
+
const fullPath = path33.join(dir, entry.name);
|
|
24651
26415
|
if (entry.isDirectory()) {
|
|
24652
26416
|
await visit(fullPath);
|
|
24653
26417
|
continue;
|
|
24654
26418
|
}
|
|
24655
26419
|
if (!entry.isFile()) continue;
|
|
24656
|
-
const ext =
|
|
26420
|
+
const ext = path33.extname(entry.name).toLowerCase();
|
|
24657
26421
|
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
24658
26422
|
out.push(fullPath);
|
|
24659
26423
|
if (out.length > maxFiles) {
|
|
@@ -24680,7 +26444,7 @@ function normalizeInputDir(inputDir) {
|
|
|
24680
26444
|
if (typeof inputDir !== "string" || inputDir.trim().length === 0) {
|
|
24681
26445
|
throw new Error("inputDir must be a non-empty string");
|
|
24682
26446
|
}
|
|
24683
|
-
return
|
|
26447
|
+
return path33.resolve(expandTildePath(inputDir.trim()));
|
|
24684
26448
|
}
|
|
24685
26449
|
function initRoleCounts() {
|
|
24686
26450
|
return {
|
|
@@ -24826,7 +26590,7 @@ async function collectLocalSessionSummaries(options) {
|
|
|
24826
26590
|
});
|
|
24827
26591
|
}
|
|
24828
26592
|
for (const filePath of files) {
|
|
24829
|
-
const content = await
|
|
26593
|
+
const content = await readFile7(filePath, "utf-8");
|
|
24830
26594
|
const fileHash = shortHash(content, 24);
|
|
24831
26595
|
if (seenFileHashes.has(fileHash)) {
|
|
24832
26596
|
warnings.push({
|
|
@@ -24837,11 +26601,11 @@ async function collectLocalSessionSummaries(options) {
|
|
|
24837
26601
|
continue;
|
|
24838
26602
|
}
|
|
24839
26603
|
seenFileHashes.add(fileHash);
|
|
24840
|
-
const fileExtension =
|
|
26604
|
+
const fileExtension = path33.extname(filePath).toLowerCase();
|
|
24841
26605
|
const parsed = await adapter.parseFile(
|
|
24842
26606
|
{
|
|
24843
26607
|
content,
|
|
24844
|
-
fileName:
|
|
26608
|
+
fileName: path33.basename(filePath),
|
|
24845
26609
|
fileExtension,
|
|
24846
26610
|
fileRef: fileHash
|
|
24847
26611
|
},
|
|
@@ -24897,7 +26661,7 @@ async function collectLocalSessionSummaries(options) {
|
|
|
24897
26661
|
}
|
|
24898
26662
|
async function readRedactionConfig(pathLike) {
|
|
24899
26663
|
if (!pathLike) return void 0;
|
|
24900
|
-
const raw = await
|
|
26664
|
+
const raw = await readFile7(path33.resolve(expandTildePath(pathLike)), "utf-8");
|
|
24901
26665
|
const parsed = JSON.parse(raw);
|
|
24902
26666
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
24903
26667
|
throw new Error("redaction config must be a JSON object");
|
|
@@ -24910,15 +26674,15 @@ function toJsonl(drafts) {
|
|
|
24910
26674
|
}
|
|
24911
26675
|
function defaultDraftOutputPath(memoryDir, generatedAt) {
|
|
24912
26676
|
const stamp = generatedAt.replace(/[:.]/g, "-");
|
|
24913
|
-
return
|
|
24914
|
-
|
|
26677
|
+
return path33.join(
|
|
26678
|
+
path33.resolve(expandTildePath(memoryDir)),
|
|
24915
26679
|
"state",
|
|
24916
26680
|
"session-summary-drafts",
|
|
24917
26681
|
`session-summaries-${stamp}.jsonl`
|
|
24918
26682
|
);
|
|
24919
26683
|
}
|
|
24920
26684
|
async function writeDrafts(filePath, drafts) {
|
|
24921
|
-
await mkdir7(
|
|
26685
|
+
await mkdir7(path33.dirname(filePath), { recursive: true });
|
|
24922
26686
|
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
24923
26687
|
try {
|
|
24924
26688
|
await writeFile7(tempPath, toJsonl(drafts), "utf-8");
|
|
@@ -24936,7 +26700,7 @@ async function runLocalSessionSummaryCliCommand(options) {
|
|
|
24936
26700
|
});
|
|
24937
26701
|
const wroteFiles = [];
|
|
24938
26702
|
if (options.output) {
|
|
24939
|
-
const outputPath =
|
|
26703
|
+
const outputPath = path33.resolve(expandTildePath(options.output));
|
|
24940
26704
|
await writeDrafts(outputPath, report.drafts);
|
|
24941
26705
|
wroteFiles.push(outputPath);
|
|
24942
26706
|
}
|
|
@@ -24970,20 +26734,20 @@ async function runLocalSessionSummaryCliCommand(options) {
|
|
|
24970
26734
|
}
|
|
24971
26735
|
|
|
24972
26736
|
// src/transfer/capsule-fork.ts
|
|
24973
|
-
import { lstat as lstat4, mkdir as mkdir8, readFile as
|
|
24974
|
-
import
|
|
26737
|
+
import { lstat as lstat4, mkdir as mkdir8, readFile as readFile8, realpath as realpath3, writeFile as writeFile8 } from "fs/promises";
|
|
26738
|
+
import path34 from "path";
|
|
24975
26739
|
async function forkCapsule(opts) {
|
|
24976
26740
|
validateForkId(opts.forkId);
|
|
24977
|
-
const rootAbs =
|
|
26741
|
+
const rootAbs = path34.resolve(opts.targetRoot);
|
|
24978
26742
|
await assertIsDirectoryNotSymlink(rootAbs, "forkCapsule", "targetRoot");
|
|
24979
|
-
const forkDirAbs =
|
|
26743
|
+
const forkDirAbs = path34.join(rootAbs, "forks", opts.forkId);
|
|
24980
26744
|
const forkEntryExists = await pathEntryExists(forkDirAbs);
|
|
24981
26745
|
if (forkEntryExists) {
|
|
24982
26746
|
throw new Error(
|
|
24983
26747
|
`forkCapsule: fork path already exists \u2014 forkId "${opts.forkId}" is already in use at: ${forkDirAbs}`
|
|
24984
26748
|
);
|
|
24985
26749
|
}
|
|
24986
|
-
const archiveAbs =
|
|
26750
|
+
const archiveAbs = path34.resolve(opts.sourceArchive);
|
|
24987
26751
|
const importResult = await importCapsule({
|
|
24988
26752
|
archivePath: archiveAbs,
|
|
24989
26753
|
root: rootAbs,
|
|
@@ -25005,10 +26769,10 @@ async function forkCapsule(opts) {
|
|
|
25005
26769
|
importedRecords: importResult.imported.length,
|
|
25006
26770
|
skippedRecords: importResult.skipped.length
|
|
25007
26771
|
};
|
|
25008
|
-
const lineagePath =
|
|
26772
|
+
const lineagePath = path34.join(forkDirAbs, "lineage.json");
|
|
25009
26773
|
const rootReal = await realpath3(rootAbs);
|
|
25010
26774
|
await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, "forkCapsule");
|
|
25011
|
-
await mkdir8(
|
|
26775
|
+
await mkdir8(path34.dirname(lineagePath), { recursive: true });
|
|
25012
26776
|
await writeFile8(lineagePath, JSON.stringify(lineage, null, 2) + "\n", "utf-8");
|
|
25013
26777
|
return {
|
|
25014
26778
|
archivePath: archiveAbs,
|
|
@@ -25022,17 +26786,17 @@ async function readForkLineage(targetRoot, forkId) {
|
|
|
25022
26786
|
if (typeof forkId !== "string" || forkId.length === 0 || forkId.length > 64 || !CAPSULE_ID_PATTERN.test(forkId)) {
|
|
25023
26787
|
return null;
|
|
25024
26788
|
}
|
|
25025
|
-
const rootAbs =
|
|
26789
|
+
const rootAbs = path34.resolve(targetRoot);
|
|
25026
26790
|
const rootReal = await realpath3(rootAbs).catch(() => rootAbs);
|
|
25027
|
-
const lineagePath =
|
|
25028
|
-
const rel =
|
|
25029
|
-
if (rel.startsWith("..") ||
|
|
26791
|
+
const lineagePath = path34.join(rootReal, "forks", forkId, "lineage.json");
|
|
26792
|
+
const rel = path34.relative(rootReal, lineagePath);
|
|
26793
|
+
if (rel.startsWith("..") || path34.isAbsolute(rel)) {
|
|
25030
26794
|
return null;
|
|
25031
26795
|
}
|
|
25032
26796
|
if (!await isLineagePathContained(rootReal, lineagePath)) {
|
|
25033
26797
|
return null;
|
|
25034
26798
|
}
|
|
25035
|
-
const raw = await
|
|
26799
|
+
const raw = await readFile8(lineagePath, "utf-8").catch(() => null);
|
|
25036
26800
|
if (raw === null) return null;
|
|
25037
26801
|
try {
|
|
25038
26802
|
const parsed = JSON.parse(raw);
|
|
@@ -25067,19 +26831,19 @@ async function pathEntryExists(absPath) {
|
|
|
25067
26831
|
async function isLineagePathContained(rootReal, lineagePath) {
|
|
25068
26832
|
let existing = lineagePath;
|
|
25069
26833
|
const suffix = [];
|
|
25070
|
-
while (existing !==
|
|
26834
|
+
while (existing !== path34.dirname(existing)) {
|
|
25071
26835
|
const st = await lstat4(existing).catch(() => null);
|
|
25072
26836
|
if (st !== null) break;
|
|
25073
|
-
suffix.unshift(
|
|
25074
|
-
existing =
|
|
26837
|
+
suffix.unshift(path34.basename(existing));
|
|
26838
|
+
existing = path34.dirname(existing);
|
|
25075
26839
|
}
|
|
25076
26840
|
const existingReal = await realpath3(existing).catch(() => existing);
|
|
25077
|
-
const targetReal = suffix.length > 0 ?
|
|
25078
|
-
const rel =
|
|
26841
|
+
const targetReal = suffix.length > 0 ? path34.join(existingReal, ...suffix) : existingReal;
|
|
26842
|
+
const rel = path34.relative(rootReal, targetReal);
|
|
25079
26843
|
if (rel === "") return true;
|
|
25080
26844
|
if (rel === "..") return false;
|
|
25081
|
-
if (rel.startsWith(`..${
|
|
25082
|
-
if (
|
|
26845
|
+
if (rel.startsWith(`..${path34.sep}`)) return false;
|
|
26846
|
+
if (path34.isAbsolute(rel)) return false;
|
|
25083
26847
|
return true;
|
|
25084
26848
|
}
|
|
25085
26849
|
|
|
@@ -25198,4 +26962,4 @@ export {
|
|
|
25198
26962
|
blendGraphExpandedRecallScore,
|
|
25199
26963
|
Orchestrator
|
|
25200
26964
|
};
|
|
25201
|
-
//# sourceMappingURL=chunk-
|
|
26965
|
+
//# sourceMappingURL=chunk-RMHMUUBW.js.map
|