@remnic/core 9.3.517 → 9.3.519
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-cli.js +13 -13
- package/dist/access-http.js +8 -8
- package/dist/access-mcp.js +7 -7
- package/dist/access-schema.d.ts +4 -4
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +5 -5
- package/dist/briefing.js +2 -2
- package/dist/causal-consolidation.js +3 -3
- package/dist/{chunk-6F6BXB7A.js → chunk-67VP6I47.js} +2 -2
- package/dist/{chunk-6XSPNR6L.js → chunk-6HF5VUBQ.js} +2 -2
- package/dist/{chunk-V2RCP53Q.js → chunk-6QKWLP4V.js} +2 -2
- package/dist/{chunk-XNLXAWHX.js → chunk-7TVK7E3R.js} +2 -2
- package/dist/{chunk-23UORJ4S.js → chunk-BE5XAWSA.js} +2 -2
- package/dist/{chunk-5UHVGNZD.js → chunk-DGDQZ3JW.js} +2 -2
- package/dist/{chunk-2AN2L4NL.js → chunk-DKOIMCGB.js} +2 -2
- package/dist/{chunk-5V456VRV.js → chunk-HX5XV3GL.js} +5 -5
- package/dist/{chunk-UIPDNLXA.js → chunk-K4LDMTTY.js} +118 -13
- package/dist/chunk-K4LDMTTY.js.map +1 -0
- package/dist/{chunk-YDMVYYD2.js → chunk-KAUXI453.js} +8 -8
- package/dist/{chunk-FCOQXV3T.js → chunk-KCLX6LOV.js} +12 -12
- package/dist/{chunk-ZEY4KYRQ.js → chunk-LKCE3NPZ.js} +13 -2
- package/dist/chunk-LKCE3NPZ.js.map +1 -0
- package/dist/{chunk-CHBI22MI.js → chunk-O2HWL47E.js} +2 -2
- package/dist/{chunk-TTGZV5R3.js → chunk-OHZXPJK7.js} +4 -4
- package/dist/{chunk-FPGE5NVO.js → chunk-OMHRQTOD.js} +2 -2
- package/dist/{chunk-E62SBGQ3.js → chunk-PYGKYEDU.js} +3 -3
- package/dist/{chunk-IQ3OI2RR.js → chunk-QK2G4EYH.js} +2 -2
- package/dist/{chunk-YNXOKMJP.js → chunk-RB7LF7K7.js} +4 -4
- package/dist/{chunk-YHV3KRKS.js → chunk-TKUQG2PE.js} +2 -2
- package/dist/{chunk-HC6EKOID.js → chunk-UMZRCQFC.js} +5 -5
- package/dist/{chunk-LJBOVCQG.js → chunk-XI5V7WSJ.js} +2 -2
- package/dist/{chunk-6BR7L222.js → chunk-ZQLC75BF.js} +2 -2
- package/dist/cli.js +17 -17
- package/dist/compounding/engine.js +2 -2
- package/dist/connectors/codex-materialize-runner.js +2 -2
- package/dist/connectors/index.js +2 -2
- package/dist/entity-retrieval.js +2 -2
- package/dist/index.js +22 -22
- package/dist/maintenance/memory-governance.js +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +3 -3
- package/dist/namespaces/migrate.js +3 -3
- package/dist/namespaces/storage.js +2 -2
- package/dist/offline-sync.js +1 -1
- package/dist/operator-toolkit.js +5 -5
- package/dist/orchestrator.js +9 -9
- package/dist/schemas.d.ts +22 -22
- package/dist/semantic-consolidation.js +3 -3
- package/dist/semantic-rule-promotion.js +2 -2
- package/dist/semantic-rule-verifier.js +2 -2
- package/dist/storage.d.ts +10 -0
- package/dist/storage.js +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/verified-recall.js +2 -2
- package/package.json +1 -1
- package/src/offline-sync.test.ts +84 -0
- package/src/offline-sync.ts +17 -1
- package/src/storage.ts +156 -12
- package/dist/chunk-UIPDNLXA.js.map +0 -1
- package/dist/chunk-ZEY4KYRQ.js.map +0 -1
- /package/dist/{chunk-6F6BXB7A.js.map → chunk-67VP6I47.js.map} +0 -0
- /package/dist/{chunk-6XSPNR6L.js.map → chunk-6HF5VUBQ.js.map} +0 -0
- /package/dist/{chunk-V2RCP53Q.js.map → chunk-6QKWLP4V.js.map} +0 -0
- /package/dist/{chunk-XNLXAWHX.js.map → chunk-7TVK7E3R.js.map} +0 -0
- /package/dist/{chunk-23UORJ4S.js.map → chunk-BE5XAWSA.js.map} +0 -0
- /package/dist/{chunk-5UHVGNZD.js.map → chunk-DGDQZ3JW.js.map} +0 -0
- /package/dist/{chunk-2AN2L4NL.js.map → chunk-DKOIMCGB.js.map} +0 -0
- /package/dist/{chunk-5V456VRV.js.map → chunk-HX5XV3GL.js.map} +0 -0
- /package/dist/{chunk-YDMVYYD2.js.map → chunk-KAUXI453.js.map} +0 -0
- /package/dist/{chunk-FCOQXV3T.js.map → chunk-KCLX6LOV.js.map} +0 -0
- /package/dist/{chunk-CHBI22MI.js.map → chunk-O2HWL47E.js.map} +0 -0
- /package/dist/{chunk-TTGZV5R3.js.map → chunk-OHZXPJK7.js.map} +0 -0
- /package/dist/{chunk-FPGE5NVO.js.map → chunk-OMHRQTOD.js.map} +0 -0
- /package/dist/{chunk-E62SBGQ3.js.map → chunk-PYGKYEDU.js.map} +0 -0
- /package/dist/{chunk-IQ3OI2RR.js.map → chunk-QK2G4EYH.js.map} +0 -0
- /package/dist/{chunk-YNXOKMJP.js.map → chunk-RB7LF7K7.js.map} +0 -0
- /package/dist/{chunk-YHV3KRKS.js.map → chunk-TKUQG2PE.js.map} +0 -0
- /package/dist/{chunk-HC6EKOID.js.map → chunk-UMZRCQFC.js.map} +0 -0
- /package/dist/{chunk-LJBOVCQG.js.map → chunk-XI5V7WSJ.js.map} +0 -0
- /package/dist/{chunk-6BR7L222.js.map → chunk-ZQLC75BF.js.map} +0 -0
package/src/storage.ts
CHANGED
|
@@ -135,6 +135,15 @@ const ARTIFACT_SEARCH_STOPWORDS = new Set([
|
|
|
135
135
|
"with",
|
|
136
136
|
]);
|
|
137
137
|
|
|
138
|
+
type OfflineSyncDigestCacheEntry = {
|
|
139
|
+
statBytes: number;
|
|
140
|
+
mtimeMs: number;
|
|
141
|
+
ctimeMs: number;
|
|
142
|
+
encrypted: boolean;
|
|
143
|
+
sha256: string;
|
|
144
|
+
bytes: number;
|
|
145
|
+
};
|
|
146
|
+
|
|
138
147
|
export interface ReextractJobRequest {
|
|
139
148
|
memoryId: string;
|
|
140
149
|
model: string;
|
|
@@ -2236,6 +2245,10 @@ export class StorageManager {
|
|
|
2236
2245
|
private factHashIndexAuthoritative: boolean | null = null;
|
|
2237
2246
|
private factHashIndexAuthoritativePromise: Promise<void> | null = null;
|
|
2238
2247
|
private readonly secureAppendChains = new Map<string, Promise<void>>();
|
|
2248
|
+
private offlineSyncDigestCache: Map<string, OfflineSyncDigestCacheEntry> | null = null;
|
|
2249
|
+
private offlineSyncDigestCacheLoadPromise: Promise<Map<string, OfflineSyncDigestCacheEntry>> | null = null;
|
|
2250
|
+
private offlineSyncDigestCacheWriteChain: Promise<void> = Promise.resolve();
|
|
2251
|
+
private offlineSyncDigestCacheWriteTimer: ReturnType<typeof setTimeout> | null = null;
|
|
2239
2252
|
/** Optional: set by the orchestrator after construction to enable template-aware citation stripping during legacy hash rebuild. */
|
|
2240
2253
|
citationTemplate: string = DEFAULT_CITATION_FORMAT;
|
|
2241
2254
|
|
|
@@ -2516,24 +2529,152 @@ export class StorageManager {
|
|
|
2516
2529
|
|
|
2517
2530
|
async digestOfflineSyncFile(filePath: string): Promise<{ sha256: string; bytes: number }> {
|
|
2518
2531
|
const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
|
|
2519
|
-
|
|
2520
|
-
|
|
2532
|
+
const st = await stat(target);
|
|
2533
|
+
const relPath = path.relative(this.baseDir, target).split(path.sep).join("/");
|
|
2534
|
+
const cache = await this.loadOfflineSyncDigestCache();
|
|
2535
|
+
const cached = cache.get(relPath);
|
|
2536
|
+
if (
|
|
2537
|
+
cached &&
|
|
2538
|
+
cached.statBytes === st.size &&
|
|
2539
|
+
cached.mtimeMs === st.mtimeMs &&
|
|
2540
|
+
cached.ctimeMs === st.ctimeMs &&
|
|
2541
|
+
!cached.encrypted
|
|
2542
|
+
) {
|
|
2521
2543
|
return {
|
|
2544
|
+
sha256: cached.sha256,
|
|
2545
|
+
bytes: cached.bytes,
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
const encrypted = await this.offlineSyncFileIsEncrypted(target);
|
|
2550
|
+
let digest: { sha256: string; bytes: number };
|
|
2551
|
+
if (encrypted) {
|
|
2552
|
+
const content = await readMaybeEncryptedFileBuffer(target, this._secureStoreKey, this.baseDir);
|
|
2553
|
+
digest = {
|
|
2522
2554
|
sha256: createHash("sha256").update(content).digest("hex"),
|
|
2523
2555
|
bytes: content.byteLength,
|
|
2524
2556
|
};
|
|
2557
|
+
} else {
|
|
2558
|
+
const hash = createHash("sha256");
|
|
2559
|
+
let bytes = 0;
|
|
2560
|
+
for await (const rawChunk of createReadStream(target)) {
|
|
2561
|
+
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
|
|
2562
|
+
hash.update(chunk);
|
|
2563
|
+
bytes += chunk.length;
|
|
2564
|
+
}
|
|
2565
|
+
digest = {
|
|
2566
|
+
sha256: hash.digest("hex"),
|
|
2567
|
+
bytes,
|
|
2568
|
+
};
|
|
2525
2569
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
for await (const rawChunk of createReadStream(target)) {
|
|
2529
|
-
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
|
|
2530
|
-
hash.update(chunk);
|
|
2531
|
-
bytes += chunk.length;
|
|
2570
|
+
if (!encrypted) {
|
|
2571
|
+
this.rememberOfflineSyncDigest(relPath, st, digest);
|
|
2532
2572
|
}
|
|
2533
|
-
return
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2573
|
+
return digest;
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
private async loadOfflineSyncDigestCache(): Promise<Map<string, OfflineSyncDigestCacheEntry>> {
|
|
2577
|
+
if (this.offlineSyncDigestCache) return this.offlineSyncDigestCache;
|
|
2578
|
+
if (!this.offlineSyncDigestCacheLoadPromise) {
|
|
2579
|
+
this.offlineSyncDigestCacheLoadPromise = this.readOfflineSyncDigestCache();
|
|
2580
|
+
}
|
|
2581
|
+
this.offlineSyncDigestCache = await this.offlineSyncDigestCacheLoadPromise;
|
|
2582
|
+
return this.offlineSyncDigestCache;
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
private async readOfflineSyncDigestCache(): Promise<Map<string, OfflineSyncDigestCacheEntry>> {
|
|
2586
|
+
const cache = new Map<string, OfflineSyncDigestCacheEntry>();
|
|
2587
|
+
try {
|
|
2588
|
+
const raw = await readFile(this.offlineSyncDigestCachePath, "utf-8");
|
|
2589
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
2590
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return cache;
|
|
2591
|
+
const entries = (parsed as { entries?: unknown }).entries;
|
|
2592
|
+
if (!Array.isArray(entries)) return cache;
|
|
2593
|
+
for (const entry of entries) {
|
|
2594
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
2595
|
+
const record = entry as Record<string, unknown>;
|
|
2596
|
+
const cachePath = typeof record.path === "string" ? record.path : "";
|
|
2597
|
+
const statBytes = typeof record.statBytes === "number" ? record.statBytes : NaN;
|
|
2598
|
+
const mtimeMs = typeof record.mtimeMs === "number" ? record.mtimeMs : NaN;
|
|
2599
|
+
const ctimeMs = typeof record.ctimeMs === "number" ? record.ctimeMs : NaN;
|
|
2600
|
+
const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
|
|
2601
|
+
const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
|
|
2602
|
+
const encrypted = record.encrypted === true;
|
|
2603
|
+
if (
|
|
2604
|
+
cachePath.length === 0 ||
|
|
2605
|
+
cachePath === ".." ||
|
|
2606
|
+
cachePath.startsWith("../") ||
|
|
2607
|
+
path.isAbsolute(cachePath) ||
|
|
2608
|
+
!Number.isFinite(statBytes) ||
|
|
2609
|
+
!Number.isFinite(mtimeMs) ||
|
|
2610
|
+
!Number.isFinite(ctimeMs) ||
|
|
2611
|
+
!Number.isFinite(bytes) ||
|
|
2612
|
+
!/^[a-f0-9]{64}$/i.test(sha256)
|
|
2613
|
+
) {
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
|
|
2617
|
+
}
|
|
2618
|
+
} catch (err) {
|
|
2619
|
+
if (!isErrnoCode(err, "ENOENT")) {
|
|
2620
|
+
log.warn(
|
|
2621
|
+
`storage.offlineSyncDigestCache: ignoring unreadable cache: ${err instanceof Error ? err.message : String(err)}`,
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
return cache;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
private rememberOfflineSyncDigest(
|
|
2629
|
+
relPath: string,
|
|
2630
|
+
st: { size: number; mtimeMs: number; ctimeMs: number },
|
|
2631
|
+
digest: { sha256: string; bytes: number },
|
|
2632
|
+
): void {
|
|
2633
|
+
const cache = this.offlineSyncDigestCache;
|
|
2634
|
+
if (!cache) return;
|
|
2635
|
+
cache.set(relPath, {
|
|
2636
|
+
statBytes: st.size,
|
|
2637
|
+
mtimeMs: st.mtimeMs,
|
|
2638
|
+
ctimeMs: st.ctimeMs,
|
|
2639
|
+
encrypted: false,
|
|
2640
|
+
sha256: digest.sha256,
|
|
2641
|
+
bytes: digest.bytes,
|
|
2642
|
+
});
|
|
2643
|
+
this.scheduleOfflineSyncDigestCacheWrite();
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
private scheduleOfflineSyncDigestCacheWrite(): void {
|
|
2647
|
+
if (this.offlineSyncDigestCacheWriteTimer) {
|
|
2648
|
+
clearTimeout(this.offlineSyncDigestCacheWriteTimer);
|
|
2649
|
+
}
|
|
2650
|
+
this.offlineSyncDigestCacheWriteTimer = setTimeout(() => {
|
|
2651
|
+
this.offlineSyncDigestCacheWriteTimer = null;
|
|
2652
|
+
this.queueOfflineSyncDigestCacheWrite();
|
|
2653
|
+
}, 1_000);
|
|
2654
|
+
this.offlineSyncDigestCacheWriteTimer.unref?.();
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
private queueOfflineSyncDigestCacheWrite(): void {
|
|
2658
|
+
this.offlineSyncDigestCacheWriteChain = this.offlineSyncDigestCacheWriteChain
|
|
2659
|
+
.catch(() => undefined)
|
|
2660
|
+
.then(async () => {
|
|
2661
|
+
const cache = this.offlineSyncDigestCache;
|
|
2662
|
+
if (!cache) return;
|
|
2663
|
+
const entries = [...cache.entries()]
|
|
2664
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
2665
|
+
.map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
|
|
2666
|
+
await mkdir(path.dirname(this.offlineSyncDigestCachePath), { recursive: true });
|
|
2667
|
+
await writeFile(
|
|
2668
|
+
this.offlineSyncDigestCachePath,
|
|
2669
|
+
`${JSON.stringify({ version: 1, entries })}\n`,
|
|
2670
|
+
"utf-8",
|
|
2671
|
+
);
|
|
2672
|
+
})
|
|
2673
|
+
.catch((err) => {
|
|
2674
|
+
log.warn(
|
|
2675
|
+
`storage.offlineSyncDigestCache: failed to write cache: ${err instanceof Error ? err.message : String(err)}`,
|
|
2676
|
+
);
|
|
2677
|
+
});
|
|
2537
2678
|
}
|
|
2538
2679
|
|
|
2539
2680
|
private async offlineSyncFileIsEncrypted(filePath: string): Promise<boolean> {
|
|
@@ -2644,6 +2785,9 @@ export class StorageManager {
|
|
|
2644
2785
|
private get stateDir(): string {
|
|
2645
2786
|
return path.join(this.baseDir, "state");
|
|
2646
2787
|
}
|
|
2788
|
+
private get offlineSyncDigestCachePath(): string {
|
|
2789
|
+
return path.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
|
|
2790
|
+
}
|
|
2647
2791
|
private get entitySynthesisQueuePath(): string {
|
|
2648
2792
|
return path.join(this.stateDir, "entity-synthesis-queue.json");
|
|
2649
2793
|
}
|