@remnic/cli 9.69.32 → 9.69.34

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/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  isOpenaiApiKeyDisabled,
32
32
  resolveEnvVars,
33
33
  resolveRemnicConfigRecord as resolveRemnicConfigRecord16,
34
- Orchestrator as Orchestrator10,
34
+ Orchestrator as Orchestrator11,
35
35
  EngramAccessService as EngramAccessService2,
36
36
  initLogger as initLogger5,
37
37
  onboard,
@@ -105,7 +105,7 @@ import {
105
105
  discoverMemoryExtensions,
106
106
  resolveExtensionsRoot,
107
107
  coerceInstallExtension,
108
- StorageManager as StorageManager3,
108
+ StorageManager as StorageManager4,
109
109
  parseXrayCliOptions,
110
110
  renderXray,
111
111
  extractWhoKnowsRawArgs,
@@ -204,10 +204,46 @@ async function runMeetingsBinaryCommand(rest) {
204
204
  // src/commands/timeline.ts
205
205
  import fs2 from "fs";
206
206
  import {
207
+ ActivityStore,
208
+ activityDateInTimezone,
209
+ listPersistedTimelineDates,
207
210
  parseConfig as parseConfig2,
211
+ regenerateTimelineDay,
208
212
  resolveRemnicConfigRecord as resolveRemnicConfigRecord2,
213
+ resolveTimelineLoadDates,
209
214
  runTimelineCliCommand
210
215
  } from "@remnic/core";
216
+ async function loadProductionTimelineCards(config, window, now = () => /* @__PURE__ */ new Date()) {
217
+ const timeline = config.activity.timeline;
218
+ if (!timeline.enabled) return null;
219
+ const timezone = config.activity.timezone;
220
+ const store = ActivityStore.open(config.memoryDir);
221
+ try {
222
+ const dates = resolveTimelineLoadDates({
223
+ window,
224
+ timezone,
225
+ today: activityDateInTimezone(now(), timezone),
226
+ store,
227
+ persistedDates: listPersistedTimelineDates(config.memoryDir)
228
+ });
229
+ const cards = [];
230
+ for (const date of dates) {
231
+ const result = await regenerateTimelineDay({
232
+ date,
233
+ timezone,
234
+ memoryDir: config.memoryDir,
235
+ store,
236
+ timelineEnabled: true,
237
+ analysis: timeline.analysis,
238
+ pluginConfig: config
239
+ });
240
+ cards.push(...result.cards);
241
+ }
242
+ return cards;
243
+ } finally {
244
+ store.close();
245
+ }
246
+ }
211
247
  async function runTimelineBinaryCommand(rest) {
212
248
  const timelineArgs = rest.length === 0 || rest[0] === "--help" || rest[0] === "-h" ? ["help"] : rest;
213
249
  try {
@@ -228,7 +264,13 @@ async function runTimelineBinaryCommand(rest) {
228
264
  return;
229
265
  }
230
266
  const code = await runTimelineCliCommand(
231
- { cards: null, qa, timelineEnabled, config },
267
+ {
268
+ cards: null,
269
+ qa,
270
+ timelineEnabled,
271
+ config,
272
+ loadCards: (window) => loadProductionTimelineCards(config, window)
273
+ },
232
274
  timelineArgs,
233
275
  { stdout: process.stdout, stderr: process.stderr }
234
276
  );
@@ -548,12 +590,44 @@ async function runStandupBinaryCommand(rest) {
548
590
  // src/commands/journal.ts
549
591
  import fs9 from "fs";
550
592
  import {
593
+ ExtractionEngine,
594
+ Orchestrator as Orchestrator7,
595
+ activityDateInTimezone as activityDateInTimezone2,
596
+ commitJournalHash,
597
+ createJournalMemoryWriter,
598
+ hashJournalText,
551
599
  journalPath,
600
+ journalUnchanged,
552
601
  parseConfig as parseConfig9,
602
+ readJournalForDate,
603
+ readTimelineState,
553
604
  resolveRemnicConfigRecord as resolveRemnicConfigRecord9,
605
+ runJournalReviewExtraction,
554
606
  seedJournal,
555
- todayJournalDate
607
+ withJournalDateLock
556
608
  } from "@remnic/core";
609
+ function defaultDeps(config, storage) {
610
+ return {
611
+ readJournal: readJournalForDate,
612
+ async extractionDeps() {
613
+ if (!storage) {
614
+ throw new Error(
615
+ "journal extract requires a resolved StorageManager from the orchestrator; constructing StorageManager(memoryDir) is not permitted"
616
+ );
617
+ }
618
+ await storage.ensureDirectories();
619
+ const engine = new ExtractionEngine(config, void 0, void 0, config.gatewayConfig);
620
+ return {
621
+ extract: (turns) => engine.extract(turns),
622
+ writer: createJournalMemoryWriter(storage)
623
+ };
624
+ },
625
+ now: () => /* @__PURE__ */ new Date()
626
+ };
627
+ }
628
+ function createJournalCommandDeps(config, storage) {
629
+ return defaultDeps(config, storage);
630
+ }
557
631
  function takeFlag4(rest, name) {
558
632
  const index = rest.indexOf(name);
559
633
  if (index < 0) return void 0;
@@ -562,26 +636,156 @@ function takeFlag4(rest, name) {
562
636
  return value;
563
637
  }
564
638
  function journalHelp() {
565
- return `Usage: remnic journal <show|edit-path|seed> [--date YYYY-MM-DD] [--force]
639
+ return `Usage: remnic journal <show|edit-path|seed|extract> [--date YYYY-MM-DD] [--force]
566
640
 
567
- show Print the journal file for the date (default: today).
568
- edit-path Print the journal file path.
569
- seed Write the file only if it is absent. --force overwrites.
641
+ show Print the journal for the date (default: today in
642
+ activity.timezone). Vault mode prints a provenance header
643
+ naming the vault note first.
644
+ edit-path Print the journal file path (vault mode: the vault note path).
645
+ seed Write the file only if it is absent (memoryDir mode only).
646
+ extract Run the review-only extraction pass (requires
647
+ activity.timeline.journal.extractionMode "review").
648
+ Candidates land pending_review only \u2014 never auto-approved;
649
+ an unchanged day is hash-skipped.
570
650
  `;
571
651
  }
572
- function loadMemoryDir() {
573
- const configPath = resolveConfigPath();
574
- const raw = fs9.existsSync(configPath) ? JSON.parse(fs9.readFileSync(configPath, "utf8")) : {};
575
- return parseConfig9(resolveRemnicConfigRecord9(raw)).memoryDir;
652
+ async function runJournalCommand(config, rest, io, deps = defaultDeps(config)) {
653
+ if (rest.length === 0 || rest[0] === "--help" || rest[0] === "-h" || rest[0] === "help") {
654
+ io.out(journalHelp().trimEnd());
655
+ return 0;
656
+ }
657
+ const action = rest[0];
658
+ const journalConfig = config.activity.timeline.journal;
659
+ if (!journalConfig.enabled) {
660
+ io.err("journal: timeline.journal.enabled is false \u2014 enable the journal first.");
661
+ return 1;
662
+ }
663
+ const date = takeFlag4(rest, "--date") ?? activityDateInTimezone2(deps.now(), config.activity.timezone);
664
+ const force = rest.includes("--force");
665
+ const vaultMode = journalConfig.source === "vault";
666
+ if (action === "edit-path") {
667
+ if (vaultMode) {
668
+ const read = deps.readJournal({
669
+ vault: config.activity.timeline.vault,
670
+ date,
671
+ timezone: config.activity.timezone
672
+ });
673
+ io.out(read.filePath);
674
+ return 0;
675
+ }
676
+ io.out(journalPath(config.memoryDir, date));
677
+ return 0;
678
+ }
679
+ if (action === "show") {
680
+ if (vaultMode) {
681
+ const read = deps.readJournal({
682
+ vault: config.activity.timeline.vault,
683
+ date,
684
+ timezone: config.activity.timezone
685
+ });
686
+ if (!read.ok) {
687
+ io.err(`journal: cannot read the vault note (${read.reason}): ${read.filePath}`);
688
+ return 1;
689
+ }
690
+ if (!read.exists) {
691
+ io.out(`exists:false (${read.reason})`);
692
+ return 0;
693
+ }
694
+ io.out(`# journal source: ${read.filePath} :: ${read.heading}`);
695
+ io.out(read.text);
696
+ return 0;
697
+ }
698
+ const filePath = journalPath(config.memoryDir, date);
699
+ if (!fs9.existsSync(filePath)) {
700
+ io.err(`journal: no file at ${filePath}. Run remnic journal seed --date ${date}.`);
701
+ return 1;
702
+ }
703
+ io.out(fs9.readFileSync(filePath, "utf8").trimEnd());
704
+ return 0;
705
+ }
706
+ if (action === "seed") {
707
+ if (vaultMode) {
708
+ io.err(
709
+ 'journal: seed is not available when activity.timeline.journal.source is "vault" \u2014 the vault daily note owns the journal section and Remnic never writes to it. Create the note/section in your vault (or your vault note template) instead.'
710
+ );
711
+ return 1;
712
+ }
713
+ const result = seedJournal({ memoryDir: config.memoryDir, date, force });
714
+ io.out(result.wrote ? `wrote ${result.path}` : `unchanged ${result.path}`);
715
+ return 0;
716
+ }
717
+ if (action === "extract") {
718
+ if (journalConfig.extractionMode !== "review") {
719
+ io.err(
720
+ `journal: extract requires activity.timeline.journal.extractionMode "review" (currently "${journalConfig.extractionMode}") \u2014 extraction is opt-in and review-only by design.`
721
+ );
722
+ return 1;
723
+ }
724
+ let text;
725
+ if (vaultMode) {
726
+ const read = deps.readJournal({
727
+ vault: config.activity.timeline.vault,
728
+ date,
729
+ timezone: config.activity.timezone
730
+ });
731
+ if (!read.ok) {
732
+ io.err(`journal: cannot read the vault note (${read.reason}): ${read.filePath}`);
733
+ return 1;
734
+ }
735
+ if (!read.exists) {
736
+ io.out(`no journal ${date} (${read.reason})`);
737
+ return 0;
738
+ }
739
+ text = read.text;
740
+ } else {
741
+ const filePath = journalPath(config.memoryDir, date);
742
+ if (!fs9.existsSync(filePath)) {
743
+ io.out(`no journal ${date} (missing_file)`);
744
+ return 0;
745
+ }
746
+ text = fs9.readFileSync(filePath, "utf8");
747
+ }
748
+ return withJournalDateLock(config.memoryDir, date, async () => {
749
+ const state = readTimelineState(config.memoryDir);
750
+ if (journalUnchanged(state, date, text)) {
751
+ io.out(`unchanged ${date} (hash-skip)`);
752
+ return 0;
753
+ }
754
+ const result = await runJournalReviewExtraction({
755
+ date,
756
+ journalText: text,
757
+ source: vaultMode ? "vault" : "memoryDir",
758
+ journalConfig,
759
+ deps: await deps.extractionDeps()
760
+ });
761
+ if (!result.completed) {
762
+ io.err(`journal: extraction did not complete for ${date} \u2014 the day retries on the next run.`);
763
+ return 1;
764
+ }
765
+ await commitJournalHash(config.memoryDir, date, hashJournalText(text));
766
+ io.out(`pending_review: ${result.pendingReview}`);
767
+ io.out(`rejected_by_judge: ${result.rejectedByJudge}`);
768
+ io.out(`skipped: ${result.skipped}`);
769
+ for (const warning of result.warnings) {
770
+ io.err(`journal: ${warning}`);
771
+ }
772
+ return 0;
773
+ });
774
+ }
775
+ io.err(`journal: unknown action "${action}".`);
776
+ io.err(journalHelp().trimEnd());
777
+ return 1;
576
778
  }
577
779
  async function runJournalBinaryCommand(rest) {
578
780
  if (rest.length === 0 || rest[0] === "--help" || rest[0] === "-h" || rest[0] === "help") {
579
- console.log(journalHelp());
781
+ console.log(journalHelp().trimEnd());
580
782
  return;
581
783
  }
582
- let memoryDir;
784
+ let config;
583
785
  try {
584
- memoryDir = loadMemoryDir();
786
+ const configPath = resolveConfigPath();
787
+ const raw = fs9.existsSync(configPath) ? JSON.parse(fs9.readFileSync(configPath, "utf8")) : {};
788
+ config = parseConfig9(resolveRemnicConfigRecord9(raw));
585
789
  } catch {
586
790
  console.error(
587
791
  "journal: failed to load the Remnic config \u2014 run `remnic doctor` and check the config file for errors"
@@ -589,35 +793,32 @@ async function runJournalBinaryCommand(rest) {
589
793
  process.exitCode = 1;
590
794
  return;
591
795
  }
796
+ const action = rest[0];
797
+ let orchestrator;
592
798
  try {
593
- const action = rest[0];
594
- const date = takeFlag4(rest, "--date") ?? todayJournalDate();
595
- const force = rest.includes("--force");
596
- const filePath = journalPath(memoryDir, date);
597
- if (action === "edit-path") {
598
- console.log(filePath);
599
- return;
600
- }
601
- if (action === "show") {
602
- if (!fs9.existsSync(filePath)) {
603
- console.error(`journal: no file at ${filePath}. Run remnic journal seed --date ${date}.`);
604
- process.exitCode = 1;
605
- return;
606
- }
607
- process.stdout.write(fs9.readFileSync(filePath, "utf8"));
608
- return;
609
- }
610
- if (action === "seed") {
611
- const result = seedJournal({ memoryDir, date, force });
612
- console.log(result.wrote ? `wrote ${result.path}` : `unchanged ${result.path}`);
613
- return;
799
+ let deps;
800
+ if (action === "extract") {
801
+ orchestrator = new Orchestrator7(config);
802
+ await orchestrator.initialize();
803
+ await orchestrator.deferredReady;
804
+ const storage = await orchestrator.getStorageForNamespace(config.defaultNamespace);
805
+ deps = createJournalCommandDeps(config, storage);
614
806
  }
615
- console.error(`journal: unknown action "${action}".`);
616
- console.error(journalHelp());
617
- process.exitCode = 1;
807
+ const code = await runJournalCommand(
808
+ config,
809
+ rest,
810
+ {
811
+ out: (line) => console.log(line),
812
+ err: (line) => console.error(line)
813
+ },
814
+ deps ?? defaultDeps(config)
815
+ );
816
+ if (code !== 0) process.exitCode = code;
618
817
  } catch (err) {
619
818
  console.error(err instanceof Error ? err.message : String(err));
620
819
  process.exitCode = 1;
820
+ } finally {
821
+ await orchestrator?.destroy();
621
822
  }
622
823
  }
623
824
 
@@ -990,7 +1191,7 @@ async function runExternalWikiBinaryCommand(rest) {
990
1191
  // src/commands/procedural.ts
991
1192
  import fs13 from "fs";
992
1193
  import {
993
- StorageManager,
1194
+ StorageManager as StorageManager2,
994
1195
  computeProcedureStats,
995
1196
  formatProcedureStatsText,
996
1197
  initLogger,
@@ -1075,7 +1276,7 @@ Shared with:
1075
1276
  const memoryDir = expandTilde(
1076
1277
  typeof memoryDirOverride === "string" && memoryDirOverride.length > 0 ? memoryDirOverride : config.memoryDir ?? resolveMemoryDir()
1077
1278
  );
1078
- const storage = new StorageManager(memoryDir);
1279
+ const storage = new StorageManager2(memoryDir);
1079
1280
  if (subcommand === "maintain") {
1080
1281
  const report2 = await runProcedureLibraryMaintenance({
1081
1282
  memoryDir,
@@ -1128,7 +1329,7 @@ function formatProcedureMaintenanceText(report) {
1128
1329
  // src/commands/drift.ts
1129
1330
  import fs14 from "fs";
1130
1331
  import {
1131
- Orchestrator as Orchestrator7,
1332
+ Orchestrator as Orchestrator8,
1132
1333
  initLogger as initLogger2,
1133
1334
  parseConfig as parseConfig12,
1134
1335
  resolveRemnicConfigRecord as resolveRemnicConfigRecord12,
@@ -1197,7 +1398,7 @@ Resolve a drifted item with the existing review surface:
1197
1398
  const memoryDir = expandTilde(
1198
1399
  memoryDirOverridden ? memoryDirOverride : config.memoryDir ?? resolveMemoryDir()
1199
1400
  );
1200
- const orchestrator = new Orchestrator7(
1401
+ const orchestrator = new Orchestrator8(
1201
1402
  memoryDirOverridden ? { ...config, memoryDir } : config
1202
1403
  );
1203
1404
  await orchestrator.initialize();
@@ -1403,418 +1604,205 @@ async function loadWecloneExportModule() {
1403
1604
  }
1404
1605
 
1405
1606
  // src/converge.ts
1406
- import * as fs17 from "fs";
1407
1607
  import { createHash as createHash3 } from "crypto";
1408
- import * as path2 from "path";
1608
+ import * as fs18 from "fs";
1609
+ import * as path3 from "path";
1409
1610
  import {
1410
1611
  CONVERGE_CONFLICT_POLICIES,
1411
1612
  DEFAULT_CONVERGE_CONFLICT_POLICY,
1412
- parseConfig as parseConfig13,
1413
- envConvergePeerRequestTimeoutMs as envConvergePeerRequestTimeoutMs2,
1414
- normalizeConvergePeerRequestTimeoutMs,
1415
- buildOfflineSyncSnapshotFromBase,
1613
+ OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES3,
1416
1614
  applyOfflineSyncFileContentChunk,
1615
+ buildOfflineSyncSnapshotFromBase,
1616
+ envConvergePeerRequestTimeoutMs as envConvergePeerRequestTimeoutMs2,
1417
1617
  isInternalRemnicStatePath as isInternalRemnicStatePath3,
1418
- OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES3
1618
+ normalizeConvergePeerRequestTimeoutMs,
1619
+ parseConfig as parseConfig13
1419
1620
  } from "@remnic/core";
1420
- import { parseFrontmatter } from "@remnic/core/storage.js";
1421
1621
  import { resolveCorpusNamespaceRoots } from "@remnic/core/corpus-watermark.js";
1422
1622
  import { listNamespaces } from "@remnic/core/namespaces/migrate.js";
1423
1623
  import {
1424
- planReconciliation
1425
- } from "@remnic/core/reconcile/plan.js";
1426
- import {
1624
+ convergeIdentityCachePath,
1427
1625
  defaultConvergeCursorPath,
1428
1626
  deriveConvergeCursorBase,
1627
+ normalizeConvergePeerUrl as normalizeConvergePeerUrl2,
1429
1628
  readConvergeCursor,
1430
- writeConvergeCursor,
1431
- normalizeConvergePeerUrl as normalizeConvergePeerUrl2
1629
+ writeConvergeCursor
1432
1630
  } from "@remnic/core/reconcile/cursor.js";
1433
1631
  import {
1434
1632
  buildReconcileManifest,
1435
1633
  collapseActiveFactDuplicates
1436
1634
  } from "@remnic/core/reconcile/manifest.js";
1437
-
1438
- // src/offline-storage-io.ts
1439
- import { createDecipheriv, createHash } from "crypto";
1440
- import fs15 from "fs";
1441
- import { lstat, mkdtemp, readdir, rm } from "fs/promises";
1442
- import path from "path";
1443
1635
  import {
1444
- OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES,
1445
- StorageManager as StorageManager2,
1446
- createSupportPassportPrivateFileExclusion
1447
- } from "@remnic/core";
1448
- import { OFFLINE_DECRYPT_STAGING_DIR_PREFIX } from "@remnic/core/offline-sync-exclude-globs";
1636
+ planReconciliation
1637
+ } from "@remnic/core/reconcile/plan.js";
1638
+ import { resolveAgentAccessAuthToken } from "@remnic/core/resolve-auth-token.js";
1639
+ import { parseFrontmatter } from "@remnic/core/storage.js";
1640
+
1641
+ // src/converge-identity-cache.ts
1642
+ import * as fs15 from "fs";
1643
+ import * as path from "path";
1449
1644
  import {
1450
- AUTH_TAG_LENGTH,
1451
- ENVELOPE_HEADER_SIZE,
1452
- ENVELOPE_LAYOUT,
1453
- ENVELOPE_SALT_LENGTH,
1454
- ENVELOPE_VERSION,
1455
- FILE_FORMAT_FLAGS,
1456
- FILE_FORMAT_VERSION,
1457
- IV_LENGTH,
1458
- MAGIC_BYTES,
1459
- MAGIC_HEADER_SIZE,
1460
- SecureStoreLockedError,
1461
- filePathAad,
1462
- isEncryptedFile,
1463
- keyring,
1464
- readHeader,
1465
- secureStoreDir
1466
- } from "@remnic/core/secure-store";
1467
- var OFFLINE_SYNC_EXCLUSION_CONCURRENCY = 16;
1468
- function resolveOfflineDirectHydrationPath(memoryDir, relPath) {
1469
- const base = path.resolve(memoryDir);
1470
- const target = path.resolve(base, relPath);
1471
- const relative = path.relative(base, target);
1472
- if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
1473
- throw new Error(`offline sync direct hydration path escapes memory dir: ${relPath}`);
1474
- }
1475
- return target;
1476
- }
1477
- async function filterOfflineSyncBaseFiles(memoryDir, files, excludeFile) {
1478
- const excluded = new Array(files.length);
1479
- for (let offset = 0; offset < files.length; offset += OFFLINE_SYNC_EXCLUSION_CONCURRENCY) {
1480
- await Promise.all(
1481
- files.slice(offset, offset + OFFLINE_SYNC_EXCLUSION_CONCURRENCY).map(async (file, index) => {
1482
- const filePath = resolveOfflineDirectHydrationPath(memoryDir, file.path);
1483
- excluded[offset + index] = await excludeFile({ root: memoryDir, path: file.path, filePath });
1484
- })
1485
- );
1645
+ citationTemplateFingerprint,
1646
+ isReconcileMemoryIdentity
1647
+ } from "@remnic/core/reconcile/manifest.js";
1648
+ var cacheWriteLocks = /* @__PURE__ */ new Map();
1649
+ async function withCacheWriteLock(cachePath, write) {
1650
+ const previous = cacheWriteLocks.get(cachePath) ?? Promise.resolve();
1651
+ const next = previous.then(write, write);
1652
+ cacheWriteLocks.set(cachePath, next);
1653
+ try {
1654
+ await next;
1655
+ } finally {
1656
+ if (cacheWriteLocks.get(cachePath) === next) cacheWriteLocks.delete(cachePath);
1486
1657
  }
1487
- return files.filter((_file, index) => excluded[index] === false);
1488
1658
  }
1489
- async function createConfiguredOfflineStorage(memoryDir, secureStoreEncryptOnWrite = true) {
1490
- const storage = new StorageManager2(memoryDir);
1491
- const header = await readHeader(memoryDir);
1492
- let secureStoreKey = null;
1493
- let secureStoreRequired = false;
1494
- if (header) {
1495
- secureStoreRequired = true;
1496
- storage.setSecureStoreRequired(true);
1497
- const key = keyring.getKey(secureStoreDir(memoryDir));
1498
- if (key) {
1499
- await storage.setSecureStoreKeyAndWait(key, secureStoreEncryptOnWrite);
1500
- secureStoreKey = key;
1659
+ async function loadConvergeIdentityCache(cachePath, citationTemplate) {
1660
+ const cache = /* @__PURE__ */ new Map();
1661
+ if (!cachePath) return cache;
1662
+ try {
1663
+ const raw = JSON.parse(await fs15.promises.readFile(cachePath, "utf8"));
1664
+ if (raw.citationTemplate !== citationTemplateFingerprint(citationTemplate)) return cache;
1665
+ for (const candidate of raw.files ?? []) {
1666
+ const entry = candidate;
1667
+ if (typeof entry?.path !== "string" || typeof entry.sha256 !== "string") continue;
1668
+ if (entry.memory !== void 0 && !isReconcileMemoryIdentity(entry.memory)) continue;
1669
+ if (entry.statIdentity !== void 0 && typeof entry.statIdentity !== "string") continue;
1670
+ if (entry.excluded !== void 0 && typeof entry.excluded !== "boolean") continue;
1671
+ if (entry.normalizerVersion !== void 0 && typeof entry.normalizerVersion !== "number") continue;
1672
+ if (entry.identityResolutionVersion !== void 0 && typeof entry.identityResolutionVersion !== "number") {
1673
+ continue;
1674
+ }
1675
+ cache.set(entry.path, {
1676
+ path: entry.path,
1677
+ sha256: entry.sha256,
1678
+ ...entry.memory ? { memory: entry.memory } : {},
1679
+ ...entry.memory ? {} : {
1680
+ ...typeof entry.normalizerVersion === "number" ? { normalizerVersion: entry.normalizerVersion } : {},
1681
+ ...typeof entry.identityResolutionVersion === "number" ? { identityResolutionVersion: entry.identityResolutionVersion } : {}
1682
+ },
1683
+ ...entry.statIdentity !== void 0 && entry.excluded !== void 0 ? { statIdentity: entry.statIdentity, excluded: entry.excluded } : {}
1684
+ });
1501
1685
  }
1686
+ } catch {
1502
1687
  }
1503
- return { storage, secureStoreKey, secureStoreRequired };
1688
+ return cache;
1504
1689
  }
1505
- async function createOfflineStorageForPath(memoryDir, filePath, configured, secureStoreEncryptOnWrite) {
1506
- const memoryRoot = path.resolve(memoryDir);
1507
- const stateDir = path.dirname(filePath);
1508
- if (path.basename(stateDir) !== "state" || path.basename(filePath) !== "memory-lifecycle-ledger.jsonl") {
1509
- throw new Error(`invalid lifecycle ledger path: ${filePath}`);
1510
- }
1511
- const storageRoot = path.resolve(path.dirname(stateDir));
1512
- if (storageRoot !== memoryRoot && !storageRoot.startsWith(`${memoryRoot}${path.sep}`)) {
1513
- throw new Error(`lifecycle ledger path is outside the offline memory directory: ${filePath}`);
1514
- }
1515
- const storage = new StorageManager2(storageRoot);
1516
- if (configured.secureStoreRequired) {
1517
- storage.setSecureStoreRequired(true);
1518
- }
1519
- if (configured.secureStoreKey) {
1520
- await storage.setSecureStoreKeyAndWait(configured.secureStoreKey, secureStoreEncryptOnWrite);
1690
+ async function saveConvergeIdentityCache(cachePath, manifest, citationTemplate, loaded, classifications) {
1691
+ if (!cachePath) return;
1692
+ const files = manifest.files.map((file) => ({
1693
+ path: file.path,
1694
+ sha256: file.sha256,
1695
+ ...file.memory !== void 0 ? { memory: file.memory } : file.normalizerVersion !== void 0 && file.identityResolutionVersion !== void 0 ? {
1696
+ normalizerVersion: file.normalizerVersion,
1697
+ identityResolutionVersion: file.identityResolutionVersion
1698
+ } : {}
1699
+ }));
1700
+ for (const entry of files) {
1701
+ const classification = classifications?.get(entry.path);
1702
+ if (classification === void 0) continue;
1703
+ entry.statIdentity = classification.statIdentity;
1704
+ entry.excluded = classification.excluded;
1705
+ }
1706
+ if (loaded !== void 0 && loaded.size === files.length && files.every((entry) => {
1707
+ const previous = loaded.get(entry.path);
1708
+ if (previous === void 0) return false;
1709
+ if (previous.sha256 !== entry.sha256) return false;
1710
+ if (previous.memory !== entry.memory) return false;
1711
+ if ((previous.normalizerVersion ?? void 0) !== (entry.normalizerVersion ?? void 0)) return false;
1712
+ if ((previous.identityResolutionVersion ?? void 0) !== (entry.identityResolutionVersion ?? void 0)) {
1713
+ return false;
1714
+ }
1715
+ if ((previous.statIdentity ?? void 0) !== (entry.statIdentity ?? void 0)) return false;
1716
+ return (previous.excluded ?? void 0) === (entry.excluded ?? void 0);
1717
+ })) {
1718
+ return;
1521
1719
  }
1522
- return storage;
1523
- }
1524
- async function createOfflineStorageIo(memoryDir, configuredStorage) {
1525
- await cleanupOrphanedOfflineDecryptStaging(memoryDir);
1526
- const { storage, secureStoreKey } = configuredStorage ?? await createConfiguredOfflineStorage(memoryDir);
1527
- return {
1528
- excludeFile: createSupportPassportPrivateFileExclusion(storage),
1529
- readFile: async ({ filePath }) => storage.readOfflineSyncFile(filePath),
1530
- readDeletionRevisions: () => storage.readDeletionRevisions(),
1531
- readFileDigest: async ({ filePath }) => {
1532
- const hash = createHash("sha256");
1533
- let bytes = 0;
1534
- for await (const rawChunk of readOfflineSyncFileChunks({
1535
- filePath,
1536
- memoryDir,
1537
- secureStoreKey,
1538
- chunkSize: OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES
1539
- })) {
1540
- const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
1541
- hash.update(chunk);
1542
- bytes += chunk.length;
1543
- }
1544
- return {
1545
- sha256: hash.digest("hex"),
1546
- bytes
1547
- };
1548
- },
1549
- readFileChunks: ({ filePath, chunkSize }) => readOfflineSyncFileChunks({
1550
- filePath,
1551
- memoryDir,
1552
- secureStoreKey,
1553
- chunkSize
1554
- }),
1555
- writeFile: async ({ filePath, content }) => storage.writeOfflineSyncFile(filePath, content),
1556
- writeStagingFile: async ({ filePath, content }) => storage.writeOfflineSyncStagingFile(filePath, content),
1557
- writeFileChunks: async ({ filePath, chunks }) => storage.writeOfflineSyncFileChunks(filePath, chunks),
1558
- deleteFile: async ({ filePath, mtimeMs }) => storage.deleteOfflineSyncFile(filePath, mtimeMs ?? null),
1559
- recordDeletionRevision: async ({ filePath, mtimeMs }) => storage.recordReplicatedDeletionRevision(filePath, mtimeMs)
1560
- };
1561
- }
1562
- var OFFLINE_DECRYPT_STAGING_ORPHAN_MS = 60 * 60 * 1e3;
1563
- async function cleanupOrphanedOfflineDecryptStaging(memoryDir) {
1564
- let entries;
1565
1720
  try {
1566
- entries = await readdir(memoryDir);
1721
+ await withCacheWriteLock(cachePath, async () => {
1722
+ const merged = new Map(files.map((entry) => [entry.path, entry]));
1723
+ const dropped = new Set(
1724
+ [...loaded?.keys() ?? []].filter((pathName) => !merged.has(pathName))
1725
+ );
1726
+ try {
1727
+ const raw = JSON.parse(await fs15.promises.readFile(cachePath, "utf8"));
1728
+ if (raw.citationTemplate === citationTemplateFingerprint(citationTemplate)) {
1729
+ for (const candidate of raw.files ?? []) {
1730
+ const entry = candidate;
1731
+ if (typeof entry?.path !== "string" || typeof entry.sha256 !== "string") continue;
1732
+ if (entry.memory !== void 0 && !isReconcileMemoryIdentity(entry.memory)) continue;
1733
+ if (dropped.has(entry.path) || merged.has(entry.path)) continue;
1734
+ merged.set(entry.path, {
1735
+ path: entry.path,
1736
+ sha256: entry.sha256,
1737
+ ...entry.memory ? { memory: entry.memory } : {},
1738
+ ...entry.memory ? {} : {
1739
+ ...typeof entry.normalizerVersion === "number" ? { normalizerVersion: entry.normalizerVersion } : {},
1740
+ ...typeof entry.identityResolutionVersion === "number" ? { identityResolutionVersion: entry.identityResolutionVersion } : {}
1741
+ }
1742
+ });
1743
+ }
1744
+ }
1745
+ } catch {
1746
+ }
1747
+ const tmp = `${cachePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
1748
+ await fs15.promises.mkdir(path.dirname(cachePath), { recursive: true });
1749
+ await fs15.promises.writeFile(
1750
+ tmp,
1751
+ JSON.stringify({
1752
+ citationTemplate: citationTemplateFingerprint(citationTemplate),
1753
+ files: [...merged.values()]
1754
+ })
1755
+ );
1756
+ await fs15.promises.rename(tmp, cachePath);
1757
+ });
1567
1758
  } catch {
1568
- return;
1569
- }
1570
- const now = Date.now();
1571
- for (const name of entries) {
1572
- if (!name.startsWith(OFFLINE_DECRYPT_STAGING_DIR_PREFIX)) continue;
1573
- const dir = path.join(memoryDir, name);
1574
- try {
1575
- const info = await lstat(dir);
1576
- if (!info.isDirectory() || info.isSymbolicLink()) continue;
1577
- if (now - info.mtimeMs < OFFLINE_DECRYPT_STAGING_ORPHAN_MS) continue;
1578
- await rm(dir, { recursive: true, force: true });
1579
- } catch {
1580
- }
1581
1759
  }
1582
1760
  }
1583
- async function* readOfflineSyncFileChunks(options) {
1584
- const header = await readFilePrefix(options.filePath, MAGIC_HEADER_SIZE);
1585
- if (!isEncryptedFile(header)) {
1586
- yield* readPlainOfflineFileChunks(options.filePath, options.chunkSize);
1587
- return;
1588
- }
1589
- if (!options.secureStoreKey) {
1590
- throw new SecureStoreLockedError(
1591
- `secure-store is locked \u2014 cannot read encrypted file at ${options.filePath}. Run \`remnic secure-store unlock\` to decrypt.`
1592
- );
1593
- }
1594
- yield* readEncryptedOfflineFileChunks({
1595
- filePath: options.filePath,
1596
- memoryDir: options.memoryDir,
1597
- key: options.secureStoreKey,
1598
- chunkSize: options.chunkSize
1599
- });
1600
- }
1601
- async function readFilePrefix(filePath, length) {
1602
- const handle = await fs15.promises.open(filePath, "r");
1603
- try {
1604
- const out = Buffer.alloc(length);
1605
- const { bytesRead } = await handle.read(out, 0, length, 0);
1606
- return out.subarray(0, bytesRead);
1607
- } finally {
1608
- await handle.close();
1609
- }
1610
- }
1611
- async function* readPlainOfflineFileChunks(filePath, chunkSize) {
1612
- const stream = fs15.createReadStream(filePath, { highWaterMark: chunkSize });
1613
- for await (const chunk of stream) {
1614
- yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1615
- }
1616
- }
1617
- async function* readEncryptedOfflineFileChunks(options) {
1618
- const header = await readFilePrefix(options.filePath, MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);
1619
- if (header.length < MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE || !isEncryptedFile(header)) {
1620
- throw new Error(`secure-store encrypted file is truncated: ${options.filePath}`);
1621
- }
1622
- const version = header.readUInt8(MAGIC_BYTES.length);
1623
- const flags = header.readUInt8(MAGIC_BYTES.length + 1);
1624
- if (version !== FILE_FORMAT_VERSION) {
1625
- throw new Error(`secure-store file has unsupported version ${version}: ${options.filePath}`);
1626
- }
1627
- if (flags !== FILE_FORMAT_FLAGS) {
1628
- throw new Error(`secure-store file has unsupported flags 0x${flags.toString(16)}: ${options.filePath}`);
1629
- }
1630
- const envelopeHeader = header.subarray(MAGIC_HEADER_SIZE);
1631
- const envelopeVersion = envelopeHeader.readUInt8(ENVELOPE_LAYOUT.version);
1632
- if (envelopeVersion !== ENVELOPE_VERSION) {
1633
- throw new Error(`secure-store envelope has unsupported version ${envelopeVersion}: ${options.filePath}`);
1634
- }
1635
- const salt = envelopeHeader.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);
1636
- const iv = envelopeHeader.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);
1637
- const authTag = envelopeHeader.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);
1638
- const aadCandidates = offlineFileAadCandidates(options.filePath, options.memoryDir);
1639
- let lastError;
1640
- for (const aad of aadCandidates) {
1641
- const tempDir = await mkdtemp(path.join(options.memoryDir, OFFLINE_DECRYPT_STAGING_DIR_PREFIX));
1642
- const tempPath = path.join(tempDir, "content");
1643
- try {
1644
- const decipher = createDecipheriv("aes-256-gcm", options.key, iv, {
1645
- authTagLength: AUTH_TAG_LENGTH
1646
- });
1647
- decipher.setAuthTag(authTag);
1648
- decipher.setAAD(Buffer.concat([secureStoreEnvelopeHeaderAad(salt), aad]));
1649
- const output = fs15.createWriteStream(tempPath, { mode: 384 });
1650
- try {
1651
- const stream = fs15.createReadStream(options.filePath, {
1652
- start: MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE,
1653
- highWaterMark: options.chunkSize
1654
- });
1655
- for await (const encryptedChunk of stream) {
1656
- const plain = decipher.update(Buffer.isBuffer(encryptedChunk) ? encryptedChunk : Buffer.from(encryptedChunk));
1657
- if (plain.length > 0 && !output.write(plain)) {
1658
- await new Promise((resolve2, reject) => {
1659
- output.once("drain", resolve2);
1660
- output.once("error", reject);
1661
- });
1662
- }
1663
- }
1664
- const finalPlain = decipher.final();
1665
- if (finalPlain.length > 0 && !output.write(finalPlain)) {
1666
- await new Promise((resolve2, reject) => {
1667
- output.once("drain", resolve2);
1668
- output.once("error", reject);
1669
- });
1670
- }
1671
- } finally {
1672
- await closeWriteStream(output);
1673
- }
1674
- yield* readPlainOfflineFileChunks(tempPath, options.chunkSize);
1675
- return;
1676
- } catch (error) {
1677
- lastError = error;
1678
- } finally {
1679
- await rm(tempDir, { recursive: true, force: true });
1680
- }
1681
- }
1682
- throw lastError instanceof Error ? lastError : new Error(`secure-store could not decrypt file: ${options.filePath}`);
1683
- }
1684
- function offlineFileAadCandidates(filePath, memoryDir) {
1685
- const candidates = [filePathAad(filePath, memoryDir)];
1686
- const relative = path.relative(memoryDir, filePath);
1687
- if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return candidates;
1688
- const parts = relative.split(path.sep);
1689
- if (parts[0] === "namespaces" && parts.length >= 3 && parts[1]) {
1690
- candidates.push(filePathAad(filePath, path.join(memoryDir, "namespaces", parts[1])));
1691
- }
1692
- const memoryParts = path.resolve(memoryDir).split(path.sep);
1693
- if (memoryParts.length >= 3 && memoryParts.at(-2) === "namespaces" && memoryParts.at(-1)) {
1694
- const topLevelRoot = memoryParts.slice(0, -2).join(path.sep) || path.sep;
1695
- const topRelative = path.relative(topLevelRoot, filePath);
1696
- if (topRelative && !topRelative.startsWith("..") && !path.isAbsolute(topRelative) && topRelative.split(path.sep)[0] === "namespaces" && topRelative.split(path.sep)[1] === memoryParts.at(-1)) {
1697
- candidates.push(filePathAad(filePath, topLevelRoot));
1698
- }
1699
- }
1700
- return candidates;
1701
- }
1702
- async function closeWriteStream(stream) {
1703
- await new Promise((resolve2, reject) => {
1704
- stream.once("error", reject);
1705
- stream.end(() => resolve2());
1706
- });
1707
- }
1708
- function secureStoreEnvelopeHeaderAad(salt) {
1709
- const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);
1710
- out.writeUInt8(ENVELOPE_VERSION, 0);
1711
- Buffer.from(salt).copy(out, 1);
1712
- return out;
1713
- }
1714
1761
 
1715
- // src/converge-watch.ts
1716
- var CONVERGE_WATCH_MIN_INTERVAL_MS = 1e3;
1717
- var CONVERGE_WATCH_DEFAULT_INTERVAL_MS = 3e5;
1718
- var CONVERGE_WATCH_MAX_INTERVAL_MS = 2147483647;
1719
- function sleepAborted(ms, signal) {
1720
- return new Promise((resolve2) => {
1721
- const timer = setTimeout(() => {
1722
- signal.removeEventListener("abort", onAbort);
1723
- resolve2(true);
1724
- }, ms);
1725
- const onAbort = () => {
1726
- clearTimeout(timer);
1727
- resolve2(false);
1728
- };
1729
- if (signal.aborted) {
1730
- clearTimeout(timer);
1731
- resolve2(false);
1732
- return;
1733
- }
1734
- signal.addEventListener("abort", onAbort, { once: true });
1735
- });
1736
- }
1737
- async function convergeWatch(options) {
1738
- const intervalMs = Math.min(
1739
- CONVERGE_WATCH_MAX_INTERVAL_MS,
1740
- Math.max(CONVERGE_WATCH_MIN_INTERVAL_MS, options.intervalMs ?? CONVERGE_WATCH_DEFAULT_INTERVAL_MS)
1741
- );
1742
- const { apply, intervalMs: _intervalMs, maxCycles, onCycle, signal, ...applyOptions } = options;
1743
- const outcome = {
1744
- cycles: 0,
1745
- convergedCycles: 0,
1746
- appliedCycles: 0,
1747
- failedCycles: 0,
1748
- lastStatus: "aborted"
1749
- };
1750
- while (maxCycles === void 0 || outcome.cycles < maxCycles) {
1751
- if (signal?.aborted) break;
1752
- try {
1753
- const result = await apply(applyOptions);
1754
- outcome.cycles += 1;
1755
- if (result.status === "converged") outcome.convergedCycles += 1;
1756
- else if (result.status === "stopped_unresolved_conflicts" || result.status === "applied" && result.transfers.failed > 0) {
1757
- outcome.failedCycles += 1;
1758
- } else outcome.appliedCycles += 1;
1759
- outcome.lastStatus = result.status;
1760
- onCycle?.(outcome.cycles, { result });
1761
- } catch (err) {
1762
- outcome.cycles += 1;
1763
- outcome.failedCycles += 1;
1764
- outcome.lastStatus = "error";
1765
- onCycle?.(outcome.cycles, { error: err });
1762
+ // src/converge-report.ts
1763
+ function formatConvergeReport(plan) {
1764
+ const lines = [];
1765
+ lines.push(`Convergence Status: ${plan.converged ? "CONVERGED" : "DIVERGED"}`);
1766
+ lines.push("");
1767
+ lines.push("Per-Namespace Summary:");
1768
+ if (plan.byNamespace.length === 0) {
1769
+ lines.push(" (no namespaces evaluated)");
1770
+ } else {
1771
+ for (const report of plan.byNamespace) {
1772
+ lines.push(` [${report.namespace}]`);
1773
+ lines.push(` identical: ${report.identical}`);
1774
+ lines.push(` pull: ${report.pull}`);
1775
+ lines.push(` push: ${report.push}`);
1776
+ lines.push(` conflict: ${report.conflict}`);
1777
+ lines.push(` suppress: ${report.suppress}`);
1778
+ lines.push(` unresolved: ${report.unresolved}`);
1766
1779
  }
1767
- if (maxCycles !== void 0 && outcome.cycles >= maxCycles) break;
1768
- const slept = await sleepAborted(intervalMs, signal ?? new AbortController().signal);
1769
- if (!slept) break;
1770
1780
  }
1771
- return outcome;
1772
- }
1773
-
1774
- // src/converge-token-channel.ts
1775
- import * as fs16 from "fs";
1776
- function parseConvergeTokenFileFlag(raw) {
1777
- return raw !== void 0 && raw.length > 0 ? raw : null;
1781
+ return lines.join("\n");
1778
1782
  }
1779
- function resolveConvergeTokenChannel(input, env) {
1780
- if (input.argvToken !== void 0 && input.argvToken.length === 0) {
1781
- return { ok: false, error: "--token requires a non-empty value" };
1782
- }
1783
- const tokenFromArgv = input.argvToken !== void 0;
1784
- if (!tokenFromArgv && input.tokenFile !== void 0) {
1785
- try {
1786
- const stat2 = fs16.statSync(input.tokenFile);
1787
- if (process.platform !== "win32" && stat2.mode & 63) {
1788
- return { ok: false, error: `--token-file ${input.tokenFile} must not be group- or world-readable (chmod 600)` };
1789
- }
1790
- const token = fs16.readFileSync(input.tokenFile, "utf8").trim();
1791
- if (token.length === 0) {
1792
- return { ok: false, error: `--token-file ${input.tokenFile} is empty` };
1793
- }
1794
- return { ok: true, token, tokenFromArgv };
1795
- } catch (err) {
1796
- return { ok: false, error: `--token-file ${input.tokenFile} could not be read: ${err}` };
1797
- }
1798
- }
1799
- if (!tokenFromArgv && input.tokenFile === void 0 && env.REMNIC_CONVERGE_PEER_TOKEN !== void 0) {
1800
- if (env.REMNIC_CONVERGE_PEER_TOKEN.length === 0) {
1801
- return { ok: false, error: "REMNIC_CONVERGE_PEER_TOKEN is set but empty" };
1802
- }
1803
- return { ok: true, token: env.REMNIC_CONVERGE_PEER_TOKEN, tokenFromArgv: false };
1804
- }
1805
- return { ok: true, token: input.argvToken, tokenFromArgv };
1783
+ function formatConvergeApplyReport(result) {
1784
+ const lines = [];
1785
+ lines.push(`Convergence Execution Status: ${result.status.toUpperCase()}`);
1786
+ lines.push(`Converged: ${result.converged ? "YES" : "NO"}`);
1787
+ lines.push("");
1788
+ lines.push("Transfers Executed:");
1789
+ lines.push(` pulled: ${result.transfers.pulled}`);
1790
+ lines.push(` pushed: ${result.transfers.pushed}`);
1791
+ lines.push(` conflictsResolved: ${result.transfers.conflictsResolved}`);
1792
+ lines.push(` suppressed: ${result.transfers.suppressed}`);
1793
+ lines.push(` failed: ${result.transfers.failed}`);
1794
+ lines.push("");
1795
+ lines.push(formatConvergeReport(result.plan));
1796
+ return lines.join("\n");
1806
1797
  }
1807
1798
 
1808
- // src/converge.ts
1809
- import { resolveAgentAccessAuthToken } from "@remnic/core/resolve-auth-token.js";
1810
-
1811
1799
  // src/converge-peer-transport.ts
1812
- import { createHash as createHash2 } from "crypto";
1800
+ import { createHash } from "crypto";
1813
1801
  import {
1814
1802
  isInternalRemnicStatePath as isInternalRemnicStatePath2,
1815
1803
  OFFLINE_SYNC_CHANGESET_FORMAT,
1816
1804
  OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
1817
- OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2
1805
+ OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES
1818
1806
  } from "@remnic/core";
1819
1807
  import { normalizeConvergePeerUrl } from "@remnic/core/reconcile/cursor.js";
1820
1808
 
@@ -2101,7 +2089,7 @@ async function streamPeerFileContent(peerUrl, namespace, filePath, onChunk, toke
2101
2089
  "content-type": "application/json",
2102
2090
  ...token ? { authorization: `Bearer ${token}` } : {}
2103
2091
  };
2104
- const hash = createHash2("sha256");
2092
+ const hash = createHash("sha256");
2105
2093
  let offset = 0;
2106
2094
  let expectedBytes;
2107
2095
  let expectedSha256;
@@ -2202,7 +2190,7 @@ async function postPeerFileContent(peerUrl, namespace, filePath, source, token,
2202
2190
  for (const route of routes) {
2203
2191
  let restartedRoute = false;
2204
2192
  while (offset < source.bytes || source.bytes === 0 && offset === 0) {
2205
- const length = Math.min(OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2, source.bytes - offset);
2193
+ const length = Math.min(OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES, source.bytes - offset);
2206
2194
  let chunk;
2207
2195
  try {
2208
2196
  chunk = await source.readChunk(offset, length);
@@ -2241,92 +2229,531 @@ async function postPeerFileContent(peerUrl, namespace, filePath, source, token,
2241
2229
  restartedRoute = true;
2242
2230
  continue;
2243
2231
  }
2244
- previousAttemptFailed = true;
2245
- break;
2246
- }
2247
- const result = await response.json().catch(() => null);
2248
- if (!result || typeof result !== "object" || !("done" in result) || typeof result.done !== "boolean" || !("applied" in result) || typeof result.applied !== "boolean" || !("skipped" in result) || typeof result.skipped !== "boolean" || "conflict" in result && result.conflict) {
2249
- return false;
2250
- }
2251
- if (result.done) {
2252
- if (result.skipped) return previousAttemptFailed ? "applied" : "skipped";
2253
- return result.applied && offset + chunk.length === source.bytes ? "applied" : false;
2232
+ previousAttemptFailed = true;
2233
+ break;
2234
+ }
2235
+ const result = await response.json().catch(() => null);
2236
+ if (!result || typeof result !== "object" || !("done" in result) || typeof result.done !== "boolean" || !("applied" in result) || typeof result.applied !== "boolean" || !("skipped" in result) || typeof result.skipped !== "boolean" || "conflict" in result && result.conflict) {
2237
+ return false;
2238
+ }
2239
+ if (result.done) {
2240
+ if (result.skipped) return previousAttemptFailed ? "applied" : "skipped";
2241
+ return result.applied && offset + chunk.length === source.bytes ? "applied" : false;
2242
+ }
2243
+ if (result.applied || result.skipped || chunk.length === 0) return false;
2244
+ offset += chunk.length;
2245
+ }
2246
+ }
2247
+ return false;
2248
+ }
2249
+ async function postPeerConvergenceComplete(peerUrl, namespaces, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
2250
+ const base = normalizePeerBaseUrl(peerUrl);
2251
+ const query = namespaces.map((namespace) => `namespace=${encodeURIComponent(namespace)}`).join("&");
2252
+ const routes = ["/remnic/v1/offline-sync/convergence-complete", "/engram/v1/offline-sync/convergence-complete"];
2253
+ for (const route of routes) {
2254
+ const response = await fetchPeerRequest(
2255
+ fetchImpl,
2256
+ `${base}${route}?${query}`,
2257
+ {
2258
+ method: "POST",
2259
+ headers: {
2260
+ "x-remnic-source-id": encodeURIComponent("remnic-converge"),
2261
+ ...token ? { authorization: `Bearer ${token}` } : {}
2262
+ }
2263
+ },
2264
+ timeoutMs
2265
+ ).catch(() => null);
2266
+ if (!response?.ok) continue;
2267
+ const result = await response.json().catch(() => null);
2268
+ if (result && typeof result === "object" && "namespaces" in result && Array.isArray(result.namespaces) && result.namespaces.length === namespaces.length && result.namespaces.every((namespace, index) => namespace === namespaces[index]) && "refreshed" in result && result.refreshed === true) {
2269
+ return true;
2270
+ }
2271
+ }
2272
+ return false;
2273
+ }
2274
+ async function postPeerFileDeletion(peerUrl, namespace, filePath, baseSha256, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
2275
+ assertTransferablePeerPath(filePath);
2276
+ const base = normalizePeerBaseUrl(peerUrl);
2277
+ const routes = ["/remnic/v1/offline-sync/apply", "/engram/v1/offline-sync/apply"];
2278
+ const headers = {
2279
+ "content-type": "application/json",
2280
+ ...token ? { authorization: `Bearer ${token}` } : {}
2281
+ };
2282
+ let previousAttemptFailed = false;
2283
+ for (const route of routes) {
2284
+ try {
2285
+ const response = await fetchPeerRequest(
2286
+ fetchImpl,
2287
+ `${base}${route}`,
2288
+ {
2289
+ method: "POST",
2290
+ headers,
2291
+ body: JSON.stringify({
2292
+ namespace,
2293
+ changeset: {
2294
+ format: OFFLINE_SYNC_CHANGESET_FORMAT,
2295
+ schemaVersion: 1,
2296
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2297
+ sourceId: "remnic-converge",
2298
+ includeTranscripts: false,
2299
+ changes: [{ type: "delete", path: filePath, baseSha256 }]
2300
+ }
2301
+ })
2302
+ },
2303
+ timeoutMs
2304
+ );
2305
+ if (!response.ok) throw new Error(`offline apply request failed: ${response.status}`);
2306
+ const result = await response.json().catch(() => null);
2307
+ if (!result || typeof result !== "object" || !("appliedDeletes" in result) || typeof result.appliedDeletes !== "number" || !("skipped" in result) || typeof result.skipped !== "number" || !("conflicts" in result) || !Array.isArray(result.conflicts) || result.conflicts.length > 0) {
2308
+ return false;
2309
+ }
2310
+ if (result.appliedDeletes === 1) return "applied";
2311
+ if (result.skipped === 1) return previousAttemptFailed ? "applied" : "skipped";
2312
+ return false;
2313
+ } catch {
2314
+ previousAttemptFailed = true;
2315
+ }
2316
+ }
2317
+ return false;
2318
+ }
2319
+
2320
+ // src/credential-channel.ts
2321
+ import * as fs16 from "fs";
2322
+ function parseTokenFileFlag(raw) {
2323
+ return raw !== void 0 && raw.length > 0 ? raw : null;
2324
+ }
2325
+ function readTokenFileSameInode(tokenFile, afterValidate) {
2326
+ let fd;
2327
+ try {
2328
+ if (process.platform === "win32") {
2329
+ const pre = fs16.lstatSync(tokenFile);
2330
+ if (pre.isSymbolicLink()) {
2331
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file, not a symlink` };
2332
+ }
2333
+ if (!pre.isFile()) {
2334
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file` };
2335
+ }
2336
+ }
2337
+ fd = fs16.openSync(
2338
+ tokenFile,
2339
+ fs16.constants.O_RDONLY | (fs16.constants.O_NOFOLLOW ?? 0) | (fs16.constants.O_NONBLOCK ?? 0)
2340
+ );
2341
+ } catch (err) {
2342
+ const code = err.code;
2343
+ if (code === "ELOOP") {
2344
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file, not a symlink` };
2345
+ }
2346
+ if (code === "EISDIR" || code === "ENOTDIR" || code === "ENXIO" || code === "EAGAIN" || code === "EWOULDBLOCK" || code === "EOPNOTSUPP" || code === "ENOTSUP") {
2347
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file` };
2348
+ }
2349
+ return { ok: false, error: `--token-file ${tokenFile} could not be read: ${err}` };
2350
+ }
2351
+ try {
2352
+ const opened = fs16.fstatSync(fd);
2353
+ if (!opened.isFile()) {
2354
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file` };
2355
+ }
2356
+ if (process.platform !== "win32" && opened.mode & 63) {
2357
+ return { ok: false, error: `--token-file ${tokenFile} must not be group- or world-readable (chmod 600)` };
2358
+ }
2359
+ if (process.platform === "win32" || (fs16.constants.O_NOFOLLOW ?? 0) === 0) {
2360
+ const pathStat = fs16.lstatSync(tokenFile);
2361
+ if (pathStat.isSymbolicLink()) {
2362
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file, not a symlink` };
2363
+ }
2364
+ if (!pathStat.isFile() || pathStat.dev !== opened.dev || pathStat.ino !== opened.ino) {
2365
+ return { ok: false, error: `--token-file ${tokenFile} must be a regular file` };
2366
+ }
2367
+ }
2368
+ afterValidate?.();
2369
+ const size = opened.size;
2370
+ const buf = Buffer.alloc(size);
2371
+ const got = size > 0 ? fs16.readSync(fd, buf, 0, size, 0) : 0;
2372
+ const token = buf.subarray(0, got).toString("utf8").trim();
2373
+ const after = fs16.fstatSync(fd);
2374
+ if (opened.dev !== after.dev || opened.ino !== after.ino || !after.isFile()) {
2375
+ return { ok: false, error: `--token-file ${tokenFile} could not be read: file changed during read` };
2376
+ }
2377
+ if (token.length === 0) {
2378
+ return { ok: false, error: `--token-file ${tokenFile} is empty` };
2379
+ }
2380
+ return { ok: true, token, tokenFromArgv: false };
2381
+ } catch (err) {
2382
+ return { ok: false, error: `--token-file ${tokenFile} could not be read: ${err}` };
2383
+ } finally {
2384
+ if (fd !== void 0) {
2385
+ try {
2386
+ fs16.closeSync(fd);
2387
+ } catch {
2388
+ }
2389
+ }
2390
+ }
2391
+ }
2392
+ function resolveCredentialChannel(input, env, hooks) {
2393
+ if (input.argvToken !== void 0 && input.argvToken.trim().length === 0) {
2394
+ return { ok: false, error: "--token requires a non-empty value" };
2395
+ }
2396
+ if (input.argvToken !== void 0) {
2397
+ return { ok: true, token: input.argvToken, tokenFromArgv: true };
2398
+ }
2399
+ if (input.tokenFile !== void 0) {
2400
+ if (input.tokenFile.length === 0) {
2401
+ return { ok: false, error: "--token-file requires a non-empty path" };
2402
+ }
2403
+ return readTokenFileSameInode(input.tokenFile, hooks?.afterTokenFileValidated);
2404
+ }
2405
+ for (const name of input.envNames) {
2406
+ const value = env[name];
2407
+ if (value === void 0) continue;
2408
+ if (value.trim().length === 0) {
2409
+ return { ok: false, error: `${name} is set but empty` };
2410
+ }
2411
+ return { ok: true, token: value, tokenFromArgv: false };
2412
+ }
2413
+ return { ok: true, token: void 0, tokenFromArgv: false };
2414
+ }
2415
+
2416
+ // src/converge-watch.ts
2417
+ var CONVERGE_WATCH_MIN_INTERVAL_MS = 1e3;
2418
+ var CONVERGE_WATCH_DEFAULT_INTERVAL_MS = 3e5;
2419
+ var CONVERGE_WATCH_MAX_INTERVAL_MS = 2147483647;
2420
+ function sleepAborted(ms, signal) {
2421
+ return new Promise((resolve2) => {
2422
+ const timer = setTimeout(() => {
2423
+ signal.removeEventListener("abort", onAbort);
2424
+ resolve2(true);
2425
+ }, ms);
2426
+ const onAbort = () => {
2427
+ clearTimeout(timer);
2428
+ resolve2(false);
2429
+ };
2430
+ if (signal.aborted) {
2431
+ clearTimeout(timer);
2432
+ resolve2(false);
2433
+ return;
2434
+ }
2435
+ signal.addEventListener("abort", onAbort, { once: true });
2436
+ });
2437
+ }
2438
+ async function convergeWatch(options) {
2439
+ const intervalMs = Math.min(
2440
+ CONVERGE_WATCH_MAX_INTERVAL_MS,
2441
+ Math.max(CONVERGE_WATCH_MIN_INTERVAL_MS, options.intervalMs ?? CONVERGE_WATCH_DEFAULT_INTERVAL_MS)
2442
+ );
2443
+ const { apply, intervalMs: _intervalMs, maxCycles, onCycle, signal, ...applyOptions } = options;
2444
+ const outcome = {
2445
+ cycles: 0,
2446
+ convergedCycles: 0,
2447
+ appliedCycles: 0,
2448
+ failedCycles: 0,
2449
+ lastStatus: "aborted"
2450
+ };
2451
+ while (maxCycles === void 0 || outcome.cycles < maxCycles) {
2452
+ if (signal?.aborted) break;
2453
+ try {
2454
+ const result = await apply(applyOptions);
2455
+ outcome.cycles += 1;
2456
+ if (result.status === "converged") outcome.convergedCycles += 1;
2457
+ else if (result.status === "stopped_unresolved_conflicts" || result.status === "applied" && result.transfers.failed > 0) {
2458
+ outcome.failedCycles += 1;
2459
+ } else outcome.appliedCycles += 1;
2460
+ outcome.lastStatus = result.status;
2461
+ onCycle?.(outcome.cycles, { result });
2462
+ } catch (err) {
2463
+ outcome.cycles += 1;
2464
+ outcome.failedCycles += 1;
2465
+ outcome.lastStatus = "error";
2466
+ onCycle?.(outcome.cycles, { error: err });
2467
+ }
2468
+ if (maxCycles !== void 0 && outcome.cycles >= maxCycles) break;
2469
+ const slept = await sleepAborted(intervalMs, signal ?? new AbortController().signal);
2470
+ if (!slept) break;
2471
+ }
2472
+ return outcome;
2473
+ }
2474
+
2475
+ // src/offline-storage-io.ts
2476
+ import { createDecipheriv, createHash as createHash2 } from "crypto";
2477
+ import fs17 from "fs";
2478
+ import { lstat, mkdtemp, readdir, rm } from "fs/promises";
2479
+ import path2 from "path";
2480
+ import {
2481
+ OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES as OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2,
2482
+ StorageManager as StorageManager3,
2483
+ createPersistedSupportPassportPrivateFileExclusion,
2484
+ createSupportPassportPrivateFileExclusion
2485
+ } from "@remnic/core";
2486
+ import { OFFLINE_DECRYPT_STAGING_DIR_PREFIX } from "@remnic/core/offline-sync-exclude-globs";
2487
+ import {
2488
+ AUTH_TAG_LENGTH,
2489
+ ENVELOPE_HEADER_SIZE,
2490
+ ENVELOPE_LAYOUT,
2491
+ ENVELOPE_SALT_LENGTH,
2492
+ ENVELOPE_VERSION,
2493
+ FILE_FORMAT_FLAGS,
2494
+ FILE_FORMAT_VERSION,
2495
+ IV_LENGTH,
2496
+ MAGIC_BYTES,
2497
+ MAGIC_HEADER_SIZE,
2498
+ SecureStoreLockedError,
2499
+ filePathAad,
2500
+ isEncryptedFile,
2501
+ keyring,
2502
+ readHeader,
2503
+ secureStoreDir
2504
+ } from "@remnic/core/secure-store";
2505
+ var OFFLINE_SYNC_EXCLUSION_CONCURRENCY = 16;
2506
+ function resolveOfflineDirectHydrationPath(memoryDir, relPath) {
2507
+ const base = path2.resolve(memoryDir);
2508
+ const target = path2.resolve(base, relPath);
2509
+ const relative = path2.relative(base, target);
2510
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
2511
+ throw new Error(`offline sync direct hydration path escapes memory dir: ${relPath}`);
2512
+ }
2513
+ return target;
2514
+ }
2515
+ async function filterOfflineSyncBaseFiles(memoryDir, files, excludeFile) {
2516
+ const excluded = new Array(files.length);
2517
+ for (let offset = 0; offset < files.length; offset += OFFLINE_SYNC_EXCLUSION_CONCURRENCY) {
2518
+ await Promise.all(
2519
+ files.slice(offset, offset + OFFLINE_SYNC_EXCLUSION_CONCURRENCY).map(async (file, index) => {
2520
+ const filePath = resolveOfflineDirectHydrationPath(memoryDir, file.path);
2521
+ excluded[offset + index] = await excludeFile({ root: memoryDir, path: file.path, filePath });
2522
+ })
2523
+ );
2524
+ }
2525
+ return files.filter((_file, index) => excluded[index] === false);
2526
+ }
2527
+ async function createConfiguredOfflineStorage(memoryDir, secureStoreEncryptOnWrite = true) {
2528
+ const storage = new StorageManager3(memoryDir);
2529
+ const header = await readHeader(memoryDir);
2530
+ let secureStoreKey = null;
2531
+ let secureStoreRequired = false;
2532
+ if (header) {
2533
+ secureStoreRequired = true;
2534
+ storage.setSecureStoreRequired(true);
2535
+ const key = keyring.getKey(secureStoreDir(memoryDir));
2536
+ if (key) {
2537
+ await storage.setSecureStoreKeyAndWait(key, secureStoreEncryptOnWrite);
2538
+ secureStoreKey = key;
2539
+ }
2540
+ }
2541
+ return { storage, secureStoreKey, secureStoreRequired };
2542
+ }
2543
+ async function createOfflineStorageForPath(memoryDir, filePath, configured, secureStoreEncryptOnWrite) {
2544
+ const memoryRoot = path2.resolve(memoryDir);
2545
+ const stateDir = path2.dirname(filePath);
2546
+ if (path2.basename(stateDir) !== "state" || path2.basename(filePath) !== "memory-lifecycle-ledger.jsonl") {
2547
+ throw new Error(`invalid lifecycle ledger path: ${filePath}`);
2548
+ }
2549
+ const storageRoot = path2.resolve(path2.dirname(stateDir));
2550
+ if (storageRoot !== memoryRoot && !storageRoot.startsWith(`${memoryRoot}${path2.sep}`)) {
2551
+ throw new Error(`lifecycle ledger path is outside the offline memory directory: ${filePath}`);
2552
+ }
2553
+ const storage = new StorageManager3(storageRoot);
2554
+ if (configured.secureStoreRequired) {
2555
+ storage.setSecureStoreRequired(true);
2556
+ }
2557
+ if (configured.secureStoreKey) {
2558
+ await storage.setSecureStoreKeyAndWait(configured.secureStoreKey, secureStoreEncryptOnWrite);
2559
+ }
2560
+ return storage;
2561
+ }
2562
+ async function createOfflineStorageIo(memoryDir, configuredStorage, persistedExclusion) {
2563
+ await cleanupOrphanedOfflineDecryptStaging(memoryDir);
2564
+ const { storage, secureStoreKey } = configuredStorage ?? await createConfiguredOfflineStorage(memoryDir);
2565
+ return {
2566
+ // With persisted classifications the warm path skips the per-file
2567
+ // read+parse the plain exclusion performs for every candidate.
2568
+ excludeFile: persistedExclusion ? createPersistedSupportPassportPrivateFileExclusion(
2569
+ storage,
2570
+ persistedExclusion.persisted,
2571
+ persistedExclusion.updates
2572
+ ) : createSupportPassportPrivateFileExclusion(storage),
2573
+ readFile: async ({ filePath }) => storage.readOfflineSyncFile(filePath),
2574
+ readDeletionRevisions: () => storage.readDeletionRevisions(),
2575
+ readFileDigest: async ({ filePath }) => {
2576
+ const hash = createHash2("sha256");
2577
+ let bytes = 0;
2578
+ for await (const rawChunk of readOfflineSyncFileChunks({
2579
+ filePath,
2580
+ memoryDir,
2581
+ secureStoreKey,
2582
+ chunkSize: OFFLINE_SYNC_FILE_CONTENT_TRANSFER_CHUNK_BYTES2
2583
+ })) {
2584
+ const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
2585
+ hash.update(chunk);
2586
+ bytes += chunk.length;
2587
+ }
2588
+ return {
2589
+ sha256: hash.digest("hex"),
2590
+ bytes
2591
+ };
2592
+ },
2593
+ readFileChunks: ({ filePath, chunkSize }) => readOfflineSyncFileChunks({
2594
+ filePath,
2595
+ memoryDir,
2596
+ secureStoreKey,
2597
+ chunkSize
2598
+ }),
2599
+ writeFile: async ({ filePath, content }) => storage.writeOfflineSyncFile(filePath, content),
2600
+ writeStagingFile: async ({ filePath, content }) => storage.writeOfflineSyncStagingFile(filePath, content),
2601
+ writeFileChunks: async ({ filePath, chunks }) => storage.writeOfflineSyncFileChunks(filePath, chunks),
2602
+ deleteFile: async ({ filePath, mtimeMs }) => storage.deleteOfflineSyncFile(filePath, mtimeMs ?? null),
2603
+ recordDeletionRevision: async ({ filePath, mtimeMs }) => storage.recordReplicatedDeletionRevision(filePath, mtimeMs)
2604
+ };
2605
+ }
2606
+ var OFFLINE_DECRYPT_STAGING_ORPHAN_MS = 60 * 60 * 1e3;
2607
+ async function cleanupOrphanedOfflineDecryptStaging(memoryDir) {
2608
+ let entries;
2609
+ try {
2610
+ entries = await readdir(memoryDir);
2611
+ } catch {
2612
+ return;
2613
+ }
2614
+ const now = Date.now();
2615
+ for (const name of entries) {
2616
+ if (!name.startsWith(OFFLINE_DECRYPT_STAGING_DIR_PREFIX)) continue;
2617
+ const dir = path2.join(memoryDir, name);
2618
+ try {
2619
+ const info = await lstat(dir);
2620
+ if (!info.isDirectory() || info.isSymbolicLink()) continue;
2621
+ if (now - info.mtimeMs < OFFLINE_DECRYPT_STAGING_ORPHAN_MS) continue;
2622
+ await rm(dir, { recursive: true, force: true });
2623
+ } catch {
2624
+ }
2625
+ }
2626
+ }
2627
+ async function* readOfflineSyncFileChunks(options) {
2628
+ const header = await readFilePrefix(options.filePath, MAGIC_HEADER_SIZE);
2629
+ if (!isEncryptedFile(header)) {
2630
+ yield* readPlainOfflineFileChunks(options.filePath, options.chunkSize);
2631
+ return;
2632
+ }
2633
+ if (!options.secureStoreKey) {
2634
+ throw new SecureStoreLockedError(
2635
+ `secure-store is locked \u2014 cannot read encrypted file at ${options.filePath}. Run \`remnic secure-store unlock\` to decrypt.`
2636
+ );
2637
+ }
2638
+ yield* readEncryptedOfflineFileChunks({
2639
+ filePath: options.filePath,
2640
+ memoryDir: options.memoryDir,
2641
+ key: options.secureStoreKey,
2642
+ chunkSize: options.chunkSize
2643
+ });
2644
+ }
2645
+ async function readFilePrefix(filePath, length) {
2646
+ const handle = await fs17.promises.open(filePath, "r");
2647
+ try {
2648
+ const out = Buffer.alloc(length);
2649
+ const { bytesRead } = await handle.read(out, 0, length, 0);
2650
+ return out.subarray(0, bytesRead);
2651
+ } finally {
2652
+ await handle.close();
2653
+ }
2654
+ }
2655
+ async function* readPlainOfflineFileChunks(filePath, chunkSize) {
2656
+ const stream = fs17.createReadStream(filePath, { highWaterMark: chunkSize });
2657
+ for await (const chunk of stream) {
2658
+ yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2659
+ }
2660
+ }
2661
+ async function* readEncryptedOfflineFileChunks(options) {
2662
+ const header = await readFilePrefix(options.filePath, MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE);
2663
+ if (header.length < MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE || !isEncryptedFile(header)) {
2664
+ throw new Error(`secure-store encrypted file is truncated: ${options.filePath}`);
2665
+ }
2666
+ const version = header.readUInt8(MAGIC_BYTES.length);
2667
+ const flags = header.readUInt8(MAGIC_BYTES.length + 1);
2668
+ if (version !== FILE_FORMAT_VERSION) {
2669
+ throw new Error(`secure-store file has unsupported version ${version}: ${options.filePath}`);
2670
+ }
2671
+ if (flags !== FILE_FORMAT_FLAGS) {
2672
+ throw new Error(`secure-store file has unsupported flags 0x${flags.toString(16)}: ${options.filePath}`);
2673
+ }
2674
+ const envelopeHeader = header.subarray(MAGIC_HEADER_SIZE);
2675
+ const envelopeVersion = envelopeHeader.readUInt8(ENVELOPE_LAYOUT.version);
2676
+ if (envelopeVersion !== ENVELOPE_VERSION) {
2677
+ throw new Error(`secure-store envelope has unsupported version ${envelopeVersion}: ${options.filePath}`);
2678
+ }
2679
+ const salt = envelopeHeader.subarray(ENVELOPE_LAYOUT.salt, ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH);
2680
+ const iv = envelopeHeader.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);
2681
+ const authTag = envelopeHeader.subarray(ENVELOPE_LAYOUT.authTag, ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH);
2682
+ const aadCandidates = offlineFileAadCandidates(options.filePath, options.memoryDir);
2683
+ let lastError;
2684
+ for (const aad of aadCandidates) {
2685
+ const tempDir = await mkdtemp(path2.join(options.memoryDir, OFFLINE_DECRYPT_STAGING_DIR_PREFIX));
2686
+ const tempPath = path2.join(tempDir, "content");
2687
+ try {
2688
+ const decipher = createDecipheriv("aes-256-gcm", options.key, iv, {
2689
+ authTagLength: AUTH_TAG_LENGTH
2690
+ });
2691
+ decipher.setAuthTag(authTag);
2692
+ decipher.setAAD(Buffer.concat([secureStoreEnvelopeHeaderAad(salt), aad]));
2693
+ const output = fs17.createWriteStream(tempPath, { mode: 384 });
2694
+ try {
2695
+ const stream = fs17.createReadStream(options.filePath, {
2696
+ start: MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE,
2697
+ highWaterMark: options.chunkSize
2698
+ });
2699
+ for await (const encryptedChunk of stream) {
2700
+ const plain = decipher.update(Buffer.isBuffer(encryptedChunk) ? encryptedChunk : Buffer.from(encryptedChunk));
2701
+ if (plain.length > 0 && !output.write(plain)) {
2702
+ await new Promise((resolve2, reject) => {
2703
+ output.once("drain", resolve2);
2704
+ output.once("error", reject);
2705
+ });
2706
+ }
2707
+ }
2708
+ const finalPlain = decipher.final();
2709
+ if (finalPlain.length > 0 && !output.write(finalPlain)) {
2710
+ await new Promise((resolve2, reject) => {
2711
+ output.once("drain", resolve2);
2712
+ output.once("error", reject);
2713
+ });
2714
+ }
2715
+ } finally {
2716
+ await closeWriteStream(output);
2254
2717
  }
2255
- if (result.applied || result.skipped || chunk.length === 0) return false;
2256
- offset += chunk.length;
2718
+ yield* readPlainOfflineFileChunks(tempPath, options.chunkSize);
2719
+ return;
2720
+ } catch (error) {
2721
+ lastError = error;
2722
+ } finally {
2723
+ await rm(tempDir, { recursive: true, force: true });
2257
2724
  }
2258
2725
  }
2259
- return false;
2726
+ throw lastError instanceof Error ? lastError : new Error(`secure-store could not decrypt file: ${options.filePath}`);
2260
2727
  }
2261
- async function postPeerConvergenceComplete(peerUrl, namespaces, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
2262
- const base = normalizePeerBaseUrl(peerUrl);
2263
- const query = namespaces.map((namespace) => `namespace=${encodeURIComponent(namespace)}`).join("&");
2264
- const routes = ["/remnic/v1/offline-sync/convergence-complete", "/engram/v1/offline-sync/convergence-complete"];
2265
- for (const route of routes) {
2266
- const response = await fetchPeerRequest(
2267
- fetchImpl,
2268
- `${base}${route}?${query}`,
2269
- {
2270
- method: "POST",
2271
- headers: {
2272
- "x-remnic-source-id": encodeURIComponent("remnic-converge"),
2273
- ...token ? { authorization: `Bearer ${token}` } : {}
2274
- }
2275
- },
2276
- timeoutMs
2277
- ).catch(() => null);
2278
- if (!response?.ok) continue;
2279
- const result = await response.json().catch(() => null);
2280
- if (result && typeof result === "object" && "namespaces" in result && Array.isArray(result.namespaces) && result.namespaces.length === namespaces.length && result.namespaces.every((namespace, index) => namespace === namespaces[index]) && "refreshed" in result && result.refreshed === true) {
2281
- return true;
2282
- }
2728
+ function offlineFileAadCandidates(filePath, memoryDir) {
2729
+ const candidates = [filePathAad(filePath, memoryDir)];
2730
+ const relative = path2.relative(memoryDir, filePath);
2731
+ if (!relative || relative.startsWith("..") || path2.isAbsolute(relative)) return candidates;
2732
+ const parts = relative.split(path2.sep);
2733
+ if (parts[0] === "namespaces" && parts.length >= 3 && parts[1]) {
2734
+ candidates.push(filePathAad(filePath, path2.join(memoryDir, "namespaces", parts[1])));
2283
2735
  }
2284
- return false;
2285
- }
2286
- async function postPeerFileDeletion(peerUrl, namespace, filePath, baseSha256, token, fetchImpl = globalThis.fetch, timeoutMs = DEFAULT_PEER_REQUEST_TIMEOUT_MS) {
2287
- assertTransferablePeerPath(filePath);
2288
- const base = normalizePeerBaseUrl(peerUrl);
2289
- const routes = ["/remnic/v1/offline-sync/apply", "/engram/v1/offline-sync/apply"];
2290
- const headers = {
2291
- "content-type": "application/json",
2292
- ...token ? { authorization: `Bearer ${token}` } : {}
2293
- };
2294
- let previousAttemptFailed = false;
2295
- for (const route of routes) {
2296
- try {
2297
- const response = await fetchPeerRequest(
2298
- fetchImpl,
2299
- `${base}${route}`,
2300
- {
2301
- method: "POST",
2302
- headers,
2303
- body: JSON.stringify({
2304
- namespace,
2305
- changeset: {
2306
- format: OFFLINE_SYNC_CHANGESET_FORMAT,
2307
- schemaVersion: 1,
2308
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2309
- sourceId: "remnic-converge",
2310
- includeTranscripts: false,
2311
- changes: [{ type: "delete", path: filePath, baseSha256 }]
2312
- }
2313
- })
2314
- },
2315
- timeoutMs
2316
- );
2317
- if (!response.ok) throw new Error(`offline apply request failed: ${response.status}`);
2318
- const result = await response.json().catch(() => null);
2319
- if (!result || typeof result !== "object" || !("appliedDeletes" in result) || typeof result.appliedDeletes !== "number" || !("skipped" in result) || typeof result.skipped !== "number" || !("conflicts" in result) || !Array.isArray(result.conflicts) || result.conflicts.length > 0) {
2320
- return false;
2321
- }
2322
- if (result.appliedDeletes === 1) return "applied";
2323
- if (result.skipped === 1) return previousAttemptFailed ? "applied" : "skipped";
2324
- return false;
2325
- } catch {
2326
- previousAttemptFailed = true;
2736
+ const memoryParts = path2.resolve(memoryDir).split(path2.sep);
2737
+ if (memoryParts.length >= 3 && memoryParts.at(-2) === "namespaces" && memoryParts.at(-1)) {
2738
+ const topLevelRoot = memoryParts.slice(0, -2).join(path2.sep) || path2.sep;
2739
+ const topRelative = path2.relative(topLevelRoot, filePath);
2740
+ if (topRelative && !topRelative.startsWith("..") && !path2.isAbsolute(topRelative) && topRelative.split(path2.sep)[0] === "namespaces" && topRelative.split(path2.sep)[1] === memoryParts.at(-1)) {
2741
+ candidates.push(filePathAad(filePath, topLevelRoot));
2327
2742
  }
2328
2743
  }
2329
- return false;
2744
+ return candidates;
2745
+ }
2746
+ async function closeWriteStream(stream) {
2747
+ await new Promise((resolve2, reject) => {
2748
+ stream.once("error", reject);
2749
+ stream.end(() => resolve2());
2750
+ });
2751
+ }
2752
+ function secureStoreEnvelopeHeaderAad(salt) {
2753
+ const out = Buffer.alloc(1 + ENVELOPE_SALT_LENGTH);
2754
+ out.writeUInt8(ENVELOPE_VERSION, 0);
2755
+ Buffer.from(salt).copy(out, 1);
2756
+ return out;
2330
2757
  }
2331
2758
 
2332
2759
  // src/converge.ts
@@ -2365,7 +2792,7 @@ async function readLocalTombstoneEvidence(rootDir) {
2365
2792
  for (const relativePath of TOMBSTONE_PATHS) {
2366
2793
  let content;
2367
2794
  try {
2368
- content = await fs17.promises.readFile(path2.join(rootDir, relativePath), "utf-8");
2795
+ content = await fs18.promises.readFile(path3.join(rootDir, relativePath), "utf-8");
2369
2796
  } catch (error) {
2370
2797
  if (error.code === "ENOENT") continue;
2371
2798
  throw error;
@@ -2377,20 +2804,20 @@ async function readLocalTombstoneEvidence(rootDir) {
2377
2804
  return merged;
2378
2805
  }
2379
2806
  async function discoverCursorNamespaces(memoryDir, peerUrl) {
2380
- const cursorDir = path2.join(path2.resolve(memoryDir), ".remnic", "state", "converge-cursors");
2807
+ const cursorDir = path3.join(path3.resolve(memoryDir), ".remnic", "state", "converge-cursors");
2381
2808
  let entries;
2382
2809
  try {
2383
- entries = await fs17.promises.readdir(cursorDir, { withFileTypes: true });
2810
+ entries = await fs18.promises.readdir(cursorDir, { withFileTypes: true });
2384
2811
  } catch (error) {
2385
2812
  if (error.code === "ENOENT") return [];
2386
2813
  throw error;
2387
2814
  }
2388
2815
  const namespaces = /* @__PURE__ */ new Set();
2389
2816
  for (const entry of entries) {
2390
- if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
2391
- const cursor = await readConvergeCursor(path2.join(cursorDir, entry.name));
2817
+ if (!entry.isFile() || !entry.name.endsWith(".json") || entry.name.startsWith("identity-")) continue;
2818
+ const cursor = await readConvergeCursor(path3.join(cursorDir, entry.name));
2392
2819
  if (!cursor) throw new Error(`invalid converge cursor: ${entry.name}`);
2393
- if (path2.basename(defaultConvergeCursorPath(memoryDir, peerUrl, cursor.namespace)) !== entry.name) continue;
2820
+ if (path3.basename(defaultConvergeCursorPath(memoryDir, peerUrl, cursor.namespace)) !== entry.name) continue;
2394
2821
  namespaces.add(cursor.namespace);
2395
2822
  }
2396
2823
  return [...namespaces].sort();
@@ -2480,7 +2907,13 @@ async function computeConvergePlan(options = {}) {
2480
2907
  for (const rootInfo of roots) {
2481
2908
  const ns = rootInfo.namespace;
2482
2909
  namespacesToPlan.add(ns);
2483
- const io = await createOfflineStorageIo(rootInfo.rootDir);
2910
+ const identityCachePath = memoryDir ? convergeIdentityCachePath(memoryDir, options.peerUrl ?? "local", ns) : void 0;
2911
+ const identityCache = await loadConvergeIdentityCache(identityCachePath, config.inlineSourceAttributionFormat);
2912
+ const classificationUpdates = /* @__PURE__ */ new Map();
2913
+ const io = await createOfflineStorageIo(rootInfo.rootDir, void 0, {
2914
+ persisted: identityCache,
2915
+ updates: classificationUpdates
2916
+ });
2484
2917
  const snapshot = await buildOfflineSyncSnapshotFromBase({
2485
2918
  root: rootInfo.rootDir,
2486
2919
  sourceId: "local",
@@ -2502,17 +2935,18 @@ async function computeConvergePlan(options = {}) {
2502
2935
  files,
2503
2936
  parseMemory: parseFrontmatter,
2504
2937
  citationTemplate: config.inlineSourceAttributionFormat,
2938
+ ...identityCache.size > 0 ? { cachedFiles: [...identityCache.values()] } : {},
2505
2939
  readFile: async (file) => {
2506
2940
  const readFile3 = io.readFile;
2507
2941
  if (!readFile3) {
2508
2942
  manifestReadFailed = true;
2509
- throw new Error("offline storage cannot read reconciliation manifest files");
2943
+ throw new Error("offline storage io cannot read manifest files");
2510
2944
  }
2511
2945
  try {
2512
2946
  return await readFile3({
2513
2947
  root: rootInfo.rootDir,
2514
2948
  path: file.path,
2515
- filePath: path2.join(rootInfo.rootDir, file.path)
2949
+ filePath: path3.join(rootInfo.rootDir, file.path)
2516
2950
  });
2517
2951
  } catch (error) {
2518
2952
  manifestReadFailed = true;
@@ -2525,6 +2959,13 @@ async function computeConvergePlan(options = {}) {
2525
2959
  }
2526
2960
  localManifests.set(ns, manifest);
2527
2961
  localTombstones.set(ns, tombstonedFileDigests(evidence, manifest));
2962
+ await saveConvergeIdentityCache(
2963
+ identityCachePath,
2964
+ manifest,
2965
+ config.inlineSourceAttributionFormat,
2966
+ identityCache,
2967
+ classificationUpdates
2968
+ );
2528
2969
  }
2529
2970
  }
2530
2971
  const peerUrl = options.peerUrl;
@@ -2873,13 +3314,13 @@ async function executeConvergeApply(options = {}) {
2873
3314
  const rootDir = rootMap.get(entry.namespace);
2874
3315
  if (rootDir) {
2875
3316
  try {
2876
- const filePath = path2.join(rootDir, localPath);
3317
+ const filePath = path3.join(rootDir, localPath);
2877
3318
  const io = await createOfflineStorageIo(rootDir);
2878
3319
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
2879
3320
  if (current.sha256 !== entry.localSha256) {
2880
3321
  throw new Error(`local file changed during push: ${localPath}`);
2881
3322
  }
2882
- const stat2 = await fs17.promises.stat(filePath);
3323
+ const stat2 = await fs18.promises.stat(filePath);
2883
3324
  let chunks;
2884
3325
  let chunkOffset = 0;
2885
3326
  const resetChunks = async () => {
@@ -2970,7 +3411,7 @@ async function executeConvergeApply(options = {}) {
2970
3411
  if (rootDir && entry.localSha256) {
2971
3412
  try {
2972
3413
  const io = await createOfflineStorageIo(rootDir);
2973
- const filePath = path2.join(rootDir, localPath);
3414
+ const filePath = path3.join(rootDir, localPath);
2974
3415
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
2975
3416
  if (current.sha256 === entry.localSha256) {
2976
3417
  await io.deleteFile({ root: rootDir, path: localPath, filePath });
@@ -3023,7 +3464,7 @@ async function executeConvergeApply(options = {}) {
3023
3464
  if (rootDir) {
3024
3465
  try {
3025
3466
  const io = await createOfflineStorageIo(rootDir);
3026
- const filePath = path2.join(rootDir, localPath);
3467
+ const filePath = path3.join(rootDir, localPath);
3027
3468
  const current = await io.readFileDigest({ root: rootDir, path: localPath, filePath });
3028
3469
  if (current.sha256 === entry.localSha256) {
3029
3470
  await io.deleteFile({ root: rootDir, path: localPath, filePath });
@@ -3117,41 +3558,6 @@ async function updateCursorsForPlan(plan, options) {
3117
3558
  }
3118
3559
  }
3119
3560
  }
3120
- function formatConvergeReport(plan) {
3121
- const lines = [];
3122
- lines.push(`Convergence Status: ${plan.converged ? "CONVERGED" : "DIVERGED"}`);
3123
- lines.push("");
3124
- lines.push("Per-Namespace Summary:");
3125
- if (plan.byNamespace.length === 0) {
3126
- lines.push(" (no namespaces evaluated)");
3127
- } else {
3128
- for (const report of plan.byNamespace) {
3129
- lines.push(` [${report.namespace}]`);
3130
- lines.push(` identical: ${report.identical}`);
3131
- lines.push(` pull: ${report.pull}`);
3132
- lines.push(` push: ${report.push}`);
3133
- lines.push(` conflict: ${report.conflict}`);
3134
- lines.push(` suppress: ${report.suppress}`);
3135
- lines.push(` unresolved: ${report.unresolved}`);
3136
- }
3137
- }
3138
- return lines.join("\n");
3139
- }
3140
- function formatConvergeApplyReport(result) {
3141
- const lines = [];
3142
- lines.push(`Convergence Execution Status: ${result.status.toUpperCase()}`);
3143
- lines.push(`Converged: ${result.converged ? "YES" : "NO"}`);
3144
- lines.push("");
3145
- lines.push("Transfers Executed:");
3146
- lines.push(` pulled: ${result.transfers.pulled}`);
3147
- lines.push(` pushed: ${result.transfers.pushed}`);
3148
- lines.push(` conflictsResolved: ${result.transfers.conflictsResolved}`);
3149
- lines.push(` suppressed: ${result.transfers.suppressed}`);
3150
- lines.push(` failed: ${result.transfers.failed}`);
3151
- lines.push("");
3152
- lines.push(formatConvergeReport(result.plan));
3153
- return lines.join("\n");
3154
- }
3155
3561
  function convergeTimeoutFlagToMs(seconds) {
3156
3562
  return normalizeConvergePeerRequestTimeoutMs(Math.round(seconds * 1e3), "--timeout");
3157
3563
  }
@@ -3189,7 +3595,7 @@ Subcommands:
3189
3595
  for (let i = 0; i < rest.length; i += 1) {
3190
3596
  const arg = rest[i];
3191
3597
  if (arg === "--token-file") {
3192
- tokenFile = parseConvergeTokenFileFlag(rest[i + 1]);
3598
+ tokenFile = parseTokenFileFlag(rest[i + 1]);
3193
3599
  if (tokenFile === null) {
3194
3600
  process.stderr.write("converge: --token-file requires a path.\n");
3195
3601
  process.exitCode = 2;
@@ -3237,8 +3643,8 @@ Subcommands:
3237
3643
  i += 1;
3238
3644
  }
3239
3645
  }
3240
- const tokenChannel = resolveConvergeTokenChannel(
3241
- { argvToken: peerToken, tokenFile: tokenFile ?? void 0 },
3646
+ const tokenChannel = resolveCredentialChannel(
3647
+ { argvToken: peerToken, tokenFile: tokenFile ?? void 0, envNames: ["REMNIC_CONVERGE_PEER_TOKEN"] },
3242
3648
  process.env
3243
3649
  );
3244
3650
  if (!tokenChannel.ok) {
@@ -3462,8 +3868,8 @@ function renderReplayResult(result, targetNamespace, format) {
3462
3868
  }
3463
3869
 
3464
3870
  // src/quarantine-replay.ts
3465
- import * as fs18 from "fs";
3466
- import { EngramAccessService, Orchestrator as Orchestrator8, initLogger as initLogger3, parseConfig as parseConfig14, resolveRemnicConfigRecord as resolveRemnicConfigRecord13 } from "@remnic/core";
3871
+ import * as fs19 from "fs";
3872
+ import { EngramAccessService, Orchestrator as Orchestrator9, initLogger as initLogger3, parseConfig as parseConfig14, resolveRemnicConfigRecord as resolveRemnicConfigRecord13 } from "@remnic/core";
3467
3873
  import { WriteQuarantineStore } from "@remnic/core/write-quarantine.js";
3468
3874
  function valueFlag(args, flag) {
3469
3875
  const occurrences = args.filter((a) => a === flag).length;
@@ -3511,9 +3917,9 @@ async function runQuarantineReplay(rest, format, resolveConfigPath2) {
3511
3917
  let orchestrator;
3512
3918
  try {
3513
3919
  const configPath = resolveConfigPath2();
3514
- const raw = fs18.existsSync(configPath) ? JSON.parse(fs18.readFileSync(configPath, "utf8")) : {};
3920
+ const raw = fs19.existsSync(configPath) ? JSON.parse(fs19.readFileSync(configPath, "utf8")) : {};
3515
3921
  const config = parseConfig14(resolveRemnicConfigRecord13(raw));
3516
- orchestrator = new Orchestrator8(config);
3922
+ orchestrator = new Orchestrator9(config);
3517
3923
  await orchestrator.initialize();
3518
3924
  await orchestrator.deferredReady;
3519
3925
  const service = new EngramAccessService(orchestrator);
@@ -3543,7 +3949,7 @@ async function runQuarantineReplay(rest, format, resolveConfigPath2) {
3543
3949
  }
3544
3950
 
3545
3951
  // src/offline-impression-rotation.ts
3546
- import fs19 from "fs";
3952
+ import fs20 from "fs";
3547
3953
  import { parseConfig as parseConfig15, resolveRemnicConfigRecord as resolveRemnicConfigRecord14, drainPendingImpressionsForOfflineSync } from "@remnic/core";
3548
3954
  import { LastRecallStore } from "@remnic/core/recall-state";
3549
3955
  function parseConfigQuietly(raw) {
@@ -3578,7 +3984,7 @@ function pickOfflineConfigRecord(raw) {
3578
3984
  function resolveOfflineImpressionRotation(configPath) {
3579
3985
  let raw;
3580
3986
  try {
3581
- raw = fs19.existsSync(configPath) ? JSON.parse(fs19.readFileSync(configPath, "utf8")) : {};
3987
+ raw = fs20.existsSync(configPath) ? JSON.parse(fs20.readFileSync(configPath, "utf8")) : {};
3582
3988
  } catch {
3583
3989
  throw new Error(
3584
3990
  `cannot read recall-impression rotation from ${configPath}: config file could not be read as JSON`
@@ -3609,20 +4015,20 @@ async function drainOfflineSyncImpressions(memoryDir, rotation) {
3609
4015
  // src/bench-build-freshness.ts
3610
4016
  import {
3611
4017
  existsSync as existsSync2,
3612
- lstatSync,
4018
+ lstatSync as lstatSync2,
3613
4019
  readdirSync,
3614
- readFileSync as readFileSync3,
3615
- statSync as statSync2
4020
+ readFileSync as readFileSync2,
4021
+ statSync
3616
4022
  } from "fs";
3617
- import path3 from "path";
4023
+ import path4 from "path";
3618
4024
  import { fileURLToPath } from "url";
3619
4025
  var STALE_BUILD_TOLERANCE_MS = 1e3;
3620
4026
  function assertLocalBenchBuildFreshForDevelopment(currentModuleUrl) {
3621
4027
  if (isTruthyEnv(process.env.REMNIC_BENCH_ALLOW_STALE_DIST)) {
3622
4028
  return;
3623
4029
  }
3624
- const currentDir = path3.dirname(fileURLToPath(currentModuleUrl));
3625
- const benchPackageDir = path3.resolve(currentDir, "../../bench");
4030
+ const currentDir = path4.dirname(fileURLToPath(currentModuleUrl));
4031
+ const benchPackageDir = path4.resolve(currentDir, "../../bench");
3626
4032
  const freshness = checkBenchBuildFreshness(benchPackageDir);
3627
4033
  if (!freshness.stale) {
3628
4034
  return;
@@ -3639,30 +4045,30 @@ function assertLocalBenchBuildFreshForDevelopment(currentModuleUrl) {
3639
4045
  );
3640
4046
  }
3641
4047
  function checkBenchBuildFreshness(benchPackageDir) {
3642
- const packageJsonPath = path3.join(benchPackageDir, "package.json");
4048
+ const packageJsonPath = path4.join(benchPackageDir, "package.json");
3643
4049
  if (!existsSync2(packageJsonPath)) {
3644
4050
  return { stale: false };
3645
4051
  }
3646
4052
  let packageName;
3647
4053
  try {
3648
- packageName = JSON.parse(readFileSync3(packageJsonPath, "utf8")).name;
4054
+ packageName = JSON.parse(readFileSync2(packageJsonPath, "utf8")).name;
3649
4055
  } catch {
3650
4056
  return { stale: false };
3651
4057
  }
3652
4058
  if (packageName !== "@remnic/bench") {
3653
4059
  return { stale: false };
3654
4060
  }
3655
- const srcDir = path3.join(benchPackageDir, "src");
4061
+ const srcDir = path4.join(benchPackageDir, "src");
3656
4062
  if (!isDirectory(srcDir)) {
3657
4063
  return { stale: false };
3658
4064
  }
3659
4065
  const sourceRoots = [
3660
4066
  srcDir,
3661
4067
  packageJsonPath,
3662
- path3.join(benchPackageDir, "tsup.config.ts"),
3663
- path3.join(benchPackageDir, "tsconfig.json")
4068
+ path4.join(benchPackageDir, "tsup.config.ts"),
4069
+ path4.join(benchPackageDir, "tsconfig.json")
3664
4070
  ];
3665
- const distPath = path3.join(benchPackageDir, "dist", "index.js");
4071
+ const distPath = path4.join(benchPackageDir, "dist", "index.js");
3666
4072
  if (!existsSync2(distPath)) {
3667
4073
  return {
3668
4074
  stale: true,
@@ -3674,7 +4080,7 @@ function checkBenchBuildFreshness(benchPackageDir) {
3674
4080
  if (!newestSource) {
3675
4081
  return { stale: false };
3676
4082
  }
3677
- const distMtimeMs = statSync2(distPath).mtimeMs;
4083
+ const distMtimeMs = statSync(distPath).mtimeMs;
3678
4084
  if (newestSource.mtimeMs > distMtimeMs + STALE_BUILD_TOLERANCE_MS) {
3679
4085
  return {
3680
4086
  stale: true,
@@ -3699,13 +4105,13 @@ function newestMtime(roots) {
3699
4105
  if (!existsSync2(entryPath)) {
3700
4106
  return;
3701
4107
  }
3702
- const stat2 = lstatSync(entryPath);
4108
+ const stat2 = lstatSync2(entryPath);
3703
4109
  if (stat2.isSymbolicLink()) {
3704
4110
  return;
3705
4111
  }
3706
4112
  if (stat2.isDirectory()) {
3707
4113
  for (const child of readdirSync(entryPath)) {
3708
- visit(path3.join(entryPath, child));
4114
+ visit(path4.join(entryPath, child));
3709
4115
  }
3710
4116
  return;
3711
4117
  }
@@ -3723,7 +4129,7 @@ function newestMtime(roots) {
3723
4129
  }
3724
4130
  function isDirectory(entryPath) {
3725
4131
  try {
3726
- return statSync2(entryPath).isDirectory();
4132
+ return statSync(entryPath).isDirectory();
3727
4133
  } catch {
3728
4134
  return false;
3729
4135
  }
@@ -3738,18 +4144,18 @@ function isTruthyEnv(value) {
3738
4144
 
3739
4145
  // src/optional-bench.ts
3740
4146
  import { existsSync as existsSync3 } from "fs";
3741
- import path4 from "path";
4147
+ import path5 from "path";
3742
4148
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
3743
4149
  var SPECIFIER2 = "@remnic/bench";
3744
4150
  var TSX_ESM_API_SPECIFIER = "tsx/esm/api";
3745
4151
  var cached2;
3746
4152
  var cachedFromLocalWorkspaceBenchSource = false;
3747
4153
  function resolveLocalWorkspaceBenchPaths() {
3748
- const currentDir = path4.dirname(fileURLToPath2(import.meta.url));
3749
- const benchPackageDir = path4.resolve(currentDir, "../../bench");
4154
+ const currentDir = path5.dirname(fileURLToPath2(import.meta.url));
4155
+ const benchPackageDir = path5.resolve(currentDir, "../../bench");
3750
4156
  return {
3751
- distEntry: path4.join(benchPackageDir, "dist", "index.js"),
3752
- sourceEntry: path4.join(benchPackageDir, "src", "index.ts")
4157
+ distEntry: path5.join(benchPackageDir, "dist", "index.js"),
4158
+ sourceEntry: path5.join(benchPackageDir, "src", "index.ts")
3753
4159
  };
3754
4160
  }
3755
4161
  async function tryImportLocalWorkspaceBenchSource(err) {
@@ -3836,9 +4242,9 @@ function assertBenchModuleFreshForDevelopment() {
3836
4242
  }
3837
4243
 
3838
4244
  // src/cmd-security.ts
3839
- import fs20 from "fs";
4245
+ import fs21 from "fs";
3840
4246
  import {
3841
- Orchestrator as Orchestrator9,
4247
+ Orchestrator as Orchestrator10,
3842
4248
  parseConfig as parseConfig16,
3843
4249
  initLogger as initLogger4,
3844
4250
  resolveRemnicConfigRecord as resolveRemnicConfigRecord15,
@@ -3856,9 +4262,9 @@ async function cmdSecurity(rest) {
3856
4262
  }
3857
4263
  initLogger4();
3858
4264
  const configPath = resolveConfigPath();
3859
- const raw = fs20.existsSync(configPath) ? JSON.parse(fs20.readFileSync(configPath, "utf8")) : {};
4265
+ const raw = fs21.existsSync(configPath) ? JSON.parse(fs21.readFileSync(configPath, "utf8")) : {};
3860
4266
  const config = parseConfig16(resolveRemnicConfigRecord15(raw));
3861
- const orchestrator = new Orchestrator9(config);
4267
+ const orchestrator = new Orchestrator10(config);
3862
4268
  await orchestrator.initialize();
3863
4269
  try {
3864
4270
  const sinceFlag = rest.indexOf("--since");
@@ -3881,8 +4287,8 @@ async function cmdSecurity(rest) {
3881
4287
  }
3882
4288
 
3883
4289
  // src/daemon-service-candidates.ts
3884
- import fs21 from "fs";
3885
- import path5 from "path";
4290
+ import fs22 from "fs";
4291
+ import path6 from "path";
3886
4292
  var LAUNCHD_LABEL = "ai.remnic.daemon";
3887
4293
  var LEGACY_REMNIC_SERVER_LAUNCHD_LABEL = "ai.remnic.server";
3888
4294
  var LEGACY_LAUNCHD_LABEL = "ai.engram.daemon";
@@ -3895,15 +4301,15 @@ var SYSTEMD_SERVICE = "remnic.service";
3895
4301
  var LEGACY_SYSTEMD_SERVICE = "engram.service";
3896
4302
  var SYSTEMD_SERVICE_CANDIDATES = [SYSTEMD_SERVICE, LEGACY_SYSTEMD_SERVICE];
3897
4303
  function launchdPlistPaths(homeDir) {
3898
- return LAUNCHD_LABEL_CANDIDATES.map((label) => path5.join(homeDir, "Library", "LaunchAgents", `${label}.plist`));
4304
+ return LAUNCHD_LABEL_CANDIDATES.map((label) => path6.join(homeDir, "Library", "LaunchAgents", `${label}.plist`));
3899
4305
  }
3900
4306
  function systemdUnitPaths(homeDir) {
3901
- return SYSTEMD_SERVICE_CANDIDATES.map((service) => path5.join(homeDir, ".config", "systemd", "user", service));
4307
+ return SYSTEMD_SERVICE_CANDIDATES.map((service) => path6.join(homeDir, ".config", "systemd", "user", service));
3902
4308
  }
3903
4309
  function anyFileExists(paths) {
3904
4310
  return paths.some((candidate) => {
3905
4311
  try {
3906
- return fs21.statSync(candidate).isFile();
4312
+ return fs22.statSync(candidate).isFile();
3907
4313
  } catch {
3908
4314
  return false;
3909
4315
  }
@@ -3915,7 +4321,7 @@ function commandNames(command) {
3915
4321
  }
3916
4322
  function isRunnableNodeScript(filePath) {
3917
4323
  try {
3918
- const text = fs21.readFileSync(filePath, "utf8").slice(0, 4096);
4324
+ const text = fs22.readFileSync(filePath, "utf8").slice(0, 4096);
3919
4325
  const firstLine = text.split(/\r?\n/, 1)[0] ?? "";
3920
4326
  if (/^#!.*\bnode\b/.test(firstLine)) return true;
3921
4327
  if (firstLine.startsWith("#!")) return false;
@@ -3928,20 +4334,20 @@ function isRunnableNodeScript(filePath) {
3928
4334
  function resolveShimNodeScript(filePath) {
3929
4335
  let text;
3930
4336
  try {
3931
- text = fs21.readFileSync(filePath, "utf8").slice(0, 16384);
4337
+ text = fs22.readFileSync(filePath, "utf8").slice(0, 16384);
3932
4338
  } catch {
3933
4339
  return void 0;
3934
4340
  }
3935
- const basedir = path5.dirname(filePath);
4341
+ const basedir = path6.dirname(filePath);
3936
4342
  const jsReferencePattern = /"([^"]+\.js)"|'([^']+\.js)'|([^\s"'`]+\.js)/g;
3937
4343
  for (const match of text.matchAll(jsReferencePattern)) {
3938
4344
  const raw = match[1] ?? match[2] ?? match[3];
3939
4345
  if (!raw) continue;
3940
4346
  const candidate = raw.replaceAll("${basedir}", basedir).replaceAll("$basedir", basedir).replaceAll("\\ ", " ");
3941
- const resolved = path5.isAbsolute(candidate) ? candidate : path5.resolve(basedir, candidate);
4347
+ const resolved = path6.isAbsolute(candidate) ? candidate : path6.resolve(basedir, candidate);
3942
4348
  try {
3943
- if (fs21.statSync(resolved).isFile() && isRunnableNodeScript(resolved)) {
3944
- return fs21.realpathSync(resolved);
4349
+ if (fs22.statSync(resolved).isFile() && isRunnableNodeScript(resolved)) {
4350
+ return fs22.realpathSync(resolved);
3945
4351
  }
3946
4352
  } catch {
3947
4353
  }
@@ -3949,19 +4355,19 @@ function resolveShimNodeScript(filePath) {
3949
4355
  return void 0;
3950
4356
  }
3951
4357
  function resolveRunnableNodeScript(filePath) {
3952
- const realPath = fs21.realpathSync(filePath);
4358
+ const realPath = fs22.realpathSync(filePath);
3953
4359
  if (isRunnableNodeScript(realPath)) return realPath;
3954
4360
  return resolveShimNodeScript(realPath);
3955
4361
  }
3956
4362
  function findCommandOnPath(command, pathEnv = process.env.PATH ?? "") {
3957
- for (const dir of pathEnv.split(path5.delimiter)) {
4363
+ for (const dir of pathEnv.split(path6.delimiter)) {
3958
4364
  if (!dir) continue;
3959
4365
  for (const name of commandNames(command)) {
3960
- const candidate = path5.join(dir, name);
4366
+ const candidate = path6.join(dir, name);
3961
4367
  try {
3962
- const stat2 = fs21.statSync(candidate);
4368
+ const stat2 = fs22.statSync(candidate);
3963
4369
  if (!stat2.isFile()) continue;
3964
- if (process.platform !== "win32") fs21.accessSync(candidate, fs21.constants.X_OK);
4370
+ if (process.platform !== "win32") fs22.accessSync(candidate, fs22.constants.X_OK);
3965
4371
  const runnable = resolveRunnableNodeScript(candidate);
3966
4372
  if (runnable) return runnable;
3967
4373
  } catch {
@@ -3971,11 +4377,11 @@ function findCommandOnPath(command, pathEnv = process.env.PATH ?? "") {
3971
4377
  return void 0;
3972
4378
  }
3973
4379
  function serverBinWrapperRequiredPath(candidate) {
3974
- const filename = path5.basename(candidate);
4380
+ const filename = path6.basename(candidate);
3975
4381
  if (filename !== "remnic-server.js" && filename !== "engram-server.js") return void 0;
3976
- const binDir = path5.dirname(candidate);
3977
- if (path5.basename(binDir) !== "bin") return void 0;
3978
- return path5.join(path5.dirname(binDir), "dist", "index.js");
4382
+ const binDir = path6.dirname(candidate);
4383
+ if (path6.basename(binDir) !== "bin") return void 0;
4384
+ return path6.join(path6.dirname(binDir), "dist", "index.js");
3979
4385
  }
3980
4386
 
3981
4387
  // src/service-candidates.ts
@@ -3997,7 +4403,7 @@ function firstSuccessfulCandidate(candidates, attempt) {
3997
4403
  }
3998
4404
 
3999
4405
  // src/bench-args.ts
4000
- import path7 from "path";
4406
+ import path8 from "path";
4001
4407
 
4002
4408
  // src/bench-flags.ts
4003
4409
  function readBenchOptionValue(argv, flag) {
@@ -4362,7 +4768,7 @@ function collectBenchmarks(argv) {
4362
4768
  }
4363
4769
 
4364
4770
  // src/bench-args-research.ts
4365
- import path6 from "path";
4771
+ import path7 from "path";
4366
4772
  function readPositiveInteger(args, flag) {
4367
4773
  const raw = readBenchOptionValue(args, flag);
4368
4774
  if (raw === void 0) return void 0;
@@ -4408,7 +4814,7 @@ function parseBenchResearchArgs(action, args) {
4408
4814
  }
4409
4815
  const outRaw = readBenchOptionValue(args, "--out");
4410
4816
  if (outRaw !== void 0) {
4411
- out = path6.resolve(expandTilde(outRaw));
4817
+ out = path7.resolve(expandTilde(outRaw));
4412
4818
  }
4413
4819
  }
4414
4820
  const epochs = readPositiveInteger(args, "--epochs");
@@ -4422,8 +4828,8 @@ function parseBenchResearchArgs(action, args) {
4422
4828
  }
4423
4829
  return {
4424
4830
  runRef,
4425
- memoryDir: memoryDirRaw ? path6.resolve(expandTilde(memoryDirRaw)) : void 0,
4426
- qmdPath: qmdPathRaw ? path6.resolve(expandTilde(qmdPathRaw)) : void 0,
4831
+ memoryDir: memoryDirRaw ? path7.resolve(expandTilde(memoryDirRaw)) : void 0,
4832
+ qmdPath: qmdPathRaw ? path7.resolve(expandTilde(qmdPathRaw)) : void 0,
4427
4833
  collection,
4428
4834
  users: readPositiveInteger(args, "--users"),
4429
4835
  epochs,
@@ -4643,7 +5049,7 @@ function parseBenchArgs(argv) {
4643
5049
  }
4644
5050
  validateBenchFlags(action, args);
4645
5051
  const driftGenPositionals = action === "drift-gen" && driftGenAction === "validate" ? collectBenchmarks(args.slice(1)) : [];
4646
- const driftGenDir = driftGenPositionals[0] ? path7.resolve(expandTilde(driftGenPositionals[0])) : void 0;
5052
+ const driftGenDir = driftGenPositionals[0] ? path8.resolve(expandTilde(driftGenPositionals[0])) : void 0;
4647
5053
  const benchmarkArgs = action === "baseline" || action === "datasets" || action === "providers" || action === "runs" || action === "drift-gen" && (args[0] === "validate" || args[0] === "generate") ? args.slice(1) : args;
4648
5054
  const benchmarks = collectBenchmarks(benchmarkArgs);
4649
5055
  const datasetDir = readBenchOptionValue(args, "--dataset-dir") ?? readBenchOptionValue(args, "--dataset");
@@ -5193,13 +5599,13 @@ function parseBenchArgs(argv) {
5193
5599
  mcpUrl,
5194
5600
  mcpToolMap,
5195
5601
  mcpDemo,
5196
- datasetDir: datasetDir ? path7.resolve(expandTilde(datasetDir)) : void 0,
5197
- resultsDir: resultsDir ? path7.resolve(expandTilde(resultsDir)) : void 0,
5198
- baselinesDir: baselinesDir ? path7.resolve(expandTilde(baselinesDir)) : void 0,
5602
+ datasetDir: datasetDir ? path8.resolve(expandTilde(datasetDir)) : void 0,
5603
+ resultsDir: resultsDir ? path8.resolve(expandTilde(resultsDir)) : void 0,
5604
+ baselinesDir: baselinesDir ? path8.resolve(expandTilde(baselinesDir)) : void 0,
5199
5605
  runtimeProfile,
5200
5606
  matrixProfiles,
5201
- remnicConfigPath: remnicConfigRaw ? path7.resolve(expandTilde(remnicConfigRaw)) : void 0,
5202
- openclawConfigPath: openclawConfigRaw ? path7.resolve(expandTilde(openclawConfigRaw)) : void 0,
5607
+ remnicConfigPath: remnicConfigRaw ? path8.resolve(expandTilde(remnicConfigRaw)) : void 0,
5608
+ openclawConfigPath: openclawConfigRaw ? path8.resolve(expandTilde(openclawConfigRaw)) : void 0,
5203
5609
  modelSource,
5204
5610
  gatewayAgentId,
5205
5611
  fastGatewayAgentId,
@@ -5222,13 +5628,13 @@ function parseBenchArgs(argv) {
5222
5628
  internalDisableThinking: args.includes("--internal-disable-thinking"),
5223
5629
  internalCodexReasoningEffort,
5224
5630
  threshold,
5225
- custom: customRaw ? path7.resolve(expandTilde(customRaw)) : void 0,
5631
+ custom: customRaw ? path8.resolve(expandTilde(customRaw)) : void 0,
5226
5632
  baselineAction,
5227
5633
  datasetAction,
5228
5634
  providerAction,
5229
5635
  runAction,
5230
5636
  format,
5231
- output: output ? path7.resolve(expandTilde(output)) : void 0,
5637
+ output: output ? path8.resolve(expandTilde(output)) : void 0,
5232
5638
  target,
5233
5639
  publishedName,
5234
5640
  publishedSeed,
@@ -5238,24 +5644,24 @@ function parseBenchArgs(argv) {
5238
5644
  publishedIngestConcurrency,
5239
5645
  publishedTaskFilter,
5240
5646
  memcorrectAdapter,
5241
- publishedOut: publishedOutRaw ? path7.resolve(expandTilde(publishedOutRaw)) : void 0,
5647
+ publishedOut: publishedOutRaw ? path8.resolve(expandTilde(publishedOutRaw)) : void 0,
5242
5648
  publishedDryRun: args.includes("--dry-run"),
5243
5649
  requestTimeout,
5244
5650
  localJudgeRequestTimeout,
5245
5651
  frontierJudgeRequestTimeout,
5246
- calibrationDir: calibrationDirRaw ? path7.resolve(expandTilde(calibrationDirRaw)) : void 0,
5652
+ calibrationDir: calibrationDirRaw ? path8.resolve(expandTilde(calibrationDirRaw)) : void 0,
5247
5653
  calibrationLocalConfigSha256,
5248
5654
  calibrationFrontierConfigSha256,
5249
5655
  sourceResultId,
5250
5656
  expectedAnswerSetSha256,
5251
5657
  expectedQuestionIdListSha256,
5252
- taskIdsFile: taskIdsFileRaw ? path7.resolve(expandTilde(taskIdsFileRaw)) : void 0,
5658
+ taskIdsFile: taskIdsFileRaw ? path8.resolve(expandTilde(taskIdsFileRaw)) : void 0,
5253
5659
  expectedTaskIdListSha256,
5254
5660
  drainTimeout,
5255
5661
  // Issue #1573 PR1: surface judge-cache flags into the runner options.
5256
5662
  noJudgeCache: args.includes("--no-judge-cache"),
5257
- judgeCacheDir: judgeCacheDirRaw ? path7.resolve(expandTilde(judgeCacheDirRaw)) : void 0,
5258
- localLabManifestPath: localLabManifestRaw ? path7.resolve(expandTilde(localLabManifestRaw)) : void 0,
5663
+ judgeCacheDir: judgeCacheDirRaw ? path8.resolve(expandTilde(judgeCacheDirRaw)) : void 0,
5664
+ localLabManifestPath: localLabManifestRaw ? path8.resolve(expandTilde(localLabManifestRaw)) : void 0,
5259
5665
  max429WaitMs,
5260
5666
  disableThinking: args.includes("--disable-thinking"),
5261
5667
  amaBenchJudgeProtocol,
@@ -5293,9 +5699,9 @@ function assertCalibrationProvenanceMatches(binding, state, benchmarkId) {
5293
5699
 
5294
5700
  // src/bench-status.ts
5295
5701
  import { mkdir, readFile, readdir as readdir2, rename, writeFile } from "fs/promises";
5296
- import path8 from "path";
5702
+ import path9 from "path";
5297
5703
  function createBenchStatusPath(resultsDir, pid, startedAtMs = Date.now()) {
5298
- return path8.join(resultsDir, `bench-status-${startedAtMs}-${pid}.json`);
5704
+ return path9.join(resultsDir, `bench-status-${startedAtMs}-${pid}.json`);
5299
5705
  }
5300
5706
  var BENCH_STATUS_FILENAME = /^bench-status-\d+-\d+\.json$/;
5301
5707
  var VALID_BENCH_ENTRY_STATUSES = /* @__PURE__ */ new Set(["pending", "running", "complete", "failed"]);
@@ -5308,7 +5714,7 @@ async function findLatestBenchStatusFile(resultsDir) {
5308
5714
  }
5309
5715
  const candidates = entries.filter((name) => BENCH_STATUS_FILENAME.test(name)).sort().reverse();
5310
5716
  for (const name of candidates) {
5311
- const filePath = path8.join(resultsDir, name);
5717
+ const filePath = path9.join(resultsDir, name);
5312
5718
  const status = await readBenchStatus(filePath);
5313
5719
  if (status) {
5314
5720
  return filePath;
@@ -5317,7 +5723,7 @@ async function findLatestBenchStatusFile(resultsDir) {
5317
5723
  return null;
5318
5724
  }
5319
5725
  async function atomicWriteJSON(filePath, data) {
5320
- await mkdir(path8.dirname(filePath), { recursive: true });
5726
+ await mkdir(path9.dirname(filePath), { recursive: true });
5321
5727
  const tmp = `${filePath}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;
5322
5728
  await writeFile(tmp, JSON.stringify(data, null, 2) + "\n");
5323
5729
  await rename(tmp, filePath);
@@ -5433,86 +5839,6 @@ function finalizeBenchStatus(filePath) {
5433
5839
  });
5434
5840
  }
5435
5841
 
5436
- // src/bench-fallback.ts
5437
- import fs22 from "fs";
5438
- import path9 from "path";
5439
- var FALLBACK_RESULTS_DIRNAME = "fallback-runs";
5440
- function buildBenchRunnerArgs(parsed, benchmarkId, outputDir) {
5441
- const args = ["--benchmark", benchmarkId];
5442
- if (parsed.quick) {
5443
- args.push("--lightweight");
5444
- }
5445
- if (parsed.publishedLimit !== void 0) {
5446
- args.push("--limit", String(parsed.publishedLimit));
5447
- } else if (parsed.quick) {
5448
- args.push("--limit", "1");
5449
- }
5450
- if (parsed.datasetDir) {
5451
- args.push("--dataset-dir", parsed.datasetDir);
5452
- }
5453
- if (outputDir) {
5454
- args.push("--output-dir", outputDir);
5455
- }
5456
- return args;
5457
- }
5458
- function findUnsupportedFallbackBenchOptions(parsed) {
5459
- const unsupported = [];
5460
- const add = (condition, flag) => {
5461
- if (condition) unsupported.push(flag);
5462
- };
5463
- add(parsed.modelSource !== void 0, "--model-source");
5464
- add(parsed.gatewayAgentId !== void 0, "--gateway-agent-id");
5465
- add(parsed.fastGatewayAgentId !== void 0, "--fast-gateway-agent-id");
5466
- add(parsed.systemProvider !== void 0, "--system-provider/--provider");
5467
- add(parsed.systemModel !== void 0, "--system-model/--model");
5468
- add(parsed.systemBaseUrl !== void 0, "--system-base-url/--base-url");
5469
- add(parsed.systemApiKey !== void 0, "--system-api-key");
5470
- add(parsed.systemCodexReasoningEffort !== void 0, "--system-codex-reasoning-effort");
5471
- add(parsed.systemResponderContextBudgetChars !== void 0, "--system-responder-context-budget-chars");
5472
- add(parsed.systemResponderPromptBudgetChars !== void 0, "--system-responder-prompt-budget-chars");
5473
- add(parsed.judgeProvider !== void 0, "--judge-provider");
5474
- add(parsed.judgeModel !== void 0, "--judge-model");
5475
- add(parsed.judgeBaseUrl !== void 0, "--judge-base-url");
5476
- add(parsed.judgeApiKey !== void 0, "--judge-api-key");
5477
- add(parsed.judgeCodexReasoningEffort !== void 0, "--judge-codex-reasoning-effort");
5478
- add(parsed.internalProvider !== void 0, "--internal-provider");
5479
- add(parsed.internalModel !== void 0, "--internal-model");
5480
- add(parsed.internalBaseUrl !== void 0, "--internal-base-url");
5481
- add(parsed.internalApiKey !== void 0, "--internal-api-key");
5482
- add(parsed.internalDisableThinking === true, "--internal-disable-thinking");
5483
- add(parsed.internalCodexReasoningEffort !== void 0, "--internal-codex-reasoning-effort");
5484
- add(parsed.amaBenchJudgeProtocol !== void 0, "--ama-bench-judge-protocol");
5485
- add(parsed.amaBenchCrossJudgeProvider !== void 0, "--ama-bench-cross-judge-provider");
5486
- add(parsed.amaBenchCrossJudgeModel !== void 0, "--ama-bench-cross-judge-model");
5487
- add(parsed.amaBenchCrossJudgeBaseUrl !== void 0, "--ama-bench-cross-judge-base-url");
5488
- add(parsed.amaBenchCrossJudgeApiKey !== void 0, "--ama-bench-cross-judge-api-key");
5489
- add(parsed.amaBenchCrossJudgeCodexReasoningEffort !== void 0, "--ama-bench-cross-judge-codex-reasoning-effort");
5490
- add(parsed.disableThinking === true, "--disable-thinking");
5491
- add(parsed.requestTimeout !== void 0, "--request-timeout");
5492
- add(parsed.drainTimeout !== void 0, "--drain-timeout");
5493
- add(parsed.max429WaitMs !== void 0, "--max-429-wait");
5494
- add(parsed.publishedTrialLimit !== void 0, "--trial-limit");
5495
- add(parsed.publishedTrialConcurrency !== void 0, "--trial-concurrency");
5496
- add(parsed.publishedIngestConcurrency !== void 0, "--ingest-concurrency");
5497
- add(parsed.publishedTaskFilter !== void 0, "--task-filter");
5498
- add(parsed.publishedSeed !== void 0, "--seed");
5499
- return unsupported;
5500
- }
5501
- function createFallbackBenchOutputDir(resultsDir, benchmarkId, pid, startedAtMs = Date.now()) {
5502
- return path9.join(
5503
- resultsDir,
5504
- FALLBACK_RESULTS_DIRNAME,
5505
- `${benchmarkId}-${startedAtMs}-${pid}`
5506
- );
5507
- }
5508
- function resolveFallbackBenchResultPath(outputDir) {
5509
- const entries = fs22.readdirSync(outputDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort();
5510
- if (entries.length === 0) {
5511
- throw new Error(`Fallback benchmark runner did not write a JSON result artifact in ${outputDir}`);
5512
- }
5513
- return path9.join(outputDir, entries[0]);
5514
- }
5515
-
5516
5842
  // src/openclaw-upgrade-swap.ts
5517
5843
  import fs23 from "fs";
5518
5844
  import path10 from "path";
@@ -6254,7 +6580,7 @@ function resolveServerBin(options = {}) {
6254
6580
  return resolveServerBinDetails(options).path;
6255
6581
  }
6256
6582
  function readVerifiedDaemonPid(options) {
6257
- const readFileSync5 = options.readFileSync ?? fs26.readFileSync;
6583
+ const readFileSync4 = options.readFileSync ?? fs26.readFileSync;
6258
6584
  const unlinkSync = options.unlinkSync ?? fs26.unlinkSync;
6259
6585
  const processKill = options.processKill ?? process.kill;
6260
6586
  const platform = options.platform ?? process.platform;
@@ -6262,7 +6588,7 @@ function readVerifiedDaemonPid(options) {
6262
6588
  for (const file of options.pidFiles) {
6263
6589
  let pid;
6264
6590
  try {
6265
- pid = parseDaemonPid(readFileSync5(file, "utf8"));
6591
+ pid = parseDaemonPid(readFileSync4(file, "utf8"));
6266
6592
  } catch {
6267
6593
  continue;
6268
6594
  }
@@ -6356,7 +6682,7 @@ function removePidFileBestEffort(file, unlinkSync) {
6356
6682
  }
6357
6683
  function inspectLaunchdPlist(plistPath, options = {}) {
6358
6684
  const existsSync4 = options.existsSync ?? fs26.existsSync;
6359
- const readFileSync5 = options.readFileSync ?? fs26.readFileSync;
6685
+ const readFileSync4 = options.readFileSync ?? fs26.readFileSync;
6360
6686
  if (!existsSync4(plistPath)) {
6361
6687
  return {
6362
6688
  installed: false,
@@ -6367,7 +6693,7 @@ function inspectLaunchdPlist(plistPath, options = {}) {
6367
6693
  }
6368
6694
  let content;
6369
6695
  try {
6370
- content = readFileSync5(plistPath, "utf8");
6696
+ content = readFileSync4(plistPath, "utf8");
6371
6697
  } catch {
6372
6698
  return {
6373
6699
  installed: true,
@@ -6542,7 +6868,7 @@ import {
6542
6868
  } from "@remnic/core";
6543
6869
 
6544
6870
  // src/import-bundle-detect.ts
6545
- import { lstatSync as lstatSync2, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
6871
+ import { lstatSync as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
6546
6872
  import path13 from "path";
6547
6873
  function detectBundleEntries(bundleDir, options = {}) {
6548
6874
  const readdir3 = options.readdirImpl ?? defaultReaddir;
@@ -6551,7 +6877,7 @@ function detectBundleEntries(bundleDir, options = {}) {
6551
6877
  const isRegularFile = options.isRegularFileImpl ?? (options.readdirImpl !== void 0 || options.isDirectoryImpl !== void 0 ? (p) => !isDirectory2(p) : defaultIsRegularFile);
6552
6878
  if (options.readdirImpl === void 0 && options.isDirectoryImpl === void 0) {
6553
6879
  try {
6554
- const rootStat = lstatSync2(bundleDir);
6880
+ const rootStat = lstatSync3(bundleDir);
6555
6881
  if (rootStat.isSymbolicLink()) {
6556
6882
  throw new Error(
6557
6883
  `Bundle directory '${bundleDir}' is a symbolic link. Pass the resolved directory path instead.`
@@ -6664,11 +6990,11 @@ function defaultReaddir(dir) {
6664
6990
  return readdirSync2(dir);
6665
6991
  }
6666
6992
  function defaultReadFile(p) {
6667
- return readFileSync4(p, "utf-8");
6993
+ return readFileSync3(p, "utf-8");
6668
6994
  }
6669
6995
  function defaultIsDirectory(p) {
6670
6996
  try {
6671
- const s = lstatSync2(p);
6997
+ const s = lstatSync3(p);
6672
6998
  if (s.isSymbolicLink()) return false;
6673
6999
  return s.isDirectory();
6674
7000
  } catch {
@@ -6677,7 +7003,7 @@ function defaultIsDirectory(p) {
6677
7003
  }
6678
7004
  function defaultIsRegularFile(p) {
6679
7005
  try {
6680
- const s = lstatSync2(p);
7006
+ const s = lstatSync3(p);
6681
7007
  if (s.isSymbolicLink()) return false;
6682
7008
  return s.isFile();
6683
7009
  } catch {
@@ -8506,7 +8832,6 @@ var LOG_FILE = path18.join(PID_DIR, "server.log");
8506
8832
  var LEGACY_LOG_FILE = path18.join(LEGACY_PID_DIR, "server.log");
8507
8833
  var CLI_MODULE_DIR = path18.dirname(fileURLToPath5(import.meta.url));
8508
8834
  var CLI_REPO_ROOT = path18.resolve(CLI_MODULE_DIR, "../../..");
8509
- var EVAL_RUNNER_PATH = path18.join(CLI_REPO_ROOT, "evals", "run.ts");
8510
8835
  var OPENCLAW_GATEWAY_LABEL = "ai.openclaw.gateway";
8511
8836
  var CLI_SUCCESS_EXIT_GRACE_MS = 5e3;
8512
8837
  var CLI_OUTPUT_FLUSH_GRACE_MS = 250;
@@ -8666,15 +8991,13 @@ async function loadBenchDefinitionsFromPackage() {
8666
8991
  const result = benchModule.listBenchmarks();
8667
8992
  return Array.isArray(result) ? result : void 0;
8668
8993
  }
8994
+ var MISSING_BENCH_RUNTIME_HINT = "@remnic/bench. Build the workspace packages (or install @remnic/bench) and retry.";
8669
8995
  async function resolveAllBenchmarks() {
8670
8996
  const packageBenchmarks = await loadBenchDefinitionsFromPackage();
8671
8997
  if (packageBenchmarks) {
8672
8998
  return packageBenchmarks.filter((entry) => entry.runnerAvailable).map((entry) => entry.id);
8673
8999
  }
8674
- if (!fs29.existsSync(EVAL_RUNNER_PATH)) {
8675
- return [];
8676
- }
8677
- return BENCHMARK_CATALOG.filter((entry) => entry.category !== "ingestion").map((entry) => entry.id);
9000
+ return [];
8678
9001
  }
8679
9002
  async function resolveKnownBenchmarkIds() {
8680
9003
  const knownIds = new Set(BENCHMARK_IDS);
@@ -8686,65 +9009,6 @@ async function resolveKnownBenchmarkIds() {
8686
9009
  }
8687
9010
  return knownIds;
8688
9011
  }
8689
- async function runBenchViaFallback(parsed, benchmarkId, runtimeProfile) {
8690
- if (parsed.taskIdsFile) {
8691
- throw new Error(
8692
- "Fallback benchmark runner does not support hash-pinned LoCoMo task selection. Build/install @remnic/bench to use --task-ids-file."
8693
- );
8694
- }
8695
- if (runtimeProfile === "real" && parsed.remnicConfigPath) {
8696
- resolveExistingBenchRemnicConfigPath(parsed.remnicConfigPath);
8697
- }
8698
- if (runtimeProfile === "openclaw-chain" && parsed.openclawConfigPath) {
8699
- resolveExistingBenchOpenclawConfigPath(parsed.openclawConfigPath);
8700
- }
8701
- if (runtimeProfile === "real") {
8702
- throw new Error(
8703
- 'Fallback benchmark runner does not support --runtime-profile "real". Build/install @remnic/bench to use package-backed runtime profiles.'
8704
- );
8705
- }
8706
- if (runtimeProfile === "openclaw-chain") {
8707
- throw new Error(
8708
- 'Fallback benchmark runner does not support --runtime-profile "openclaw-chain". Build/install @remnic/bench to use package-backed runtime profiles.'
8709
- );
8710
- }
8711
- if (runtimeProfile === "local-lab") {
8712
- throw new Error(
8713
- 'Fallback benchmark runner does not support --runtime-profile "local-lab". Build/install @remnic/bench to use package-backed runtime profiles with local-lab manifests.'
8714
- );
8715
- }
8716
- const unsupportedOptions = findUnsupportedFallbackBenchOptions(parsed);
8717
- if (unsupportedOptions.length > 0) {
8718
- throw new Error(
8719
- `Fallback benchmark runner does not support provider-backed, gateway, or thinking/timeout flags (${unsupportedOptions.join(", ")}). Build/install @remnic/bench to use those options.`
8720
- );
8721
- }
8722
- if (!fs29.existsSync(EVAL_RUNNER_PATH)) {
8723
- console.error(
8724
- "Benchmark runner not found. Expected eval runner at evals/run.ts or a phase-1 @remnic/bench runtime export."
8725
- );
8726
- process.exit(1);
8727
- }
8728
- const tsxCandidates = [
8729
- path18.join(CLI_REPO_ROOT, "node_modules", ".bin", "tsx"),
8730
- path18.join(CLI_REPO_ROOT, "packages", "remnic-cli", "node_modules", ".bin", "tsx")
8731
- ];
8732
- const tsxCmd = tsxCandidates.find((candidate) => fs29.existsSync(candidate)) ?? "tsx";
8733
- const fallbackOutputDir = createFallbackBenchOutputDir(
8734
- parsed.resultsDir ?? resolveBenchOutputDir(),
8735
- benchmarkId,
8736
- process.pid
8737
- );
8738
- const fallbackArgs = [
8739
- EVAL_RUNNER_PATH,
8740
- ...buildBenchRunnerArgs(parsed, benchmarkId, fallbackOutputDir)
8741
- ];
8742
- childProcess2.execFileSync(tsxCmd, fallbackArgs, {
8743
- stdio: "inherit",
8744
- env: process.env
8745
- });
8746
- return resolveFallbackBenchResultPath(fallbackOutputDir);
8747
- }
8748
9012
  function resolveBenchOutputDir() {
8749
9013
  return path18.join(resolveHomeDir(), ".remnic", "bench", "results");
8750
9014
  }
@@ -9099,13 +9363,7 @@ async function launchBenchUi(resultsDir) {
9099
9363
  });
9100
9364
  });
9101
9365
  }
9102
- function resolveRepoDatasetRoot() {
9103
- const repoCandidate = path18.join(CLI_REPO_ROOT, "evals", "datasets");
9104
- if (isRepoCheckout()) {
9105
- return repoCandidate;
9106
- }
9107
- return path18.join(resolveHomeDir(), ".remnic", "bench", "datasets");
9108
- }
9366
+ var BENCH_DATASET_ROOT = path18.join(resolveHomeDir(), ".remnic", "bench", "datasets");
9109
9367
  function listDownloadableBenchmarks() {
9110
9368
  return [...DOWNLOADABLE_BENCHMARK_DATASETS];
9111
9369
  }
@@ -9114,10 +9372,7 @@ function resolveDatasetDownloadScriptPath() {
9114
9372
  if (fs29.existsSync(bundled)) {
9115
9373
  return bundled;
9116
9374
  }
9117
- return path18.join(CLI_REPO_ROOT, "evals", "scripts", "download-datasets.sh");
9118
- }
9119
- function isRepoCheckout() {
9120
- return fs29.existsSync(path18.join(CLI_REPO_ROOT, "pnpm-workspace.yaml")) && fs29.existsSync(path18.join(CLI_REPO_ROOT, "evals", "scripts", "download-datasets.sh"));
9375
+ return path18.join(CLI_REPO_ROOT, "packages", "remnic-cli", "assets", "download-datasets.sh");
9121
9376
  }
9122
9377
  function runDatasetDownloadScript(scriptPath, benchmarkId, datasetRoot, jsonMode) {
9123
9378
  const stdio = jsonMode ? ["inherit", process.stderr, "inherit"] : "inherit";
@@ -9168,7 +9423,7 @@ function resolveBenchDatasetDir(benchmarkId, quick, datasetDirOverride) {
9168
9423
  if (quick) {
9169
9424
  return void 0;
9170
9425
  }
9171
- const datasetDir = path18.join(resolveRepoDatasetRoot(), benchmarkId);
9426
+ const datasetDir = path18.join(BENCH_DATASET_ROOT, benchmarkId);
9172
9427
  if (isDatasetDownloaded(datasetDir, benchmarkId)) {
9173
9428
  return datasetDir;
9174
9429
  }
@@ -9438,7 +9693,7 @@ async function exportBenchPackageResult(parsed) {
9438
9693
  process.stdout.write(rendered);
9439
9694
  }
9440
9695
  async function manageBenchDatasets(parsed) {
9441
- const datasetRoot = resolveRepoDatasetRoot();
9696
+ const datasetRoot = BENCH_DATASET_ROOT;
9442
9697
  const supported = listDownloadableBenchmarks();
9443
9698
  if (parsed.datasetAction === "status") {
9444
9699
  if (parsed.benchmarks.length > 0 || parsed.all) {
@@ -10187,7 +10442,7 @@ async function loadPublishedPromotionHelpers() {
10187
10442
  const benchModule = await loadBenchModule();
10188
10443
  return {
10189
10444
  async promoteArtifactsToPublished(args) {
10190
- const { mkdirSync, readFileSync: readFileSync5, writeFileSync } = await import("fs");
10445
+ const { mkdirSync, readFileSync: readFileSync4, writeFileSync } = await import("fs");
10191
10446
  const path19 = await import("path");
10192
10447
  mkdirSync(args.publishedOutDir, { recursive: true });
10193
10448
  if (args.artifactPaths.length === 0) {
@@ -10197,7 +10452,7 @@ async function loadPublishedPromotionHelpers() {
10197
10452
  return;
10198
10453
  }
10199
10454
  for (const artifactPath of args.artifactPaths) {
10200
- const raw = readFileSync5(artifactPath, "utf8");
10455
+ const raw = readFileSync4(artifactPath, "utf8");
10201
10456
  const parsedUnknown = JSON.parse(raw);
10202
10457
  const parsedObj = parsedUnknown !== null && typeof parsedUnknown === "object" && !Array.isArray(parsedUnknown) ? parsedUnknown : {};
10203
10458
  const gitShaShort = (parsedObj.meta?.gitSha ?? "unknown").slice(0, 7);
@@ -11596,7 +11851,7 @@ async function cmdQuery(queryText, json, explain) {
11596
11851
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
11597
11852
  const remnicCfg = resolveRemnicConfigRecord16(raw);
11598
11853
  const config = parseConfig17(remnicCfg);
11599
- const orchestrator = new Orchestrator10(config);
11854
+ const orchestrator = new Orchestrator11(config);
11600
11855
  await orchestrator.initialize();
11601
11856
  const service = new EngramAccessService2(orchestrator);
11602
11857
  const recallRequest = buildQueryRecallRequest(queryText);
@@ -11776,7 +12031,7 @@ async function cmdXray(rest) {
11776
12031
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
11777
12032
  const remnicCfg = resolveRemnicConfigRecord16(raw);
11778
12033
  const config = parseConfig17(remnicCfg);
11779
- const orchestrator = new Orchestrator10(config);
12034
+ const orchestrator = new Orchestrator11(config);
11780
12035
  await orchestrator.initialize();
11781
12036
  await orchestrator.deferredReady;
11782
12037
  const service = new EngramAccessService2(orchestrator);
@@ -11807,7 +12062,7 @@ async function withLocalService(fn) {
11807
12062
  initLogger5();
11808
12063
  const configPath = resolveConfigPath();
11809
12064
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
11810
- const orchestrator = new Orchestrator10(parseConfig17(resolveRemnicConfigRecord16(raw)));
12065
+ const orchestrator = new Orchestrator11(parseConfig17(resolveRemnicConfigRecord16(raw)));
11811
12066
  await orchestrator.initialize();
11812
12067
  await orchestrator.deferredReady;
11813
12068
  const service = new EngramAccessService2(orchestrator);
@@ -11985,7 +12240,7 @@ async function cmdEnrich(rest) {
11985
12240
  pipelineConfig2.providers = [
11986
12241
  { id: "web-search", enabled: true, costTier: "cheap" }
11987
12242
  ];
11988
- const orchestrator2 = new Orchestrator10(config);
12243
+ const orchestrator2 = new Orchestrator11(config);
11989
12244
  await orchestrator2.initialize();
11990
12245
  await orchestrator2.deferredReady;
11991
12246
  const searchBackend2 = orchestrator2.qmd;
@@ -12021,7 +12276,7 @@ Registered providers:`);
12021
12276
  console.error("Usage: remnic enrich <entity-name> | --all | --dry-run | audit | providers");
12022
12277
  process.exit(1);
12023
12278
  }
12024
- const orchestrator = new Orchestrator10(config);
12279
+ const orchestrator = new Orchestrator11(config);
12025
12280
  await orchestrator.initialize();
12026
12281
  await orchestrator.deferredReady;
12027
12282
  const storage = await orchestrator.getStorage(config.defaultNamespace);
@@ -12298,7 +12553,7 @@ async function cmdBriefing(rest) {
12298
12553
  process.exit(1);
12299
12554
  }
12300
12555
  const format = effectiveFormatFlag === "json" ? "json" : effectiveFormatFlag === "markdown" ? "markdown" : config.briefing.defaultFormat;
12301
- const orchestrator = new Orchestrator10(config);
12556
+ const orchestrator = new Orchestrator11(config);
12302
12557
  await orchestrator.initialize();
12303
12558
  const storage = await orchestrator.getStorage(config.defaultNamespace);
12304
12559
  const calendarSource = config.briefing.calendarSource ? new FileCalendarSource(config.briefing.calendarSource) : void 0;
@@ -12751,7 +13006,7 @@ async function cmdReview(action, rest) {
12751
13006
  console.error("Usage: remnic review <approve|dismiss|flag> <id>");
12752
13007
  process.exit(1);
12753
13008
  }
12754
- const storage = new StorageManager3(memoryDir);
13009
+ const storage = new StorageManager4(memoryDir);
12755
13010
  const configPath = resolveConfigPath();
12756
13011
  let tombstonesConfig = null;
12757
13012
  try {
@@ -12877,14 +13132,25 @@ function resolveOfflineRemoteUrl(args) {
12877
13132
  }
12878
13133
  return parsed;
12879
13134
  }
13135
+ var OFFLINE_TOKEN_ENV_NAMES = ["REMNIC_OFFLINE_TOKEN", "REMNIC_AUTH_TOKEN", "ENGRAM_AUTH_TOKEN"];
12880
13136
  function resolveOfflineToken(args) {
12881
- const token = resolveRequiredValueFlag(args, "--token") ?? process.env.REMNIC_OFFLINE_TOKEN ?? process.env.REMNIC_AUTH_TOKEN ?? process.env.ENGRAM_AUTH_TOKEN;
12882
- if (!token || token.trim().length === 0) {
12883
- throw new Error(
12884
- "offline mode requires --token <token>, REMNIC_OFFLINE_TOKEN, or REMNIC_AUTH_TOKEN"
13137
+ const channel = resolveCredentialChannel(
13138
+ {
13139
+ argvToken: resolveRequiredValueFlag(args, "--token"),
13140
+ tokenFile: resolveRequiredValueFlag(args, "--token-file"),
13141
+ envNames: OFFLINE_TOKEN_ENV_NAMES
13142
+ },
13143
+ process.env
13144
+ );
13145
+ if (!channel.ok) {
13146
+ throw new Error(`offline: ${channel.error}`);
13147
+ }
13148
+ if (channel.tokenFromArgv) {
13149
+ process.stderr.write(
13150
+ "offline: note: --token is argv-visible; prefer --token-file or REMNIC_OFFLINE_TOKEN\n"
12885
13151
  );
12886
13152
  }
12887
- return token.trim();
13153
+ return channel.token?.trim();
12888
13154
  }
12889
13155
  function offlineEndpoint(remoteUrl, pathname, params = {}) {
12890
13156
  const url = new URL(remoteUrl);
@@ -14600,7 +14866,9 @@ async function cmdOffline(action, rest, json) {
14600
14866
 
14601
14867
  Options:
14602
14868
  --remote-url <url> Remote Remnic server URL, e.g. http://home:4242 (--remote alias accepted)
14603
- --token <token> Bearer token for the remote server
14869
+ --token <token> Bearer token for the remote server (argv-visible;
14870
+ prefer --token-file or the env fallbacks)
14871
+ --token-file <path> Read the bearer token from a 0600 regular file
14604
14872
  --namespace <name> Namespace to sync
14605
14873
  --memory-dir <dir> Local memory dir (defaults to resolved memoryDir)
14606
14874
  --state <path> Override offline sync state file
@@ -14612,7 +14880,9 @@ Options:
14612
14880
  --json JSON output
14613
14881
 
14614
14882
  Environment fallbacks:
14615
- REMNIC_OFFLINE_REMOTE_URL, REMNIC_OFFLINE_TOKEN, REMNIC_AUTH_TOKEN`);
14883
+ REMNIC_OFFLINE_REMOTE_URL, REMNIC_OFFLINE_TOKEN, REMNIC_AUTH_TOKEN,
14884
+ ENGRAM_AUTH_TOKEN (legacy). Token precedence: --token > --token-file >
14885
+ REMNIC_OFFLINE_TOKEN > REMNIC_AUTH_TOKEN > ENGRAM_AUTH_TOKEN.`);
14616
14886
  return;
14617
14887
  }
14618
14888
  const memoryDir = path18.resolve(expandTilde(resolveRequiredValueFlag(rest, "--memory-dir") ?? resolveMemoryDir()));
@@ -14634,7 +14904,8 @@ Environment fallbacks:
14634
14904
  const impressionRotation = resolveOfflineImpressionRotation(configPath);
14635
14905
  const needsRemote = action === "prepare" || action === "sync" || action === "watch";
14636
14906
  const remoteUrl = needsRemote ? resolveOfflineRemoteUrl(rest) : resolveOptionalOfflineRemoteUrl(rest);
14637
- const token = needsRemote ? resolveOfflineToken(rest) : void 0;
14907
+ const knownAction = needsRemote || action === "status";
14908
+ const token = knownAction ? resolveOfflineToken(rest) : void 0;
14638
14909
  const statePath = statePathExplicit ? path18.resolve(expandTilde(stateOverride)) : remoteUrl !== void 0 ? defaultOfflineSyncStatePath(memoryDir, remoteUrl, namespace) : void 0;
14639
14910
  if (action === "prepare") {
14640
14911
  if (!remoteUrl || !token || !statePath) throw new Error("offline prepare requires remote URL and token");
@@ -15206,7 +15477,7 @@ async function cmdConnectors(action, rest, json) {
15206
15477
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
15207
15478
  const remnicCfg = resolveRemnicConfigRecord16(raw);
15208
15479
  const config = parseConfig17(remnicCfg);
15209
- const orchestrator = new Orchestrator10(config);
15480
+ const orchestrator = new Orchestrator11(config);
15210
15481
  try {
15211
15482
  await orchestrator.initialize();
15212
15483
  await orchestrator.deferredReady;
@@ -15562,7 +15833,7 @@ async function cmdLegacyBenchmark(action, rest, json) {
15562
15833
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
15563
15834
  const remnicCfg = resolveRemnicConfigRecord16(raw);
15564
15835
  const config = parseConfig17(remnicCfg);
15565
- const orchestrator = new Orchestrator10(config);
15836
+ const orchestrator = new Orchestrator11(config);
15566
15837
  const service = new EngramAccessService2(orchestrator);
15567
15838
  const { runBenchSuite, loadBaseline, checkRegression } = await loadBenchModule();
15568
15839
  const benchConfig = {
@@ -15729,6 +16000,10 @@ async function cmdBench(rest) {
15729
16000
  }
15730
16001
  return;
15731
16002
  }
16003
+ if (parsed.all && !await tryLoadBenchModule()) {
16004
+ console.error(`ERROR: bench run --all requires ${MISSING_BENCH_RUNTIME_HINT}`);
16005
+ process.exit(1);
16006
+ }
15732
16007
  let selectedBenchmarks = parsed.all ? await resolveAllBenchmarks() : parsed.benchmarks;
15733
16008
  if (selectedBenchmarks.length === 0) {
15734
16009
  console.error(
@@ -15839,12 +16114,9 @@ async function cmdBench(rest) {
15839
16114
  } catch {
15840
16115
  }
15841
16116
  } else {
15842
- const fallbackResultPath = await runBenchViaFallback(parsed, benchmarkId, runtimeProfile);
15843
- writtenPaths.push(fallbackResultPath);
15844
- try {
15845
- await updateBenchmarkCompleted(benchStatusPath, statusId, fallbackResultPath);
15846
- } catch {
15847
- }
16117
+ throw new Error(
16118
+ `Benchmark "${benchmarkId}" requires ${MISSING_BENCH_RUNTIME_HINT}`
16119
+ );
15848
16120
  }
15849
16121
  } catch (err) {
15850
16122
  const message = err instanceof Error ? err.message : String(err);
@@ -17783,7 +18055,7 @@ Other:
17783
18055
  const raw = fs29.existsSync(configPath) ? JSON.parse(fs29.readFileSync(configPath, "utf8")) : {};
17784
18056
  const remnicCfg = resolveRemnicConfigRecord16(raw);
17785
18057
  const config = parseConfig17(remnicCfg);
17786
- orchestratorSingleton = new Orchestrator10(config);
18058
+ orchestratorSingleton = new Orchestrator11(config);
17787
18059
  await orchestratorSingleton.initialize();
17788
18060
  await orchestratorSingleton.deferredReady;
17789
18061
  }