@remnic/core 1.1.17 → 1.1.19
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 +16 -16
- package/dist/access-http.d.ts +2 -1
- package/dist/access-http.js +10 -10
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +9 -9
- package/dist/access-schema.d.ts +23 -3
- package/dist/access-schema.js +6 -1
- package/dist/{access-service-DZXc7qwR.d.ts → access-service-DT9L2DW4.d.ts} +14 -2
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +7 -7
- package/dist/briefing.js +4 -4
- package/dist/causal-consolidation.js +5 -5
- package/dist/{chunk-NOHC2L57.js → chunk-2IRT26RZ.js} +2 -2
- package/dist/{chunk-HMZYQPT5.js → chunk-4KMA7OKQ.js} +101 -4
- package/dist/chunk-4KMA7OKQ.js.map +1 -0
- package/dist/{chunk-MS3ULOZF.js → chunk-5IQC4OG6.js} +2 -2
- package/dist/{chunk-ZPXYWTN5.js → chunk-5ML4TH3E.js} +4 -4
- package/dist/{chunk-V7WH7DEM.js → chunk-6ORWKANA.js} +2 -2
- package/dist/{chunk-IOAY54RF.js → chunk-7Q2P774N.js} +11 -11
- package/dist/{chunk-BYACCC5C.js → chunk-BFAMI2LQ.js} +4 -4
- package/dist/{chunk-JFEH2LZM.js → chunk-CN4P6SVA.js} +2 -2
- package/dist/{chunk-CDQNR7SV.js → chunk-CRYI3XUQ.js} +15 -15
- package/dist/{chunk-VLQWOGYM.js → chunk-DOXBLGVJ.js} +34 -9
- package/dist/chunk-DOXBLGVJ.js.map +1 -0
- package/dist/{chunk-OGROP7ZN.js → chunk-FFU4GMST.js} +5 -5
- package/dist/{chunk-C7DGCHJE.js → chunk-FSODDMR2.js} +2 -2
- package/dist/{chunk-AAX3SUM3.js → chunk-GGCJ253V.js} +11 -11
- package/dist/{chunk-GCR4JFKK.js → chunk-HZX5IGIC.js} +38 -4
- package/dist/chunk-HZX5IGIC.js.map +1 -0
- package/dist/{chunk-IG5VGHYB.js → chunk-KSFBM6TV.js} +2 -2
- package/dist/{chunk-M3AA636B.js → chunk-NOQ74SJN.js} +2 -2
- package/dist/{chunk-QLKBF3TI.js → chunk-QOHBYVZG.js} +2 -2
- package/dist/{chunk-OJRKZLZ4.js → chunk-SGIXDVSF.js} +2 -2
- package/dist/{chunk-YCVWX2NF.js → chunk-SZKCBLS5.js} +2 -2
- package/dist/{chunk-BEB4GUU5.js → chunk-TLM762GT.js} +2 -2
- package/dist/{chunk-65HQPW6O.js → chunk-TOFUTKQN.js} +2 -2
- package/dist/{chunk-66H2DZYB.js → chunk-YANROFJY.js} +13 -1
- package/dist/chunk-YANROFJY.js.map +1 -0
- package/dist/{chunk-G7JBLD65.js → chunk-YO3AZEE5.js} +3 -3
- package/dist/{cli-kVwab1_L.d.ts → cli-BN0CkYzI.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +19 -19
- 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/entity-retrieval.js +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +50 -46
- package/dist/index.js.map +1 -1
- 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 +1 -1
- package/dist/namespaces/migrate.js +5 -5
- package/dist/namespaces/storage.js +4 -4
- package/dist/offline-sync.d.ts +16 -1
- package/dist/offline-sync.js +8 -2
- package/dist/operator-toolkit.js +7 -7
- package/dist/orchestrator.js +12 -12
- package/dist/secure-store/index.js +17 -17
- 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.js +3 -3
- package/dist/verified-recall.js +4 -4
- package/package.json +1 -1
- package/src/access-http.test.ts +90 -0
- package/src/access-http.ts +47 -0
- package/src/access-schema.ts +13 -0
- package/src/access-service.ts +48 -0
- package/src/index.ts +3 -0
- package/src/offline-sync.test.ts +90 -0
- package/src/offline-sync.ts +118 -0
- package/dist/chunk-66H2DZYB.js.map +0 -1
- package/dist/chunk-GCR4JFKK.js.map +0 -1
- package/dist/chunk-HMZYQPT5.js.map +0 -1
- package/dist/chunk-VLQWOGYM.js.map +0 -1
- /package/dist/{chunk-NOHC2L57.js.map → chunk-2IRT26RZ.js.map} +0 -0
- /package/dist/{chunk-MS3ULOZF.js.map → chunk-5IQC4OG6.js.map} +0 -0
- /package/dist/{chunk-ZPXYWTN5.js.map → chunk-5ML4TH3E.js.map} +0 -0
- /package/dist/{chunk-V7WH7DEM.js.map → chunk-6ORWKANA.js.map} +0 -0
- /package/dist/{chunk-IOAY54RF.js.map → chunk-7Q2P774N.js.map} +0 -0
- /package/dist/{chunk-BYACCC5C.js.map → chunk-BFAMI2LQ.js.map} +0 -0
- /package/dist/{chunk-JFEH2LZM.js.map → chunk-CN4P6SVA.js.map} +0 -0
- /package/dist/{chunk-CDQNR7SV.js.map → chunk-CRYI3XUQ.js.map} +0 -0
- /package/dist/{chunk-OGROP7ZN.js.map → chunk-FFU4GMST.js.map} +0 -0
- /package/dist/{chunk-C7DGCHJE.js.map → chunk-FSODDMR2.js.map} +0 -0
- /package/dist/{chunk-AAX3SUM3.js.map → chunk-GGCJ253V.js.map} +0 -0
- /package/dist/{chunk-IG5VGHYB.js.map → chunk-KSFBM6TV.js.map} +0 -0
- /package/dist/{chunk-M3AA636B.js.map → chunk-NOQ74SJN.js.map} +0 -0
- /package/dist/{chunk-QLKBF3TI.js.map → chunk-QOHBYVZG.js.map} +0 -0
- /package/dist/{chunk-OJRKZLZ4.js.map → chunk-SGIXDVSF.js.map} +0 -0
- /package/dist/{chunk-YCVWX2NF.js.map → chunk-SZKCBLS5.js.map} +0 -0
- /package/dist/{chunk-BEB4GUU5.js.map → chunk-TLM762GT.js.map} +0 -0
- /package/dist/{chunk-65HQPW6O.js.map → chunk-TOFUTKQN.js.map} +0 -0
- /package/dist/{chunk-G7JBLD65.js.map → chunk-YO3AZEE5.js.map} +0 -0
package/src/access-http.ts
CHANGED
|
@@ -641,6 +641,34 @@ export class EngramAccessHttpServer {
|
|
|
641
641
|
return;
|
|
642
642
|
}
|
|
643
643
|
|
|
644
|
+
if (
|
|
645
|
+
req.method === "POST" &&
|
|
646
|
+
(
|
|
647
|
+
pathname === "/engram/v1/offline-sync/file-content" ||
|
|
648
|
+
pathname === "/remnic/v1/offline-sync/file-content"
|
|
649
|
+
)
|
|
650
|
+
) {
|
|
651
|
+
const body = await this.readValidatedBody(req, "offlineSyncFileContent");
|
|
652
|
+
const result = await this.service.offlineSyncFileContent({
|
|
653
|
+
namespace: this.resolveNamespace(req, body.namespace),
|
|
654
|
+
principal: this.resolveRequestPrincipal(req),
|
|
655
|
+
includeTranscripts: body.includeTranscripts,
|
|
656
|
+
path: body.path,
|
|
657
|
+
offset: body.offset,
|
|
658
|
+
length: body.length,
|
|
659
|
+
});
|
|
660
|
+
this.respondBinary(res, 200, result.content, {
|
|
661
|
+
"x-remnic-namespace": encodeURIComponent(result.namespace),
|
|
662
|
+
"x-remnic-file-path": encodeURIComponent(result.path),
|
|
663
|
+
"x-remnic-file-bytes": String(result.bytes),
|
|
664
|
+
"x-remnic-file-mtime-ms": String(result.mtimeMs),
|
|
665
|
+
"x-remnic-chunk-offset": String(result.offset),
|
|
666
|
+
"x-remnic-chunk-bytes": String(result.chunkBytes),
|
|
667
|
+
...(result.sha256 ? { "x-remnic-file-sha256": result.sha256 } : {}),
|
|
668
|
+
});
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
|
|
644
672
|
if (
|
|
645
673
|
req.method === "POST" &&
|
|
646
674
|
(pathname === "/engram/v1/offline-sync/apply" || pathname === "/remnic/v1/offline-sync/apply")
|
|
@@ -1803,6 +1831,25 @@ export class EngramAccessHttpServer {
|
|
|
1803
1831
|
res.end(body);
|
|
1804
1832
|
}
|
|
1805
1833
|
|
|
1834
|
+
private respondBinary(
|
|
1835
|
+
res: ServerResponse,
|
|
1836
|
+
status: number,
|
|
1837
|
+
body: Buffer,
|
|
1838
|
+
headers: Record<string, string> = {},
|
|
1839
|
+
): void {
|
|
1840
|
+
res.statusCode = status;
|
|
1841
|
+
res.setHeader("content-type", "application/octet-stream");
|
|
1842
|
+
res.setHeader("content-length", String(body.length));
|
|
1843
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1844
|
+
res.setHeader(key, value);
|
|
1845
|
+
}
|
|
1846
|
+
const cid = correlationIdStore.getStore();
|
|
1847
|
+
if (cid) {
|
|
1848
|
+
res.setHeader("x-request-id", cid);
|
|
1849
|
+
}
|
|
1850
|
+
res.end(body);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1806
1853
|
private async handleAdminConsole(
|
|
1807
1854
|
req: IncomingMessage,
|
|
1808
1855
|
res: ServerResponse,
|
package/src/access-schema.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "./action-confidence.js";
|
|
11
11
|
import { isValidCapsuleSince } from "./transfer/capsule-export.js";
|
|
12
12
|
import { CAPSULE_ID_PATTERN } from "./transfer/types.js";
|
|
13
|
+
import { OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES } from "./offline-sync.js";
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Error formatting
|
|
@@ -386,6 +387,14 @@ export const offlineSyncFilesRequestSchema = z.object({
|
|
|
386
387
|
.max(5000, "paths must contain 5000 or fewer entries"),
|
|
387
388
|
});
|
|
388
389
|
|
|
390
|
+
export const offlineSyncFileContentRequestSchema = z.object({
|
|
391
|
+
namespace: namespaceSchema,
|
|
392
|
+
includeTranscripts: z.boolean().optional(),
|
|
393
|
+
path: z.string().trim().min(1, "path must be non-empty").max(4096),
|
|
394
|
+
offset: z.number().int().min(0).optional(),
|
|
395
|
+
length: z.number().int().min(1).max(OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES).optional(),
|
|
396
|
+
});
|
|
397
|
+
|
|
389
398
|
// ---------------------------------------------------------------------------
|
|
390
399
|
// Action confidence
|
|
391
400
|
// ---------------------------------------------------------------------------
|
|
@@ -453,6 +462,7 @@ export type CapsuleImportRequest = z.infer<typeof capsuleImportRequestSchema>;
|
|
|
453
462
|
export type CapsuleListRequest = z.infer<typeof capsuleListRequestSchema>;
|
|
454
463
|
export type OfflineSyncApplyRequest = z.infer<typeof offlineSyncApplyRequestSchema>;
|
|
455
464
|
export type OfflineSyncFilesRequest = z.infer<typeof offlineSyncFilesRequestSchema>;
|
|
465
|
+
export type OfflineSyncFileContentRequest = z.infer<typeof offlineSyncFileContentRequestSchema>;
|
|
456
466
|
export type ActionConfidenceRequest = z.infer<typeof actionConfidenceRequestSchema>;
|
|
457
467
|
|
|
458
468
|
// ---------------------------------------------------------------------------
|
|
@@ -477,6 +487,7 @@ export type SchemaName =
|
|
|
477
487
|
| "capsuleImport"
|
|
478
488
|
| "capsuleList"
|
|
479
489
|
| "offlineSyncFiles"
|
|
490
|
+
| "offlineSyncFileContent"
|
|
480
491
|
| "offlineSyncApply"
|
|
481
492
|
| "actionConfidence";
|
|
482
493
|
|
|
@@ -498,6 +509,7 @@ export type SchemaTypeFor<N extends SchemaName> =
|
|
|
498
509
|
: N extends "capsuleImport" ? CapsuleImportRequest
|
|
499
510
|
: N extends "capsuleList" ? CapsuleListRequest
|
|
500
511
|
: N extends "offlineSyncFiles" ? OfflineSyncFilesRequest
|
|
512
|
+
: N extends "offlineSyncFileContent" ? OfflineSyncFileContentRequest
|
|
501
513
|
: N extends "offlineSyncApply" ? OfflineSyncApplyRequest
|
|
502
514
|
: N extends "actionConfidence" ? ActionConfidenceRequest
|
|
503
515
|
: never;
|
|
@@ -520,6 +532,7 @@ const schemas: Record<SchemaName, z.ZodTypeAny> = {
|
|
|
520
532
|
capsuleImport: capsuleImportRequestSchema,
|
|
521
533
|
capsuleList: capsuleListRequestSchema,
|
|
522
534
|
offlineSyncFiles: offlineSyncFilesRequestSchema,
|
|
535
|
+
offlineSyncFileContent: offlineSyncFileContentRequestSchema,
|
|
523
536
|
offlineSyncApply: offlineSyncApplyRequestSchema,
|
|
524
537
|
actionConfidence: actionConfidenceRequestSchema,
|
|
525
538
|
};
|
package/src/access-service.ts
CHANGED
|
@@ -139,7 +139,9 @@ import {
|
|
|
139
139
|
applyOfflineSyncChangeset,
|
|
140
140
|
buildOfflineSyncSnapshot,
|
|
141
141
|
buildOfflineSyncSnapshotForPaths,
|
|
142
|
+
readOfflineSyncFileContentChunk,
|
|
142
143
|
type OfflineSyncApplyChangesetResult,
|
|
144
|
+
type OfflineSyncFileContentChunk,
|
|
143
145
|
type OfflineSyncSnapshot,
|
|
144
146
|
} from "./offline-sync.js";
|
|
145
147
|
import {
|
|
@@ -624,6 +626,15 @@ export interface EngramAccessOfflineSyncFilesRequest {
|
|
|
624
626
|
paths: string[];
|
|
625
627
|
}
|
|
626
628
|
|
|
629
|
+
export interface EngramAccessOfflineSyncFileContentRequest {
|
|
630
|
+
namespace?: string;
|
|
631
|
+
principal?: string;
|
|
632
|
+
includeTranscripts?: boolean;
|
|
633
|
+
path: string;
|
|
634
|
+
offset?: number;
|
|
635
|
+
length?: number;
|
|
636
|
+
}
|
|
637
|
+
|
|
627
638
|
export interface EngramAccessOfflineSyncApplyRequest {
|
|
628
639
|
namespace?: string;
|
|
629
640
|
principal?: string;
|
|
@@ -638,6 +649,10 @@ export interface EngramAccessOfflineSyncFilesResponse extends OfflineSyncSnapsho
|
|
|
638
649
|
namespace: string;
|
|
639
650
|
}
|
|
640
651
|
|
|
652
|
+
export interface EngramAccessOfflineSyncFileContentResponse extends OfflineSyncFileContentChunk {
|
|
653
|
+
namespace: string;
|
|
654
|
+
}
|
|
655
|
+
|
|
641
656
|
export interface EngramAccessOfflineSyncApplyResponse extends OfflineSyncApplyChangesetResult {
|
|
642
657
|
namespace: string;
|
|
643
658
|
}
|
|
@@ -5612,6 +5627,39 @@ export class EngramAccessService {
|
|
|
5612
5627
|
}
|
|
5613
5628
|
}
|
|
5614
5629
|
|
|
5630
|
+
async offlineSyncFileContent(
|
|
5631
|
+
options: EngramAccessOfflineSyncFileContentRequest,
|
|
5632
|
+
): Promise<EngramAccessOfflineSyncFileContentResponse> {
|
|
5633
|
+
const resolvedNamespace = this.resolveReadableNamespace(options.namespace, options.principal);
|
|
5634
|
+
const storage = await this.orchestrator.getStorage(resolvedNamespace);
|
|
5635
|
+
try {
|
|
5636
|
+
const chunk = await readOfflineSyncFileContentChunk({
|
|
5637
|
+
root: storage.dir,
|
|
5638
|
+
path: options.path,
|
|
5639
|
+
offset: options.offset,
|
|
5640
|
+
length: options.length,
|
|
5641
|
+
includeTranscripts: options.includeTranscripts !== false,
|
|
5642
|
+
readFile: async ({ filePath }) => storage.readOfflineSyncFile(filePath),
|
|
5643
|
+
});
|
|
5644
|
+
return {
|
|
5645
|
+
namespace: resolvedNamespace,
|
|
5646
|
+
...chunk,
|
|
5647
|
+
};
|
|
5648
|
+
} catch (error) {
|
|
5649
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5650
|
+
if (
|
|
5651
|
+
message.startsWith("path:") ||
|
|
5652
|
+
message.startsWith("offset ") ||
|
|
5653
|
+
message.startsWith("offset must ") ||
|
|
5654
|
+
message.startsWith("length ") ||
|
|
5655
|
+
message.startsWith("offline sync file content ")
|
|
5656
|
+
) {
|
|
5657
|
+
throw new EngramAccessInputError(message);
|
|
5658
|
+
}
|
|
5659
|
+
throw error;
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
|
|
5615
5663
|
async offlineSyncApply(
|
|
5616
5664
|
options: EngramAccessOfflineSyncApplyRequest,
|
|
5617
5665
|
): Promise<EngramAccessOfflineSyncApplyResponse> {
|
package/src/index.ts
CHANGED
|
@@ -680,6 +680,7 @@ export {
|
|
|
680
680
|
|
|
681
681
|
export {
|
|
682
682
|
OFFLINE_SYNC_CHANGESET_FORMAT,
|
|
683
|
+
OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
|
|
683
684
|
OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
684
685
|
OFFLINE_SYNC_STATE_VERSION,
|
|
685
686
|
applyOfflineSyncChangeset,
|
|
@@ -692,6 +693,7 @@ export {
|
|
|
692
693
|
normalizeOfflineSyncChangeset,
|
|
693
694
|
normalizeOfflineSyncSnapshot,
|
|
694
695
|
offlineSyncStateFromSnapshot,
|
|
696
|
+
readOfflineSyncFileContentChunk,
|
|
695
697
|
readOfflineSyncState,
|
|
696
698
|
summarizeOfflineSyncChangeset,
|
|
697
699
|
writeOfflineSyncState,
|
|
@@ -701,6 +703,7 @@ export {
|
|
|
701
703
|
type OfflineSyncChangeset,
|
|
702
704
|
type OfflineSyncConflict,
|
|
703
705
|
type OfflineSyncFileRecord,
|
|
706
|
+
type OfflineSyncFileContentChunk,
|
|
704
707
|
type OfflineSyncFileState,
|
|
705
708
|
type OfflineSyncFileTarget,
|
|
706
709
|
type OfflineSyncFileWriteTarget,
|
package/src/offline-sync.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildOfflineSyncChangeset,
|
|
12
12
|
buildOfflineSyncSnapshot,
|
|
13
13
|
buildOfflineSyncSnapshotForPaths,
|
|
14
|
+
readOfflineSyncFileContentChunk,
|
|
14
15
|
} from "./offline-sync.js";
|
|
15
16
|
import { isEncryptedFile } from "./secure-store/secure-fs.js";
|
|
16
17
|
import { StorageManager } from "./storage.js";
|
|
@@ -40,6 +41,10 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
|
|
|
40
41
|
await write(root, ".offline-sync/state/local.json", "state");
|
|
41
42
|
await write(root, "state/fact-hashes.txt", "derived");
|
|
42
43
|
await write(root, "state/fact-hashes.ready", "v1");
|
|
44
|
+
await write(root, "state/last_graph_recall.json", "{}");
|
|
45
|
+
await write(root, "state/last_intent.json", "{}");
|
|
46
|
+
await write(root, "state/last_qmd_recall.json", "{}");
|
|
47
|
+
await write(root, "state/last_recall.json", "{}");
|
|
43
48
|
await write(root, "state/lcm.sqlite", "live db");
|
|
44
49
|
await write(root, "state/lcm.sqlite-shm", "live shm");
|
|
45
50
|
await write(root, "state/lcm.sqlite-wal", "live wal");
|
|
@@ -72,6 +77,60 @@ test("offline snapshot captures source-of-truth files and excludes private/inter
|
|
|
72
77
|
}
|
|
73
78
|
});
|
|
74
79
|
|
|
80
|
+
test("offline sync excludes volatile retrieval debug snapshots without deleting existing local copies", async () => {
|
|
81
|
+
const root = await tempDir("remnic-offline-debug-snapshots");
|
|
82
|
+
try {
|
|
83
|
+
await write(root, "facts/a.md", "alpha");
|
|
84
|
+
await write(root, "state/last_graph_recall.json", "graph");
|
|
85
|
+
await write(root, "state/last_intent.json", "intent");
|
|
86
|
+
await write(root, "state/last_qmd_recall.json", "qmd");
|
|
87
|
+
await write(root, "state/last_recall.json", "recall");
|
|
88
|
+
|
|
89
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
90
|
+
root,
|
|
91
|
+
sourceId: "remote",
|
|
92
|
+
includeContent: true,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
|
|
96
|
+
await assert.rejects(
|
|
97
|
+
() =>
|
|
98
|
+
buildOfflineSyncSnapshotForPaths({
|
|
99
|
+
root,
|
|
100
|
+
sourceId: "remote",
|
|
101
|
+
paths: ["state/last_graph_recall.json"],
|
|
102
|
+
includeContent: true,
|
|
103
|
+
}),
|
|
104
|
+
/offline sync snapshot path is excluded: state\/last_graph_recall\.json/,
|
|
105
|
+
);
|
|
106
|
+
await assert.rejects(
|
|
107
|
+
() =>
|
|
108
|
+
readOfflineSyncFileContentChunk({
|
|
109
|
+
root,
|
|
110
|
+
path: "state/last_graph_recall.json",
|
|
111
|
+
}),
|
|
112
|
+
/offline sync file content path is excluded: state\/last_graph_recall\.json/,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const oldGraph = Buffer.from("old graph");
|
|
116
|
+
const pull = await applyOfflineSyncSnapshot({
|
|
117
|
+
root,
|
|
118
|
+
snapshot,
|
|
119
|
+
baseFiles: [{
|
|
120
|
+
path: "state/last_graph_recall.json",
|
|
121
|
+
sha256: createHash("sha256").update(oldGraph).digest("hex"),
|
|
122
|
+
bytes: oldGraph.byteLength,
|
|
123
|
+
mtimeMs: 0,
|
|
124
|
+
}],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
assert.equal(pull.deleted, 0);
|
|
128
|
+
assert.equal(await readUtf8(root, "state/last_graph_recall.json"), "graph");
|
|
129
|
+
} finally {
|
|
130
|
+
await rm(root, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
75
134
|
test("offline sync excludes live LCM sqlite artifacts without deleting existing local copies", async () => {
|
|
76
135
|
const root = await tempDir("remnic-offline-lcm-sqlite");
|
|
77
136
|
try {
|
|
@@ -117,6 +176,37 @@ test("offline sync excludes live LCM sqlite artifacts without deleting existing
|
|
|
117
176
|
}
|
|
118
177
|
});
|
|
119
178
|
|
|
179
|
+
test("offline sync reads bounded file content chunks with metadata", async () => {
|
|
180
|
+
const root = await tempDir("remnic-offline-file-content");
|
|
181
|
+
try {
|
|
182
|
+
await write(root, "state/memory-lifecycle-ledger.jsonl", "alpha\nbeta\ngamma\n");
|
|
183
|
+
|
|
184
|
+
const chunk = await readOfflineSyncFileContentChunk({
|
|
185
|
+
root,
|
|
186
|
+
path: "state/memory-lifecycle-ledger.jsonl",
|
|
187
|
+
offset: 6,
|
|
188
|
+
length: 5,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
assert.equal(chunk.path, "state/memory-lifecycle-ledger.jsonl");
|
|
192
|
+
assert.equal(chunk.offset, 6);
|
|
193
|
+
assert.equal(chunk.chunkBytes, 5);
|
|
194
|
+
assert.equal(chunk.content.toString("utf-8"), "beta\n");
|
|
195
|
+
assert.equal(chunk.bytes, Buffer.byteLength("alpha\nbeta\ngamma\n"));
|
|
196
|
+
|
|
197
|
+
await assert.rejects(
|
|
198
|
+
() =>
|
|
199
|
+
readOfflineSyncFileContentChunk({
|
|
200
|
+
root,
|
|
201
|
+
path: "state/lcm.sqlite",
|
|
202
|
+
}),
|
|
203
|
+
/offline sync file content path is excluded: state\/lcm\.sqlite/,
|
|
204
|
+
);
|
|
205
|
+
} finally {
|
|
206
|
+
await rm(root, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
120
210
|
test("offline changeset pushes local edits when the remote is still at the shared base", async () => {
|
|
121
211
|
const remote = await tempDir("remnic-offline-remote");
|
|
122
212
|
const local = await tempDir("remnic-offline-local");
|
package/src/offline-sync.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import {
|
|
3
3
|
lstat,
|
|
4
4
|
mkdir,
|
|
5
|
+
open,
|
|
5
6
|
readdir,
|
|
6
7
|
readFile,
|
|
7
8
|
rename,
|
|
@@ -13,6 +14,7 @@ import path from "node:path";
|
|
|
13
14
|
import {
|
|
14
15
|
DEFAULT_TRANSFER_EXCLUDE_DIRS,
|
|
15
16
|
} from "./transfer/exclusions.js";
|
|
17
|
+
import { isEncryptedFile, MAGIC_HEADER_SIZE } from "./secure-store/secure-fs.js";
|
|
16
18
|
import {
|
|
17
19
|
prepareSafeArchiveRoot,
|
|
18
20
|
resolveSafeArchiveTarget,
|
|
@@ -25,6 +27,7 @@ import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
|
|
|
25
27
|
export const OFFLINE_SYNC_SNAPSHOT_FORMAT = "remnic.offline-sync.snapshot.v1";
|
|
26
28
|
export const OFFLINE_SYNC_CHANGESET_FORMAT = "remnic.offline-sync.changeset.v1";
|
|
27
29
|
export const OFFLINE_SYNC_STATE_VERSION = 1;
|
|
30
|
+
export const OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES = 64 * 1024 * 1024;
|
|
28
31
|
|
|
29
32
|
export interface OfflineSyncFileState {
|
|
30
33
|
path: string;
|
|
@@ -127,6 +130,13 @@ export interface OfflineSyncFileWriteTarget extends OfflineSyncFileTarget {
|
|
|
127
130
|
content: Buffer;
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
export interface OfflineSyncFileContentChunk extends Omit<OfflineSyncFileState, "sha256"> {
|
|
134
|
+
sha256?: string;
|
|
135
|
+
offset: number;
|
|
136
|
+
chunkBytes: number;
|
|
137
|
+
content: Buffer;
|
|
138
|
+
}
|
|
139
|
+
|
|
130
140
|
interface OfflineSyncFileRecordOptions {
|
|
131
141
|
root: SafeArchiveRoot;
|
|
132
142
|
relPath: string;
|
|
@@ -143,6 +153,10 @@ const EXCLUDED_FILE_NAMES = new Set([
|
|
|
143
153
|
const EXCLUDED_REL_PATHS = new Set([
|
|
144
154
|
"state/fact-hashes.ready",
|
|
145
155
|
"state/fact-hashes.txt",
|
|
156
|
+
"state/last_graph_recall.json",
|
|
157
|
+
"state/last_intent.json",
|
|
158
|
+
"state/last_qmd_recall.json",
|
|
159
|
+
"state/last_recall.json",
|
|
146
160
|
"state/lcm.sqlite",
|
|
147
161
|
"state/lcm.sqlite-shm",
|
|
148
162
|
"state/lcm.sqlite-wal",
|
|
@@ -435,6 +449,35 @@ async function readOfflineSyncFileRecord(
|
|
|
435
449
|
};
|
|
436
450
|
}
|
|
437
451
|
|
|
452
|
+
async function fileIsSecureStoreEncrypted(filePath: string): Promise<boolean> {
|
|
453
|
+
const handle = await open(filePath, "r");
|
|
454
|
+
try {
|
|
455
|
+
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
456
|
+
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
457
|
+
return bytesRead >= MAGIC_HEADER_SIZE && isEncryptedFile(header);
|
|
458
|
+
} finally {
|
|
459
|
+
await handle.close();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function readPlainFileContentChunk(options: {
|
|
464
|
+
filePath: string;
|
|
465
|
+
offset: number;
|
|
466
|
+
length: number;
|
|
467
|
+
bytes: number;
|
|
468
|
+
}): Promise<Buffer> {
|
|
469
|
+
const chunkBytes = Math.min(options.length, options.bytes - options.offset);
|
|
470
|
+
const chunk = Buffer.alloc(chunkBytes);
|
|
471
|
+
if (chunkBytes === 0) return chunk;
|
|
472
|
+
const handle = await open(options.filePath, "r");
|
|
473
|
+
try {
|
|
474
|
+
const { bytesRead } = await handle.read(chunk, 0, chunk.length, options.offset);
|
|
475
|
+
return bytesRead === chunk.length ? chunk : chunk.subarray(0, bytesRead);
|
|
476
|
+
} finally {
|
|
477
|
+
await handle.close();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
438
481
|
export async function buildOfflineSyncSnapshot(options: {
|
|
439
482
|
root: string;
|
|
440
483
|
sourceId: string;
|
|
@@ -530,6 +573,81 @@ export async function buildOfflineSyncSnapshotForPaths(options: {
|
|
|
530
573
|
};
|
|
531
574
|
}
|
|
532
575
|
|
|
576
|
+
export async function readOfflineSyncFileContentChunk(options: {
|
|
577
|
+
root: string;
|
|
578
|
+
path: string;
|
|
579
|
+
offset?: number;
|
|
580
|
+
length?: number;
|
|
581
|
+
includeTranscripts?: boolean;
|
|
582
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
583
|
+
}): Promise<OfflineSyncFileContentChunk> {
|
|
584
|
+
const rootAbs = path.resolve(options.root);
|
|
585
|
+
const root = await prepareSafeArchiveRoot(rootAbs, "readOfflineSyncFileContentChunk", "root");
|
|
586
|
+
const includeTranscripts = options.includeTranscripts !== false;
|
|
587
|
+
const relPath = normalizeRelativePath(options.path, "path");
|
|
588
|
+
if (shouldExcludeRelPath(relPath, includeTranscripts)) {
|
|
589
|
+
throw new Error(`offline sync file content path is excluded: ${relPath}`);
|
|
590
|
+
}
|
|
591
|
+
const offset = options.offset === undefined
|
|
592
|
+
? 0
|
|
593
|
+
: assertNonNegativeInteger(options.offset, "offset");
|
|
594
|
+
const requestedLength = options.length === undefined
|
|
595
|
+
? OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES
|
|
596
|
+
: assertNonNegativeInteger(options.length, "length");
|
|
597
|
+
if (requestedLength < 1 || requestedLength > OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
`length must be an integer from 1 to ${OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES}`,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
const filePath = await resolveSafeArchiveTarget(root, relPath);
|
|
603
|
+
const st = await lstat(filePath).catch((error: unknown) => {
|
|
604
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
605
|
+
throw error;
|
|
606
|
+
});
|
|
607
|
+
if (!st || st.isSymbolicLink() || !st.isFile()) {
|
|
608
|
+
throw new Error(`offline sync file content path not found: ${relPath}`);
|
|
609
|
+
}
|
|
610
|
+
const encrypted = await fileIsSecureStoreEncrypted(filePath);
|
|
611
|
+
if (!encrypted) {
|
|
612
|
+
if (offset > st.size) {
|
|
613
|
+
throw new Error(`offset must be <= file size for ${relPath}`);
|
|
614
|
+
}
|
|
615
|
+
const chunk = await readPlainFileContentChunk({
|
|
616
|
+
filePath,
|
|
617
|
+
offset,
|
|
618
|
+
length: requestedLength,
|
|
619
|
+
bytes: st.size,
|
|
620
|
+
});
|
|
621
|
+
return {
|
|
622
|
+
path: relPath,
|
|
623
|
+
bytes: st.size,
|
|
624
|
+
mtimeMs: st.mtimeMs,
|
|
625
|
+
offset,
|
|
626
|
+
chunkBytes: chunk.length,
|
|
627
|
+
content: chunk,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (!options.readFile) {
|
|
631
|
+
throw new Error(`offline sync file content requires a secure-store read hook: ${relPath}`);
|
|
632
|
+
}
|
|
633
|
+
const content = await options.readFile({ root: root.abs, path: relPath, filePath });
|
|
634
|
+
if (offset > content.length) {
|
|
635
|
+
throw new Error(`offset must be <= file size for ${relPath}`);
|
|
636
|
+
}
|
|
637
|
+
const digest = sha256Buffer(content);
|
|
638
|
+
const end = Math.min(content.length, offset + requestedLength);
|
|
639
|
+
const chunk = content.subarray(offset, end);
|
|
640
|
+
return {
|
|
641
|
+
path: relPath,
|
|
642
|
+
sha256: digest.sha256,
|
|
643
|
+
bytes: digest.bytes,
|
|
644
|
+
mtimeMs: st.mtimeMs,
|
|
645
|
+
offset,
|
|
646
|
+
chunkBytes: chunk.length,
|
|
647
|
+
content: Buffer.from(chunk),
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
533
651
|
export async function buildOfflineSyncChangeset(options: {
|
|
534
652
|
root: string;
|
|
535
653
|
sourceId: string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access-schema.ts"],"sourcesContent":["// Request/response schema validation for the Remnic HTTP API.\n// Uses zod for runtime validation — returns structured 400 errors with\n// field-level detail so consumers get clear feedback on malformed requests.\n\nimport { z } from \"zod\";\nimport {\n ACTION_CONFIDENCE_CONTEXT_READINESS,\n ACTION_CONFIDENCE_RISK_CATEGORIES,\n ACTION_CONFIDENCE_RULE_KINDS,\n} from \"./action-confidence.js\";\nimport { isValidCapsuleSince } from \"./transfer/capsule-export.js\";\nimport { CAPSULE_ID_PATTERN } from \"./transfer/types.js\";\n\n// ---------------------------------------------------------------------------\n// Error formatting\n// ---------------------------------------------------------------------------\n\nexport interface SchemaValidationError {\n error: string;\n code: \"validation_error\";\n details: Array<{ field: string; message: string }>;\n}\n\nexport function formatZodError(error: z.ZodError): SchemaValidationError {\n return {\n error: \"request validation failed\",\n code: \"validation_error\",\n details: error.issues.map((issue) => ({\n field: issue.path.join(\".\") || \"(root)\",\n message: issue.message,\n })),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared fields\n// ---------------------------------------------------------------------------\n\nconst namespaceSchema = z.string().trim().max(256).optional();\nconst sessionKeySchema = z.string().trim().min(1).max(512).optional();\nconst idempotencyKeySchema = z.string().trim().min(1).max(256).optional();\nconst dryRunSchema = z.boolean().optional();\nconst schemaVersionSchema = z.number().int().optional();\n\n// ---------------------------------------------------------------------------\n// Recall\n// ---------------------------------------------------------------------------\n\n/**\n * Coding-agent context (issue #569). Optional payload that connectors may\n * ship with a recall request so the project/branch namespace overlay\n * applies to that recall. All fields are validated per CLAUDE.md #51 —\n * empty-string projectId / rootPath is rejected, not silently accepted.\n */\nexport const codingContextSchema = z\n .object({\n projectId: z.string().trim().min(1, \"codingContext.projectId is required\").max(128),\n branch: z.string().trim().max(256).nullable(),\n rootPath: z.string().trim().min(1, \"codingContext.rootPath is required\").max(1024),\n defaultBranch: z.string().trim().max(256).nullable(),\n })\n .nullable();\n\n/**\n * Recall disclosure depth (issue #677). Mirrors the `RecallDisclosure`\n * type in `types.ts` — keep these in sync. Default-application happens\n * inside `EngramAccessService.recall()`; the schema only accepts/rejects.\n * Invalid values throw a structured 400 instead of silently defaulting,\n * per CLAUDE.md rule 51.\n */\nexport const recallDisclosureSchema = z.enum([\"chunk\", \"section\", \"raw\"]);\n\n/**\n * Tag-match semantics (issue #689). `any` (default when `tags` is provided\n * and `tagMatch` is omitted) admits a result when it carries at least one\n * of the filter tags. `all` requires every filter tag to be present.\n * Schema rejects unknown values up front — never silently defaults\n * (CLAUDE.md rule 51).\n */\nexport const tagMatchSchema = z.enum([\"any\", \"all\"]);\n\nexport const recallRequestSchema = z.object({\n query: z.string().min(1, \"query is required\"),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n topK: z.number().int().min(0).max(200).optional(),\n mode: z.enum([\"auto\", \"no_recall\", \"minimal\", \"full\", \"graph_mode\"]).optional(),\n includeDebug: z.boolean().optional(),\n idempotencyKey: idempotencyKeySchema,\n disclosure: recallDisclosureSchema.optional(),\n codingContext: codingContextSchema.optional(),\n /** Working directory for auto git-context resolution (issue #569). */\n cwd: z.string().trim().min(1, \"cwd must be non-empty when provided\").max(2048).optional(),\n /**\n * Arbitrary project tag for non-git-based project scoping (issue #569).\n * Creates a coding context with `projectId: \"tag:<projectTag>\"`.\n */\n projectTag: z.string().trim().min(1, \"projectTag must be non-empty when provided\").max(256).optional(),\n /**\n * Historical recall pin (issue #680). ISO 8601 timestamp. The\n * schema only enforces the basic shape; the access service runs\n * `Date.parse` and emits a structured 400 on malformed input\n * (CLAUDE.md rule 51).\n */\n asOf: z.string().trim().min(1, \"asOf must be a non-empty ISO 8601 timestamp\").max(64).optional(),\n /**\n * Free-form recall tag filter (issue #689). When provided, recall results\n * whose frontmatter `tags` do not match the filter are removed before the\n * response is returned. Comparison is case-sensitive exact match.\n */\n tags: z.array(z.string().trim().min(1).max(256)).max(50).optional(),\n /**\n * Match mode for `tags` (issue #689). Defaults to `\"any\"` when `tags` is\n * provided and `tagMatch` is omitted. Ignored when `tags` is absent.\n */\n tagMatch: tagMatchSchema.optional(),\n /**\n * Include graph edges below `graphTraversalConfidenceFloor` for diagnostic\n * recall traversal (issue #681). Defaults to false.\n */\n includeLowConfidence: z.boolean().optional(),\n});\n\nexport const recallExplainRequestSchema = z.object({\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n});\n\n/**\n * Standalone \"set coding context\" request. Used by the HTTP endpoint\n * `POST /engram/v1/coding-context` and the MCP `remnic.set_coding_context`\n * tool (PR 7). `codingContext: null` clears the attached context.\n */\nexport const setCodingContextRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n codingContext: codingContextSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Observe\n// ---------------------------------------------------------------------------\n\nconst messageSchema = z.object({\n role: z.enum([\"user\", \"assistant\"]),\n content: z.string().min(1, \"message content must be non-empty\"),\n sourceFormat: z\n .enum([\"openai\", \"anthropic\", \"openclaw\", \"pi\", \"lossless-claw\", \"remnic\"])\n .nullable()\n .optional(),\n rawContent: z.unknown().nullable().optional(),\n parts: z\n .array(\n z.object({\n ordinal: z.number().int().min(0).nullable().optional(),\n kind: z.enum([\n \"text\",\n \"tool_call\",\n \"tool_result\",\n \"patch\",\n \"file_read\",\n \"file_write\",\n \"step_start\",\n \"step_finish\",\n \"snapshot\",\n \"retry\",\n ]),\n payload: z.record(z.string(), z.unknown()),\n toolName: z.string().nullable().optional(),\n tool_name: z.string().nullable().optional(),\n filePath: z.string().nullable().optional(),\n file_path: z.string().nullable().optional(),\n createdAt: z.string().nullable().optional(),\n created_at: z.string().nullable().optional(),\n }),\n )\n .nullable()\n .optional(),\n});\n\nexport const observeRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n messages: z.array(messageSchema).min(1, \"messages must be a non-empty array\"),\n namespace: namespaceSchema,\n skipExtraction: z.boolean().optional(),\n /** Working directory for auto git-context resolution (issue #569). */\n cwd: z.string().trim().min(1, \"cwd must be non-empty when provided\").max(2048).optional(),\n /**\n * Arbitrary project tag for non-git-based project scoping (issue #569).\n * Creates a coding context with `projectId: \"tag:<projectTag>\"`.\n */\n projectTag: z.string().trim().min(1, \"projectTag must be non-empty when provided\").max(256).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Memory store / suggestion submit\n// ---------------------------------------------------------------------------\n\nconst writeContentSchema = z.string().min(1, \"content is required\").max(50000);\nconst categorySchema = z\n .enum([\n \"fact\", \"preference\", \"correction\", \"entity\", \"decision\",\n \"relationship\", \"principle\", \"commitment\", \"moment\", \"skill\", \"rule\", \"procedure\",\n \"reasoning_trace\",\n ])\n .optional();\nconst confidenceSchema = z.number().min(0).max(1).optional();\nconst tagsSchema = z.array(z.string().max(256)).max(50).optional();\nconst entityRefSchema = z.string().trim().max(512).optional();\nconst ttlSchema = z.string().trim().max(128).optional();\nconst sourceReasonSchema = z.string().trim().max(2000).optional();\n\nexport const memoryStoreRequestSchema = z.object({\n schemaVersion: schemaVersionSchema,\n idempotencyKey: idempotencyKeySchema,\n dryRun: dryRunSchema,\n sessionKey: sessionKeySchema,\n content: writeContentSchema,\n category: categorySchema,\n confidence: confidenceSchema,\n namespace: namespaceSchema,\n tags: tagsSchema,\n entityRef: entityRefSchema,\n ttl: ttlSchema,\n sourceReason: sourceReasonSchema,\n});\n\nexport const suggestionSubmitRequestSchema = memoryStoreRequestSchema;\n\n// ---------------------------------------------------------------------------\n// Review disposition\n// ---------------------------------------------------------------------------\n\nexport const reviewDispositionRequestSchema = z.object({\n memoryId: z.string().trim().min(1, \"memoryId is required\"),\n status: z.enum([\n \"active\", \"pending_review\", \"quarantined\", \"rejected\", \"superseded\", \"archived\",\n ]),\n reasonCode: z.string().trim().min(1, \"reasonCode is required\"),\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Trust-zone promote\n// ---------------------------------------------------------------------------\n\nexport const trustZonePromoteRequestSchema = z.object({\n recordId: z.string().trim().min(1, \"recordId is required\"),\n targetZone: z.enum([\"working\", \"trusted\"], {\n errorMap: () => ({ message: \"targetZone must be 'working' or 'trusted'\" }),\n }),\n promotionReason: z.string().trim().min(1, \"promotionReason is required\"),\n recordedAt: z.string().trim().optional(),\n summary: z.string().trim().max(5000).optional(),\n dryRun: dryRunSchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Trust-zone demo-seed\n// ---------------------------------------------------------------------------\n\nexport const trustZoneDemoSeedRequestSchema = z.object({\n scenario: z.string().trim().max(256).optional(),\n recordedAt: z.string().trim().optional(),\n dryRun: dryRunSchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// LCM search\n// ---------------------------------------------------------------------------\n\nexport const lcmSearchRequestSchema = z.object({\n query: z.string().min(1, \"query is required\"),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n limit: z.number().int().min(1).max(100).optional(),\n});\n\nexport const lcmCompactionFlushRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n namespace: namespaceSchema,\n});\n\nexport const lcmCompactionRecordRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n namespace: namespaceSchema,\n tokensBefore: z.number().int().min(0, \"tokensBefore must be a non-negative integer\"),\n tokensAfter: z.number().int().min(0, \"tokensAfter must be a non-negative integer\"),\n});\n\n// ---------------------------------------------------------------------------\n// Day summary\n// ---------------------------------------------------------------------------\n\nexport const daySummaryRequestSchema = z.object({\n memories: z.string().max(100000).optional(),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Capsule export\n// ---------------------------------------------------------------------------\n\nconst capsuleTopLevelSegmentSchema = z\n .string()\n .trim()\n .min(1)\n .max(128)\n .refine(\n (value) => !value.includes(\"/\") && !value.includes(\"\\\\\"),\n \"must be a top-level directory name without path separators\",\n );\n\nconst capsulePeerIdSchema = z\n .string()\n .trim()\n .min(1)\n .max(256)\n .refine(\n (value) => value !== \".\" && value !== \"..\" && !value.includes(\"/\") && !value.includes(\"\\\\\"),\n \"must be a plain peer id without path separators\",\n );\n\nconst capsuleIsoSinceSchema = z\n .string()\n .trim()\n .min(1, \"since must be a non-empty ISO 8601 timestamp\")\n .max(128)\n .refine(\n isValidCapsuleSince,\n \"since must be a valid ISO 8601 timestamp with no calendar overflow\",\n );\n\nexport const capsuleExportRequestSchema = z\n .object({\n name: z\n .string()\n .trim()\n .min(1, \"name is required\")\n .max(64, \"name must be 64 characters or fewer\")\n .regex(\n CAPSULE_ID_PATTERN,\n \"name must be alphanumeric with single dashes (no spaces, no leading/trailing dashes)\",\n ),\n namespace: namespaceSchema,\n since: capsuleIsoSinceSchema.optional(),\n includeKinds: z.array(capsuleTopLevelSegmentSchema).max(50).optional(),\n peerIds: z.array(capsulePeerIdSchema).max(100).optional(),\n includeTranscripts: z.boolean().optional(),\n encrypt: z.boolean().optional(),\n });\n\nexport const capsuleImportRequestSchema = z\n .object({\n archivePath: z.string().trim().min(1, \"archivePath is required\").max(4096),\n namespace: namespaceSchema,\n mode: z.enum([\"skip\", \"overwrite\", \"fork\"]).optional(),\n });\n\nexport const capsuleListRequestSchema = z\n .object({\n namespace: namespaceSchema,\n });\n\n// ---------------------------------------------------------------------------\n// Offline sync\n// ---------------------------------------------------------------------------\n\nexport const offlineSyncApplyRequestSchema = z\n .object({\n namespace: namespaceSchema,\n changeset: z.unknown(),\n })\n .refine((value) => value.changeset !== undefined && value.changeset !== null, {\n message: \"changeset is required\",\n path: [\"changeset\"],\n });\n\nexport const offlineSyncFilesRequestSchema = z.object({\n namespace: namespaceSchema,\n includeTranscripts: z.boolean().optional(),\n paths: z\n .array(z.string().trim().min(1, \"path must be non-empty\").max(4096))\n .max(5000, \"paths must contain 5000 or fewer entries\"),\n});\n\n// ---------------------------------------------------------------------------\n// Action confidence\n// ---------------------------------------------------------------------------\n\nconst nullableOptional = <T extends z.ZodTypeAny>(schema: T) =>\n schema.optional().nullable().transform((value) => value ?? undefined);\n\nconst actionConfidenceRuleSchema = z\n .object({\n kind: z.enum(ACTION_CONFIDENCE_RULE_KINDS),\n description: nullableOptional(z.string().trim().min(1).max(2000)),\n matched: nullableOptional(z.boolean()),\n })\n .strict();\n\nconst actionConfidenceMemorySchema = z\n .object({\n source: nullableOptional(z.string().trim().min(1).max(256)),\n created: nullableOptional(z.string().trim().min(1).max(128)),\n updated: nullableOptional(z.string().trim().min(1).max(128)),\n scope: nullableOptional(z.string().trim().min(1).max(512)),\n userContextScopes: nullableOptional(z.array(z.string().trim().min(1).max(128)).max(50)),\n retrievalReason: nullableOptional(z.string().trim().min(1).max(2000)),\n confidence: nullableOptional(z.number().min(0).max(1)),\n stale: nullableOptional(z.boolean()),\n corrected: nullableOptional(z.boolean()),\n correctionState: nullableOptional(z.enum([\"none\", \"correction\", \"superseded\", \"disputed\", \"forgotten\"])),\n safeToUse: nullableOptional(z.boolean()),\n safety: nullableOptional(z.enum([\"safe\", \"requires-review\", \"blocked\"])),\n safetyReasons: nullableOptional(z.array(z.string().trim().min(1).max(1000)).max(50)),\n })\n .strict();\n\nexport const actionConfidenceRequestSchema = z\n .object({\n intendedAction: nullableOptional(z.string().trim().min(1).max(1000)),\n confidence: nullableOptional(z.number().min(0).max(1)),\n risk: nullableOptional(z.enum(ACTION_CONFIDENCE_RISK_CATEGORIES)),\n contextReadiness: nullableOptional(z.enum(ACTION_CONFIDENCE_CONTEXT_READINESS)),\n currentContextScopes: nullableOptional(z.array(z.string().trim().min(1).max(128)).max(50)),\n userRules: nullableOptional(z.array(actionConfidenceRuleSchema).max(100)),\n retrievedMemories: nullableOptional(z.array(actionConfidenceMemorySchema).max(200)),\n })\n .strict();\n\n// ---------------------------------------------------------------------------\n// Inferred types\n// ---------------------------------------------------------------------------\n\nexport type RecallRequest = z.infer<typeof recallRequestSchema>;\nexport type RecallExplainRequest = z.infer<typeof recallExplainRequestSchema>;\nexport type SetCodingContextRequest = z.infer<typeof setCodingContextRequestSchema>;\nexport type ObserveRequest = z.infer<typeof observeRequestSchema>;\nexport type MemoryStoreRequest = z.infer<typeof memoryStoreRequestSchema>;\nexport type SuggestionSubmitRequest = z.infer<typeof suggestionSubmitRequestSchema>;\nexport type ReviewDispositionRequest = z.infer<typeof reviewDispositionRequestSchema>;\nexport type TrustZonePromoteRequest = z.infer<typeof trustZonePromoteRequestSchema>;\nexport type TrustZoneDemoSeedRequest = z.infer<typeof trustZoneDemoSeedRequestSchema>;\nexport type LcmSearchRequest = z.infer<typeof lcmSearchRequestSchema>;\nexport type LcmCompactionFlushRequest = z.infer<typeof lcmCompactionFlushRequestSchema>;\nexport type LcmCompactionRecordRequest = z.infer<typeof lcmCompactionRecordRequestSchema>;\nexport type DaySummaryRequest = z.infer<typeof daySummaryRequestSchema>;\nexport type CapsuleExportRequest = z.infer<typeof capsuleExportRequestSchema>;\nexport type CapsuleImportRequest = z.infer<typeof capsuleImportRequestSchema>;\nexport type CapsuleListRequest = z.infer<typeof capsuleListRequestSchema>;\nexport type OfflineSyncApplyRequest = z.infer<typeof offlineSyncApplyRequestSchema>;\nexport type OfflineSyncFilesRequest = z.infer<typeof offlineSyncFilesRequestSchema>;\nexport type ActionConfidenceRequest = z.infer<typeof actionConfidenceRequestSchema>;\n\n// ---------------------------------------------------------------------------\n// Validation helper\n// ---------------------------------------------------------------------------\n\nexport type SchemaName =\n | \"recall\"\n | \"recallExplain\"\n | \"setCodingContext\"\n | \"observe\"\n | \"memoryStore\"\n | \"suggestionSubmit\"\n | \"reviewDisposition\"\n | \"trustZonePromote\"\n | \"trustZoneDemoSeed\"\n | \"lcmSearch\"\n | \"lcmCompactionFlush\"\n | \"lcmCompactionRecord\"\n | \"daySummary\"\n | \"capsuleExport\"\n | \"capsuleImport\"\n | \"capsuleList\"\n | \"offlineSyncFiles\"\n | \"offlineSyncApply\"\n | \"actionConfidence\";\n\nexport type SchemaTypeFor<N extends SchemaName> =\n N extends \"recall\" ? RecallRequest\n : N extends \"recallExplain\" ? RecallExplainRequest\n : N extends \"setCodingContext\" ? SetCodingContextRequest\n : N extends \"observe\" ? ObserveRequest\n : N extends \"memoryStore\" ? MemoryStoreRequest\n : N extends \"suggestionSubmit\" ? SuggestionSubmitRequest\n : N extends \"reviewDisposition\" ? ReviewDispositionRequest\n : N extends \"trustZonePromote\" ? TrustZonePromoteRequest\n : N extends \"trustZoneDemoSeed\" ? TrustZoneDemoSeedRequest\n : N extends \"lcmSearch\" ? LcmSearchRequest\n : N extends \"lcmCompactionFlush\" ? LcmCompactionFlushRequest\n : N extends \"lcmCompactionRecord\" ? LcmCompactionRecordRequest\n : N extends \"daySummary\" ? DaySummaryRequest\n : N extends \"capsuleExport\" ? CapsuleExportRequest\n : N extends \"capsuleImport\" ? CapsuleImportRequest\n : N extends \"capsuleList\" ? CapsuleListRequest\n : N extends \"offlineSyncFiles\" ? OfflineSyncFilesRequest\n : N extends \"offlineSyncApply\" ? OfflineSyncApplyRequest\n : N extends \"actionConfidence\" ? ActionConfidenceRequest\n : never;\n\nconst schemas: Record<SchemaName, z.ZodTypeAny> = {\n recall: recallRequestSchema,\n recallExplain: recallExplainRequestSchema,\n setCodingContext: setCodingContextRequestSchema,\n observe: observeRequestSchema,\n memoryStore: memoryStoreRequestSchema,\n suggestionSubmit: suggestionSubmitRequestSchema,\n reviewDisposition: reviewDispositionRequestSchema,\n trustZonePromote: trustZonePromoteRequestSchema,\n trustZoneDemoSeed: trustZoneDemoSeedRequestSchema,\n lcmSearch: lcmSearchRequestSchema,\n lcmCompactionFlush: lcmCompactionFlushRequestSchema,\n lcmCompactionRecord: lcmCompactionRecordRequestSchema,\n daySummary: daySummaryRequestSchema,\n capsuleExport: capsuleExportRequestSchema,\n capsuleImport: capsuleImportRequestSchema,\n capsuleList: capsuleListRequestSchema,\n offlineSyncFiles: offlineSyncFilesRequestSchema,\n offlineSyncApply: offlineSyncApplyRequestSchema,\n actionConfidence: actionConfidenceRequestSchema,\n};\n\n/**\n * Validate a request body against the named schema.\n * Returns `{ success: true, data }` on pass or\n * `{ success: false, error }` on failure with field-level detail.\n */\nexport function validateRequest<T = unknown>(\n schemaName: SchemaName,\n body: unknown,\n): { success: true; data: T } | { success: false; error: SchemaValidationError } {\n const schema = schemas[schemaName];\n if (!schema) {\n return {\n success: false,\n error: {\n error: `unknown schema: ${schemaName}`,\n code: \"validation_error\",\n details: [],\n },\n };\n }\n const result = schema.safeParse(body);\n if (result.success) {\n return { success: true, data: result.data as T };\n }\n return { success: false, error: formatZodError(result.error) };\n}\n"],"mappings":"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","names":[]}
|