@remnic/cli 9.69.45 → 9.69.47

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.
Files changed (2) hide show
  1. package/dist/index.js +183 -80
  2. package/package.json +32 -32
package/dist/index.js CHANGED
@@ -2084,7 +2084,7 @@ import { buildReconcileManifest } from "@remnic/core/reconcile/manifest.js";
2084
2084
  import { convergeIdentityCachePath } from "@remnic/core/reconcile/cursor.js";
2085
2085
 
2086
2086
  // src/converge-plan-cache.ts
2087
- import { createHash } from "crypto";
2087
+ import { createHash, randomUUID } from "crypto";
2088
2088
  import { readFileSync } from "fs";
2089
2089
  import * as fs16 from "fs/promises";
2090
2090
  import * as path2 from "path";
@@ -2099,6 +2099,15 @@ var CONVERGE_PLAN_CACHE_MAX_ENTRIES = 128;
2099
2099
  var CONVERGE_PLAN_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
2100
2100
  var CONVERGE_PLAN_CACHE_MAX_SCOPE_DIRS = 8;
2101
2101
  var SHA256_HEX = /^[0-9a-f]{64}$/i;
2102
+ var MEMORY_STATUSES = [
2103
+ "active",
2104
+ "pending_review",
2105
+ "rejected",
2106
+ "quarantined",
2107
+ "superseded",
2108
+ "archived",
2109
+ "forgotten"
2110
+ ];
2102
2111
  var ConvergePlanCacheBusyError = class extends Error {
2103
2112
  constructor(holderPid) {
2104
2113
  super(
@@ -2155,7 +2164,7 @@ function normalizeEntryFile(raw) {
2155
2164
  if (typeof candidate.id !== "string" || candidate.id.length === 0) return null;
2156
2165
  if (typeof candidate.contentHash !== "string" || !SHA256_HEX.test(candidate.contentHash)) return null;
2157
2166
  if (typeof candidate.category !== "string") return null;
2158
- if (typeof candidate.status !== "string") return null;
2167
+ if (!MEMORY_STATUSES.includes(candidate.status)) return null;
2159
2168
  const normalizerVersion = typeof candidate.normalizerVersion === "number" ? candidate.normalizerVersion : void 0;
2160
2169
  const identityResolutionVersion = typeof candidate.identityResolutionVersion === "number" ? candidate.identityResolutionVersion : void 0;
2161
2170
  const contentHashAliases = Array.isArray(candidate.contentHashAliases) && candidate.contentHashAliases.every((alias) => typeof alias === "string") ? candidate.contentHashAliases : void 0;
@@ -2331,57 +2340,76 @@ async function pruneSiblingScopes(root, activeScope) {
2331
2340
  });
2332
2341
  }
2333
2342
  }
2343
+ var heldLockPaths = /* @__PURE__ */ new Set();
2344
+ var failedReleaseNonces = /* @__PURE__ */ new Map();
2334
2345
  var ConvergePlanCache = class _ConvergePlanCache {
2335
2346
  scope;
2336
2347
  scopeDir;
2337
2348
  lockPath;
2349
+ nonce = randomUUID();
2338
2350
  closed = false;
2339
2351
  constructor(root, scope) {
2340
2352
  this.scope = scope;
2341
2353
  this.scopeDir = path2.join(root, scope);
2342
2354
  this.lockPath = path2.join(root, "lock.json");
2343
2355
  }
2356
+ lockPayload() {
2357
+ return `${JSON.stringify({
2358
+ pid: process.pid,
2359
+ startTicks: PROCESS_START_TICKS,
2360
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
2361
+ nonce: this.nonce
2362
+ })}
2363
+ `;
2364
+ }
2344
2365
  /**
2345
2366
  * Open (and lock) the cache for one planning run. The lock is
2346
2367
  * cross-process: a second live planner fails fast with
2347
2368
  * {@link ConvergePlanCacheBusyError} instead of racing checkpoint writes.
2348
- * A lock left by a dead process is stolen; a lock file is only ever
2349
- * replaced atomically, so a steal racing the true owner at worst causes
2350
- * redundant recompute entries themselves are content-addressed and
2351
- * written via rename, so concurrent identical writes cannot interleave.
2369
+ * A lock left by a dead process or by this process's own failed
2370
+ * release is stolen; a lock file is only ever replaced atomically, so
2371
+ * a steal racing the true owner at worst causes redundant recompute —
2372
+ * entries themselves are content-addressed and written via rename, so
2373
+ * concurrent identical writes cannot interleave.
2352
2374
  */
2353
2375
  static async open(memoryDir, scope) {
2354
2376
  const root = convergePlanCacheRoot(memoryDir);
2355
2377
  const scopeDir = path2.join(root, scope);
2356
2378
  await ensureSafePlanCacheTree(memoryDir, scopeDir);
2357
2379
  const lockPath = path2.join(root, "lock.json");
2358
- const payload = `${JSON.stringify({
2359
- pid: process.pid,
2360
- startTicks: PROCESS_START_TICKS,
2361
- savedAt: (/* @__PURE__ */ new Date()).toISOString()
2362
- })}
2363
- `;
2380
+ if (heldLockPaths.has(lockPath)) throw new ConvergePlanCacheBusyError(process.pid);
2381
+ heldLockPaths.add(lockPath);
2382
+ const cache = new _ConvergePlanCache(root, scope);
2364
2383
  try {
2365
- await fs16.writeFile(lockPath, payload, { flag: "wx" });
2366
- } catch (error) {
2367
- if (error.code !== "EEXIST") throw error;
2368
- let held = {};
2384
+ const payload = cache.lockPayload();
2369
2385
  try {
2370
- held = JSON.parse(await fs16.readFile(lockPath, "utf8"));
2386
+ await fs16.writeFile(lockPath, payload, { flag: "wx" });
2387
+ } catch (error) {
2388
+ if (error.code !== "EEXIST") throw error;
2389
+ let held = {};
2390
+ try {
2391
+ held = JSON.parse(await fs16.readFile(lockPath, "utf8"));
2392
+ } catch {
2393
+ held = {};
2394
+ }
2395
+ const sameOwner = held.pid === process.pid && (typeof held.nonce === "string" && failedReleaseNonces.get(lockPath) === held.nonce || PROCESS_START_TICKS !== null && held.startTicks === PROCESS_START_TICKS);
2396
+ if (!sameOwner) {
2397
+ const owner = lockOwnerLive(held);
2398
+ if (owner.live) throw new ConvergePlanCacheBusyError(owner.pid);
2399
+ }
2400
+ const tmp = `${lockPath}.${process.pid}.tmp`;
2401
+ await fs16.writeFile(tmp, payload);
2402
+ await fs16.rename(tmp, lockPath);
2403
+ }
2404
+ failedReleaseNonces.delete(lockPath);
2405
+ try {
2406
+ await pruneSiblingScopes(root, scope);
2407
+ await pruneScopeDir(cache.scopeDir);
2371
2408
  } catch {
2372
- held = {};
2373
2409
  }
2374
- const owner = lockOwnerLive(held);
2375
- if (owner.live) throw new ConvergePlanCacheBusyError(owner.pid);
2376
- const tmp = `${lockPath}.${process.pid}.tmp`;
2377
- await fs16.writeFile(tmp, payload);
2378
- await fs16.rename(tmp, lockPath);
2379
- }
2380
- const cache = new _ConvergePlanCache(root, scope);
2381
- try {
2382
- await pruneSiblingScopes(root, scope);
2383
- await pruneScopeDir(cache.scopeDir);
2384
- } catch {
2410
+ } catch (error) {
2411
+ heldLockPaths.delete(lockPath);
2412
+ throw error;
2385
2413
  }
2386
2414
  return cache;
2387
2415
  }
@@ -2417,6 +2445,7 @@ var ConvergePlanCache = class _ConvergePlanCache {
2417
2445
  */
2418
2446
  async writeEntry(entry) {
2419
2447
  if (this.closed) return;
2448
+ await this.renewLease();
2420
2449
  const fileName = entryFileName(entry.side, entry.namespace, entry.watermark);
2421
2450
  const finalPath = path2.join(this.scopeDir, fileName);
2422
2451
  const tmpPath = `${finalPath}.${process.pid}.${Date.now()}.tmp`;
@@ -2439,11 +2468,49 @@ var ConvergePlanCache = class _ConvergePlanCache {
2439
2468
  } catch {
2440
2469
  }
2441
2470
  }
2442
- /** Release the cross-process lock. Safe to call more than once. */
2471
+ /**
2472
+ * Refresh savedAt while the cache stays open (#2965): on hosts without
2473
+ * readable /proc start ticks the 24h lease is the only steal guard, and a
2474
+ * plan legitimately longer than that must not have its lock stolen
2475
+ * mid-run. Renewal rides the per-namespace checkpoint cadence; it skips
2476
+ * a lock that was stolen from us (nonce mismatch).
2477
+ */
2478
+ async renewLease() {
2479
+ if (this.closed) return;
2480
+ try {
2481
+ const held = JSON.parse(await fs16.readFile(this.lockPath, "utf8"));
2482
+ if (typeof held.nonce !== "string" || held.nonce !== this.nonce) return;
2483
+ const tmp = `${this.lockPath}.${process.pid}.renew.tmp`;
2484
+ await fs16.writeFile(tmp, this.lockPayload());
2485
+ await fs16.rename(tmp, this.lockPath);
2486
+ } catch {
2487
+ }
2488
+ }
2489
+ /**
2490
+ * Release the cross-process lock. Safe to call more than once. Only the
2491
+ * lock instance this cache owns is unlinked (#2965): a lock stolen from
2492
+ * us carries another owner's nonce and must survive this close. When our
2493
+ * own unlink fails, the nonce is retained so a later open in this
2494
+ * process reclaims the orphan instead of busy-failing on it.
2495
+ */
2443
2496
  async close() {
2444
2497
  if (this.closed) return;
2445
2498
  this.closed = true;
2446
- await unlinkQuiet(this.lockPath);
2499
+ heldLockPaths.delete(this.lockPath);
2500
+ let owned = false;
2501
+ try {
2502
+ const held = JSON.parse(await fs16.readFile(this.lockPath, "utf8"));
2503
+ owned = typeof held.nonce === "string" && held.nonce === this.nonce;
2504
+ } catch {
2505
+ }
2506
+ if (!owned) return;
2507
+ try {
2508
+ await fs16.unlink(this.lockPath);
2509
+ } catch (error) {
2510
+ if (error.code !== "ENOENT") {
2511
+ failedReleaseNonces.set(this.lockPath, this.nonce);
2512
+ }
2513
+ }
2447
2514
  }
2448
2515
  };
2449
2516
 
@@ -2938,6 +3005,7 @@ async function planLocalNamespaceCensus(args) {
2938
3005
  const evidence = await readLocalTombstoneEvidence(rootDir);
2939
3006
  let manifestReadFailed = false;
2940
3007
  const manifest = await buildReconcileManifest({
3008
+ signal: args.signal,
2941
3009
  files,
2942
3010
  parseMemory: parseFrontmatter,
2943
3011
  citationTemplate: args.citationTemplate,
@@ -2970,20 +3038,20 @@ async function planLocalNamespaceCensus(args) {
2970
3038
  identityCache,
2971
3039
  classificationUpdates
2972
3040
  );
3041
+ const priorByPath = new Map((priorFiles ?? []).map((file) => [file.path, file.sha256.toLowerCase()]));
3042
+ let reused = 0;
3043
+ for (const file of files) {
3044
+ if (priorByPath.get(file.path) === file.sha256.toLowerCase()) reused += 1;
3045
+ }
3046
+ args.onProgress?.({
3047
+ side: "local",
3048
+ namespace: ns,
3049
+ index: args.index,
3050
+ total: args.total,
3051
+ reused,
3052
+ computed: files.length - reused
3053
+ });
2973
3054
  if (args.cache) {
2974
- const priorByPath = new Map((priorFiles ?? []).map((file) => [file.path, file.sha256.toLowerCase()]));
2975
- let reused = 0;
2976
- for (const file of files) {
2977
- if (priorByPath.get(file.path) === file.sha256.toLowerCase()) reused += 1;
2978
- }
2979
- args.onProgress?.({
2980
- side: "local",
2981
- namespace: ns,
2982
- index: args.index,
2983
- total: args.total,
2984
- reused,
2985
- computed: files.length - reused
2986
- });
2987
3055
  await args.cache.writeEntry({
2988
3056
  version: 1,
2989
3057
  scope: args.cache.scope,
@@ -3025,7 +3093,7 @@ import {
3025
3093
  RECONCILE_MANIFEST_SCHEMA_VERSION as RECONCILE_MANIFEST_SCHEMA_VERSION2
3026
3094
  } from "@remnic/core/reconcile/manifest.js";
3027
3095
  var SHA256_PATTERN = /^[a-f0-9]{64}$/i;
3028
- var MEMORY_STATUSES = /* @__PURE__ */ new Set([
3096
+ var MEMORY_STATUSES2 = /* @__PURE__ */ new Set([
3029
3097
  "active",
3030
3098
  "pending_review",
3031
3099
  "rejected",
@@ -3062,7 +3130,7 @@ function parseMemory(value) {
3062
3130
  if (typeof memory.contentHash !== "string" || !SHA256_PATTERN.test(memory.contentHash)) {
3063
3131
  throw new Error("peer manifest memory metadata had invalid contentHash");
3064
3132
  }
3065
- if (typeof memory.status !== "string" || !MEMORY_STATUSES.has(memory.status)) {
3133
+ if (typeof memory.status !== "string" || !MEMORY_STATUSES2.has(memory.status)) {
3066
3134
  throw new Error("peer manifest memory metadata had invalid status");
3067
3135
  }
3068
3136
  const contentHash = memory.contentHash.toLowerCase();
@@ -3221,7 +3289,7 @@ async function fetchPeerSyncCapabilities(peerUrl, token, fetchImpl, timeoutMs) {
3221
3289
  }
3222
3290
  return null;
3223
3291
  }
3224
- async function fetchPeerManifestStream(peerUrl, namespace, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
3292
+ async function fetchPeerManifestStream(peerUrl, namespace, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal) {
3225
3293
  const base = normalizePeerBaseUrl(peerUrl);
3226
3294
  const headers = token ? { authorization: `Bearer ${token}` } : {};
3227
3295
  const routes = [
@@ -3229,7 +3297,12 @@ async function fetchPeerManifestStream(peerUrl, namespace, token, fetchImpl = gl
3229
3297
  `/engram/v1/offline-sync/manifest-stream?namespace=${encodeURIComponent(namespace)}&include_transcripts=false`
3230
3298
  ];
3231
3299
  for (const route of routes) {
3232
- const response = await fetchPeerRequest(fetchImpl, `${base}${route}`, { headers }, timeoutMs);
3300
+ const response = await fetchPeerRequest(
3301
+ fetchImpl,
3302
+ `${base}${route}`,
3303
+ { headers, ...signal ? { signal } : {} },
3304
+ timeoutMs
3305
+ );
3233
3306
  if (response.status === 404 || response.status === 405) continue;
3234
3307
  if (response.status === 401 || response.status === 403) {
3235
3308
  throw new Error(`peer manifest authentication failed: HTTP ${response.status}`);
@@ -3239,7 +3312,7 @@ async function fetchPeerManifestStream(peerUrl, namespace, token, fetchImpl = gl
3239
3312
  }
3240
3313
  return null;
3241
3314
  }
3242
- async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
3315
+ async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS, signal) {
3243
3316
  const base = normalizePeerBaseUrl(peerUrl);
3244
3317
  const routes = [
3245
3318
  // The snapshot validates the cached peer manifest, whose file set
@@ -3255,8 +3328,14 @@ async function fetchPeerSnapshot(peerUrl, namespace, token, fetchImpl = globalTh
3255
3328
  for (const route of routes) {
3256
3329
  let response;
3257
3330
  try {
3258
- response = await fetchPeerRequest(fetchImpl, `${base}${route}`, { headers }, timeoutMs);
3331
+ response = await fetchPeerRequest(
3332
+ fetchImpl,
3333
+ `${base}${route}`,
3334
+ { headers, ...signal ? { signal } : {} },
3335
+ timeoutMs
3336
+ );
3259
3337
  } catch (error) {
3338
+ if (signal?.aborted) throw error;
3260
3339
  lastFailure = error instanceof Error ? error.message : String(error);
3261
3340
  continue;
3262
3341
  }
@@ -3552,7 +3631,14 @@ async function postPeerFileDeletion(peerUrl, namespace, filePath, baseSha256, to
3552
3631
  async function planPeerNamespaceCensus(args) {
3553
3632
  const { peerUrl, ns } = { peerUrl: args.peerUrl, ns: args.namespace };
3554
3633
  args.signal?.throwIfAborted();
3555
- const peerData = await fetchPeerSnapshot(peerUrl, ns, args.resolvedToken, args.fetchFn, args.timeoutMs);
3634
+ const peerData = await fetchPeerSnapshot(
3635
+ peerUrl,
3636
+ ns,
3637
+ args.resolvedToken,
3638
+ args.fetchFn,
3639
+ args.timeoutMs,
3640
+ args.signal
3641
+ );
3556
3642
  const priorPeerEntry = args.cache ? await args.cache.readEntry("peer", ns) : null;
3557
3643
  const revisionTrusted = args.peerManifestRevision !== void 0 && priorPeerEntry?.peerManifestRevision === args.peerManifestRevision;
3558
3644
  const clientBuiltPrior = priorPeerEntry?.clientBuilt === true;
@@ -3561,7 +3647,11 @@ async function planPeerNamespaceCensus(args) {
3561
3647
  const watermark = censusWatermark(peerData.files);
3562
3648
  let peerManifest = null;
3563
3649
  let clientBuilt = clientBuiltPrior;
3564
- if (reusableEntry && reusableEntry.watermark === watermark && reusableEntry.fileCount === peerData.files.length) {
3650
+ if (reusableEntry && reusableEntry.watermark === watermark && reusableEntry.fileCount === peerData.files.length && // A peer upgraded to manifest streaming must not keep serving this
3651
+ // client's older parser semantics on an unchanged watermark (#2965):
3652
+ // client-built rows stay a warm base only; the stream runs once and
3653
+ // the checkpoint becomes a streamed entry.
3654
+ !(args.manifestStream && clientBuiltPrior)) {
3565
3655
  const freshByPath = new Map(peerData.files.map((file) => [file.path, file]));
3566
3656
  peerManifest = {
3567
3657
  format: RECONCILE_MANIFEST_FORMAT2,
@@ -3572,7 +3662,14 @@ async function planPeerNamespaceCensus(args) {
3572
3662
  })
3573
3663
  };
3574
3664
  } else {
3575
- const streamedManifest = args.manifestStream ? await fetchPeerManifestStream(peerUrl, ns, args.resolvedToken, args.fetchFn, args.timeoutMs) : null;
3665
+ const streamedManifest = args.manifestStream ? await fetchPeerManifestStream(
3666
+ peerUrl,
3667
+ ns,
3668
+ args.resolvedToken,
3669
+ args.fetchFn,
3670
+ args.timeoutMs,
3671
+ args.signal
3672
+ ) : null;
3576
3673
  peerManifest = streamedManifest;
3577
3674
  if (peerManifest) {
3578
3675
  clientBuilt = false;
@@ -3583,6 +3680,7 @@ async function planPeerNamespaceCensus(args) {
3583
3680
  files: peerData.files,
3584
3681
  parseMemory: parseFrontmatter2,
3585
3682
  citationTemplate: args.citationTemplate,
3683
+ signal: args.signal,
3586
3684
  // Older peers need one content request per memory file; prior cache
3587
3685
  // rows (sha-keyed) skip the ones already fetched.
3588
3686
  cachedFiles: priorPeerFiles ?? args.localManifestFiles,
@@ -3595,7 +3693,8 @@ async function planPeerNamespaceCensus(args) {
3595
3693
  file.path,
3596
3694
  args.resolvedToken,
3597
3695
  args.fetchFn,
3598
- args.timeoutMs
3696
+ args.timeoutMs,
3697
+ args.signal
3599
3698
  );
3600
3699
  } catch (error) {
3601
3700
  readFailure = error instanceof Error ? error : new Error(String(error));
@@ -3622,7 +3721,8 @@ async function planPeerNamespaceCensus(args) {
3622
3721
  tombstonePath,
3623
3722
  args.resolvedToken,
3624
3723
  args.fetchFn,
3625
- args.timeoutMs
3724
+ args.timeoutMs,
3725
+ args.signal
3626
3726
  );
3627
3727
  if (!remote) {
3628
3728
  throw new Error(`failed to read peer tombstone evidence: ${tombstonePath}`);
@@ -3640,33 +3740,35 @@ async function planPeerNamespaceCensus(args) {
3640
3740
  }
3641
3741
  const mapped = tombstonedFileDigests(evidence, peerManifests);
3642
3742
  for (const digest of peerData.tombstones) mapped.add(digest);
3743
+ const priorByPath = new Map((priorPeerFiles ?? []).map((file) => [file.path, file.sha256.toLowerCase()]));
3744
+ let reused = 0;
3745
+ for (const file of peerManifests.files) {
3746
+ if (priorByPath.get(file.path) === file.sha256.toLowerCase()) reused += 1;
3747
+ }
3748
+ args.onProgress?.({
3749
+ side: "peer",
3750
+ namespace: ns,
3751
+ index: args.index,
3752
+ total: args.total,
3753
+ reused,
3754
+ computed: peerManifests.files.length - reused
3755
+ });
3643
3756
  if (args.cache) {
3644
- const priorByPath = new Map((priorPeerFiles ?? []).map((file) => [file.path, file.sha256.toLowerCase()]));
3645
- let reused = 0;
3646
- for (const file of peerManifests.files) {
3647
- if (priorByPath.get(file.path) === file.sha256.toLowerCase()) reused += 1;
3757
+ if (censusWatermark(peerManifests.files) === watermark) {
3758
+ await args.cache.writeEntry({
3759
+ version: 1,
3760
+ scope: args.cache.scope,
3761
+ side: "peer",
3762
+ namespace: ns,
3763
+ watermark,
3764
+ fileCount: peerManifests.files.length,
3765
+ capturedAtMs: Date.now(),
3766
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
3767
+ ...args.peerManifestRevision !== void 0 ? { peerManifestRevision: args.peerManifestRevision } : {},
3768
+ ...clientBuilt ? { clientBuilt: true } : {},
3769
+ files: peerManifests.files
3770
+ });
3648
3771
  }
3649
- args.onProgress?.({
3650
- side: "peer",
3651
- namespace: ns,
3652
- index: args.index,
3653
- total: args.total,
3654
- reused,
3655
- computed: peerManifests.files.length - reused
3656
- });
3657
- await args.cache.writeEntry({
3658
- version: 1,
3659
- scope: args.cache.scope,
3660
- side: "peer",
3661
- namespace: ns,
3662
- watermark,
3663
- fileCount: peerManifests.files.length,
3664
- capturedAtMs: Date.now(),
3665
- savedAt: (/* @__PURE__ */ new Date()).toISOString(),
3666
- ...args.peerManifestRevision !== void 0 ? { peerManifestRevision: args.peerManifestRevision } : {},
3667
- ...clientBuilt ? { clientBuilt: true } : {},
3668
- files: peerManifests.files
3669
- });
3670
3772
  }
3671
3773
  return { manifest: peerManifests, tombstones: mapped };
3672
3774
  }
@@ -4560,6 +4662,7 @@ async function updateCursorsForPlan(plan, options) {
4560
4662
  if (!memoryDir) return;
4561
4663
  const namespaces = new Set(plan.byNamespace.map((n) => n.namespace));
4562
4664
  for (const ns of namespaces) {
4665
+ options.signal?.throwIfAborted();
4563
4666
  const cursorPath = defaultConvergeCursorPath(memoryDir, peerUrl, ns);
4564
4667
  const priorSemanticAgreements = options.semanticAgreementsByNamespace?.get(ns) ?? (await readConvergeCursor(cursorPath))?.semanticAgreements ?? [];
4565
4668
  const { baseFiles, semanticAgreements } = deriveConvergeCursorBase(plan.entries, ns, priorSemanticAgreements);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/cli",
3
- "version": "9.69.45",
3
+ "version": "9.69.47",
4
4
  "description": "CLI for Remnic memory — init, query, doctor, daemon management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,26 +26,26 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "yaml": "^2.4.2",
29
- "@remnic/plugin-pi": "^9.69.45",
30
- "@remnic/server": "^9.69.45",
31
- "@remnic/core": "^9.69.45"
29
+ "@remnic/plugin-pi": "^9.69.47",
30
+ "@remnic/server": "^9.69.47",
31
+ "@remnic/core": "^9.69.47"
32
32
  },
33
33
  "peerDependencies": {
34
- "@remnic/bench": "^9.69.45",
35
- "@remnic/plugin-openclaw": "^9.69.45",
36
- "@remnic/export-weclone": "^9.69.45",
37
- "@remnic/import-weclone": "^9.69.45",
38
- "@remnic/import-chatgpt": "^9.69.45",
39
- "@remnic/import-claude": "^9.69.45",
40
- "@remnic/import-gemini": "^9.69.45",
41
- "@remnic/import-lossless-claw": "^9.69.45",
42
- "@remnic/import-mem0": "^9.69.45",
43
- "@remnic/import-supermemory": "^9.69.45",
44
- "@remnic/import-okf": "^9.69.45",
45
- "@remnic/connector-limitless": "^9.69.45",
46
- "@remnic/connector-bee": "^9.69.45",
47
- "@remnic/connector-omi": "^9.69.45",
48
- "@remnic/capture-audio": "^9.69.45"
34
+ "@remnic/bench": "^9.69.47",
35
+ "@remnic/plugin-openclaw": "^9.69.47",
36
+ "@remnic/export-weclone": "^9.69.47",
37
+ "@remnic/import-weclone": "^9.69.47",
38
+ "@remnic/import-chatgpt": "^9.69.47",
39
+ "@remnic/import-claude": "^9.69.47",
40
+ "@remnic/import-gemini": "^9.69.47",
41
+ "@remnic/import-lossless-claw": "^9.69.47",
42
+ "@remnic/import-mem0": "^9.69.47",
43
+ "@remnic/import-supermemory": "^9.69.47",
44
+ "@remnic/import-okf": "^9.69.47",
45
+ "@remnic/connector-limitless": "^9.69.47",
46
+ "@remnic/connector-bee": "^9.69.47",
47
+ "@remnic/connector-omi": "^9.69.47",
48
+ "@remnic/capture-audio": "^9.69.47"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "@remnic/bench": {
@@ -97,19 +97,19 @@
97
97
  "devDependencies": {
98
98
  "tsup": "^8.5.1",
99
99
  "typescript": "^5.9.3",
100
- "@remnic/plugin-openclaw": "9.69.45",
101
- "@remnic/bench": "9.69.45",
102
- "@remnic/import-weclone": "9.69.45",
103
- "@remnic/export-weclone": "9.69.45",
104
- "@remnic/import-claude": "9.69.45",
105
- "@remnic/import-chatgpt": "9.69.45",
106
- "@remnic/import-gemini": "9.69.45",
107
- "@remnic/import-supermemory": "9.69.45",
108
- "@remnic/import-mem0": "9.69.45",
109
- "@remnic/connector-limitless": "9.69.45",
110
- "@remnic/connector-bee": "9.69.45",
111
- "@remnic/connector-omi": "9.69.45",
112
- "@remnic/import-lossless-claw": "9.69.45"
100
+ "@remnic/bench": "9.69.47",
101
+ "@remnic/plugin-openclaw": "9.69.47",
102
+ "@remnic/export-weclone": "9.69.47",
103
+ "@remnic/import-weclone": "9.69.47",
104
+ "@remnic/import-claude": "9.69.47",
105
+ "@remnic/import-chatgpt": "9.69.47",
106
+ "@remnic/import-gemini": "9.69.47",
107
+ "@remnic/import-lossless-claw": "9.69.47",
108
+ "@remnic/import-mem0": "9.69.47",
109
+ "@remnic/connector-limitless": "9.69.47",
110
+ "@remnic/connector-bee": "9.69.47",
111
+ "@remnic/import-supermemory": "9.69.47",
112
+ "@remnic/connector-omi": "9.69.47"
113
113
  },
114
114
  "license": "MIT",
115
115
  "repository": {