@askexenow/exe-os 0.9.66 → 0.9.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/deploy/stack-manifests/v0.9.json +54 -5
  2. package/dist/bin/age-ontology-load.js +61 -0
  3. package/dist/bin/agentic-ontology-backfill.js +4708 -0
  4. package/dist/bin/agentic-reflection-backfill.js +4144 -0
  5. package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1523 -2275
  6. package/dist/bin/backfill-conversations.js +506 -20
  7. package/dist/bin/backfill-responses.js +506 -20
  8. package/dist/bin/backfill-vectors.js +233 -20
  9. package/dist/bin/bulk-sync-postgres.js +4876 -0
  10. package/dist/bin/cleanup-stale-review-tasks.js +507 -21
  11. package/dist/bin/cli.js +2450 -1530
  12. package/dist/bin/exe-assign.js +506 -20
  13. package/dist/bin/exe-boot.js +410 -60
  14. package/dist/bin/exe-cloud.js +795 -105
  15. package/dist/bin/exe-dispatch.js +516 -22
  16. package/dist/bin/exe-doctor.js +587 -30
  17. package/dist/bin/exe-export-behaviors.js +518 -24
  18. package/dist/bin/exe-forget.js +507 -21
  19. package/dist/bin/exe-gateway.js +571 -25
  20. package/dist/bin/exe-heartbeat.js +518 -24
  21. package/dist/bin/exe-kill.js +507 -21
  22. package/dist/bin/exe-launch-agent.js +2312 -1069
  23. package/dist/bin/exe-new-employee.js +197 -165
  24. package/dist/bin/exe-pending-messages.js +507 -21
  25. package/dist/bin/exe-pending-notifications.js +507 -21
  26. package/dist/bin/exe-pending-reviews.js +507 -21
  27. package/dist/bin/exe-rename.js +507 -21
  28. package/dist/bin/exe-review.js +507 -21
  29. package/dist/bin/exe-search.js +518 -24
  30. package/dist/bin/exe-session-cleanup.js +516 -22
  31. package/dist/bin/exe-settings.js +4 -0
  32. package/dist/bin/exe-start-codex.js +682 -143
  33. package/dist/bin/exe-start-opencode.js +627 -79
  34. package/dist/bin/exe-status.js +507 -21
  35. package/dist/bin/exe-team.js +507 -21
  36. package/dist/bin/git-sweep.js +516 -22
  37. package/dist/bin/graph-backfill.js +558 -21
  38. package/dist/bin/graph-export.js +507 -21
  39. package/dist/bin/graph-layer-benchmark.js +109 -0
  40. package/dist/bin/install.js +305 -288
  41. package/dist/bin/intercom-check.js +516 -22
  42. package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
  43. package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
  44. package/dist/bin/scan-tasks.js +516 -22
  45. package/dist/bin/setup.js +412 -62
  46. package/dist/bin/shard-migrate.js +506 -20
  47. package/dist/gateway/index.js +569 -23
  48. package/dist/hooks/bug-report-worker.js +519 -25
  49. package/dist/hooks/codex-stop-task-finalizer.js +516 -22
  50. package/dist/hooks/commit-complete.js +516 -22
  51. package/dist/hooks/error-recall.js +518 -24
  52. package/dist/hooks/ingest.js +516 -22
  53. package/dist/hooks/instructions-loaded.js +507 -21
  54. package/dist/hooks/notification.js +507 -21
  55. package/dist/hooks/post-compact.js +507 -21
  56. package/dist/hooks/post-tool-combined.js +519 -25
  57. package/dist/hooks/pre-compact.js +516 -22
  58. package/dist/hooks/pre-tool-use.js +507 -21
  59. package/dist/hooks/prompt-submit.js +519 -25
  60. package/dist/hooks/session-end.js +516 -22
  61. package/dist/hooks/session-start.js +520 -26
  62. package/dist/hooks/stop.js +517 -23
  63. package/dist/hooks/subagent-stop.js +507 -21
  64. package/dist/hooks/summary-worker.js +411 -61
  65. package/dist/index.js +569 -23
  66. package/dist/lib/cloud-sync.js +391 -53
  67. package/dist/lib/config.js +13 -1
  68. package/dist/lib/consolidation.js +1 -1
  69. package/dist/lib/database.js +124 -0
  70. package/dist/lib/db.js +124 -0
  71. package/dist/lib/device-registry.js +124 -0
  72. package/dist/lib/embedder.js +13 -1
  73. package/dist/lib/exe-daemon.js +2184 -561
  74. package/dist/lib/hybrid-search.js +518 -24
  75. package/dist/lib/identity.js +3 -0
  76. package/dist/lib/keychain.js +178 -22
  77. package/dist/lib/messaging.js +3 -0
  78. package/dist/lib/reminders.js +3 -0
  79. package/dist/lib/schedules.js +233 -20
  80. package/dist/lib/skill-learning.js +16 -1
  81. package/dist/lib/store.js +506 -20
  82. package/dist/lib/tasks.js +16 -1
  83. package/dist/lib/tmux-routing.js +16 -1
  84. package/dist/lib/token-spend.js +3 -0
  85. package/dist/mcp/server.js +1757 -428
  86. package/dist/mcp/tools/complete-reminder.js +3 -0
  87. package/dist/mcp/tools/create-reminder.js +3 -0
  88. package/dist/mcp/tools/create-task.js +16 -1
  89. package/dist/mcp/tools/deactivate-behavior.js +3 -0
  90. package/dist/mcp/tools/list-reminders.js +3 -0
  91. package/dist/mcp/tools/list-tasks.js +3 -0
  92. package/dist/mcp/tools/send-message.js +3 -0
  93. package/dist/mcp/tools/update-task.js +16 -1
  94. package/dist/runtime/index.js +516 -22
  95. package/dist/tui/App.js +594 -29
  96. package/package.json +8 -5
  97. package/src/commands/exe/cloud.md +6 -10
  98. package/stack.release.json +3 -3
  99. package/src/commands/exe/link.md +0 -18
package/dist/bin/setup.js CHANGED
@@ -173,6 +173,10 @@ async function loadConfig() {
173
173
  if (config.dbPath.startsWith("~")) {
174
174
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
175
175
  }
176
+ const envDbPath = path.join(dir, "memories.db");
177
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
178
+ config.dbPath = envDbPath;
179
+ }
176
180
  return config;
177
181
  } catch {
178
182
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -193,7 +197,15 @@ function loadConfigSync() {
193
197
  normalizeSessionLifecycle(migratedCfg);
194
198
  normalizeAutoUpdate(migratedCfg);
195
199
  normalizeOrchestration(migratedCfg);
196
- return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
200
+ const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
201
+ if (config.dbPath.startsWith("~")) {
202
+ config.dbPath = config.dbPath.replace(/^~/, os.homedir());
203
+ }
204
+ const envDbPath = path.join(dir, "memories.db");
205
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
206
+ config.dbPath = envDbPath;
207
+ }
208
+ return config;
197
209
  } catch {
198
210
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
199
211
  }
@@ -310,12 +322,13 @@ var keychain_exports = {};
310
322
  __export(keychain_exports, {
311
323
  deleteMasterKey: () => deleteMasterKey,
312
324
  exportMnemonic: () => exportMnemonic,
325
+ getKeyStorageInfo: () => getKeyStorageInfo,
313
326
  getMasterKey: () => getMasterKey,
314
327
  importMnemonic: () => importMnemonic,
315
328
  setMasterKey: () => setMasterKey
316
329
  });
317
330
  import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
318
- import { existsSync as existsSync3 } from "fs";
331
+ import { existsSync as existsSync3, statSync } from "fs";
319
332
  import { execSync } from "child_process";
320
333
  import path2 from "path";
321
334
  import os2 from "os";
@@ -325,29 +338,65 @@ function getKeyDir() {
325
338
  function getKeyPath() {
326
339
  return path2.join(getKeyDir(), "master.key");
327
340
  }
328
- function macKeychainGet() {
341
+ function nativeKeychainAllowed() {
342
+ return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
343
+ }
344
+ function linuxSecretAvailable() {
345
+ if (!nativeKeychainAllowed()) return false;
346
+ if (process.platform !== "linux") return false;
347
+ if (linuxSecretAvailability !== null) return linuxSecretAvailability;
348
+ try {
349
+ execSync("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
350
+ } catch {
351
+ linuxSecretAvailability = false;
352
+ return false;
353
+ }
354
+ try {
355
+ execSync("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
356
+ linuxSecretAvailability = true;
357
+ } catch {
358
+ linuxSecretAvailability = false;
359
+ }
360
+ return linuxSecretAvailability;
361
+ }
362
+ function isRootOnlyTrustedServerKeyFile(keyPath) {
363
+ if (process.platform !== "linux") return false;
364
+ try {
365
+ const uid = typeof os2.userInfo().uid === "number" ? os2.userInfo().uid : -1;
366
+ const st = statSync(keyPath);
367
+ if (!st.isFile() || (st.mode & 63) !== 0) return false;
368
+ if (uid === 0) return true;
369
+ const exeOsDir = process.env.EXE_OS_DIR;
370
+ return Boolean(exeOsDir && path2.resolve(keyPath).startsWith(path2.resolve(exeOsDir) + path2.sep));
371
+ } catch {
372
+ return false;
373
+ }
374
+ }
375
+ function macKeychainGet(service = SERVICE) {
376
+ if (!nativeKeychainAllowed()) return null;
329
377
  if (process.platform !== "darwin") return null;
330
378
  try {
331
379
  return execSync(
332
- `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
380
+ `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
333
381
  { encoding: "utf-8", timeout: 5e3 }
334
382
  ).trim();
335
383
  } catch {
336
384
  return null;
337
385
  }
338
386
  }
339
- function macKeychainSet(value) {
387
+ function macKeychainSet(value, service = SERVICE) {
388
+ if (!nativeKeychainAllowed()) return false;
340
389
  if (process.platform !== "darwin") return false;
341
390
  try {
342
391
  try {
343
392
  execSync(
344
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
393
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
345
394
  { timeout: 5e3 }
346
395
  );
347
396
  } catch {
348
397
  }
349
398
  execSync(
350
- `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
399
+ `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
351
400
  { timeout: 5e3 }
352
401
  );
353
402
  return true;
@@ -355,11 +404,12 @@ function macKeychainSet(value) {
355
404
  return false;
356
405
  }
357
406
  }
358
- function macKeychainDelete() {
407
+ function macKeychainDelete(service = SERVICE) {
408
+ if (!nativeKeychainAllowed()) return false;
359
409
  if (process.platform !== "darwin") return false;
360
410
  try {
361
411
  execSync(
362
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
412
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
363
413
  { timeout: 5e3 }
364
414
  );
365
415
  return true;
@@ -367,22 +417,22 @@ function macKeychainDelete() {
367
417
  return false;
368
418
  }
369
419
  }
370
- function linuxSecretGet() {
371
- if (process.platform !== "linux") return null;
420
+ function linuxSecretGet(service = SERVICE) {
421
+ if (!linuxSecretAvailable()) return null;
372
422
  try {
373
423
  return execSync(
374
- `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
424
+ `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
375
425
  { encoding: "utf-8", timeout: 5e3 }
376
426
  ).trim();
377
427
  } catch {
378
428
  return null;
379
429
  }
380
430
  }
381
- function linuxSecretSet(value) {
382
- if (process.platform !== "linux") return false;
431
+ function linuxSecretSet(value, service = SERVICE) {
432
+ if (!linuxSecretAvailable()) return false;
383
433
  try {
384
434
  execSync(
385
- `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
435
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
386
436
  { timeout: 5e3 }
387
437
  );
388
438
  return true;
@@ -390,11 +440,12 @@ function linuxSecretSet(value) {
390
440
  return false;
391
441
  }
392
442
  }
393
- function linuxSecretDelete() {
443
+ function linuxSecretDelete(service = SERVICE) {
444
+ if (!nativeKeychainAllowed()) return false;
394
445
  if (process.platform !== "linux") return false;
395
446
  try {
396
447
  execSync(
397
- `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
448
+ `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
398
449
  { timeout: 5e3 }
399
450
  );
400
451
  return true;
@@ -403,6 +454,7 @@ function linuxSecretDelete() {
403
454
  }
404
455
  }
405
456
  async function tryKeytar() {
457
+ if (!nativeKeychainAllowed()) return null;
406
458
  try {
407
459
  return await import("keytar");
408
460
  } catch {
@@ -476,7 +528,19 @@ async function writeMachineBoundFileFallback(b64) {
476
528
  return "plaintext";
477
529
  }
478
530
  async function getMasterKey() {
479
- const nativeValue = macKeychainGet() ?? linuxSecretGet();
531
+ let nativeValue = macKeychainGet() ?? linuxSecretGet();
532
+ if (!nativeValue) {
533
+ const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
534
+ if (legacyValue) {
535
+ const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
536
+ if (migrated) {
537
+ macKeychainDelete(LEGACY_SERVICE);
538
+ linuxSecretDelete(LEGACY_SERVICE);
539
+ process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
540
+ }
541
+ nativeValue = legacyValue;
542
+ }
543
+ }
480
544
  if (nativeValue) {
481
545
  return Buffer.from(nativeValue, "base64");
482
546
  }
@@ -484,12 +548,17 @@ async function getMasterKey() {
484
548
  if (keytar) {
485
549
  try {
486
550
  const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
487
- if (keytarValue) {
488
- const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
551
+ const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
552
+ if (legacyKeytarValue) {
553
+ const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
489
554
  if (migrated) {
490
555
  process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
556
+ try {
557
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
558
+ } catch {
559
+ }
491
560
  }
492
- return Buffer.from(keytarValue, "base64");
561
+ return Buffer.from(legacyKeytarValue, "base64");
493
562
  }
494
563
  } catch {
495
564
  }
@@ -514,7 +583,7 @@ async function getMasterKey() {
514
583
  const decrypted = decryptWithMachineKey(content, machineKey);
515
584
  if (!decrypted) {
516
585
  process.stderr.write(
517
- "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
586
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
518
587
  );
519
588
  return null;
520
589
  }
@@ -523,6 +592,9 @@ async function getMasterKey() {
523
592
  b64Value = content;
524
593
  }
525
594
  const key = Buffer.from(b64Value, "base64");
595
+ if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
596
+ return key;
597
+ }
526
598
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
527
599
  if (migrated) {
528
600
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
@@ -550,6 +622,97 @@ async function getMasterKey() {
550
622
  return null;
551
623
  }
552
624
  }
625
+ async function getKeyStorageInfo() {
626
+ if (macKeychainGet()) {
627
+ return {
628
+ kind: "macos-keychain",
629
+ secure: true,
630
+ note: "stored in macOS Keychain via built-in security CLI"
631
+ };
632
+ }
633
+ if (macKeychainGet(LEGACY_SERVICE)) {
634
+ return {
635
+ kind: "macos-keychain",
636
+ secure: true,
637
+ note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
638
+ };
639
+ }
640
+ if (linuxSecretGet()) {
641
+ return {
642
+ kind: "linux-secret-service",
643
+ secure: true,
644
+ note: "stored in Linux Secret Service via secret-tool"
645
+ };
646
+ }
647
+ if (linuxSecretGet(LEGACY_SERVICE)) {
648
+ return {
649
+ kind: "linux-secret-service",
650
+ secure: true,
651
+ note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
652
+ };
653
+ }
654
+ const keytar = await tryKeytar();
655
+ if (keytar) {
656
+ try {
657
+ if (await keytar.getPassword(SERVICE, ACCOUNT)) {
658
+ return {
659
+ kind: "legacy-keytar",
660
+ secure: true,
661
+ note: "stored in legacy keytar backend; will migrate to native keychain when possible"
662
+ };
663
+ }
664
+ if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
665
+ return {
666
+ kind: "legacy-keytar",
667
+ secure: true,
668
+ note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
669
+ };
670
+ }
671
+ } catch {
672
+ }
673
+ }
674
+ const keyPath = getKeyPath();
675
+ if (!existsSync3(keyPath)) {
676
+ return {
677
+ kind: "missing",
678
+ secure: false,
679
+ path: keyPath,
680
+ note: "no key found in OS keychain, legacy keytar, or file fallback"
681
+ };
682
+ }
683
+ try {
684
+ const content = (await readFile2(keyPath, "utf-8")).trim();
685
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
686
+ return {
687
+ kind: "encrypted-file",
688
+ secure: true,
689
+ path: keyPath,
690
+ note: "stored in machine-bound encrypted file fallback"
691
+ };
692
+ }
693
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
694
+ return {
695
+ kind: "server-secret-file",
696
+ secure: true,
697
+ path: keyPath,
698
+ note: "stored as root-only trusted server secret file"
699
+ };
700
+ }
701
+ return {
702
+ kind: "plaintext-file",
703
+ secure: false,
704
+ path: keyPath,
705
+ note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
706
+ };
707
+ } catch {
708
+ return {
709
+ kind: "missing",
710
+ secure: false,
711
+ path: keyPath,
712
+ note: "key file exists but could not be read"
713
+ };
714
+ }
715
+ }
553
716
  async function setMasterKey(key) {
554
717
  const b64 = key.toString("base64");
555
718
  if (macKeychainSet(b64) || linuxSecretSet(b64)) {
@@ -575,10 +738,13 @@ async function setMasterKey(key) {
575
738
  async function deleteMasterKey() {
576
739
  macKeychainDelete();
577
740
  linuxSecretDelete();
741
+ macKeychainDelete(LEGACY_SERVICE);
742
+ linuxSecretDelete(LEGACY_SERVICE);
578
743
  const keytar = await tryKeytar();
579
744
  if (keytar) {
580
745
  try {
581
746
  await keytar.deletePassword(SERVICE, ACCOUNT);
747
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
582
748
  } catch {
583
749
  }
584
750
  }
@@ -616,12 +782,14 @@ async function importMnemonic(mnemonic) {
616
782
  const entropy = mnemonicToEntropy(trimmed);
617
783
  return Buffer.from(entropy, "hex");
618
784
  }
619
- var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
785
+ var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
620
786
  var init_keychain = __esm({
621
787
  "src/lib/keychain.ts"() {
622
788
  "use strict";
623
- SERVICE = "exe-mem";
789
+ SERVICE = "exe-os";
790
+ LEGACY_SERVICE = "exe-mem";
624
791
  ACCOUNT = "master-key";
792
+ linuxSecretAvailability = null;
625
793
  ENCRYPTED_PREFIX = "enc:";
626
794
  }
627
795
  });
@@ -677,7 +845,7 @@ import net from "net";
677
845
  import os3 from "os";
678
846
  import { spawn } from "child_process";
679
847
  import { randomUUID } from "crypto";
680
- import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
848
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync as statSync2 } from "fs";
681
849
  import path5 from "path";
682
850
  import { fileURLToPath } from "url";
683
851
  function handleData(chunk) {
@@ -827,7 +995,7 @@ function acquireSpawnLock() {
827
995
  return true;
828
996
  } catch {
829
997
  try {
830
- const stat = statSync(SPAWN_LOCK_PATH);
998
+ const stat = statSync2(SPAWN_LOCK_PATH);
831
999
  if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
832
1000
  try {
833
1001
  unlinkSync2(SPAWN_LOCK_PATH);
@@ -988,7 +1156,7 @@ function killAndRespawnDaemon() {
988
1156
  }
989
1157
  function isDaemonTooYoung() {
990
1158
  try {
991
- const stat = statSync(PID_PATH);
1159
+ const stat = statSync2(PID_PATH);
992
1160
  return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
993
1161
  } catch {
994
1162
  return false;
@@ -1433,8 +1601,8 @@ async function validateLicense(apiKey, deviceId) {
1433
1601
  }
1434
1602
  function getCacheAgeMs() {
1435
1603
  try {
1436
- const { statSync: statSync4 } = __require("fs");
1437
- const s = statSync4(CACHE_PATH);
1604
+ const { statSync: statSync5 } = __require("fs");
1605
+ const s = statSync5(CACHE_PATH);
1438
1606
  return Date.now() - s.mtimeMs;
1439
1607
  } catch {
1440
1608
  return Infinity;
@@ -2984,6 +3152,9 @@ function getClient() {
2984
3152
  if (_daemonClient && _daemonClient._isDaemonActive()) {
2985
3153
  return _daemonClient;
2986
3154
  }
3155
+ if (!_resilientClient) {
3156
+ return _adapterClient;
3157
+ }
2987
3158
  return _resilientClient;
2988
3159
  }
2989
3160
  async function initDaemonClient() {
@@ -4016,6 +4187,127 @@ async function ensureSchema() {
4016
4187
  VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
4017
4188
  END;
4018
4189
  `);
4190
+ await client.executeMultiple(`
4191
+ CREATE TABLE IF NOT EXISTS agent_sessions (
4192
+ id TEXT PRIMARY KEY,
4193
+ agent_id TEXT NOT NULL,
4194
+ project_name TEXT,
4195
+ started_at TEXT NOT NULL,
4196
+ last_event_at TEXT NOT NULL,
4197
+ event_count INTEGER NOT NULL DEFAULT 0,
4198
+ properties TEXT DEFAULT '{}'
4199
+ );
4200
+
4201
+ CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
4202
+ ON agent_sessions(agent_id, started_at);
4203
+
4204
+ CREATE TABLE IF NOT EXISTS agent_goals (
4205
+ id TEXT PRIMARY KEY,
4206
+ statement TEXT NOT NULL,
4207
+ owner_agent_id TEXT,
4208
+ project_name TEXT,
4209
+ status TEXT NOT NULL DEFAULT 'open',
4210
+ priority INTEGER NOT NULL DEFAULT 5,
4211
+ success_criteria TEXT,
4212
+ parent_goal_id TEXT,
4213
+ due_at TEXT,
4214
+ achieved_at TEXT,
4215
+ supersedes_id TEXT,
4216
+ created_at TEXT NOT NULL,
4217
+ updated_at TEXT NOT NULL,
4218
+ source_memory_id TEXT
4219
+ );
4220
+
4221
+ CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
4222
+ ON agent_goals(project_name, status, priority);
4223
+
4224
+ CREATE TABLE IF NOT EXISTS agent_events (
4225
+ id TEXT PRIMARY KEY,
4226
+ event_type TEXT NOT NULL,
4227
+ occurred_at TEXT NOT NULL,
4228
+ sequence_index INTEGER NOT NULL,
4229
+ actor_agent_id TEXT,
4230
+ agent_role TEXT,
4231
+ project_name TEXT,
4232
+ session_id TEXT,
4233
+ task_id TEXT,
4234
+ goal_id TEXT,
4235
+ parent_event_id TEXT,
4236
+ intention TEXT,
4237
+ outcome TEXT,
4238
+ evidence_memory_id TEXT,
4239
+ impact TEXT,
4240
+ payload TEXT DEFAULT '{}',
4241
+ created_at TEXT NOT NULL
4242
+ );
4243
+
4244
+ CREATE INDEX IF NOT EXISTS idx_agent_events_time
4245
+ ON agent_events(occurred_at, sequence_index);
4246
+
4247
+ CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
4248
+ ON agent_events(session_id, sequence_index);
4249
+
4250
+ CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
4251
+ ON agent_events(goal_id, occurred_at);
4252
+
4253
+ CREATE INDEX IF NOT EXISTS idx_agent_events_memory
4254
+ ON agent_events(evidence_memory_id);
4255
+
4256
+ CREATE TABLE IF NOT EXISTS agent_goal_links (
4257
+ id TEXT PRIMARY KEY,
4258
+ goal_id TEXT NOT NULL,
4259
+ link_type TEXT NOT NULL,
4260
+ target_id TEXT NOT NULL,
4261
+ target_type TEXT NOT NULL,
4262
+ created_at TEXT NOT NULL
4263
+ );
4264
+
4265
+ CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
4266
+ ON agent_goal_links(goal_id, target_type);
4267
+
4268
+ CREATE TABLE IF NOT EXISTS agent_semantic_labels (
4269
+ id TEXT PRIMARY KEY,
4270
+ source_memory_id TEXT NOT NULL,
4271
+ event_id TEXT,
4272
+ labeler TEXT NOT NULL,
4273
+ schema_version INTEGER NOT NULL DEFAULT 1,
4274
+ confidence REAL NOT NULL DEFAULT 0,
4275
+ labels TEXT NOT NULL,
4276
+ created_at TEXT NOT NULL,
4277
+ updated_at TEXT NOT NULL
4278
+ );
4279
+
4280
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
4281
+ ON agent_semantic_labels(source_memory_id, labeler);
4282
+
4283
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
4284
+ ON agent_semantic_labels(event_id);
4285
+
4286
+ CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
4287
+ id TEXT PRIMARY KEY,
4288
+ project_name TEXT,
4289
+ session_id TEXT,
4290
+ window_start_at TEXT NOT NULL,
4291
+ window_end_at TEXT NOT NULL,
4292
+ event_count INTEGER NOT NULL DEFAULT 0,
4293
+ goal_count INTEGER NOT NULL DEFAULT 0,
4294
+ success_count INTEGER NOT NULL DEFAULT 0,
4295
+ failure_count INTEGER NOT NULL DEFAULT 0,
4296
+ risk_count INTEGER NOT NULL DEFAULT 0,
4297
+ summary TEXT NOT NULL,
4298
+ learnings TEXT NOT NULL DEFAULT '[]',
4299
+ next_actions TEXT NOT NULL DEFAULT '[]',
4300
+ evidence_event_ids TEXT NOT NULL DEFAULT '[]',
4301
+ confidence REAL NOT NULL DEFAULT 0,
4302
+ created_at TEXT NOT NULL
4303
+ );
4304
+
4305
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
4306
+ ON agent_reflection_checkpoints(project_name, window_end_at);
4307
+
4308
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
4309
+ ON agent_reflection_checkpoints(session_id, window_end_at);
4310
+ `);
4019
4311
  try {
4020
4312
  await client.execute({
4021
4313
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4420,7 +4712,7 @@ __export(db_backup_exports, {
4420
4712
  listBackups: () => listBackups,
4421
4713
  rotateBackups: () => rotateBackups
4422
4714
  });
4423
- import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync2 } from "fs";
4715
+ import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync3 } from "fs";
4424
4716
  import path11 from "path";
4425
4717
  function findActiveDb() {
4426
4718
  for (const name of DB_NAMES) {
@@ -4464,7 +4756,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
4464
4756
  if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
4465
4757
  const filePath = path11.join(BACKUP_DIR, file);
4466
4758
  try {
4467
- const stat = statSync2(filePath);
4759
+ const stat = statSync3(filePath);
4468
4760
  if (stat.mtimeMs < cutoff) {
4469
4761
  unlinkSync5(filePath);
4470
4762
  deleted++;
@@ -4482,7 +4774,7 @@ function listBackups() {
4482
4774
  const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
4483
4775
  return files.map((name) => {
4484
4776
  const p = path11.join(BACKUP_DIR, name);
4485
- const stat = statSync2(p);
4777
+ const stat = statSync3(p);
4486
4778
  return { path: p, name, size: stat.size, date: stat.mtime };
4487
4779
  }).sort((a, b) => b.date.getTime() - a.date.getTime());
4488
4780
  } catch {
@@ -4515,10 +4807,10 @@ var init_db_backup = __esm({
4515
4807
  // src/lib/cloud-sync.ts
4516
4808
  var cloud_sync_exports = {};
4517
4809
  __export(cloud_sync_exports, {
4518
- CLOUD_RELINK_REQUIRED_MESSAGE: () => CLOUD_RELINK_REQUIRED_MESSAGE,
4810
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE: () => CLOUD_REUPLOAD_REQUIRED_MESSAGE,
4519
4811
  assertSecureEndpoint: () => assertSecureEndpoint,
4520
4812
  buildRosterBlob: () => buildRosterBlob,
4521
- clearCloudRelinkRequired: () => clearCloudRelinkRequired,
4813
+ clearCloudReuploadRequired: () => clearCloudReuploadRequired,
4522
4814
  cloudPull: () => cloudPull,
4523
4815
  cloudPullBehaviors: () => cloudPullBehaviors,
4524
4816
  cloudPullBlob: () => cloudPullBlob,
@@ -4537,14 +4829,16 @@ __export(cloud_sync_exports, {
4537
4829
  cloudPushGraphRAG: () => cloudPushGraphRAG,
4538
4830
  cloudPushRoster: () => cloudPushRoster,
4539
4831
  cloudPushTasks: () => cloudPushTasks,
4832
+ cloudResetMemoryBlobs: () => cloudResetMemoryBlobs,
4540
4833
  cloudSync: () => cloudSync,
4541
- getCloudRelinkRequired: () => getCloudRelinkRequired,
4834
+ getCloudReuploadRequired: () => getCloudReuploadRequired,
4835
+ markCloudReuploadRequired: () => markCloudReuploadRequired,
4542
4836
  mergeConfig: () => mergeConfig,
4543
4837
  mergeRosterFromRemote: () => mergeRosterFromRemote,
4544
4838
  pushToPostgres: () => pushToPostgres,
4545
4839
  recordRosterDeletion: () => recordRosterDeletion
4546
4840
  });
4547
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as statSync3 } from "fs";
4841
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as statSync4 } from "fs";
4548
4842
  import crypto3 from "crypto";
4549
4843
  import path12 from "path";
4550
4844
  import { homedir as homedir2 } from "os";
@@ -4591,18 +4885,36 @@ function loadPgClient() {
4591
4885
  const { pathToFileURL: pathToFileURL3 } = await import("url");
4592
4886
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4593
4887
  if (explicitPath) {
4594
- const mod2 = await import(pathToFileURL3(explicitPath).href);
4595
- const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4596
- if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4597
- return new Ctor2();
4888
+ const mod = await import(pathToFileURL3(explicitPath).href);
4889
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4890
+ if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
4891
+ return new Ctor();
4598
4892
  }
4599
4893
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path12.join(homedir2(), "exe-db");
4600
- const req = createRequire3(path12.join(exeDbRoot, "package.json"));
4601
- const entry = req.resolve("@prisma/client");
4602
- const mod = await import(pathToFileURL3(entry).href);
4603
- const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4604
- if (!Ctor) throw new Error("No PrismaClient");
4605
- return new Ctor();
4894
+ const packagePath = path12.join(exeDbRoot, "package.json");
4895
+ if (existsSync12(packagePath)) {
4896
+ const req = createRequire3(packagePath);
4897
+ const entry = req.resolve("@prisma/client");
4898
+ const mod = await import(pathToFileURL3(entry).href);
4899
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4900
+ if (!Ctor) throw new Error("No PrismaClient");
4901
+ return new Ctor();
4902
+ }
4903
+ const { Pool } = await import("pg");
4904
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
4905
+ return {
4906
+ async $queryRawUnsafe(query, ...values) {
4907
+ const result = await pool.query(query, values);
4908
+ return result.rows;
4909
+ },
4910
+ async $executeRawUnsafe(query, ...values) {
4911
+ const result = await pool.query(query, values);
4912
+ return result.rowCount ?? 0;
4913
+ },
4914
+ async $disconnect() {
4915
+ await pool.end();
4916
+ }
4917
+ };
4606
4918
  })().catch(() => {
4607
4919
  _pgFailed = true;
4608
4920
  _pgPromise = null;
@@ -4623,7 +4935,7 @@ async function pushToPostgres(records) {
4623
4935
  let inserted = 0;
4624
4936
  for (const rec of records) {
4625
4937
  try {
4626
- await prisma.$executeRawUnsafe(
4938
+ const changed = await prisma.$executeRawUnsafe(
4627
4939
  `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
4628
4940
  VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
4629
4941
  ON CONFLICT (source, source_id, event_type) DO NOTHING`,
@@ -4632,7 +4944,7 @@ async function pushToPostgres(records) {
4632
4944
  JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
4633
4945
  rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
4634
4946
  );
4635
- inserted++;
4947
+ inserted += Number(changed ?? 0);
4636
4948
  } catch {
4637
4949
  }
4638
4950
  }
@@ -4737,6 +5049,23 @@ async function cloudPush(records, maxVersion, config) {
4737
5049
  return false;
4738
5050
  }
4739
5051
  }
5052
+ async function cloudResetMemoryBlobs(config) {
5053
+ assertSecureEndpoint(config.endpoint);
5054
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
5055
+ method: "POST",
5056
+ headers: {
5057
+ Authorization: `Bearer ${config.apiKey}`,
5058
+ "Content-Type": "application/json",
5059
+ "X-Device-Id": loadDeviceId()
5060
+ },
5061
+ body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
5062
+ });
5063
+ if (!resp.ok) {
5064
+ throw new Error(`cloud reset failed: HTTP ${resp.status}`);
5065
+ }
5066
+ const data = await resp.json();
5067
+ return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
5068
+ }
4740
5069
  async function cloudPull(sinceVersion, config) {
4741
5070
  assertSecureEndpoint(config.endpoint);
4742
5071
  try {
@@ -4756,40 +5085,61 @@ async function cloudPull(sinceVersion, config) {
4756
5085
  if (!response.ok) return { records: [], maxVersion: sinceVersion };
4757
5086
  const data = await response.json();
4758
5087
  const allRecords = [];
4759
- for (const { blob } of data.blobs ?? []) {
5088
+ let maxReadableVersion = sinceVersion;
5089
+ let skippedBlobs = 0;
5090
+ for (const { version, blob } of data.blobs ?? []) {
4760
5091
  try {
4761
5092
  const compressed = decryptSyncBlob(blob);
4762
5093
  const json = decompress(compressed).toString("utf8");
4763
5094
  const records = JSON.parse(json);
4764
5095
  allRecords.push(...records);
5096
+ const recordMax = records.reduce((max, rec) => {
5097
+ const v = Number(rec.version ?? 0);
5098
+ return Number.isFinite(v) ? Math.max(max, v) : max;
5099
+ }, 0);
5100
+ const blobVersion = Number(version ?? 0);
5101
+ maxReadableVersion = Math.max(
5102
+ maxReadableVersion,
5103
+ Number.isFinite(blobVersion) ? blobVersion : 0,
5104
+ recordMax
5105
+ );
4765
5106
  } catch {
5107
+ skippedBlobs++;
4766
5108
  continue;
4767
5109
  }
4768
5110
  }
4769
- return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
5111
+ if (skippedBlobs > 0) {
5112
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
5113
+ }
5114
+ return { records: allRecords, maxVersion: maxReadableVersion };
4770
5115
  } catch (err) {
4771
5116
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
4772
5117
  return { records: [], maxVersion: sinceVersion };
4773
5118
  }
4774
5119
  }
4775
- async function getCloudRelinkRequired(client = getClient()) {
5120
+ async function getCloudReuploadRequired(client = getClient()) {
4776
5121
  try {
4777
5122
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
4778
- const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
4779
- return String(relink.rows[0]?.value ?? "") === "1";
5123
+ const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
5124
+ return result.rows.some((row) => String(row.value ?? "") === "1");
4780
5125
  } catch {
4781
5126
  return false;
4782
5127
  }
4783
5128
  }
4784
- async function clearCloudRelinkRequired(client = getClient()) {
5129
+ async function clearCloudReuploadRequired(client = getClient()) {
4785
5130
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5131
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
4786
5132
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
4787
5133
  await client.execute({
4788
- sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relinked_at', ?)",
5134
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
4789
5135
  args: [(/* @__PURE__ */ new Date()).toISOString()]
4790
5136
  });
4791
5137
  await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
4792
5138
  }
5139
+ async function markCloudReuploadRequired(client = getClient()) {
5140
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5141
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
5142
+ }
4793
5143
  async function cloudSync(config) {
4794
5144
  if (!isSyncCryptoInitialized()) {
4795
5145
  try {
@@ -4811,10 +5161,10 @@ async function cloudSync(config) {
4811
5161
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
4812
5162
  }
4813
5163
  try {
4814
- if (await getCloudRelinkRequired(client)) throw new Error(CLOUD_RELINK_REQUIRED_MESSAGE);
5164
+ if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
4815
5165
  } catch (err) {
4816
5166
  const msg = err instanceof Error ? err.message : String(err);
4817
- if (msg.includes("Paused after key rotation")) throw err;
5167
+ if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
4818
5168
  }
4819
5169
  try {
4820
5170
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -5072,7 +5422,7 @@ async function cloudSync(config) {
5072
5422
  const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
5073
5423
  const latestBackup = getLatestBackup2();
5074
5424
  if (latestBackup) {
5075
- const backupSize = statSync3(latestBackup).size;
5425
+ const backupSize = statSync4(latestBackup).size;
5076
5426
  const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
5077
5427
  if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
5078
5428
  const backupData = readFileSync8(latestBackup);
@@ -5743,7 +6093,7 @@ async function cloudPullDocuments(config) {
5743
6093
  }
5744
6094
  return { pulled };
5745
6095
  }
5746
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_RELINK_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
6096
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
5747
6097
  var init_cloud_sync = __esm({
5748
6098
  "src/lib/cloud-sync.ts"() {
5749
6099
  "use strict";
@@ -5762,7 +6112,7 @@ var init_cloud_sync = __esm({
5762
6112
  LOCK_STALE_MS = 3e4;
5763
6113
  _pgPromise = null;
5764
6114
  _pgFailed = false;
5765
- CLOUD_RELINK_REQUIRED_MESSAGE = "[cloud-sync] Paused after key rotation. Run `exe-os cloud relink --dry-run` for the safe relink checklist.";
6115
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
5766
6116
  ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
5767
6117
  }
5768
6118
  });
@@ -7822,8 +8172,8 @@ async function validateModel(log) {
7822
8172
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
7823
8173
  const modelPath = path17.join(MODELS_DIR, LOCAL_FILENAME);
7824
8174
  if (existsSync17(modelPath)) {
7825
- const { statSync: statSync4 } = await import("fs");
7826
- const size = statSync4(modelPath).size;
8175
+ const { statSync: statSync5 } = await import("fs");
8176
+ const size = statSync5(modelPath).size;
7827
8177
  if (size > 300 * 1e6) {
7828
8178
  log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
7829
8179
  return;