@remnic/core 1.1.13 → 1.1.15
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 +34 -33
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +2 -1
- package/dist/access-http.js +15 -14
- package/dist/access-mcp.d.ts +2 -1
- package/dist/access-mcp.js +14 -13
- package/dist/access-schema.d.ts +22 -5
- package/dist/access-schema.js +7 -5
- package/dist/{access-service-DcCDmNYC.d.ts → access-service-BCMine1s.d.ts} +21 -1
- package/dist/access-service.d.ts +2 -1
- package/dist/access-service.js +12 -11
- package/dist/briefing.js +4 -4
- package/dist/causal-consolidation.js +5 -5
- package/dist/{chunk-VNO6ZJ35.js → chunk-2PRLKQAH.js} +5 -5
- package/dist/{chunk-M23FSH32.js → chunk-5D2G67ZQ.js} +53 -6
- package/dist/chunk-5D2G67ZQ.js.map +1 -0
- package/dist/{chunk-EFJ3MQ4V.js → chunk-65HQPW6O.js} +2 -2
- package/dist/{chunk-GA454ALV.js → chunk-AAX3SUM3.js} +39 -39
- package/dist/{chunk-QQUAB63I.js → chunk-BEB4GUU5.js} +2 -2
- package/dist/{chunk-WZYKANL3.js → chunk-BNATB54A.js} +4 -4
- package/dist/{chunk-KUJVMMZQ.js → chunk-C7DGCHJE.js} +2 -2
- package/dist/{chunk-PR5FBTFU.js → chunk-CYFQJMUV.js} +5 -5
- package/dist/{chunk-KLAO5DGL.js → chunk-G7JBLD65.js} +3 -3
- package/dist/chunk-GSP6ZKOY.js +769 -0
- package/dist/chunk-GSP6ZKOY.js.map +1 -0
- package/dist/{chunk-CQZRLNMV.js → chunk-HJ2WMBFB.js} +42 -4
- package/dist/chunk-HJ2WMBFB.js.map +1 -0
- package/dist/{chunk-ME6ESPZU.js → chunk-IG5VGHYB.js} +2 -2
- package/dist/{chunk-7AAT6G4Q.js → chunk-IOAY54RF.js} +57 -5
- package/dist/chunk-IOAY54RF.js.map +1 -0
- package/dist/{chunk-XVZ7B3HG.js → chunk-JFEH2LZM.js} +2 -2
- package/dist/{chunk-JLFA7DQG.js → chunk-M3AA636B.js} +2 -2
- package/dist/{chunk-P4NEIHUT.js → chunk-MS3ULOZF.js} +2 -2
- package/dist/{chunk-7IASACLB.js → chunk-NOHC2L57.js} +2 -2
- package/dist/{chunk-6RVI47ZR.js → chunk-NTUNYIF7.js} +5 -5
- package/dist/{chunk-CK5NTM2S.js → chunk-OGROP7ZN.js} +2 -2
- package/dist/{chunk-MT25YHYH.js → chunk-OJRKZLZ4.js} +5 -5
- package/dist/{chunk-2F2W355T.js → chunk-QA2ZAPBU.js} +4 -4
- package/dist/{chunk-MC26UJIM.js → chunk-QLKBF3TI.js} +2 -2
- package/dist/{chunk-YNJHCGDT.js → chunk-SH5S7XYD.js} +8 -5
- package/dist/chunk-SH5S7XYD.js.map +1 -0
- package/dist/{chunk-VW676BEI.js → chunk-V7WH7DEM.js} +2 -2
- package/dist/{chunk-A2XUIMJ3.js → chunk-VWFIQOTJ.js} +11 -2
- package/dist/chunk-VWFIQOTJ.js.map +1 -0
- package/dist/{chunk-PU63GXWS.js → chunk-W7DK3CYM.js} +2 -2
- package/dist/{chunk-TFO23QT4.js → chunk-XKLD5OK4.js} +4 -4
- package/dist/{chunk-I5V2VDIW.js → chunk-YCVWX2NF.js} +2 -2
- package/dist/{chunk-UXHQAFNA.js → chunk-ZPXYWTN5.js} +4 -4
- package/dist/{chunk-CHEL3SKB.js → chunk-ZYRMKWVW.js} +27 -27
- package/dist/{chunk-GGKRUQOO.js → chunk-ZYVPLJ4T.js} +4 -4
- package/dist/{cli-D3VpkVwB.d.ts → cli-B71zQ6XK.d.ts} +1 -1
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +35 -34
- package/dist/compounding/engine.js +4 -4
- package/dist/connectors/codex-materialize-runner.js +4 -4
- package/dist/connectors/index.js +4 -4
- package/dist/conversation-index/backend.js +2 -2
- package/dist/entity-retrieval.js +4 -4
- package/dist/index.d.ts +4 -3
- package/dist/index.js +90 -58
- package/dist/index.js.map +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/maintenance/memory-governance.js +4 -4
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
- package/dist/maintenance/rebuild-memory-projection.js +5 -5
- package/dist/mcp-memory-inspector-app.d.ts +2 -1
- package/dist/namespaces/migrate.js +10 -10
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.js +4 -4
- package/dist/offline-sync.d.ts +136 -0
- package/dist/offline-sync.js +41 -0
- package/dist/offline-sync.js.map +1 -0
- package/dist/operator-toolkit.js +13 -13
- package/dist/orchestrator.js +24 -24
- package/dist/search/factory.js +4 -4
- package/dist/search/index.js +6 -6
- package/dist/secure-store/index.d.ts +1 -15
- package/dist/secure-store/index.js +2 -2
- package/dist/semantic-consolidation.js +5 -5
- package/dist/semantic-rule-promotion.js +4 -4
- package/dist/semantic-rule-verifier.js +4 -4
- package/dist/storage.d.ts +7 -0
- package/dist/storage.js +3 -3
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +4 -4
- package/dist/transfer/capsule-import.js +3 -3
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/verified-recall.js +4 -4
- package/package.json +1 -1
- package/src/access-http.test.ts +216 -0
- package/src/access-http.ts +54 -0
- package/src/access-schema.ts +18 -0
- package/src/access-service.ts +76 -0
- package/src/index.ts +33 -0
- package/src/offline-sync.test.ts +521 -0
- package/src/offline-sync.ts +998 -0
- package/src/qmd.test.ts +1 -0
- package/src/secure-store/secure-fs.ts +14 -7
- package/src/storage.ts +59 -0
- package/dist/chunk-7AAT6G4Q.js.map +0 -1
- package/dist/chunk-A2XUIMJ3.js.map +0 -1
- package/dist/chunk-CQZRLNMV.js.map +0 -1
- package/dist/chunk-M23FSH32.js.map +0 -1
- package/dist/chunk-YNJHCGDT.js.map +0 -1
- /package/dist/{chunk-VNO6ZJ35.js.map → chunk-2PRLKQAH.js.map} +0 -0
- /package/dist/{chunk-EFJ3MQ4V.js.map → chunk-65HQPW6O.js.map} +0 -0
- /package/dist/{chunk-GA454ALV.js.map → chunk-AAX3SUM3.js.map} +0 -0
- /package/dist/{chunk-QQUAB63I.js.map → chunk-BEB4GUU5.js.map} +0 -0
- /package/dist/{chunk-WZYKANL3.js.map → chunk-BNATB54A.js.map} +0 -0
- /package/dist/{chunk-KUJVMMZQ.js.map → chunk-C7DGCHJE.js.map} +0 -0
- /package/dist/{chunk-PR5FBTFU.js.map → chunk-CYFQJMUV.js.map} +0 -0
- /package/dist/{chunk-KLAO5DGL.js.map → chunk-G7JBLD65.js.map} +0 -0
- /package/dist/{chunk-ME6ESPZU.js.map → chunk-IG5VGHYB.js.map} +0 -0
- /package/dist/{chunk-XVZ7B3HG.js.map → chunk-JFEH2LZM.js.map} +0 -0
- /package/dist/{chunk-JLFA7DQG.js.map → chunk-M3AA636B.js.map} +0 -0
- /package/dist/{chunk-P4NEIHUT.js.map → chunk-MS3ULOZF.js.map} +0 -0
- /package/dist/{chunk-7IASACLB.js.map → chunk-NOHC2L57.js.map} +0 -0
- /package/dist/{chunk-6RVI47ZR.js.map → chunk-NTUNYIF7.js.map} +0 -0
- /package/dist/{chunk-CK5NTM2S.js.map → chunk-OGROP7ZN.js.map} +0 -0
- /package/dist/{chunk-MT25YHYH.js.map → chunk-OJRKZLZ4.js.map} +0 -0
- /package/dist/{chunk-2F2W355T.js.map → chunk-QA2ZAPBU.js.map} +0 -0
- /package/dist/{chunk-MC26UJIM.js.map → chunk-QLKBF3TI.js.map} +0 -0
- /package/dist/{chunk-VW676BEI.js.map → chunk-V7WH7DEM.js.map} +0 -0
- /package/dist/{chunk-PU63GXWS.js.map → chunk-W7DK3CYM.js.map} +0 -0
- /package/dist/{chunk-TFO23QT4.js.map → chunk-XKLD5OK4.js.map} +0 -0
- /package/dist/{chunk-I5V2VDIW.js.map → chunk-YCVWX2NF.js.map} +0 -0
- /package/dist/{chunk-UXHQAFNA.js.map → chunk-ZPXYWTN5.js.map} +0 -0
- /package/dist/{chunk-CHEL3SKB.js.map → chunk-ZYRMKWVW.js.map} +0 -0
- /package/dist/{chunk-GGKRUQOO.js.map → chunk-ZYVPLJ4T.js.map} +0 -0
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
runSecureStoreMigrate,
|
|
14
14
|
runSecureStoreStatus,
|
|
15
15
|
runSecureStoreUnlock
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-OGROP7ZN.js";
|
|
17
17
|
import {
|
|
18
18
|
FILE_FORMAT_FLAGS,
|
|
19
19
|
FILE_FORMAT_VERSION,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
migrateMemoryDirToEncrypted,
|
|
30
30
|
readMaybeEncryptedFile,
|
|
31
31
|
writeMaybeEncryptedFile
|
|
32
|
-
} from "../chunk-
|
|
32
|
+
} from "../chunk-SH5S7XYD.js";
|
|
33
33
|
import {
|
|
34
34
|
HEADER_FILENAME,
|
|
35
35
|
HEADER_FORMAT,
|
|
@@ -7,20 +7,20 @@ import {
|
|
|
7
7
|
materializeAfterSemanticConsolidation,
|
|
8
8
|
parseConsolidationResponse,
|
|
9
9
|
parseOperatorAwareConsolidationResponse
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-YCVWX2NF.js";
|
|
11
|
+
import "./chunk-IG5VGHYB.js";
|
|
12
12
|
import "./chunk-RHY3HH7P.js";
|
|
13
13
|
import {
|
|
14
14
|
resolveExtensionsRoot
|
|
15
15
|
} from "./chunk-EJI5XIBB.js";
|
|
16
16
|
import "./chunk-3UXOZBHV.js";
|
|
17
17
|
import "./chunk-U3PN77QT.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-IOAY54RF.js";
|
|
19
19
|
import "./chunk-5UZXUTVO.js";
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-P7FMDTKL.js";
|
|
20
|
+
import "./chunk-SH5S7XYD.js";
|
|
22
21
|
import "./chunk-NN2DKE4T.js";
|
|
23
22
|
import "./chunk-Q7P4WJDP.js";
|
|
23
|
+
import "./chunk-P7FMDTKL.js";
|
|
24
24
|
import "./chunk-SCU65EZI.js";
|
|
25
25
|
import "./chunk-3KW65B36.js";
|
|
26
26
|
import "./chunk-3HPAPHUK.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
promoteSemanticRuleFromMemory
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-JFEH2LZM.js";
|
|
4
|
+
import "./chunk-IOAY54RF.js";
|
|
5
5
|
import "./chunk-5UZXUTVO.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-P7FMDTKL.js";
|
|
6
|
+
import "./chunk-SH5S7XYD.js";
|
|
8
7
|
import "./chunk-NN2DKE4T.js";
|
|
9
8
|
import "./chunk-Q7P4WJDP.js";
|
|
9
|
+
import "./chunk-P7FMDTKL.js";
|
|
10
10
|
import "./chunk-SCU65EZI.js";
|
|
11
11
|
import "./chunk-3KW65B36.js";
|
|
12
12
|
import "./chunk-3HPAPHUK.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
searchVerifiedSemanticRules
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-QLKBF3TI.js";
|
|
4
|
+
import "./chunk-IOAY54RF.js";
|
|
5
5
|
import "./chunk-5UZXUTVO.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-P7FMDTKL.js";
|
|
6
|
+
import "./chunk-SH5S7XYD.js";
|
|
8
7
|
import "./chunk-NN2DKE4T.js";
|
|
9
8
|
import "./chunk-Q7P4WJDP.js";
|
|
9
|
+
import "./chunk-P7FMDTKL.js";
|
|
10
10
|
import "./chunk-SCU65EZI.js";
|
|
11
11
|
import "./chunk-3KW65B36.js";
|
|
12
12
|
import "./chunk-3HPAPHUK.js";
|
package/dist/storage.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ declare class ContentHashIndex {
|
|
|
50
50
|
/** Add content hash to the index. */
|
|
51
51
|
add(content: string): void;
|
|
52
52
|
get size(): number;
|
|
53
|
+
/** Clear all loaded hashes so the next save rewrites the index from scratch. */
|
|
54
|
+
clear(): void;
|
|
53
55
|
/** Persist index to disk if changed. */
|
|
54
56
|
save(): Promise<void>;
|
|
55
57
|
/** Remove a hash from the index (used when archiving/deleting). */
|
|
@@ -230,6 +232,11 @@ declare class StorageManager {
|
|
|
230
232
|
private get entitiesDir();
|
|
231
233
|
private readStorageSecureFile;
|
|
232
234
|
private writeStorageSecureFile;
|
|
235
|
+
private assertManagedStoragePath;
|
|
236
|
+
readOfflineSyncFile(filePath: string): Promise<Buffer>;
|
|
237
|
+
writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void>;
|
|
238
|
+
deleteOfflineSyncFile(filePath: string): Promise<void>;
|
|
239
|
+
private invalidateAfterOfflineSyncMutation;
|
|
233
240
|
createContentHashIndex(): ContentHashIndex;
|
|
234
241
|
private appendStorageSecureFile;
|
|
235
242
|
private appendStorageSecureFileUnlocked;
|
package/dist/storage.js
CHANGED
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
normalizeEntityName,
|
|
9
9
|
parseEntityFile,
|
|
10
10
|
serializeEntityFile
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IOAY54RF.js";
|
|
12
12
|
import "./chunk-5UZXUTVO.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-P7FMDTKL.js";
|
|
13
|
+
import "./chunk-SH5S7XYD.js";
|
|
15
14
|
import "./chunk-NN2DKE4T.js";
|
|
16
15
|
import "./chunk-Q7P4WJDP.js";
|
|
16
|
+
import "./chunk-P7FMDTKL.js";
|
|
17
17
|
import "./chunk-SCU65EZI.js";
|
|
18
18
|
import "./chunk-3KW65B36.js";
|
|
19
19
|
import "./chunk-3HPAPHUK.js";
|
package/dist/transfer/backup.js
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
import "../chunk-6H2TESSP.js";
|
|
5
5
|
import "../chunk-KNKUID7G.js";
|
|
6
6
|
import "../chunk-J4EB7DNW.js";
|
|
7
|
-
import "../chunk-I6K5FBRQ.js";
|
|
8
|
-
import "../chunk-AGZQD76C.js";
|
|
9
7
|
import "../chunk-BJMBJZ2Y.js";
|
|
10
8
|
import "../chunk-UKJAGEXH.js";
|
|
11
9
|
import "../chunk-FP2373TW.js";
|
|
10
|
+
import "../chunk-I6K5FBRQ.js";
|
|
11
|
+
import "../chunk-AGZQD76C.js";
|
|
12
12
|
import "../chunk-A6XUJE5D.js";
|
|
13
13
|
import "../chunk-PZ5AY32C.js";
|
|
14
14
|
export {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exportCapsule,
|
|
3
3
|
isValidCapsuleSince
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-KNKUID7G.js";
|
|
4
|
+
} from "../chunk-QA2ZAPBU.js";
|
|
6
5
|
import "../chunk-WEHSQBFR.js";
|
|
6
|
+
import "../chunk-KNKUID7G.js";
|
|
7
7
|
import "../chunk-J4EB7DNW.js";
|
|
8
|
-
import "../chunk-I6K5FBRQ.js";
|
|
9
|
-
import "../chunk-AGZQD76C.js";
|
|
10
8
|
import "../chunk-BJMBJZ2Y.js";
|
|
11
9
|
import "../chunk-UKJAGEXH.js";
|
|
12
10
|
import "../chunk-FP2373TW.js";
|
|
11
|
+
import "../chunk-I6K5FBRQ.js";
|
|
12
|
+
import "../chunk-AGZQD76C.js";
|
|
13
13
|
import "../chunk-A6XUJE5D.js";
|
|
14
14
|
import "../chunk-PZ5AY32C.js";
|
|
15
15
|
export {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
importCapsule
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-ZYVPLJ4T.js";
|
|
4
4
|
import "../chunk-FAAFWE4G.js";
|
|
5
|
-
import "../chunk-KNKUID7G.js";
|
|
6
5
|
import "../chunk-WEHSQBFR.js";
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-KNKUID7G.js";
|
|
8
7
|
import "../chunk-BJMBJZ2Y.js";
|
|
9
8
|
import "../chunk-UKJAGEXH.js";
|
|
10
9
|
import "../chunk-FP2373TW.js";
|
|
10
|
+
import "../chunk-AGZQD76C.js";
|
|
11
11
|
import "../chunk-A6XUJE5D.js";
|
|
12
12
|
import "../chunk-PZ5AY32C.js";
|
|
13
13
|
export {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
importSqlite
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-Z734BLO3.js";
|
|
3
|
+
} from "../chunk-XKLD5OK4.js";
|
|
5
4
|
import "../chunk-3JXBXXM2.js";
|
|
5
|
+
import "../chunk-Z734BLO3.js";
|
|
6
6
|
import "../chunk-3HPAPHUK.js";
|
|
7
7
|
import "../chunk-AGZQD76C.js";
|
|
8
8
|
import "../chunk-PZ5AY32C.js";
|
package/dist/verified-recall.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
searchVerifiedEpisodes
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MS3ULOZF.js";
|
|
4
4
|
import "./chunk-NZL6GGQE.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-IOAY54RF.js";
|
|
6
6
|
import "./chunk-5UZXUTVO.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-P7FMDTKL.js";
|
|
7
|
+
import "./chunk-SH5S7XYD.js";
|
|
9
8
|
import "./chunk-NN2DKE4T.js";
|
|
10
9
|
import "./chunk-Q7P4WJDP.js";
|
|
10
|
+
import "./chunk-P7FMDTKL.js";
|
|
11
11
|
import "./chunk-SCU65EZI.js";
|
|
12
12
|
import "./chunk-3KW65B36.js";
|
|
13
13
|
import "./chunk-3HPAPHUK.js";
|
package/package.json
CHANGED
package/src/access-http.test.ts
CHANGED
|
@@ -223,3 +223,219 @@ test("HTTP review show hides namespace denial as pair_not_found", async () => {
|
|
|
223
223
|
await rm(dir, { recursive: true, force: true });
|
|
224
224
|
}
|
|
225
225
|
});
|
|
226
|
+
|
|
227
|
+
test("HTTP offline snapshot forwards namespace and transfer options", async () => {
|
|
228
|
+
const calls: Array<{
|
|
229
|
+
namespace: string | undefined;
|
|
230
|
+
principal: string | undefined;
|
|
231
|
+
includeTranscripts: boolean | undefined;
|
|
232
|
+
includeContent: boolean | undefined;
|
|
233
|
+
}> = [];
|
|
234
|
+
const service = {
|
|
235
|
+
offlineSyncSnapshot: async (options: {
|
|
236
|
+
namespace?: string;
|
|
237
|
+
principal?: string;
|
|
238
|
+
includeTranscripts?: boolean;
|
|
239
|
+
includeContent?: boolean;
|
|
240
|
+
}) => {
|
|
241
|
+
calls.push({
|
|
242
|
+
namespace: options.namespace,
|
|
243
|
+
principal: options.principal,
|
|
244
|
+
includeTranscripts: options.includeTranscripts,
|
|
245
|
+
includeContent: options.includeContent,
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
namespace: options.namespace ?? "default",
|
|
249
|
+
format: "remnic.offline-sync.snapshot.v1",
|
|
250
|
+
schemaVersion: 1,
|
|
251
|
+
createdAt: new Date("2026-05-21T00:00:00Z").toISOString(),
|
|
252
|
+
sourceId: "remote:test",
|
|
253
|
+
includeTranscripts: options.includeTranscripts !== false,
|
|
254
|
+
files: [],
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
} as unknown as EngramAccessService;
|
|
258
|
+
const server = new EngramAccessHttpServer({
|
|
259
|
+
service,
|
|
260
|
+
port: 0,
|
|
261
|
+
authToken: "test-token",
|
|
262
|
+
principal: "reader",
|
|
263
|
+
adminConsoleEnabled: false,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const status = await server.start();
|
|
267
|
+
try {
|
|
268
|
+
const response = await fetch(
|
|
269
|
+
`http://127.0.0.1:${status.port}/remnic/v1/offline-sync/snapshot?namespace=team&include_transcripts=false&content=false`,
|
|
270
|
+
{ headers: { authorization: "Bearer test-token" } },
|
|
271
|
+
);
|
|
272
|
+
const body = await response.json() as { namespace?: string; includeTranscripts?: boolean; files?: unknown[] };
|
|
273
|
+
|
|
274
|
+
assert.equal(response.status, 200);
|
|
275
|
+
assert.equal(body.namespace, "team");
|
|
276
|
+
assert.equal(body.includeTranscripts, false);
|
|
277
|
+
assert.deepEqual(body.files, []);
|
|
278
|
+
assert.deepEqual(calls, [{
|
|
279
|
+
namespace: "team",
|
|
280
|
+
principal: "reader",
|
|
281
|
+
includeTranscripts: false,
|
|
282
|
+
includeContent: false,
|
|
283
|
+
}]);
|
|
284
|
+
} finally {
|
|
285
|
+
await server.stop();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("HTTP offline snapshot rejects invalid boolean query values", async () => {
|
|
290
|
+
let calls = 0;
|
|
291
|
+
const service = {
|
|
292
|
+
offlineSyncSnapshot: async () => {
|
|
293
|
+
calls += 1;
|
|
294
|
+
return {};
|
|
295
|
+
},
|
|
296
|
+
} as unknown as EngramAccessService;
|
|
297
|
+
const server = new EngramAccessHttpServer({
|
|
298
|
+
service,
|
|
299
|
+
port: 0,
|
|
300
|
+
authToken: "test-token",
|
|
301
|
+
principal: "reader",
|
|
302
|
+
adminConsoleEnabled: false,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const status = await server.start();
|
|
306
|
+
try {
|
|
307
|
+
const response = await fetch(
|
|
308
|
+
`http://127.0.0.1:${status.port}/engram/v1/offline-sync/snapshot?include_transcripts=maybe`,
|
|
309
|
+
{ headers: { authorization: "Bearer test-token" } },
|
|
310
|
+
);
|
|
311
|
+
const body = await response.json() as { error?: string; code?: string };
|
|
312
|
+
|
|
313
|
+
assert.equal(response.status, 400);
|
|
314
|
+
assert.match(body.error ?? "", /include_transcripts must be one of: true, false/);
|
|
315
|
+
assert.equal(body.code, "input_error");
|
|
316
|
+
assert.equal(calls, 0);
|
|
317
|
+
} finally {
|
|
318
|
+
await server.stop();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("HTTP offline apply validates and forwards changesets", async () => {
|
|
323
|
+
const calls: Array<{
|
|
324
|
+
namespace: string | undefined;
|
|
325
|
+
principal: string | undefined;
|
|
326
|
+
changeset: unknown;
|
|
327
|
+
}> = [];
|
|
328
|
+
const changeset = {
|
|
329
|
+
format: "remnic.offline-sync.changeset.v1",
|
|
330
|
+
schemaVersion: 1,
|
|
331
|
+
createdAt: new Date("2026-05-21T00:00:00Z").toISOString(),
|
|
332
|
+
sourceId: "laptop:test",
|
|
333
|
+
includeTranscripts: true,
|
|
334
|
+
changes: [],
|
|
335
|
+
};
|
|
336
|
+
const service = {
|
|
337
|
+
offlineSyncApply: async (options: {
|
|
338
|
+
namespace?: string;
|
|
339
|
+
principal?: string;
|
|
340
|
+
changeset: unknown;
|
|
341
|
+
}) => {
|
|
342
|
+
calls.push({
|
|
343
|
+
namespace: options.namespace,
|
|
344
|
+
principal: options.principal,
|
|
345
|
+
changeset: options.changeset,
|
|
346
|
+
});
|
|
347
|
+
return {
|
|
348
|
+
namespace: options.namespace ?? "default",
|
|
349
|
+
appliedUpserts: 0,
|
|
350
|
+
appliedDeletes: 0,
|
|
351
|
+
skipped: 0,
|
|
352
|
+
conflicts: [],
|
|
353
|
+
currentFiles: [],
|
|
354
|
+
};
|
|
355
|
+
},
|
|
356
|
+
} as unknown as EngramAccessService;
|
|
357
|
+
const server = new EngramAccessHttpServer({
|
|
358
|
+
service,
|
|
359
|
+
port: 0,
|
|
360
|
+
authToken: "test-token",
|
|
361
|
+
principal: "writer",
|
|
362
|
+
adminConsoleEnabled: false,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const status = await server.start();
|
|
366
|
+
try {
|
|
367
|
+
const response = await fetch(`http://127.0.0.1:${status.port}/remnic/v1/offline-sync/apply`, {
|
|
368
|
+
method: "POST",
|
|
369
|
+
headers: {
|
|
370
|
+
authorization: "Bearer test-token",
|
|
371
|
+
"content-type": "application/json",
|
|
372
|
+
},
|
|
373
|
+
body: JSON.stringify({ namespace: "team", changeset }),
|
|
374
|
+
});
|
|
375
|
+
const body = await response.json() as { namespace?: string; appliedUpserts?: number };
|
|
376
|
+
|
|
377
|
+
assert.equal(response.status, 200);
|
|
378
|
+
assert.equal(body.namespace, "team");
|
|
379
|
+
assert.equal(body.appliedUpserts, 0);
|
|
380
|
+
assert.deepEqual(calls, [{
|
|
381
|
+
namespace: "team",
|
|
382
|
+
principal: "writer",
|
|
383
|
+
changeset,
|
|
384
|
+
}]);
|
|
385
|
+
} finally {
|
|
386
|
+
await server.stop();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("HTTP offline apply requires a changeset", async () => {
|
|
391
|
+
let calls = 0;
|
|
392
|
+
const service = {
|
|
393
|
+
offlineSyncApply: async () => {
|
|
394
|
+
calls += 1;
|
|
395
|
+
return {};
|
|
396
|
+
},
|
|
397
|
+
} as unknown as EngramAccessService;
|
|
398
|
+
const server = new EngramAccessHttpServer({
|
|
399
|
+
service,
|
|
400
|
+
port: 0,
|
|
401
|
+
authToken: "test-token",
|
|
402
|
+
principal: "writer",
|
|
403
|
+
adminConsoleEnabled: false,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const status = await server.start();
|
|
407
|
+
try {
|
|
408
|
+
const response = await fetch(`http://127.0.0.1:${status.port}/engram/v1/offline-sync/apply`, {
|
|
409
|
+
method: "POST",
|
|
410
|
+
headers: {
|
|
411
|
+
authorization: "Bearer test-token",
|
|
412
|
+
"content-type": "application/json",
|
|
413
|
+
},
|
|
414
|
+
body: JSON.stringify({ namespace: "team" }),
|
|
415
|
+
});
|
|
416
|
+
const body = await response.json() as { code?: string; details?: Array<{ field?: string; message?: string }> };
|
|
417
|
+
|
|
418
|
+
assert.equal(response.status, 400);
|
|
419
|
+
assert.equal(body.code, "validation_error");
|
|
420
|
+
assert.equal(body.details?.[0]?.field, "changeset");
|
|
421
|
+
assert.equal(calls, 0);
|
|
422
|
+
|
|
423
|
+
const nullResponse = await fetch(`http://127.0.0.1:${status.port}/engram/v1/offline-sync/apply`, {
|
|
424
|
+
method: "POST",
|
|
425
|
+
headers: {
|
|
426
|
+
authorization: "Bearer test-token",
|
|
427
|
+
"content-type": "application/json",
|
|
428
|
+
},
|
|
429
|
+
body: JSON.stringify({ namespace: "team", changeset: null }),
|
|
430
|
+
});
|
|
431
|
+
const nullBody = await nullResponse.json() as { code?: string; details?: Array<{ field?: string; message?: string }> };
|
|
432
|
+
|
|
433
|
+
assert.equal(nullResponse.status, 400);
|
|
434
|
+
assert.equal(nullBody.code, "validation_error");
|
|
435
|
+
assert.equal(nullBody.details?.[0]?.field, "changeset");
|
|
436
|
+
assert.equal(nullBody.details?.[0]?.message, "changeset is required");
|
|
437
|
+
assert.equal(calls, 0);
|
|
438
|
+
} finally {
|
|
439
|
+
await server.stop();
|
|
440
|
+
}
|
|
441
|
+
});
|
package/src/access-http.ts
CHANGED
|
@@ -588,6 +588,60 @@ export class EngramAccessHttpServer {
|
|
|
588
588
|
return;
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
+
if (
|
|
592
|
+
req.method === "GET" &&
|
|
593
|
+
(pathname === "/engram/v1/offline-sync/snapshot" || pathname === "/remnic/v1/offline-sync/snapshot")
|
|
594
|
+
) {
|
|
595
|
+
const includeTranscriptsRaw = parsed.searchParams.get("include_transcripts");
|
|
596
|
+
const includeContentRaw = parsed.searchParams.get("content");
|
|
597
|
+
if (
|
|
598
|
+
includeTranscriptsRaw !== null &&
|
|
599
|
+
includeTranscriptsRaw !== "true" &&
|
|
600
|
+
includeTranscriptsRaw !== "false"
|
|
601
|
+
) {
|
|
602
|
+
throw new EngramAccessInputError(
|
|
603
|
+
`include_transcripts must be one of: true, false (got: ${includeTranscriptsRaw})`,
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
if (
|
|
607
|
+
includeContentRaw !== null &&
|
|
608
|
+
includeContentRaw !== "true" &&
|
|
609
|
+
includeContentRaw !== "false"
|
|
610
|
+
) {
|
|
611
|
+
throw new EngramAccessInputError(
|
|
612
|
+
`content must be one of: true, false (got: ${includeContentRaw})`,
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
const namespaceParam = parsed.searchParams.get("namespace");
|
|
616
|
+
const result = await this.service.offlineSyncSnapshot({
|
|
617
|
+
namespace: this.resolveNamespace(
|
|
618
|
+
req,
|
|
619
|
+
namespaceParam && namespaceParam.length > 0 ? namespaceParam : undefined,
|
|
620
|
+
),
|
|
621
|
+
principal: this.resolveRequestPrincipal(req),
|
|
622
|
+
includeTranscripts: includeTranscriptsRaw !== "false",
|
|
623
|
+
includeContent: includeContentRaw !== "false",
|
|
624
|
+
});
|
|
625
|
+
this.respondJson(res, 200, result);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (
|
|
630
|
+
req.method === "POST" &&
|
|
631
|
+
(pathname === "/engram/v1/offline-sync/apply" || pathname === "/remnic/v1/offline-sync/apply")
|
|
632
|
+
) {
|
|
633
|
+
const body = await this.readValidatedBody(req, "offlineSyncApply");
|
|
634
|
+
this.ensureWriteRateLimitAvailable();
|
|
635
|
+
const result = await this.service.offlineSyncApply({
|
|
636
|
+
namespace: this.resolveNamespace(req, body.namespace),
|
|
637
|
+
principal: this.resolveRequestPrincipal(req),
|
|
638
|
+
changeset: body.changeset,
|
|
639
|
+
});
|
|
640
|
+
this.recordWriteRateLimitHit();
|
|
641
|
+
this.respondJson(res, 200, result);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
591
645
|
if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
|
|
592
646
|
const body = await this.readValidatedBody(req, "recallExplain");
|
|
593
647
|
const response = await this.service.recallExplain({
|
package/src/access-schema.ts
CHANGED
|
@@ -364,6 +364,20 @@ export const capsuleListRequestSchema = z
|
|
|
364
364
|
namespace: namespaceSchema,
|
|
365
365
|
});
|
|
366
366
|
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
// Offline sync
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
export const offlineSyncApplyRequestSchema = z
|
|
372
|
+
.object({
|
|
373
|
+
namespace: namespaceSchema,
|
|
374
|
+
changeset: z.unknown(),
|
|
375
|
+
})
|
|
376
|
+
.refine((value) => value.changeset !== undefined && value.changeset !== null, {
|
|
377
|
+
message: "changeset is required",
|
|
378
|
+
path: ["changeset"],
|
|
379
|
+
});
|
|
380
|
+
|
|
367
381
|
// ---------------------------------------------------------------------------
|
|
368
382
|
// Action confidence
|
|
369
383
|
// ---------------------------------------------------------------------------
|
|
@@ -429,6 +443,7 @@ export type DaySummaryRequest = z.infer<typeof daySummaryRequestSchema>;
|
|
|
429
443
|
export type CapsuleExportRequest = z.infer<typeof capsuleExportRequestSchema>;
|
|
430
444
|
export type CapsuleImportRequest = z.infer<typeof capsuleImportRequestSchema>;
|
|
431
445
|
export type CapsuleListRequest = z.infer<typeof capsuleListRequestSchema>;
|
|
446
|
+
export type OfflineSyncApplyRequest = z.infer<typeof offlineSyncApplyRequestSchema>;
|
|
432
447
|
export type ActionConfidenceRequest = z.infer<typeof actionConfidenceRequestSchema>;
|
|
433
448
|
|
|
434
449
|
// ---------------------------------------------------------------------------
|
|
@@ -452,6 +467,7 @@ export type SchemaName =
|
|
|
452
467
|
| "capsuleExport"
|
|
453
468
|
| "capsuleImport"
|
|
454
469
|
| "capsuleList"
|
|
470
|
+
| "offlineSyncApply"
|
|
455
471
|
| "actionConfidence";
|
|
456
472
|
|
|
457
473
|
export type SchemaTypeFor<N extends SchemaName> =
|
|
@@ -471,6 +487,7 @@ export type SchemaTypeFor<N extends SchemaName> =
|
|
|
471
487
|
: N extends "capsuleExport" ? CapsuleExportRequest
|
|
472
488
|
: N extends "capsuleImport" ? CapsuleImportRequest
|
|
473
489
|
: N extends "capsuleList" ? CapsuleListRequest
|
|
490
|
+
: N extends "offlineSyncApply" ? OfflineSyncApplyRequest
|
|
474
491
|
: N extends "actionConfidence" ? ActionConfidenceRequest
|
|
475
492
|
: never;
|
|
476
493
|
|
|
@@ -491,6 +508,7 @@ const schemas: Record<SchemaName, z.ZodTypeAny> = {
|
|
|
491
508
|
capsuleExport: capsuleExportRequestSchema,
|
|
492
509
|
capsuleImport: capsuleImportRequestSchema,
|
|
493
510
|
capsuleList: capsuleListRequestSchema,
|
|
511
|
+
offlineSyncApply: offlineSyncApplyRequestSchema,
|
|
494
512
|
actionConfidence: actionConfidenceRequestSchema,
|
|
495
513
|
};
|
|
496
514
|
|
package/src/access-service.ts
CHANGED
|
@@ -135,6 +135,12 @@ import {
|
|
|
135
135
|
defaultCapsulesDir,
|
|
136
136
|
type CapsuleListEntry,
|
|
137
137
|
} from "./capsule-cli.js";
|
|
138
|
+
import {
|
|
139
|
+
applyOfflineSyncChangeset,
|
|
140
|
+
buildOfflineSyncSnapshot,
|
|
141
|
+
type OfflineSyncApplyChangesetResult,
|
|
142
|
+
type OfflineSyncSnapshot,
|
|
143
|
+
} from "./offline-sync.js";
|
|
138
144
|
import {
|
|
139
145
|
evaluateActionConfidence,
|
|
140
146
|
type ActionConfidenceInput,
|
|
@@ -603,6 +609,27 @@ export interface EngramAccessCapsuleListResponse {
|
|
|
603
609
|
capsules: CapsuleListEntry[];
|
|
604
610
|
}
|
|
605
611
|
|
|
612
|
+
export interface EngramAccessOfflineSyncSnapshotRequest {
|
|
613
|
+
namespace?: string;
|
|
614
|
+
principal?: string;
|
|
615
|
+
includeTranscripts?: boolean;
|
|
616
|
+
includeContent?: boolean;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface EngramAccessOfflineSyncApplyRequest {
|
|
620
|
+
namespace?: string;
|
|
621
|
+
principal?: string;
|
|
622
|
+
changeset: unknown;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export interface EngramAccessOfflineSyncSnapshotResponse extends OfflineSyncSnapshot {
|
|
626
|
+
namespace: string;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export interface EngramAccessOfflineSyncApplyResponse extends OfflineSyncApplyChangesetResult {
|
|
630
|
+
namespace: string;
|
|
631
|
+
}
|
|
632
|
+
|
|
606
633
|
export type EngramAccessActionConfidenceRequest = ActionConfidenceInput;
|
|
607
634
|
export type EngramAccessActionConfidenceResponse = ActionConfidenceResult;
|
|
608
635
|
|
|
@@ -5522,6 +5549,55 @@ export class EngramAccessService {
|
|
|
5522
5549
|
return { namespace: resolvedNamespace, capsulesDir, capsules };
|
|
5523
5550
|
}
|
|
5524
5551
|
|
|
5552
|
+
async offlineSyncSnapshot(
|
|
5553
|
+
options: EngramAccessOfflineSyncSnapshotRequest = {},
|
|
5554
|
+
): Promise<EngramAccessOfflineSyncSnapshotResponse> {
|
|
5555
|
+
const resolvedNamespace = this.resolveReadableNamespace(options.namespace, options.principal);
|
|
5556
|
+
const storage = await this.orchestrator.getStorage(resolvedNamespace);
|
|
5557
|
+
const storageHash = createHash("sha256").update(storage.dir).digest("hex").slice(0, 16);
|
|
5558
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
5559
|
+
root: storage.dir,
|
|
5560
|
+
sourceId: `remnic:${resolvedNamespace}:${storageHash}`,
|
|
5561
|
+
includeContent: options.includeContent !== false,
|
|
5562
|
+
includeTranscripts: options.includeTranscripts !== false,
|
|
5563
|
+
readFile: async ({ filePath }) => storage.readOfflineSyncFile(filePath),
|
|
5564
|
+
});
|
|
5565
|
+
return {
|
|
5566
|
+
namespace: resolvedNamespace,
|
|
5567
|
+
...snapshot,
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5570
|
+
|
|
5571
|
+
async offlineSyncApply(
|
|
5572
|
+
options: EngramAccessOfflineSyncApplyRequest,
|
|
5573
|
+
): Promise<EngramAccessOfflineSyncApplyResponse> {
|
|
5574
|
+
const resolvedNamespace = this.resolveWritableNamespace(
|
|
5575
|
+
options.namespace,
|
|
5576
|
+
undefined,
|
|
5577
|
+
options.principal,
|
|
5578
|
+
);
|
|
5579
|
+
const storage = await this.orchestrator.getStorage(resolvedNamespace);
|
|
5580
|
+
try {
|
|
5581
|
+
const result = await applyOfflineSyncChangeset({
|
|
5582
|
+
root: storage.dir,
|
|
5583
|
+
changeset: options.changeset,
|
|
5584
|
+
readFile: async ({ filePath }) => storage.readOfflineSyncFile(filePath),
|
|
5585
|
+
writeFile: async ({ filePath, content }) => storage.writeOfflineSyncFile(filePath, content),
|
|
5586
|
+
deleteFile: async ({ filePath }) => storage.deleteOfflineSyncFile(filePath),
|
|
5587
|
+
});
|
|
5588
|
+
return {
|
|
5589
|
+
namespace: resolvedNamespace,
|
|
5590
|
+
...result,
|
|
5591
|
+
};
|
|
5592
|
+
} catch (error) {
|
|
5593
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5594
|
+
if (message.startsWith("offline sync")) {
|
|
5595
|
+
throw new EngramAccessInputError(message);
|
|
5596
|
+
}
|
|
5597
|
+
throw error;
|
|
5598
|
+
}
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5525
5601
|
// ── Dreams pipeline telemetry surfaces (issue #678 PR 3+4) ──────────────
|
|
5526
5602
|
|
|
5527
5603
|
/**
|
package/src/index.ts
CHANGED
|
@@ -674,6 +674,39 @@ export {
|
|
|
674
674
|
type SyncState,
|
|
675
675
|
} from "./sync/index.js";
|
|
676
676
|
|
|
677
|
+
// ---------------------------------------------------------------------------
|
|
678
|
+
// Offline Sync
|
|
679
|
+
// ---------------------------------------------------------------------------
|
|
680
|
+
|
|
681
|
+
export {
|
|
682
|
+
OFFLINE_SYNC_CHANGESET_FORMAT,
|
|
683
|
+
OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
684
|
+
OFFLINE_SYNC_STATE_VERSION,
|
|
685
|
+
applyOfflineSyncChangeset,
|
|
686
|
+
applyOfflineSyncSnapshot,
|
|
687
|
+
buildOfflineSyncChangeset,
|
|
688
|
+
buildOfflineSyncSnapshot,
|
|
689
|
+
defaultOfflineSyncStatePath,
|
|
690
|
+
fileStatesFromSnapshot,
|
|
691
|
+
normalizeOfflineSyncChangeset,
|
|
692
|
+
normalizeOfflineSyncSnapshot,
|
|
693
|
+
offlineSyncStateFromSnapshot,
|
|
694
|
+
readOfflineSyncState,
|
|
695
|
+
summarizeOfflineSyncChangeset,
|
|
696
|
+
writeOfflineSyncState,
|
|
697
|
+
type OfflineSyncApplyChangesetResult,
|
|
698
|
+
type OfflineSyncApplySnapshotResult,
|
|
699
|
+
type OfflineSyncChange,
|
|
700
|
+
type OfflineSyncChangeset,
|
|
701
|
+
type OfflineSyncConflict,
|
|
702
|
+
type OfflineSyncFileRecord,
|
|
703
|
+
type OfflineSyncFileState,
|
|
704
|
+
type OfflineSyncFileTarget,
|
|
705
|
+
type OfflineSyncFileWriteTarget,
|
|
706
|
+
type OfflineSyncSnapshot,
|
|
707
|
+
type OfflineSyncState,
|
|
708
|
+
} from "./offline-sync.js";
|
|
709
|
+
|
|
677
710
|
// ---------------------------------------------------------------------------
|
|
678
711
|
// Memory Extension Host (#382)
|
|
679
712
|
// ---------------------------------------------------------------------------
|