@remnic/core 9.3.548 → 9.3.549

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.
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Orchestrator
3
- } from "./chunk-OF46AKZC.js";
3
+ } from "./chunk-XDMLTNIG.js";
4
4
  import "./chunk-5RIRL3XL.js";
5
5
  import "./chunk-BFBF3XEF.js";
6
- import "./chunk-S53PKKWK.js";
6
+ import "./chunk-KVEVLBKC.js";
7
7
  import "./chunk-HHLLAQGZ.js";
8
8
  import "./chunk-UWK5OXUJ.js";
9
9
  import "./chunk-RLV3PQGH.js";
@@ -104,9 +104,9 @@ declare const recallRequestSchema: z.ZodObject<{
104
104
  includeLowConfidence: z.ZodOptional<z.ZodBoolean>;
105
105
  }, "strip", z.ZodTypeAny, {
106
106
  query: string;
107
+ namespace?: string | undefined;
107
108
  sessionKey?: string | undefined;
108
109
  tags?: string[] | undefined;
109
- namespace?: string | undefined;
110
110
  topK?: number | undefined;
111
111
  mode?: "no_recall" | "minimal" | "full" | "graph_mode" | "auto" | undefined;
112
112
  cwd?: string | undefined;
@@ -125,9 +125,9 @@ declare const recallRequestSchema: z.ZodObject<{
125
125
  tagMatch?: "all" | "any" | undefined;
126
126
  }, {
127
127
  query: string;
128
+ namespace?: string | undefined;
128
129
  sessionKey?: string | undefined;
129
130
  tags?: string[] | undefined;
130
- namespace?: string | undefined;
131
131
  topK?: number | undefined;
132
132
  mode?: "no_recall" | "minimal" | "full" | "graph_mode" | "auto" | undefined;
133
133
  cwd?: string | undefined;
@@ -149,11 +149,11 @@ declare const recallExplainRequestSchema: z.ZodObject<{
149
149
  sessionKey: z.ZodOptional<z.ZodString>;
150
150
  namespace: z.ZodOptional<z.ZodString>;
151
151
  }, "strip", z.ZodTypeAny, {
152
- sessionKey?: string | undefined;
153
152
  namespace?: string | undefined;
154
- }, {
155
153
  sessionKey?: string | undefined;
154
+ }, {
156
155
  namespace?: string | undefined;
156
+ sessionKey?: string | undefined;
157
157
  }>;
158
158
  /**
159
159
  * Standalone "set coding context" request. Used by the HTTP endpoint
@@ -363,28 +363,28 @@ declare const memoryStoreRequestSchema: z.ZodObject<{
363
363
  sourceReason: z.ZodOptional<z.ZodString>;
364
364
  }, "strip", z.ZodTypeAny, {
365
365
  content: string;
366
+ namespace?: string | undefined;
367
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
368
+ confidence?: number | undefined;
369
+ ttl?: string | undefined;
366
370
  schemaVersion?: number | undefined;
367
371
  sessionKey?: string | undefined;
368
372
  tags?: string[] | undefined;
369
373
  dryRun?: boolean | undefined;
370
- namespace?: string | undefined;
371
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
372
- confidence?: number | undefined;
373
374
  entityRef?: string | undefined;
374
- ttl?: string | undefined;
375
375
  sourceReason?: string | undefined;
376
376
  idempotencyKey?: string | undefined;
377
377
  }, {
378
378
  content: string;
379
+ namespace?: string | undefined;
380
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
381
+ confidence?: number | undefined;
382
+ ttl?: string | undefined;
379
383
  schemaVersion?: number | undefined;
380
384
  sessionKey?: string | undefined;
381
385
  tags?: string[] | undefined;
382
386
  dryRun?: boolean | undefined;
383
- namespace?: string | undefined;
384
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
385
- confidence?: number | undefined;
386
387
  entityRef?: string | undefined;
387
- ttl?: string | undefined;
388
388
  sourceReason?: string | undefined;
389
389
  idempotencyKey?: string | undefined;
390
390
  }>;
@@ -403,28 +403,28 @@ declare const suggestionSubmitRequestSchema: z.ZodObject<{
403
403
  sourceReason: z.ZodOptional<z.ZodString>;
404
404
  }, "strip", z.ZodTypeAny, {
405
405
  content: string;
406
+ namespace?: string | undefined;
407
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
408
+ confidence?: number | undefined;
409
+ ttl?: string | undefined;
406
410
  schemaVersion?: number | undefined;
407
411
  sessionKey?: string | undefined;
408
412
  tags?: string[] | undefined;
409
413
  dryRun?: boolean | undefined;
410
- namespace?: string | undefined;
411
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
412
- confidence?: number | undefined;
413
414
  entityRef?: string | undefined;
414
- ttl?: string | undefined;
415
415
  sourceReason?: string | undefined;
416
416
  idempotencyKey?: string | undefined;
417
417
  }, {
418
418
  content: string;
419
+ namespace?: string | undefined;
420
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
421
+ confidence?: number | undefined;
422
+ ttl?: string | undefined;
419
423
  schemaVersion?: number | undefined;
420
424
  sessionKey?: string | undefined;
421
425
  tags?: string[] | undefined;
422
426
  dryRun?: boolean | undefined;
423
- namespace?: string | undefined;
424
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
425
- confidence?: number | undefined;
426
427
  entityRef?: string | undefined;
427
- ttl?: string | undefined;
428
428
  sourceReason?: string | undefined;
429
429
  idempotencyKey?: string | undefined;
430
430
  }>;
@@ -456,18 +456,18 @@ declare const trustZonePromoteRequestSchema: z.ZodObject<{
456
456
  recordId: string;
457
457
  targetZone: "working" | "trusted";
458
458
  promotionReason: string;
459
+ namespace?: string | undefined;
459
460
  recordedAt?: string | undefined;
460
461
  summary?: string | undefined;
461
462
  dryRun?: boolean | undefined;
462
- namespace?: string | undefined;
463
463
  }, {
464
464
  recordId: string;
465
465
  targetZone: "working" | "trusted";
466
466
  promotionReason: string;
467
+ namespace?: string | undefined;
467
468
  recordedAt?: string | undefined;
468
469
  summary?: string | undefined;
469
470
  dryRun?: boolean | undefined;
470
- namespace?: string | undefined;
471
471
  }>;
472
472
  declare const trustZoneDemoSeedRequestSchema: z.ZodObject<{
473
473
  scenario: z.ZodOptional<z.ZodString>;
@@ -475,14 +475,14 @@ declare const trustZoneDemoSeedRequestSchema: z.ZodObject<{
475
475
  dryRun: z.ZodOptional<z.ZodBoolean>;
476
476
  namespace: z.ZodOptional<z.ZodString>;
477
477
  }, "strip", z.ZodTypeAny, {
478
+ namespace?: string | undefined;
478
479
  recordedAt?: string | undefined;
479
480
  dryRun?: boolean | undefined;
480
- namespace?: string | undefined;
481
481
  scenario?: string | undefined;
482
482
  }, {
483
+ namespace?: string | undefined;
483
484
  recordedAt?: string | undefined;
484
485
  dryRun?: boolean | undefined;
485
- namespace?: string | undefined;
486
486
  scenario?: string | undefined;
487
487
  }>;
488
488
  declare const lcmSearchRequestSchema: z.ZodObject<{
@@ -493,15 +493,15 @@ declare const lcmSearchRequestSchema: z.ZodObject<{
493
493
  limit: z.ZodOptional<z.ZodNumber>;
494
494
  }, "strip", z.ZodTypeAny, {
495
495
  query: string;
496
- sessionKey?: string | undefined;
497
496
  namespace?: string | undefined;
498
497
  limit?: number | undefined;
498
+ sessionKey?: string | undefined;
499
499
  sessionPrefix?: string | undefined;
500
500
  }, {
501
501
  query: string;
502
- sessionKey?: string | undefined;
503
502
  namespace?: string | undefined;
504
503
  limit?: number | undefined;
504
+ sessionKey?: string | undefined;
505
505
  sessionPrefix?: string | undefined;
506
506
  }>;
507
507
  declare const lcmCompactionFlushRequestSchema: z.ZodObject<{
@@ -535,12 +535,12 @@ declare const daySummaryRequestSchema: z.ZodObject<{
535
535
  sessionKey: z.ZodOptional<z.ZodString>;
536
536
  namespace: z.ZodOptional<z.ZodString>;
537
537
  }, "strip", z.ZodTypeAny, {
538
- sessionKey?: string | undefined;
539
538
  namespace?: string | undefined;
539
+ sessionKey?: string | undefined;
540
540
  memories?: string | undefined;
541
541
  }, {
542
- sessionKey?: string | undefined;
543
542
  namespace?: string | undefined;
543
+ sessionKey?: string | undefined;
544
544
  memories?: string | undefined;
545
545
  }>;
546
546
  declare const capsuleExportRequestSchema: z.ZodObject<{
@@ -730,11 +730,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
730
730
  safety: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodEnum<["safe", "requires-review", "blocked"]>>>, NonNullable<"safe" | "requires-review" | "blocked"> | undefined, "safe" | "requires-review" | "blocked" | null | undefined>;
731
731
  safetyReasons: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>, string[] | undefined, string[] | null | undefined>;
732
732
  }, "strict", z.ZodTypeAny, {
733
+ confidence?: number | undefined;
733
734
  source?: string | undefined;
734
735
  stale?: boolean | undefined;
735
736
  created?: string | undefined;
736
737
  updated?: string | undefined;
737
- confidence?: number | undefined;
738
738
  scope?: string | undefined;
739
739
  retrievalReason?: string | undefined;
740
740
  safety?: NonNullable<"safe" | "requires-review" | "blocked"> | undefined;
@@ -744,11 +744,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
744
744
  safetyReasons?: string[] | undefined;
745
745
  userContextScopes?: string[] | undefined;
746
746
  }, {
747
+ confidence?: number | null | undefined;
747
748
  source?: string | null | undefined;
748
749
  stale?: boolean | null | undefined;
749
750
  created?: string | null | undefined;
750
751
  updated?: string | null | undefined;
751
- confidence?: number | null | undefined;
752
752
  scope?: string | null | undefined;
753
753
  retrievalReason?: string | null | undefined;
754
754
  safety?: "safe" | "requires-review" | "blocked" | null | undefined;
@@ -758,11 +758,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
758
758
  safetyReasons?: string[] | null | undefined;
759
759
  userContextScopes?: string[] | null | undefined;
760
760
  }>, "many">>>, {
761
+ confidence?: number | undefined;
761
762
  source?: string | undefined;
762
763
  stale?: boolean | undefined;
763
764
  created?: string | undefined;
764
765
  updated?: string | undefined;
765
- confidence?: number | undefined;
766
766
  scope?: string | undefined;
767
767
  retrievalReason?: string | undefined;
768
768
  safety?: NonNullable<"safe" | "requires-review" | "blocked"> | undefined;
@@ -772,11 +772,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
772
772
  safetyReasons?: string[] | undefined;
773
773
  userContextScopes?: string[] | undefined;
774
774
  }[] | undefined, {
775
+ confidence?: number | null | undefined;
775
776
  source?: string | null | undefined;
776
777
  stale?: boolean | null | undefined;
777
778
  created?: string | null | undefined;
778
779
  updated?: string | null | undefined;
779
- confidence?: number | null | undefined;
780
780
  scope?: string | null | undefined;
781
781
  retrievalReason?: string | null | undefined;
782
782
  safety?: "safe" | "requires-review" | "blocked" | null | undefined;
@@ -798,11 +798,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
798
798
  matched?: boolean | undefined;
799
799
  }[] | undefined;
800
800
  retrievedMemories?: {
801
+ confidence?: number | undefined;
801
802
  source?: string | undefined;
802
803
  stale?: boolean | undefined;
803
804
  created?: string | undefined;
804
805
  updated?: string | undefined;
805
- confidence?: number | undefined;
806
806
  scope?: string | undefined;
807
807
  retrievalReason?: string | undefined;
808
808
  safety?: NonNullable<"safe" | "requires-review" | "blocked"> | undefined;
@@ -824,11 +824,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
824
824
  matched?: boolean | null | undefined;
825
825
  }[] | null | undefined;
826
826
  retrievedMemories?: {
827
+ confidence?: number | null | undefined;
827
828
  source?: string | null | undefined;
828
829
  stale?: boolean | null | undefined;
829
830
  created?: string | null | undefined;
830
831
  updated?: string | null | undefined;
831
- confidence?: number | null | undefined;
832
832
  scope?: string | null | undefined;
833
833
  retrievalReason?: string | null | undefined;
834
834
  safety?: "safe" | "requires-review" | "blocked" | null | undefined;
@@ -80,6 +80,12 @@ function backupRoot(homeDir) {
80
80
  function defaultRollbackCommand() {
81
81
  return "remnic migrate --rollback";
82
82
  }
83
+ function resolveMigrationCwd(options) {
84
+ return path.resolve(options?.cwd ?? process.cwd());
85
+ }
86
+ function resolveConnectorConfigPath(candidate, cwd) {
87
+ return path.isAbsolute(candidate) ? path.resolve(candidate) : path.resolve(cwd, candidate);
88
+ }
83
89
  async function ensureParent(filePath) {
84
90
  await mkdir(path.dirname(filePath), { recursive: true });
85
91
  }
@@ -90,6 +96,9 @@ async function writeOwnerOnlyFile(filePath, content) {
90
96
  await writeFile(filePath, content, { encoding: "utf8", mode: TOKEN_STORE_MODE });
91
97
  await chmod(filePath, TOKEN_STORE_MODE);
92
98
  }
99
+ async function fileContentHash(filePath) {
100
+ return createHash("sha256").update(await readFile(filePath)).digest("hex");
101
+ }
93
102
  async function writeTokenStoreFile(filePath, content) {
94
103
  await writeOwnerOnlyFile(filePath, content);
95
104
  }
@@ -180,6 +189,183 @@ function parseTokenEntries(raw) {
180
189
  function isPlainJsonObject(value) {
181
190
  return typeof value === "object" && value !== null && !Array.isArray(value);
182
191
  }
192
+ function parseRollbackManifestEntry(raw, index) {
193
+ if (!isPlainJsonObject(raw)) {
194
+ throw new Error(`rollback manifest entry ${index} must be an object`);
195
+ }
196
+ if (typeof raw.targetPath !== "string" || raw.targetPath.length === 0 || !path.isAbsolute(raw.targetPath)) {
197
+ throw new Error(`rollback manifest entry ${index} has an invalid targetPath`);
198
+ }
199
+ if (raw.backupPath !== void 0 && (typeof raw.backupPath !== "string" || raw.backupPath.length === 0 || !path.isAbsolute(raw.backupPath))) {
200
+ throw new Error(`rollback manifest entry ${index} has an invalid backupPath`);
201
+ }
202
+ if (raw.createdByMigration !== void 0 && typeof raw.createdByMigration !== "boolean") {
203
+ throw new Error(`rollback manifest entry ${index} has an invalid createdByMigration flag`);
204
+ }
205
+ if (raw.contentHash !== void 0 && (typeof raw.contentHash !== "string" || !/^[a-f0-9]{64}$/u.test(raw.contentHash))) {
206
+ throw new Error(`rollback manifest entry ${index} has an invalid contentHash`);
207
+ }
208
+ return {
209
+ targetPath: raw.targetPath,
210
+ ...raw.backupPath === void 0 ? {} : { backupPath: raw.backupPath },
211
+ ...raw.createdByMigration === void 0 ? {} : { createdByMigration: raw.createdByMigration },
212
+ ...raw.contentHash === void 0 ? {} : { contentHash: raw.contentHash }
213
+ };
214
+ }
215
+ function parseRollbackManifest(raw, manifestPath) {
216
+ if (!isPlainJsonObject(raw)) {
217
+ throw new Error(`rollback manifest must be an object: ${manifestPath}`);
218
+ }
219
+ if (raw.version !== 1) {
220
+ throw new Error(`rollback manifest has unsupported version: ${manifestPath}`);
221
+ }
222
+ if (typeof raw.createdAt !== "string") {
223
+ throw new Error(`rollback manifest has an invalid createdAt: ${manifestPath}`);
224
+ }
225
+ if (!Array.isArray(raw.entries)) {
226
+ throw new Error(`rollback manifest entries must be an array: ${manifestPath}`);
227
+ }
228
+ return {
229
+ version: 1,
230
+ createdAt: raw.createdAt,
231
+ entries: raw.entries.map(parseRollbackManifestEntry)
232
+ };
233
+ }
234
+ function isPathInside(parentPath, candidatePath) {
235
+ const relative = path.relative(path.resolve(parentPath), path.resolve(candidatePath));
236
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
237
+ }
238
+ function isPathDescendant(parentPath, candidatePath) {
239
+ const relative = path.relative(path.resolve(parentPath), path.resolve(candidatePath));
240
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
241
+ }
242
+ function hasConnectorConfigShape(targetPath, homeDir) {
243
+ const relative = path.relative(path.resolve(homeDir), path.resolve(targetPath));
244
+ if (relative === ".claude.json" || relative === path.join(".claude", ".mcp.json")) {
245
+ return true;
246
+ }
247
+ return hasRepoConnectorConfigShape(relative);
248
+ }
249
+ function hasRepoConnectorConfigShape(relativePath) {
250
+ const parts = relativePath.split(path.sep);
251
+ const last = parts.at(-1);
252
+ const packageName = parts.at(-2);
253
+ const packagesDir = parts.at(-3);
254
+ return last === ".mcp.json" && packagesDir === "packages" && (packageName === "plugin-claude-code" || packageName === "plugin-codex");
255
+ }
256
+ function connectorBackupPathForTarget(targetPath, homeDir) {
257
+ const digest = createHash("sha256").update(targetPath).digest("hex").slice(0, 12);
258
+ return path.join(backupRoot(homeDir), "mcp", `${digest}.json`);
259
+ }
260
+ function isRollbackTargetAllowed(entry, targetPath, homeDir, options) {
261
+ const resolvedTarget = path.resolve(targetPath);
262
+ if (isPathDescendant(remnicRoot(homeDir), resolvedTarget)) {
263
+ if (entry.createdByMigration && !entry.backupPath) return true;
264
+ return Boolean(entry.backupPath) && isRemnicTokenStorePath(resolvedTarget, homeDir);
265
+ }
266
+ if (isPathDescendant(path.join(homeDir, ".config", "remnic"), resolvedTarget)) {
267
+ return entry.createdByMigration === true && !entry.backupPath;
268
+ }
269
+ const serviceTargets = /* @__PURE__ */ new Set([
270
+ path.resolve(path.join(homeDir, "Library", "LaunchAgents", "ai.remnic.daemon.plist")),
271
+ path.resolve(path.join(homeDir, ".config", "systemd", "user", "remnic.service"))
272
+ ]);
273
+ if (serviceTargets.has(resolvedTarget)) return true;
274
+ const cwd = resolveMigrationCwd(options);
275
+ const allowedConnectorPaths = new Set(
276
+ [
277
+ ...defaultConnectorConfigPaths(homeDir, cwd),
278
+ ...options?.connectorConfigPaths ?? []
279
+ ].map((candidate) => resolveConnectorConfigPath(candidate, cwd))
280
+ );
281
+ if (allowedConnectorPaths.has(resolvedTarget)) return true;
282
+ return isPathInside(homeDir, resolvedTarget) && hasConnectorConfigShape(resolvedTarget, homeDir);
283
+ }
284
+ function isRollbackBackupAllowed(backupPath, homeDir) {
285
+ return isPathInside(backupRoot(homeDir), backupPath);
286
+ }
287
+ async function assertNoSymlinkPathSegments(filePath, trustedRoot, label) {
288
+ const resolvedPath = path.resolve(filePath);
289
+ const resolvedRoot = path.resolve(trustedRoot);
290
+ if (!isPathInside(resolvedRoot, resolvedPath)) {
291
+ throw new Error(`${label} is outside the trusted rollback root: ${filePath}`);
292
+ }
293
+ const segments = path.relative(resolvedRoot, resolvedPath).split(path.sep).filter(Boolean);
294
+ let current = resolvedRoot;
295
+ for (const [index, segment] of segments.entries()) {
296
+ current = path.join(current, segment);
297
+ try {
298
+ const currentStat = await lstat(current);
299
+ if (currentStat.isSymbolicLink()) {
300
+ throw new Error(`${label} must not contain symlink segments: ${filePath}`);
301
+ }
302
+ } catch (error) {
303
+ if (error.code === "ENOENT" && index === segments.length - 1) return;
304
+ if (error.code === "ENOENT") {
305
+ throw new Error(`${label} must not contain missing parent segments: ${filePath}`);
306
+ }
307
+ throw error;
308
+ }
309
+ }
310
+ }
311
+ function rollbackTargetSymlinkRoot(targetPath, homeDir, options) {
312
+ if (isPathInside(homeDir, targetPath)) return homeDir;
313
+ const cwd = resolveMigrationCwd(options);
314
+ if (isPathInside(cwd, targetPath)) return cwd;
315
+ return path.parse(targetPath).root;
316
+ }
317
+ async function validateRollbackManifestEntries(manifest, homeDir, options) {
318
+ const validated = [];
319
+ for (const entry of manifest.entries) {
320
+ const targetPath = path.resolve(entry.targetPath);
321
+ if (!isRollbackTargetAllowed(entry, targetPath, homeDir, options)) {
322
+ throw new Error(`rollback manifest target is outside migration-owned paths: ${entry.targetPath}`);
323
+ }
324
+ if (entry.backupPath && entry.createdByMigration) {
325
+ throw new Error(`rollback manifest entry cannot restore and remove the same target: ${entry.targetPath}`);
326
+ }
327
+ const targetExists = await pathExistsNoFollow(targetPath);
328
+ if (!entry.createdByMigration || targetExists) {
329
+ const targetSymlinkRoot = rollbackTargetSymlinkRoot(targetPath, homeDir, options);
330
+ await assertNoSymlinkPathSegments(targetPath, targetSymlinkRoot, "rollback manifest target");
331
+ }
332
+ if (entry.createdByMigration && targetExists) {
333
+ await assertCreatedRollbackTargetMatchesRecord(entry, targetPath);
334
+ }
335
+ let backupPath;
336
+ if (entry.backupPath) {
337
+ backupPath = path.resolve(entry.backupPath);
338
+ if (!isRollbackBackupAllowed(backupPath, homeDir)) {
339
+ throw new Error(`rollback manifest backup is outside migration backup storage: ${entry.backupPath}`);
340
+ }
341
+ if (await pathExistsNoFollow(backupPath)) {
342
+ await assertNoSymlinkPathSegments(backupPath, homeDir, "rollback manifest backup");
343
+ await assertExistingRegularFileNoFollow(backupPath, "rollback manifest backup");
344
+ }
345
+ if (backupPath !== connectorBackupPathForTarget(targetPath, homeDir)) {
346
+ throw new Error(`rollback manifest backup does not match target: ${entry.targetPath}`);
347
+ }
348
+ }
349
+ validated.push({
350
+ targetPath,
351
+ ...backupPath === void 0 ? {} : { backupPath },
352
+ ...entry.createdByMigration === void 0 ? {} : { createdByMigration: entry.createdByMigration },
353
+ ...entry.contentHash === void 0 ? {} : { contentHash: entry.contentHash }
354
+ });
355
+ }
356
+ return validated;
357
+ }
358
+ async function assertCreatedRollbackTargetMatchesRecord(entry, targetPath) {
359
+ const targetStat = await lstat(targetPath);
360
+ if (targetStat.isDirectory()) {
361
+ throw new Error(`rollback manifest created target must be a file path: ${entry.targetPath}`);
362
+ }
363
+ if (!targetStat.isFile() || !entry.contentHash) return false;
364
+ if (await fileContentHash(targetPath) !== entry.contentHash) {
365
+ throw new Error(`rollback manifest created target content does not match migration record: ${entry.targetPath}`);
366
+ }
367
+ return true;
368
+ }
183
369
  async function rewriteTokensIfPresent(filePath) {
184
370
  if (!existsSync(filePath)) return 0;
185
371
  await assertExistingRegularFileNoFollow(filePath, "Remnic token store");
@@ -330,8 +516,7 @@ async function backupFile(targetPath, originalContent, homeDir, manifest, persis
330
516
  if (manifest.entries.some((entry) => entry.targetPath === targetPath && entry.backupPath)) {
331
517
  return;
332
518
  }
333
- const digest = createHash("sha256").update(targetPath).digest("hex").slice(0, 12);
334
- const backupPath = path.join(backupRoot(homeDir), "mcp", `${digest}.json`);
519
+ const backupPath = connectorBackupPathForTarget(targetPath, homeDir);
335
520
  await ensureParent(backupPath);
336
521
  if (isRemnicTokenStorePath(targetPath, homeDir)) {
337
522
  await writeOwnerOnlyFile(backupPath, originalContent);
@@ -345,7 +530,19 @@ async function backupFile(targetPath, originalContent, homeDir, manifest, persis
345
530
  }
346
531
  async function recordCreatedPath(filePath, manifest, persistManifest) {
347
532
  if (manifest.entries.some((entry) => entry.targetPath === filePath)) return;
348
- manifest.entries.push({ targetPath: filePath, createdByMigration: true });
533
+ manifest.entries.push({
534
+ targetPath: filePath,
535
+ createdByMigration: true,
536
+ contentHash: await fileContentHash(filePath)
537
+ });
538
+ await persistManifest?.();
539
+ }
540
+ async function refreshCreatedPathHash(filePath, manifest, persistManifest) {
541
+ const entry = manifest.entries.find(
542
+ (candidate) => candidate.targetPath === filePath && candidate.createdByMigration
543
+ );
544
+ if (!entry) return;
545
+ entry.contentHash = await fileContentHash(filePath);
349
546
  await persistManifest?.();
350
547
  }
351
548
  function defaultConnectorConfigPaths(homeDir, cwd) {
@@ -360,8 +557,9 @@ async function updateConnectorConfigs(homeDir, cwd, options, manifest, persistMa
360
557
  const updated = [];
361
558
  const candidates = options?.connectorConfigPaths ?? defaultConnectorConfigPaths(homeDir, cwd);
362
559
  for (const targetPath of candidates) {
363
- if (await rewriteJsonFile(targetPath, homeDir, manifest, persistManifest)) {
364
- updated.push(targetPath);
560
+ const resolvedTarget = resolveConnectorConfigPath(targetPath, cwd);
561
+ if (await rewriteJsonFile(resolvedTarget, homeDir, manifest, persistManifest)) {
562
+ updated.push(resolvedTarget);
365
563
  }
366
564
  }
367
565
  return updated;
@@ -452,12 +650,26 @@ async function writeRollbackManifest(homeDir, manifest) {
452
650
  }
453
651
  async function readRollbackManifest(homeDir) {
454
652
  const target = rollbackManifestPath(homeDir);
455
- if (!existsSync(target)) return null;
653
+ let targetStat;
654
+ try {
655
+ targetStat = await lstat(target);
656
+ } catch (error) {
657
+ if (error.code === "ENOENT") return null;
658
+ throw error;
659
+ }
660
+ if (targetStat.isSymbolicLink()) {
661
+ throw new Error(`rollback manifest must not be a symlink: ${target}`);
662
+ }
663
+ if (!targetStat.isFile()) {
664
+ throw new Error(`rollback manifest must be a regular file: ${target}`);
665
+ }
666
+ let parsed;
456
667
  try {
457
- return JSON.parse(await readFile(target, "utf8"));
668
+ parsed = JSON.parse(await readFile(target, "utf8"));
458
669
  } catch {
459
670
  return null;
460
671
  }
672
+ return parseRollbackManifest(parsed, target);
461
673
  }
462
674
  async function acquireLock(homeDir) {
463
675
  const target = lockPath(homeDir);
@@ -529,6 +741,7 @@ async function rollbackFromEngramMigration(options) {
529
741
  const restored = [];
530
742
  const removed = [];
531
743
  if (!manifest) return { restored, removed };
744
+ const entries = await validateRollbackManifestEntries(manifest, homeDir, options);
532
745
  if (platform === "darwin") {
533
746
  const remnicPlist = path.join(homeDir, "Library", "LaunchAgents", "ai.remnic.daemon.plist");
534
747
  if (existsSync(remnicPlist)) {
@@ -544,8 +757,10 @@ async function rollbackFromEngramMigration(options) {
544
757
  } catch {
545
758
  }
546
759
  }
547
- for (const entry of [...manifest.entries].reverse()) {
548
- if (entry.backupPath && existsSync(entry.backupPath)) {
760
+ for (const entry of [...entries].reverse()) {
761
+ if (entry.backupPath && await pathExistsNoFollow(entry.backupPath)) {
762
+ await assertNoSymlinkPathSegments(entry.backupPath, homeDir, "rollback manifest backup");
763
+ await assertExistingRegularFileNoFollow(entry.backupPath, "rollback manifest backup");
549
764
  await ensureParent(entry.targetPath);
550
765
  await copyFile(entry.backupPath, entry.targetPath);
551
766
  if (isRemnicTokenStorePath(entry.targetPath, homeDir)) {
@@ -555,6 +770,9 @@ async function rollbackFromEngramMigration(options) {
555
770
  continue;
556
771
  }
557
772
  if (entry.createdByMigration && existsSync(entry.targetPath)) {
773
+ const targetSymlinkRoot = rollbackTargetSymlinkRoot(entry.targetPath, homeDir, options);
774
+ await assertNoSymlinkPathSegments(entry.targetPath, targetSymlinkRoot, "rollback manifest target");
775
+ if (!await assertCreatedRollbackTargetMatchesRecord(entry, entry.targetPath)) continue;
558
776
  await rm(entry.targetPath, { recursive: true, force: true });
559
777
  removed.push(entry.targetPath);
560
778
  }
@@ -571,7 +789,7 @@ async function rollbackFromEngramMigration(options) {
571
789
  }
572
790
  async function migrateFromEngram(options) {
573
791
  const homeDir = resolveMigrationHome(options);
574
- const cwd = options?.cwd ?? process.cwd();
792
+ const cwd = resolveMigrationCwd(options);
575
793
  const logger = resolveLogger(options);
576
794
  const copied = [];
577
795
  let tokensRegenerated = 0;
@@ -622,6 +840,7 @@ async function migrateFromEngram(options) {
622
840
  const remnicTokens = path.join(remnicRoot(homeDir), "tokens.json");
623
841
  if (copied.includes(remnicTokens)) {
624
842
  tokensRegenerated += await rewriteTokensIfPresent(remnicTokens);
843
+ await refreshCreatedPathHash(remnicTokens, manifest, persistManifest);
625
844
  } else {
626
845
  tokensRegenerated += await mergeLegacyTokens(
627
846
  legacyTokens,
@@ -660,4 +879,4 @@ export {
660
879
  rollbackFromEngramMigration,
661
880
  migrateFromEngram
662
881
  };
663
- //# sourceMappingURL=chunk-S53PKKWK.js.map
882
+ //# sourceMappingURL=chunk-KVEVLBKC.js.map