@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
@@ -184,6 +184,10 @@ async function loadConfig() {
184
184
  if (config.dbPath.startsWith("~")) {
185
185
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
186
186
  }
187
+ const envDbPath = path.join(dir, "memories.db");
188
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
189
+ config.dbPath = envDbPath;
190
+ }
187
191
  return config;
188
192
  } catch {
189
193
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -204,7 +208,15 @@ function loadConfigSync() {
204
208
  normalizeSessionLifecycle(migratedCfg);
205
209
  normalizeAutoUpdate(migratedCfg);
206
210
  normalizeOrchestration(migratedCfg);
207
- return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
211
+ const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
212
+ if (config.dbPath.startsWith("~")) {
213
+ config.dbPath = config.dbPath.replace(/^~/, os.homedir());
214
+ }
215
+ const envDbPath = path.join(dir, "memories.db");
216
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
217
+ config.dbPath = envDbPath;
218
+ }
219
+ return config;
208
220
  } catch {
209
221
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
210
222
  }
@@ -1823,6 +1835,9 @@ function getClient() {
1823
1835
  if (_daemonClient && _daemonClient._isDaemonActive()) {
1824
1836
  return _daemonClient;
1825
1837
  }
1838
+ if (!_resilientClient) {
1839
+ return _adapterClient;
1840
+ }
1826
1841
  return _resilientClient;
1827
1842
  }
1828
1843
  async function initDaemonClient() {
@@ -2855,6 +2870,127 @@ async function ensureSchema() {
2855
2870
  VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2856
2871
  END;
2857
2872
  `);
2873
+ await client.executeMultiple(`
2874
+ CREATE TABLE IF NOT EXISTS agent_sessions (
2875
+ id TEXT PRIMARY KEY,
2876
+ agent_id TEXT NOT NULL,
2877
+ project_name TEXT,
2878
+ started_at TEXT NOT NULL,
2879
+ last_event_at TEXT NOT NULL,
2880
+ event_count INTEGER NOT NULL DEFAULT 0,
2881
+ properties TEXT DEFAULT '{}'
2882
+ );
2883
+
2884
+ CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
2885
+ ON agent_sessions(agent_id, started_at);
2886
+
2887
+ CREATE TABLE IF NOT EXISTS agent_goals (
2888
+ id TEXT PRIMARY KEY,
2889
+ statement TEXT NOT NULL,
2890
+ owner_agent_id TEXT,
2891
+ project_name TEXT,
2892
+ status TEXT NOT NULL DEFAULT 'open',
2893
+ priority INTEGER NOT NULL DEFAULT 5,
2894
+ success_criteria TEXT,
2895
+ parent_goal_id TEXT,
2896
+ due_at TEXT,
2897
+ achieved_at TEXT,
2898
+ supersedes_id TEXT,
2899
+ created_at TEXT NOT NULL,
2900
+ updated_at TEXT NOT NULL,
2901
+ source_memory_id TEXT
2902
+ );
2903
+
2904
+ CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
2905
+ ON agent_goals(project_name, status, priority);
2906
+
2907
+ CREATE TABLE IF NOT EXISTS agent_events (
2908
+ id TEXT PRIMARY KEY,
2909
+ event_type TEXT NOT NULL,
2910
+ occurred_at TEXT NOT NULL,
2911
+ sequence_index INTEGER NOT NULL,
2912
+ actor_agent_id TEXT,
2913
+ agent_role TEXT,
2914
+ project_name TEXT,
2915
+ session_id TEXT,
2916
+ task_id TEXT,
2917
+ goal_id TEXT,
2918
+ parent_event_id TEXT,
2919
+ intention TEXT,
2920
+ outcome TEXT,
2921
+ evidence_memory_id TEXT,
2922
+ impact TEXT,
2923
+ payload TEXT DEFAULT '{}',
2924
+ created_at TEXT NOT NULL
2925
+ );
2926
+
2927
+ CREATE INDEX IF NOT EXISTS idx_agent_events_time
2928
+ ON agent_events(occurred_at, sequence_index);
2929
+
2930
+ CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
2931
+ ON agent_events(session_id, sequence_index);
2932
+
2933
+ CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
2934
+ ON agent_events(goal_id, occurred_at);
2935
+
2936
+ CREATE INDEX IF NOT EXISTS idx_agent_events_memory
2937
+ ON agent_events(evidence_memory_id);
2938
+
2939
+ CREATE TABLE IF NOT EXISTS agent_goal_links (
2940
+ id TEXT PRIMARY KEY,
2941
+ goal_id TEXT NOT NULL,
2942
+ link_type TEXT NOT NULL,
2943
+ target_id TEXT NOT NULL,
2944
+ target_type TEXT NOT NULL,
2945
+ created_at TEXT NOT NULL
2946
+ );
2947
+
2948
+ CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
2949
+ ON agent_goal_links(goal_id, target_type);
2950
+
2951
+ CREATE TABLE IF NOT EXISTS agent_semantic_labels (
2952
+ id TEXT PRIMARY KEY,
2953
+ source_memory_id TEXT NOT NULL,
2954
+ event_id TEXT,
2955
+ labeler TEXT NOT NULL,
2956
+ schema_version INTEGER NOT NULL DEFAULT 1,
2957
+ confidence REAL NOT NULL DEFAULT 0,
2958
+ labels TEXT NOT NULL,
2959
+ created_at TEXT NOT NULL,
2960
+ updated_at TEXT NOT NULL
2961
+ );
2962
+
2963
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
2964
+ ON agent_semantic_labels(source_memory_id, labeler);
2965
+
2966
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
2967
+ ON agent_semantic_labels(event_id);
2968
+
2969
+ CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
2970
+ id TEXT PRIMARY KEY,
2971
+ project_name TEXT,
2972
+ session_id TEXT,
2973
+ window_start_at TEXT NOT NULL,
2974
+ window_end_at TEXT NOT NULL,
2975
+ event_count INTEGER NOT NULL DEFAULT 0,
2976
+ goal_count INTEGER NOT NULL DEFAULT 0,
2977
+ success_count INTEGER NOT NULL DEFAULT 0,
2978
+ failure_count INTEGER NOT NULL DEFAULT 0,
2979
+ risk_count INTEGER NOT NULL DEFAULT 0,
2980
+ summary TEXT NOT NULL,
2981
+ learnings TEXT NOT NULL DEFAULT '[]',
2982
+ next_actions TEXT NOT NULL DEFAULT '[]',
2983
+ evidence_event_ids TEXT NOT NULL DEFAULT '[]',
2984
+ confidence REAL NOT NULL DEFAULT 0,
2985
+ created_at TEXT NOT NULL
2986
+ );
2987
+
2988
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
2989
+ ON agent_reflection_checkpoints(project_name, window_end_at);
2990
+
2991
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
2992
+ ON agent_reflection_checkpoints(session_id, window_end_at);
2993
+ `);
2858
2994
  try {
2859
2995
  await client.execute({
2860
2996
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -3274,12 +3410,13 @@ var keychain_exports = {};
3274
3410
  __export(keychain_exports, {
3275
3411
  deleteMasterKey: () => deleteMasterKey,
3276
3412
  exportMnemonic: () => exportMnemonic,
3413
+ getKeyStorageInfo: () => getKeyStorageInfo,
3277
3414
  getMasterKey: () => getMasterKey,
3278
3415
  importMnemonic: () => importMnemonic,
3279
3416
  setMasterKey: () => setMasterKey
3280
3417
  });
3281
3418
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3282
- import { existsSync as existsSync6 } from "fs";
3419
+ import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3283
3420
  import { execSync as execSync2 } from "child_process";
3284
3421
  import path6 from "path";
3285
3422
  import os5 from "os";
@@ -3289,29 +3426,65 @@ function getKeyDir() {
3289
3426
  function getKeyPath() {
3290
3427
  return path6.join(getKeyDir(), "master.key");
3291
3428
  }
3292
- function macKeychainGet() {
3429
+ function nativeKeychainAllowed() {
3430
+ return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
3431
+ }
3432
+ function linuxSecretAvailable() {
3433
+ if (!nativeKeychainAllowed()) return false;
3434
+ if (process.platform !== "linux") return false;
3435
+ if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3436
+ try {
3437
+ execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3438
+ } catch {
3439
+ linuxSecretAvailability = false;
3440
+ return false;
3441
+ }
3442
+ try {
3443
+ execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3444
+ linuxSecretAvailability = true;
3445
+ } catch {
3446
+ linuxSecretAvailability = false;
3447
+ }
3448
+ return linuxSecretAvailability;
3449
+ }
3450
+ function isRootOnlyTrustedServerKeyFile(keyPath) {
3451
+ if (process.platform !== "linux") return false;
3452
+ try {
3453
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3454
+ const st = statSync2(keyPath);
3455
+ if (!st.isFile() || (st.mode & 63) !== 0) return false;
3456
+ if (uid === 0) return true;
3457
+ const exeOsDir = process.env.EXE_OS_DIR;
3458
+ return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3459
+ } catch {
3460
+ return false;
3461
+ }
3462
+ }
3463
+ function macKeychainGet(service = SERVICE) {
3464
+ if (!nativeKeychainAllowed()) return null;
3293
3465
  if (process.platform !== "darwin") return null;
3294
3466
  try {
3295
3467
  return execSync2(
3296
- `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
3468
+ `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3297
3469
  { encoding: "utf-8", timeout: 5e3 }
3298
3470
  ).trim();
3299
3471
  } catch {
3300
3472
  return null;
3301
3473
  }
3302
3474
  }
3303
- function macKeychainSet(value) {
3475
+ function macKeychainSet(value, service = SERVICE) {
3476
+ if (!nativeKeychainAllowed()) return false;
3304
3477
  if (process.platform !== "darwin") return false;
3305
3478
  try {
3306
3479
  try {
3307
3480
  execSync2(
3308
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3481
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3309
3482
  { timeout: 5e3 }
3310
3483
  );
3311
3484
  } catch {
3312
3485
  }
3313
3486
  execSync2(
3314
- `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
3487
+ `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3315
3488
  { timeout: 5e3 }
3316
3489
  );
3317
3490
  return true;
@@ -3319,11 +3492,12 @@ function macKeychainSet(value) {
3319
3492
  return false;
3320
3493
  }
3321
3494
  }
3322
- function macKeychainDelete() {
3495
+ function macKeychainDelete(service = SERVICE) {
3496
+ if (!nativeKeychainAllowed()) return false;
3323
3497
  if (process.platform !== "darwin") return false;
3324
3498
  try {
3325
3499
  execSync2(
3326
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3500
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3327
3501
  { timeout: 5e3 }
3328
3502
  );
3329
3503
  return true;
@@ -3331,22 +3505,22 @@ function macKeychainDelete() {
3331
3505
  return false;
3332
3506
  }
3333
3507
  }
3334
- function linuxSecretGet() {
3335
- if (process.platform !== "linux") return null;
3508
+ function linuxSecretGet(service = SERVICE) {
3509
+ if (!linuxSecretAvailable()) return null;
3336
3510
  try {
3337
3511
  return execSync2(
3338
- `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3512
+ `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3339
3513
  { encoding: "utf-8", timeout: 5e3 }
3340
3514
  ).trim();
3341
3515
  } catch {
3342
3516
  return null;
3343
3517
  }
3344
3518
  }
3345
- function linuxSecretSet(value) {
3346
- if (process.platform !== "linux") return false;
3519
+ function linuxSecretSet(value, service = SERVICE) {
3520
+ if (!linuxSecretAvailable()) return false;
3347
3521
  try {
3348
3522
  execSync2(
3349
- `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
3523
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3350
3524
  { timeout: 5e3 }
3351
3525
  );
3352
3526
  return true;
@@ -3354,11 +3528,12 @@ function linuxSecretSet(value) {
3354
3528
  return false;
3355
3529
  }
3356
3530
  }
3357
- function linuxSecretDelete() {
3531
+ function linuxSecretDelete(service = SERVICE) {
3532
+ if (!nativeKeychainAllowed()) return false;
3358
3533
  if (process.platform !== "linux") return false;
3359
3534
  try {
3360
3535
  execSync2(
3361
- `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3536
+ `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3362
3537
  { timeout: 5e3 }
3363
3538
  );
3364
3539
  return true;
@@ -3367,6 +3542,7 @@ function linuxSecretDelete() {
3367
3542
  }
3368
3543
  }
3369
3544
  async function tryKeytar() {
3545
+ if (!nativeKeychainAllowed()) return null;
3370
3546
  try {
3371
3547
  return await import("keytar");
3372
3548
  } catch {
@@ -3440,7 +3616,19 @@ async function writeMachineBoundFileFallback(b64) {
3440
3616
  return "plaintext";
3441
3617
  }
3442
3618
  async function getMasterKey() {
3443
- const nativeValue = macKeychainGet() ?? linuxSecretGet();
3619
+ let nativeValue = macKeychainGet() ?? linuxSecretGet();
3620
+ if (!nativeValue) {
3621
+ const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
3622
+ if (legacyValue) {
3623
+ const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
3624
+ if (migrated) {
3625
+ macKeychainDelete(LEGACY_SERVICE);
3626
+ linuxSecretDelete(LEGACY_SERVICE);
3627
+ process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
3628
+ }
3629
+ nativeValue = legacyValue;
3630
+ }
3631
+ }
3444
3632
  if (nativeValue) {
3445
3633
  return Buffer.from(nativeValue, "base64");
3446
3634
  }
@@ -3448,12 +3636,17 @@ async function getMasterKey() {
3448
3636
  if (keytar) {
3449
3637
  try {
3450
3638
  const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
3451
- if (keytarValue) {
3452
- const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
3639
+ const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
3640
+ if (legacyKeytarValue) {
3641
+ const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
3453
3642
  if (migrated) {
3454
3643
  process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
3644
+ try {
3645
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
3646
+ } catch {
3647
+ }
3455
3648
  }
3456
- return Buffer.from(keytarValue, "base64");
3649
+ return Buffer.from(legacyKeytarValue, "base64");
3457
3650
  }
3458
3651
  } catch {
3459
3652
  }
@@ -3478,7 +3671,7 @@ async function getMasterKey() {
3478
3671
  const decrypted = decryptWithMachineKey(content, machineKey);
3479
3672
  if (!decrypted) {
3480
3673
  process.stderr.write(
3481
- "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
3674
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
3482
3675
  );
3483
3676
  return null;
3484
3677
  }
@@ -3487,6 +3680,9 @@ async function getMasterKey() {
3487
3680
  b64Value = content;
3488
3681
  }
3489
3682
  const key = Buffer.from(b64Value, "base64");
3683
+ if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
3684
+ return key;
3685
+ }
3490
3686
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3491
3687
  if (migrated) {
3492
3688
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
@@ -3514,6 +3710,97 @@ async function getMasterKey() {
3514
3710
  return null;
3515
3711
  }
3516
3712
  }
3713
+ async function getKeyStorageInfo() {
3714
+ if (macKeychainGet()) {
3715
+ return {
3716
+ kind: "macos-keychain",
3717
+ secure: true,
3718
+ note: "stored in macOS Keychain via built-in security CLI"
3719
+ };
3720
+ }
3721
+ if (macKeychainGet(LEGACY_SERVICE)) {
3722
+ return {
3723
+ kind: "macos-keychain",
3724
+ secure: true,
3725
+ note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
3726
+ };
3727
+ }
3728
+ if (linuxSecretGet()) {
3729
+ return {
3730
+ kind: "linux-secret-service",
3731
+ secure: true,
3732
+ note: "stored in Linux Secret Service via secret-tool"
3733
+ };
3734
+ }
3735
+ if (linuxSecretGet(LEGACY_SERVICE)) {
3736
+ return {
3737
+ kind: "linux-secret-service",
3738
+ secure: true,
3739
+ note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
3740
+ };
3741
+ }
3742
+ const keytar = await tryKeytar();
3743
+ if (keytar) {
3744
+ try {
3745
+ if (await keytar.getPassword(SERVICE, ACCOUNT)) {
3746
+ return {
3747
+ kind: "legacy-keytar",
3748
+ secure: true,
3749
+ note: "stored in legacy keytar backend; will migrate to native keychain when possible"
3750
+ };
3751
+ }
3752
+ if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
3753
+ return {
3754
+ kind: "legacy-keytar",
3755
+ secure: true,
3756
+ note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
3757
+ };
3758
+ }
3759
+ } catch {
3760
+ }
3761
+ }
3762
+ const keyPath = getKeyPath();
3763
+ if (!existsSync6(keyPath)) {
3764
+ return {
3765
+ kind: "missing",
3766
+ secure: false,
3767
+ path: keyPath,
3768
+ note: "no key found in OS keychain, legacy keytar, or file fallback"
3769
+ };
3770
+ }
3771
+ try {
3772
+ const content = (await readFile3(keyPath, "utf-8")).trim();
3773
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
3774
+ return {
3775
+ kind: "encrypted-file",
3776
+ secure: true,
3777
+ path: keyPath,
3778
+ note: "stored in machine-bound encrypted file fallback"
3779
+ };
3780
+ }
3781
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3782
+ return {
3783
+ kind: "server-secret-file",
3784
+ secure: true,
3785
+ path: keyPath,
3786
+ note: "stored as root-only trusted server secret file"
3787
+ };
3788
+ }
3789
+ return {
3790
+ kind: "plaintext-file",
3791
+ secure: false,
3792
+ path: keyPath,
3793
+ note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
3794
+ };
3795
+ } catch {
3796
+ return {
3797
+ kind: "missing",
3798
+ secure: false,
3799
+ path: keyPath,
3800
+ note: "key file exists but could not be read"
3801
+ };
3802
+ }
3803
+ }
3517
3804
  async function setMasterKey(key) {
3518
3805
  const b64 = key.toString("base64");
3519
3806
  if (macKeychainSet(b64) || linuxSecretSet(b64)) {
@@ -3539,10 +3826,13 @@ async function setMasterKey(key) {
3539
3826
  async function deleteMasterKey() {
3540
3827
  macKeychainDelete();
3541
3828
  linuxSecretDelete();
3829
+ macKeychainDelete(LEGACY_SERVICE);
3830
+ linuxSecretDelete(LEGACY_SERVICE);
3542
3831
  const keytar = await tryKeytar();
3543
3832
  if (keytar) {
3544
3833
  try {
3545
3834
  await keytar.deletePassword(SERVICE, ACCOUNT);
3835
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
3546
3836
  } catch {
3547
3837
  }
3548
3838
  }
@@ -3580,12 +3870,14 @@ async function importMnemonic(mnemonic) {
3580
3870
  const entropy = mnemonicToEntropy(trimmed);
3581
3871
  return Buffer.from(entropy, "hex");
3582
3872
  }
3583
- var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
3873
+ var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
3584
3874
  var init_keychain = __esm({
3585
3875
  "src/lib/keychain.ts"() {
3586
3876
  "use strict";
3587
- SERVICE = "exe-mem";
3877
+ SERVICE = "exe-os";
3878
+ LEGACY_SERVICE = "exe-mem";
3588
3879
  ACCOUNT = "master-key";
3880
+ linuxSecretAvailability = null;
3589
3881
  ENCRYPTED_PREFIX = "enc:";
3590
3882
  }
3591
3883
  });
@@ -3669,7 +3961,7 @@ __export(shard_manager_exports, {
3669
3961
  shardExists: () => shardExists
3670
3962
  });
3671
3963
  import path7 from "path";
3672
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync2 } from "fs";
3964
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
3673
3965
  import { createClient as createClient2 } from "@libsql/client";
3674
3966
  function initShardManager(encryptionKey) {
3675
3967
  _encryptionKey = encryptionKey;
@@ -3733,7 +4025,7 @@ async function auditShardHealth(options = {}) {
3733
4025
  const shards = [];
3734
4026
  for (const name of names) {
3735
4027
  const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3736
- const stat = statSync2(dbPath);
4028
+ const stat = statSync3(dbPath);
3737
4029
  const item = {
3738
4030
  name,
3739
4031
  path: dbPath,
@@ -3986,7 +4278,7 @@ async function getReadyShardClient(projectName) {
3986
4278
  _shardLastAccess.delete(safeName);
3987
4279
  const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3988
4280
  if (existsSync7(dbPath)) {
3989
- const stat = statSync2(dbPath);
4281
+ const stat = statSync3(dbPath);
3990
4282
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3991
4283
  const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3992
4284
  renameSync3(dbPath, archivedPath);
@@ -4943,8 +5235,8 @@ async function validateLicense(apiKey, deviceId) {
4943
5235
  }
4944
5236
  function getCacheAgeMs() {
4945
5237
  try {
4946
- const { statSync: statSync5 } = __require("fs");
4947
- const s = statSync5(CACHE_PATH);
5238
+ const { statSync: statSync6 } = __require("fs");
5239
+ const s = statSync6(CACHE_PATH);
4948
5240
  return Date.now() - s.mtimeMs;
4949
5241
  } catch {
4950
5242
  return Infinity;
@@ -8949,7 +9241,7 @@ __export(db_backup_exports, {
8949
9241
  listBackups: () => listBackups,
8950
9242
  rotateBackups: () => rotateBackups
8951
9243
  });
8952
- import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync12, readdirSync as readdirSync7, unlinkSync as unlinkSync10, statSync as statSync3 } from "fs";
9244
+ import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync12, readdirSync as readdirSync7, unlinkSync as unlinkSync10, statSync as statSync4 } from "fs";
8953
9245
  import path24 from "path";
8954
9246
  function findActiveDb() {
8955
9247
  for (const name of DB_NAMES) {
@@ -8993,7 +9285,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
8993
9285
  if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
8994
9286
  const filePath = path24.join(BACKUP_DIR, file);
8995
9287
  try {
8996
- const stat = statSync3(filePath);
9288
+ const stat = statSync4(filePath);
8997
9289
  if (stat.mtimeMs < cutoff) {
8998
9290
  unlinkSync10(filePath);
8999
9291
  deleted++;
@@ -9011,7 +9303,7 @@ function listBackups() {
9011
9303
  const files = readdirSync7(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
9012
9304
  return files.map((name) => {
9013
9305
  const p = path24.join(BACKUP_DIR, name);
9014
- const stat = statSync3(p);
9306
+ const stat = statSync4(p);
9015
9307
  return { path: p, name, size: stat.size, date: stat.mtime };
9016
9308
  }).sort((a, b) => b.date.getTime() - a.date.getTime());
9017
9309
  } catch {
@@ -9044,10 +9336,10 @@ var init_db_backup = __esm({
9044
9336
  // src/lib/cloud-sync.ts
9045
9337
  var cloud_sync_exports = {};
9046
9338
  __export(cloud_sync_exports, {
9047
- CLOUD_RELINK_REQUIRED_MESSAGE: () => CLOUD_RELINK_REQUIRED_MESSAGE,
9339
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE: () => CLOUD_REUPLOAD_REQUIRED_MESSAGE,
9048
9340
  assertSecureEndpoint: () => assertSecureEndpoint,
9049
9341
  buildRosterBlob: () => buildRosterBlob,
9050
- clearCloudRelinkRequired: () => clearCloudRelinkRequired,
9342
+ clearCloudReuploadRequired: () => clearCloudReuploadRequired,
9051
9343
  cloudPull: () => cloudPull,
9052
9344
  cloudPullBehaviors: () => cloudPullBehaviors,
9053
9345
  cloudPullBlob: () => cloudPullBlob,
@@ -9066,14 +9358,16 @@ __export(cloud_sync_exports, {
9066
9358
  cloudPushGraphRAG: () => cloudPushGraphRAG,
9067
9359
  cloudPushRoster: () => cloudPushRoster,
9068
9360
  cloudPushTasks: () => cloudPushTasks,
9361
+ cloudResetMemoryBlobs: () => cloudResetMemoryBlobs,
9069
9362
  cloudSync: () => cloudSync,
9070
- getCloudRelinkRequired: () => getCloudRelinkRequired,
9363
+ getCloudReuploadRequired: () => getCloudReuploadRequired,
9364
+ markCloudReuploadRequired: () => markCloudReuploadRequired,
9071
9365
  mergeConfig: () => mergeConfig,
9072
9366
  mergeRosterFromRemote: () => mergeRosterFromRemote,
9073
9367
  pushToPostgres: () => pushToPostgres,
9074
9368
  recordRosterDeletion: () => recordRosterDeletion
9075
9369
  });
9076
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync21, readdirSync as readdirSync8, mkdirSync as mkdirSync13, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2, statSync as statSync4 } from "fs";
9370
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync21, readdirSync as readdirSync8, mkdirSync as mkdirSync13, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2, statSync as statSync5 } from "fs";
9077
9371
  import crypto8 from "crypto";
9078
9372
  import path25 from "path";
9079
9373
  import { homedir as homedir2 } from "os";
@@ -9120,18 +9414,36 @@ function loadPgClient() {
9120
9414
  const { pathToFileURL: pathToFileURL3 } = await import("url");
9121
9415
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
9122
9416
  if (explicitPath) {
9123
- const mod2 = await import(pathToFileURL3(explicitPath).href);
9124
- const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
9125
- if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
9126
- return new Ctor2();
9417
+ const mod = await import(pathToFileURL3(explicitPath).href);
9418
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
9419
+ if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
9420
+ return new Ctor();
9127
9421
  }
9128
9422
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path25.join(homedir2(), "exe-db");
9129
- const req = createRequire3(path25.join(exeDbRoot, "package.json"));
9130
- const entry = req.resolve("@prisma/client");
9131
- const mod = await import(pathToFileURL3(entry).href);
9132
- const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
9133
- if (!Ctor) throw new Error("No PrismaClient");
9134
- return new Ctor();
9423
+ const packagePath = path25.join(exeDbRoot, "package.json");
9424
+ if (existsSync21(packagePath)) {
9425
+ const req = createRequire3(packagePath);
9426
+ const entry = req.resolve("@prisma/client");
9427
+ const mod = await import(pathToFileURL3(entry).href);
9428
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
9429
+ if (!Ctor) throw new Error("No PrismaClient");
9430
+ return new Ctor();
9431
+ }
9432
+ const { Pool } = await import("pg");
9433
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
9434
+ return {
9435
+ async $queryRawUnsafe(query, ...values) {
9436
+ const result = await pool.query(query, values);
9437
+ return result.rows;
9438
+ },
9439
+ async $executeRawUnsafe(query, ...values) {
9440
+ const result = await pool.query(query, values);
9441
+ return result.rowCount ?? 0;
9442
+ },
9443
+ async $disconnect() {
9444
+ await pool.end();
9445
+ }
9446
+ };
9135
9447
  })().catch(() => {
9136
9448
  _pgFailed = true;
9137
9449
  _pgPromise = null;
@@ -9152,7 +9464,7 @@ async function pushToPostgres(records) {
9152
9464
  let inserted = 0;
9153
9465
  for (const rec of records) {
9154
9466
  try {
9155
- await prisma.$executeRawUnsafe(
9467
+ const changed = await prisma.$executeRawUnsafe(
9156
9468
  `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
9157
9469
  VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
9158
9470
  ON CONFLICT (source, source_id, event_type) DO NOTHING`,
@@ -9161,7 +9473,7 @@ async function pushToPostgres(records) {
9161
9473
  JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
9162
9474
  rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
9163
9475
  );
9164
- inserted++;
9476
+ inserted += Number(changed ?? 0);
9165
9477
  } catch {
9166
9478
  }
9167
9479
  }
@@ -9266,6 +9578,23 @@ async function cloudPush(records, maxVersion, config) {
9266
9578
  return false;
9267
9579
  }
9268
9580
  }
9581
+ async function cloudResetMemoryBlobs(config) {
9582
+ assertSecureEndpoint(config.endpoint);
9583
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
9584
+ method: "POST",
9585
+ headers: {
9586
+ Authorization: `Bearer ${config.apiKey}`,
9587
+ "Content-Type": "application/json",
9588
+ "X-Device-Id": loadDeviceId()
9589
+ },
9590
+ body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
9591
+ });
9592
+ if (!resp.ok) {
9593
+ throw new Error(`cloud reset failed: HTTP ${resp.status}`);
9594
+ }
9595
+ const data = await resp.json();
9596
+ return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
9597
+ }
9269
9598
  async function cloudPull(sinceVersion, config) {
9270
9599
  assertSecureEndpoint(config.endpoint);
9271
9600
  try {
@@ -9285,40 +9614,61 @@ async function cloudPull(sinceVersion, config) {
9285
9614
  if (!response.ok) return { records: [], maxVersion: sinceVersion };
9286
9615
  const data = await response.json();
9287
9616
  const allRecords = [];
9288
- for (const { blob } of data.blobs ?? []) {
9617
+ let maxReadableVersion = sinceVersion;
9618
+ let skippedBlobs = 0;
9619
+ for (const { version, blob } of data.blobs ?? []) {
9289
9620
  try {
9290
9621
  const compressed = decryptSyncBlob(blob);
9291
9622
  const json = decompress(compressed).toString("utf8");
9292
9623
  const records = JSON.parse(json);
9293
9624
  allRecords.push(...records);
9625
+ const recordMax = records.reduce((max, rec) => {
9626
+ const v = Number(rec.version ?? 0);
9627
+ return Number.isFinite(v) ? Math.max(max, v) : max;
9628
+ }, 0);
9629
+ const blobVersion = Number(version ?? 0);
9630
+ maxReadableVersion = Math.max(
9631
+ maxReadableVersion,
9632
+ Number.isFinite(blobVersion) ? blobVersion : 0,
9633
+ recordMax
9634
+ );
9294
9635
  } catch {
9636
+ skippedBlobs++;
9295
9637
  continue;
9296
9638
  }
9297
9639
  }
9298
- return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
9640
+ if (skippedBlobs > 0) {
9641
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
9642
+ }
9643
+ return { records: allRecords, maxVersion: maxReadableVersion };
9299
9644
  } catch (err) {
9300
9645
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
9301
9646
  return { records: [], maxVersion: sinceVersion };
9302
9647
  }
9303
9648
  }
9304
- async function getCloudRelinkRequired(client = getClient()) {
9649
+ async function getCloudReuploadRequired(client = getClient()) {
9305
9650
  try {
9306
9651
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
9307
- const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
9308
- return String(relink.rows[0]?.value ?? "") === "1";
9652
+ const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
9653
+ return result.rows.some((row) => String(row.value ?? "") === "1");
9309
9654
  } catch {
9310
9655
  return false;
9311
9656
  }
9312
9657
  }
9313
- async function clearCloudRelinkRequired(client = getClient()) {
9658
+ async function clearCloudReuploadRequired(client = getClient()) {
9314
9659
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
9660
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
9315
9661
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
9316
9662
  await client.execute({
9317
- sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relinked_at', ?)",
9663
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
9318
9664
  args: [(/* @__PURE__ */ new Date()).toISOString()]
9319
9665
  });
9320
9666
  await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
9321
9667
  }
9668
+ async function markCloudReuploadRequired(client = getClient()) {
9669
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
9670
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
9671
+ }
9322
9672
  async function cloudSync(config) {
9323
9673
  if (!isSyncCryptoInitialized()) {
9324
9674
  try {
@@ -9340,10 +9690,10 @@ async function cloudSync(config) {
9340
9690
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
9341
9691
  }
9342
9692
  try {
9343
- if (await getCloudRelinkRequired(client)) throw new Error(CLOUD_RELINK_REQUIRED_MESSAGE);
9693
+ if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
9344
9694
  } catch (err) {
9345
9695
  const msg = err instanceof Error ? err.message : String(err);
9346
- if (msg.includes("Paused after key rotation")) throw err;
9696
+ if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
9347
9697
  }
9348
9698
  try {
9349
9699
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -9601,7 +9951,7 @@ async function cloudSync(config) {
9601
9951
  const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
9602
9952
  const latestBackup = getLatestBackup2();
9603
9953
  if (latestBackup) {
9604
- const backupSize = statSync4(latestBackup).size;
9954
+ const backupSize = statSync5(latestBackup).size;
9605
9955
  const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
9606
9956
  if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
9607
9957
  const backupData = readFileSync15(latestBackup);
@@ -10272,7 +10622,7 @@ async function cloudPullDocuments(config) {
10272
10622
  }
10273
10623
  return { pulled };
10274
10624
  }
10275
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_RELINK_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
10625
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
10276
10626
  var init_cloud_sync = __esm({
10277
10627
  "src/lib/cloud-sync.ts"() {
10278
10628
  "use strict";
@@ -10291,7 +10641,7 @@ var init_cloud_sync = __esm({
10291
10641
  LOCK_STALE_MS = 3e4;
10292
10642
  _pgPromise = null;
10293
10643
  _pgFailed = false;
10294
- CLOUD_RELINK_REQUIRED_MESSAGE = "[cloud-sync] Paused after key rotation. Run `exe-os cloud relink --dry-run` for the safe relink checklist.";
10644
+ 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.";
10295
10645
  ROSTER_DELETIONS_PATH = path25.join(EXE_AI_DIR, "roster-deletions.json");
10296
10646
  }
10297
10647
  });