@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
@@ -247,6 +247,10 @@ async function loadConfig() {
247
247
  if (config.dbPath.startsWith("~")) {
248
248
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
249
249
  }
250
+ const envDbPath = path.join(dir, "memories.db");
251
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
252
+ config.dbPath = envDbPath;
253
+ }
250
254
  return config;
251
255
  } catch {
252
256
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -267,7 +271,15 @@ function loadConfigSync() {
267
271
  normalizeSessionLifecycle(migratedCfg);
268
272
  normalizeAutoUpdate(migratedCfg);
269
273
  normalizeOrchestration(migratedCfg);
270
- return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
274
+ const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
275
+ if (config.dbPath.startsWith("~")) {
276
+ config.dbPath = config.dbPath.replace(/^~/, os.homedir());
277
+ }
278
+ const envDbPath = path.join(dir, "memories.db");
279
+ if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
280
+ config.dbPath = envDbPath;
281
+ }
282
+ return config;
271
283
  } catch {
272
284
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
273
285
  }
@@ -1807,6 +1819,9 @@ function getClient() {
1807
1819
  if (_daemonClient && _daemonClient._isDaemonActive()) {
1808
1820
  return _daemonClient;
1809
1821
  }
1822
+ if (!_resilientClient) {
1823
+ return _adapterClient;
1824
+ }
1810
1825
  return _resilientClient;
1811
1826
  }
1812
1827
  async function initDaemonClient() {
@@ -2839,6 +2854,127 @@ async function ensureSchema() {
2839
2854
  VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2840
2855
  END;
2841
2856
  `);
2857
+ await client.executeMultiple(`
2858
+ CREATE TABLE IF NOT EXISTS agent_sessions (
2859
+ id TEXT PRIMARY KEY,
2860
+ agent_id TEXT NOT NULL,
2861
+ project_name TEXT,
2862
+ started_at TEXT NOT NULL,
2863
+ last_event_at TEXT NOT NULL,
2864
+ event_count INTEGER NOT NULL DEFAULT 0,
2865
+ properties TEXT DEFAULT '{}'
2866
+ );
2867
+
2868
+ CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
2869
+ ON agent_sessions(agent_id, started_at);
2870
+
2871
+ CREATE TABLE IF NOT EXISTS agent_goals (
2872
+ id TEXT PRIMARY KEY,
2873
+ statement TEXT NOT NULL,
2874
+ owner_agent_id TEXT,
2875
+ project_name TEXT,
2876
+ status TEXT NOT NULL DEFAULT 'open',
2877
+ priority INTEGER NOT NULL DEFAULT 5,
2878
+ success_criteria TEXT,
2879
+ parent_goal_id TEXT,
2880
+ due_at TEXT,
2881
+ achieved_at TEXT,
2882
+ supersedes_id TEXT,
2883
+ created_at TEXT NOT NULL,
2884
+ updated_at TEXT NOT NULL,
2885
+ source_memory_id TEXT
2886
+ );
2887
+
2888
+ CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
2889
+ ON agent_goals(project_name, status, priority);
2890
+
2891
+ CREATE TABLE IF NOT EXISTS agent_events (
2892
+ id TEXT PRIMARY KEY,
2893
+ event_type TEXT NOT NULL,
2894
+ occurred_at TEXT NOT NULL,
2895
+ sequence_index INTEGER NOT NULL,
2896
+ actor_agent_id TEXT,
2897
+ agent_role TEXT,
2898
+ project_name TEXT,
2899
+ session_id TEXT,
2900
+ task_id TEXT,
2901
+ goal_id TEXT,
2902
+ parent_event_id TEXT,
2903
+ intention TEXT,
2904
+ outcome TEXT,
2905
+ evidence_memory_id TEXT,
2906
+ impact TEXT,
2907
+ payload TEXT DEFAULT '{}',
2908
+ created_at TEXT NOT NULL
2909
+ );
2910
+
2911
+ CREATE INDEX IF NOT EXISTS idx_agent_events_time
2912
+ ON agent_events(occurred_at, sequence_index);
2913
+
2914
+ CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
2915
+ ON agent_events(session_id, sequence_index);
2916
+
2917
+ CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
2918
+ ON agent_events(goal_id, occurred_at);
2919
+
2920
+ CREATE INDEX IF NOT EXISTS idx_agent_events_memory
2921
+ ON agent_events(evidence_memory_id);
2922
+
2923
+ CREATE TABLE IF NOT EXISTS agent_goal_links (
2924
+ id TEXT PRIMARY KEY,
2925
+ goal_id TEXT NOT NULL,
2926
+ link_type TEXT NOT NULL,
2927
+ target_id TEXT NOT NULL,
2928
+ target_type TEXT NOT NULL,
2929
+ created_at TEXT NOT NULL
2930
+ );
2931
+
2932
+ CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
2933
+ ON agent_goal_links(goal_id, target_type);
2934
+
2935
+ CREATE TABLE IF NOT EXISTS agent_semantic_labels (
2936
+ id TEXT PRIMARY KEY,
2937
+ source_memory_id TEXT NOT NULL,
2938
+ event_id TEXT,
2939
+ labeler TEXT NOT NULL,
2940
+ schema_version INTEGER NOT NULL DEFAULT 1,
2941
+ confidence REAL NOT NULL DEFAULT 0,
2942
+ labels TEXT NOT NULL,
2943
+ created_at TEXT NOT NULL,
2944
+ updated_at TEXT NOT NULL
2945
+ );
2946
+
2947
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
2948
+ ON agent_semantic_labels(source_memory_id, labeler);
2949
+
2950
+ CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
2951
+ ON agent_semantic_labels(event_id);
2952
+
2953
+ CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
2954
+ id TEXT PRIMARY KEY,
2955
+ project_name TEXT,
2956
+ session_id TEXT,
2957
+ window_start_at TEXT NOT NULL,
2958
+ window_end_at TEXT NOT NULL,
2959
+ event_count INTEGER NOT NULL DEFAULT 0,
2960
+ goal_count INTEGER NOT NULL DEFAULT 0,
2961
+ success_count INTEGER NOT NULL DEFAULT 0,
2962
+ failure_count INTEGER NOT NULL DEFAULT 0,
2963
+ risk_count INTEGER NOT NULL DEFAULT 0,
2964
+ summary TEXT NOT NULL,
2965
+ learnings TEXT NOT NULL DEFAULT '[]',
2966
+ next_actions TEXT NOT NULL DEFAULT '[]',
2967
+ evidence_event_ids TEXT NOT NULL DEFAULT '[]',
2968
+ confidence REAL NOT NULL DEFAULT 0,
2969
+ created_at TEXT NOT NULL
2970
+ );
2971
+
2972
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
2973
+ ON agent_reflection_checkpoints(project_name, window_end_at);
2974
+
2975
+ CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
2976
+ ON agent_reflection_checkpoints(session_id, window_end_at);
2977
+ `);
2842
2978
  try {
2843
2979
  await client.execute({
2844
2980
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -2991,12 +3127,13 @@ var keychain_exports = {};
2991
3127
  __export(keychain_exports, {
2992
3128
  deleteMasterKey: () => deleteMasterKey,
2993
3129
  exportMnemonic: () => exportMnemonic,
3130
+ getKeyStorageInfo: () => getKeyStorageInfo,
2994
3131
  getMasterKey: () => getMasterKey,
2995
3132
  importMnemonic: () => importMnemonic,
2996
3133
  setMasterKey: () => setMasterKey
2997
3134
  });
2998
3135
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2999
- import { existsSync as existsSync6 } from "fs";
3136
+ import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3000
3137
  import { execSync as execSync2 } from "child_process";
3001
3138
  import path6 from "path";
3002
3139
  import os5 from "os";
@@ -3006,29 +3143,65 @@ function getKeyDir() {
3006
3143
  function getKeyPath() {
3007
3144
  return path6.join(getKeyDir(), "master.key");
3008
3145
  }
3009
- function macKeychainGet() {
3146
+ function nativeKeychainAllowed() {
3147
+ return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
3148
+ }
3149
+ function linuxSecretAvailable() {
3150
+ if (!nativeKeychainAllowed()) return false;
3151
+ if (process.platform !== "linux") return false;
3152
+ if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3153
+ try {
3154
+ execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3155
+ } catch {
3156
+ linuxSecretAvailability = false;
3157
+ return false;
3158
+ }
3159
+ try {
3160
+ execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3161
+ linuxSecretAvailability = true;
3162
+ } catch {
3163
+ linuxSecretAvailability = false;
3164
+ }
3165
+ return linuxSecretAvailability;
3166
+ }
3167
+ function isRootOnlyTrustedServerKeyFile(keyPath) {
3168
+ if (process.platform !== "linux") return false;
3169
+ try {
3170
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3171
+ const st = statSync2(keyPath);
3172
+ if (!st.isFile() || (st.mode & 63) !== 0) return false;
3173
+ if (uid === 0) return true;
3174
+ const exeOsDir = process.env.EXE_OS_DIR;
3175
+ return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3176
+ } catch {
3177
+ return false;
3178
+ }
3179
+ }
3180
+ function macKeychainGet(service = SERVICE) {
3181
+ if (!nativeKeychainAllowed()) return null;
3010
3182
  if (process.platform !== "darwin") return null;
3011
3183
  try {
3012
3184
  return execSync2(
3013
- `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
3185
+ `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3014
3186
  { encoding: "utf-8", timeout: 5e3 }
3015
3187
  ).trim();
3016
3188
  } catch {
3017
3189
  return null;
3018
3190
  }
3019
3191
  }
3020
- function macKeychainSet(value) {
3192
+ function macKeychainSet(value, service = SERVICE) {
3193
+ if (!nativeKeychainAllowed()) return false;
3021
3194
  if (process.platform !== "darwin") return false;
3022
3195
  try {
3023
3196
  try {
3024
3197
  execSync2(
3025
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3198
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3026
3199
  { timeout: 5e3 }
3027
3200
  );
3028
3201
  } catch {
3029
3202
  }
3030
3203
  execSync2(
3031
- `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
3204
+ `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3032
3205
  { timeout: 5e3 }
3033
3206
  );
3034
3207
  return true;
@@ -3036,11 +3209,12 @@ function macKeychainSet(value) {
3036
3209
  return false;
3037
3210
  }
3038
3211
  }
3039
- function macKeychainDelete() {
3212
+ function macKeychainDelete(service = SERVICE) {
3213
+ if (!nativeKeychainAllowed()) return false;
3040
3214
  if (process.platform !== "darwin") return false;
3041
3215
  try {
3042
3216
  execSync2(
3043
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3217
+ `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3044
3218
  { timeout: 5e3 }
3045
3219
  );
3046
3220
  return true;
@@ -3048,22 +3222,22 @@ function macKeychainDelete() {
3048
3222
  return false;
3049
3223
  }
3050
3224
  }
3051
- function linuxSecretGet() {
3052
- if (process.platform !== "linux") return null;
3225
+ function linuxSecretGet(service = SERVICE) {
3226
+ if (!linuxSecretAvailable()) return null;
3053
3227
  try {
3054
3228
  return execSync2(
3055
- `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3229
+ `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3056
3230
  { encoding: "utf-8", timeout: 5e3 }
3057
3231
  ).trim();
3058
3232
  } catch {
3059
3233
  return null;
3060
3234
  }
3061
3235
  }
3062
- function linuxSecretSet(value) {
3063
- if (process.platform !== "linux") return false;
3236
+ function linuxSecretSet(value, service = SERVICE) {
3237
+ if (!linuxSecretAvailable()) return false;
3064
3238
  try {
3065
3239
  execSync2(
3066
- `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
3240
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3067
3241
  { timeout: 5e3 }
3068
3242
  );
3069
3243
  return true;
@@ -3071,11 +3245,12 @@ function linuxSecretSet(value) {
3071
3245
  return false;
3072
3246
  }
3073
3247
  }
3074
- function linuxSecretDelete() {
3248
+ function linuxSecretDelete(service = SERVICE) {
3249
+ if (!nativeKeychainAllowed()) return false;
3075
3250
  if (process.platform !== "linux") return false;
3076
3251
  try {
3077
3252
  execSync2(
3078
- `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3253
+ `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3079
3254
  { timeout: 5e3 }
3080
3255
  );
3081
3256
  return true;
@@ -3084,6 +3259,7 @@ function linuxSecretDelete() {
3084
3259
  }
3085
3260
  }
3086
3261
  async function tryKeytar() {
3262
+ if (!nativeKeychainAllowed()) return null;
3087
3263
  try {
3088
3264
  return await import("keytar");
3089
3265
  } catch {
@@ -3157,7 +3333,19 @@ async function writeMachineBoundFileFallback(b64) {
3157
3333
  return "plaintext";
3158
3334
  }
3159
3335
  async function getMasterKey() {
3160
- const nativeValue = macKeychainGet() ?? linuxSecretGet();
3336
+ let nativeValue = macKeychainGet() ?? linuxSecretGet();
3337
+ if (!nativeValue) {
3338
+ const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
3339
+ if (legacyValue) {
3340
+ const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
3341
+ if (migrated) {
3342
+ macKeychainDelete(LEGACY_SERVICE);
3343
+ linuxSecretDelete(LEGACY_SERVICE);
3344
+ process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
3345
+ }
3346
+ nativeValue = legacyValue;
3347
+ }
3348
+ }
3161
3349
  if (nativeValue) {
3162
3350
  return Buffer.from(nativeValue, "base64");
3163
3351
  }
@@ -3165,12 +3353,17 @@ async function getMasterKey() {
3165
3353
  if (keytar) {
3166
3354
  try {
3167
3355
  const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
3168
- if (keytarValue) {
3169
- const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
3356
+ const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
3357
+ if (legacyKeytarValue) {
3358
+ const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
3170
3359
  if (migrated) {
3171
3360
  process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
3361
+ try {
3362
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
3363
+ } catch {
3364
+ }
3172
3365
  }
3173
- return Buffer.from(keytarValue, "base64");
3366
+ return Buffer.from(legacyKeytarValue, "base64");
3174
3367
  }
3175
3368
  } catch {
3176
3369
  }
@@ -3195,7 +3388,7 @@ async function getMasterKey() {
3195
3388
  const decrypted = decryptWithMachineKey(content, machineKey);
3196
3389
  if (!decrypted) {
3197
3390
  process.stderr.write(
3198
- "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
3391
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
3199
3392
  );
3200
3393
  return null;
3201
3394
  }
@@ -3204,6 +3397,9 @@ async function getMasterKey() {
3204
3397
  b64Value = content;
3205
3398
  }
3206
3399
  const key = Buffer.from(b64Value, "base64");
3400
+ if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
3401
+ return key;
3402
+ }
3207
3403
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3208
3404
  if (migrated) {
3209
3405
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
@@ -3231,6 +3427,97 @@ async function getMasterKey() {
3231
3427
  return null;
3232
3428
  }
3233
3429
  }
3430
+ async function getKeyStorageInfo() {
3431
+ if (macKeychainGet()) {
3432
+ return {
3433
+ kind: "macos-keychain",
3434
+ secure: true,
3435
+ note: "stored in macOS Keychain via built-in security CLI"
3436
+ };
3437
+ }
3438
+ if (macKeychainGet(LEGACY_SERVICE)) {
3439
+ return {
3440
+ kind: "macos-keychain",
3441
+ secure: true,
3442
+ note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
3443
+ };
3444
+ }
3445
+ if (linuxSecretGet()) {
3446
+ return {
3447
+ kind: "linux-secret-service",
3448
+ secure: true,
3449
+ note: "stored in Linux Secret Service via secret-tool"
3450
+ };
3451
+ }
3452
+ if (linuxSecretGet(LEGACY_SERVICE)) {
3453
+ return {
3454
+ kind: "linux-secret-service",
3455
+ secure: true,
3456
+ note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
3457
+ };
3458
+ }
3459
+ const keytar = await tryKeytar();
3460
+ if (keytar) {
3461
+ try {
3462
+ if (await keytar.getPassword(SERVICE, ACCOUNT)) {
3463
+ return {
3464
+ kind: "legacy-keytar",
3465
+ secure: true,
3466
+ note: "stored in legacy keytar backend; will migrate to native keychain when possible"
3467
+ };
3468
+ }
3469
+ if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
3470
+ return {
3471
+ kind: "legacy-keytar",
3472
+ secure: true,
3473
+ note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
3474
+ };
3475
+ }
3476
+ } catch {
3477
+ }
3478
+ }
3479
+ const keyPath = getKeyPath();
3480
+ if (!existsSync6(keyPath)) {
3481
+ return {
3482
+ kind: "missing",
3483
+ secure: false,
3484
+ path: keyPath,
3485
+ note: "no key found in OS keychain, legacy keytar, or file fallback"
3486
+ };
3487
+ }
3488
+ try {
3489
+ const content = (await readFile3(keyPath, "utf-8")).trim();
3490
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
3491
+ return {
3492
+ kind: "encrypted-file",
3493
+ secure: true,
3494
+ path: keyPath,
3495
+ note: "stored in machine-bound encrypted file fallback"
3496
+ };
3497
+ }
3498
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3499
+ return {
3500
+ kind: "server-secret-file",
3501
+ secure: true,
3502
+ path: keyPath,
3503
+ note: "stored as root-only trusted server secret file"
3504
+ };
3505
+ }
3506
+ return {
3507
+ kind: "plaintext-file",
3508
+ secure: false,
3509
+ path: keyPath,
3510
+ note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
3511
+ };
3512
+ } catch {
3513
+ return {
3514
+ kind: "missing",
3515
+ secure: false,
3516
+ path: keyPath,
3517
+ note: "key file exists but could not be read"
3518
+ };
3519
+ }
3520
+ }
3234
3521
  async function setMasterKey(key) {
3235
3522
  const b64 = key.toString("base64");
3236
3523
  if (macKeychainSet(b64) || linuxSecretSet(b64)) {
@@ -3256,10 +3543,13 @@ async function setMasterKey(key) {
3256
3543
  async function deleteMasterKey() {
3257
3544
  macKeychainDelete();
3258
3545
  linuxSecretDelete();
3546
+ macKeychainDelete(LEGACY_SERVICE);
3547
+ linuxSecretDelete(LEGACY_SERVICE);
3259
3548
  const keytar = await tryKeytar();
3260
3549
  if (keytar) {
3261
3550
  try {
3262
3551
  await keytar.deletePassword(SERVICE, ACCOUNT);
3552
+ await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
3263
3553
  } catch {
3264
3554
  }
3265
3555
  }
@@ -3297,12 +3587,14 @@ async function importMnemonic(mnemonic) {
3297
3587
  const entropy = mnemonicToEntropy(trimmed);
3298
3588
  return Buffer.from(entropy, "hex");
3299
3589
  }
3300
- var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
3590
+ var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
3301
3591
  var init_keychain = __esm({
3302
3592
  "src/lib/keychain.ts"() {
3303
3593
  "use strict";
3304
- SERVICE = "exe-mem";
3594
+ SERVICE = "exe-os";
3595
+ LEGACY_SERVICE = "exe-mem";
3305
3596
  ACCOUNT = "master-key";
3597
+ linuxSecretAvailability = null;
3306
3598
  ENCRYPTED_PREFIX = "enc:";
3307
3599
  }
3308
3600
  });
@@ -3378,7 +3670,7 @@ __export(shard_manager_exports, {
3378
3670
  shardExists: () => shardExists
3379
3671
  });
3380
3672
  import path7 from "path";
3381
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync2 } from "fs";
3673
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
3382
3674
  import { createClient as createClient2 } from "@libsql/client";
3383
3675
  function initShardManager(encryptionKey) {
3384
3676
  _encryptionKey = encryptionKey;
@@ -3442,7 +3734,7 @@ async function auditShardHealth(options = {}) {
3442
3734
  const shards = [];
3443
3735
  for (const name of names) {
3444
3736
  const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3445
- const stat = statSync2(dbPath);
3737
+ const stat = statSync3(dbPath);
3446
3738
  const item = {
3447
3739
  name,
3448
3740
  path: dbPath,
@@ -3695,7 +3987,7 @@ async function getReadyShardClient(projectName) {
3695
3987
  _shardLastAccess.delete(safeName);
3696
3988
  const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3697
3989
  if (existsSync7(dbPath)) {
3698
- const stat = statSync2(dbPath);
3990
+ const stat = statSync3(dbPath);
3699
3991
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3700
3992
  const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3701
3993
  renameSync3(dbPath, archivedPath);
@@ -4589,8 +4881,8 @@ async function validateLicense(apiKey, deviceId) {
4589
4881
  }
4590
4882
  function getCacheAgeMs() {
4591
4883
  try {
4592
- const { statSync: statSync6 } = __require("fs");
4593
- const s = statSync6(CACHE_PATH);
4884
+ const { statSync: statSync7 } = __require("fs");
4885
+ const s = statSync7(CACHE_PATH);
4594
4886
  return Date.now() - s.mtimeMs;
4595
4887
  } catch {
4596
4888
  return Infinity;
@@ -5567,7 +5859,7 @@ __export(db_backup_exports, {
5567
5859
  listBackups: () => listBackups,
5568
5860
  rotateBackups: () => rotateBackups
5569
5861
  });
5570
- import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync5, unlinkSync as unlinkSync7, statSync as statSync4 } from "fs";
5862
+ import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync5, unlinkSync as unlinkSync7, statSync as statSync5 } from "fs";
5571
5863
  import path19 from "path";
5572
5864
  function findActiveDb() {
5573
5865
  for (const name of DB_NAMES) {
@@ -5611,7 +5903,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
5611
5903
  if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
5612
5904
  const filePath = path19.join(BACKUP_DIR, file);
5613
5905
  try {
5614
- const stat = statSync4(filePath);
5906
+ const stat = statSync5(filePath);
5615
5907
  if (stat.mtimeMs < cutoff) {
5616
5908
  unlinkSync7(filePath);
5617
5909
  deleted++;
@@ -5629,7 +5921,7 @@ function listBackups() {
5629
5921
  const files = readdirSync5(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
5630
5922
  return files.map((name) => {
5631
5923
  const p = path19.join(BACKUP_DIR, name);
5632
- const stat = statSync4(p);
5924
+ const stat = statSync5(p);
5633
5925
  return { path: p, name, size: stat.size, date: stat.mtime };
5634
5926
  }).sort((a, b) => b.date.getTime() - a.date.getTime());
5635
5927
  } catch {
@@ -5662,10 +5954,10 @@ var init_db_backup = __esm({
5662
5954
  // src/lib/cloud-sync.ts
5663
5955
  var cloud_sync_exports = {};
5664
5956
  __export(cloud_sync_exports, {
5665
- CLOUD_RELINK_REQUIRED_MESSAGE: () => CLOUD_RELINK_REQUIRED_MESSAGE,
5957
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE: () => CLOUD_REUPLOAD_REQUIRED_MESSAGE,
5666
5958
  assertSecureEndpoint: () => assertSecureEndpoint,
5667
5959
  buildRosterBlob: () => buildRosterBlob,
5668
- clearCloudRelinkRequired: () => clearCloudRelinkRequired,
5960
+ clearCloudReuploadRequired: () => clearCloudReuploadRequired,
5669
5961
  cloudPull: () => cloudPull,
5670
5962
  cloudPullBehaviors: () => cloudPullBehaviors,
5671
5963
  cloudPullBlob: () => cloudPullBlob,
@@ -5684,14 +5976,16 @@ __export(cloud_sync_exports, {
5684
5976
  cloudPushGraphRAG: () => cloudPushGraphRAG,
5685
5977
  cloudPushRoster: () => cloudPushRoster,
5686
5978
  cloudPushTasks: () => cloudPushTasks,
5979
+ cloudResetMemoryBlobs: () => cloudResetMemoryBlobs,
5687
5980
  cloudSync: () => cloudSync,
5688
- getCloudRelinkRequired: () => getCloudRelinkRequired,
5981
+ getCloudReuploadRequired: () => getCloudReuploadRequired,
5982
+ markCloudReuploadRequired: () => markCloudReuploadRequired,
5689
5983
  mergeConfig: () => mergeConfig,
5690
5984
  mergeRosterFromRemote: () => mergeRosterFromRemote,
5691
5985
  pushToPostgres: () => pushToPostgres,
5692
5986
  recordRosterDeletion: () => recordRosterDeletion
5693
5987
  });
5694
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync19, readdirSync as readdirSync6, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3, unlinkSync as unlinkSync8, openSync as openSync2, closeSync as closeSync2, statSync as statSync5 } from "fs";
5988
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync19, readdirSync as readdirSync6, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3, unlinkSync as unlinkSync8, openSync as openSync2, closeSync as closeSync2, statSync as statSync6 } from "fs";
5695
5989
  import crypto4 from "crypto";
5696
5990
  import path20 from "path";
5697
5991
  import { homedir as homedir2 } from "os";
@@ -5738,18 +6032,36 @@ function loadPgClient() {
5738
6032
  const { pathToFileURL: pathToFileURL3 } = await import("url");
5739
6033
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
5740
6034
  if (explicitPath) {
5741
- const mod2 = await import(pathToFileURL3(explicitPath).href);
5742
- const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
5743
- if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
5744
- return new Ctor2();
6035
+ const mod = await import(pathToFileURL3(explicitPath).href);
6036
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
6037
+ if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
6038
+ return new Ctor();
5745
6039
  }
5746
6040
  const exeDbRoot = process.env.EXE_DB_ROOT ?? path20.join(homedir2(), "exe-db");
5747
- const req = createRequire3(path20.join(exeDbRoot, "package.json"));
5748
- const entry = req.resolve("@prisma/client");
5749
- const mod = await import(pathToFileURL3(entry).href);
5750
- const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
5751
- if (!Ctor) throw new Error("No PrismaClient");
5752
- return new Ctor();
6041
+ const packagePath = path20.join(exeDbRoot, "package.json");
6042
+ if (existsSync19(packagePath)) {
6043
+ const req = createRequire3(packagePath);
6044
+ const entry = req.resolve("@prisma/client");
6045
+ const mod = await import(pathToFileURL3(entry).href);
6046
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
6047
+ if (!Ctor) throw new Error("No PrismaClient");
6048
+ return new Ctor();
6049
+ }
6050
+ const { Pool } = await import("pg");
6051
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
6052
+ return {
6053
+ async $queryRawUnsafe(query, ...values) {
6054
+ const result = await pool.query(query, values);
6055
+ return result.rows;
6056
+ },
6057
+ async $executeRawUnsafe(query, ...values) {
6058
+ const result = await pool.query(query, values);
6059
+ return result.rowCount ?? 0;
6060
+ },
6061
+ async $disconnect() {
6062
+ await pool.end();
6063
+ }
6064
+ };
5753
6065
  })().catch(() => {
5754
6066
  _pgFailed = true;
5755
6067
  _pgPromise = null;
@@ -5770,7 +6082,7 @@ async function pushToPostgres(records) {
5770
6082
  let inserted = 0;
5771
6083
  for (const rec of records) {
5772
6084
  try {
5773
- await prisma.$executeRawUnsafe(
6085
+ const changed = await prisma.$executeRawUnsafe(
5774
6086
  `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
5775
6087
  VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
5776
6088
  ON CONFLICT (source, source_id, event_type) DO NOTHING`,
@@ -5779,7 +6091,7 @@ async function pushToPostgres(records) {
5779
6091
  JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
5780
6092
  rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
5781
6093
  );
5782
- inserted++;
6094
+ inserted += Number(changed ?? 0);
5783
6095
  } catch {
5784
6096
  }
5785
6097
  }
@@ -5884,6 +6196,23 @@ async function cloudPush(records, maxVersion, config) {
5884
6196
  return false;
5885
6197
  }
5886
6198
  }
6199
+ async function cloudResetMemoryBlobs(config) {
6200
+ assertSecureEndpoint(config.endpoint);
6201
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
6202
+ method: "POST",
6203
+ headers: {
6204
+ Authorization: `Bearer ${config.apiKey}`,
6205
+ "Content-Type": "application/json",
6206
+ "X-Device-Id": loadDeviceId()
6207
+ },
6208
+ body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
6209
+ });
6210
+ if (!resp.ok) {
6211
+ throw new Error(`cloud reset failed: HTTP ${resp.status}`);
6212
+ }
6213
+ const data = await resp.json();
6214
+ return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
6215
+ }
5887
6216
  async function cloudPull(sinceVersion, config) {
5888
6217
  assertSecureEndpoint(config.endpoint);
5889
6218
  try {
@@ -5903,40 +6232,61 @@ async function cloudPull(sinceVersion, config) {
5903
6232
  if (!response.ok) return { records: [], maxVersion: sinceVersion };
5904
6233
  const data = await response.json();
5905
6234
  const allRecords = [];
5906
- for (const { blob } of data.blobs ?? []) {
6235
+ let maxReadableVersion = sinceVersion;
6236
+ let skippedBlobs = 0;
6237
+ for (const { version, blob } of data.blobs ?? []) {
5907
6238
  try {
5908
6239
  const compressed = decryptSyncBlob(blob);
5909
6240
  const json = decompress(compressed).toString("utf8");
5910
6241
  const records = JSON.parse(json);
5911
6242
  allRecords.push(...records);
6243
+ const recordMax = records.reduce((max, rec) => {
6244
+ const v = Number(rec.version ?? 0);
6245
+ return Number.isFinite(v) ? Math.max(max, v) : max;
6246
+ }, 0);
6247
+ const blobVersion = Number(version ?? 0);
6248
+ maxReadableVersion = Math.max(
6249
+ maxReadableVersion,
6250
+ Number.isFinite(blobVersion) ? blobVersion : 0,
6251
+ recordMax
6252
+ );
5912
6253
  } catch {
6254
+ skippedBlobs++;
5913
6255
  continue;
5914
6256
  }
5915
6257
  }
5916
- return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
6258
+ if (skippedBlobs > 0) {
6259
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
6260
+ }
6261
+ return { records: allRecords, maxVersion: maxReadableVersion };
5917
6262
  } catch (err) {
5918
6263
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
5919
6264
  return { records: [], maxVersion: sinceVersion };
5920
6265
  }
5921
6266
  }
5922
- async function getCloudRelinkRequired(client = getClient()) {
6267
+ async function getCloudReuploadRequired(client = getClient()) {
5923
6268
  try {
5924
6269
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
5925
- const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
5926
- return String(relink.rows[0]?.value ?? "") === "1";
6270
+ const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
6271
+ return result.rows.some((row) => String(row.value ?? "") === "1");
5927
6272
  } catch {
5928
6273
  return false;
5929
6274
  }
5930
6275
  }
5931
- async function clearCloudRelinkRequired(client = getClient()) {
6276
+ async function clearCloudReuploadRequired(client = getClient()) {
5932
6277
  await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
6278
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
5933
6279
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
5934
6280
  await client.execute({
5935
- sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relinked_at', ?)",
6281
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
5936
6282
  args: [(/* @__PURE__ */ new Date()).toISOString()]
5937
6283
  });
5938
6284
  await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
5939
6285
  }
6286
+ async function markCloudReuploadRequired(client = getClient()) {
6287
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
6288
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
6289
+ }
5940
6290
  async function cloudSync(config) {
5941
6291
  if (!isSyncCryptoInitialized()) {
5942
6292
  try {
@@ -5958,10 +6308,10 @@ async function cloudSync(config) {
5958
6308
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
5959
6309
  }
5960
6310
  try {
5961
- if (await getCloudRelinkRequired(client)) throw new Error(CLOUD_RELINK_REQUIRED_MESSAGE);
6311
+ if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
5962
6312
  } catch (err) {
5963
6313
  const msg = err instanceof Error ? err.message : String(err);
5964
- if (msg.includes("Paused after key rotation")) throw err;
6314
+ if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
5965
6315
  }
5966
6316
  try {
5967
6317
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -6219,7 +6569,7 @@ async function cloudSync(config) {
6219
6569
  const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
6220
6570
  const latestBackup = getLatestBackup2();
6221
6571
  if (latestBackup) {
6222
- const backupSize = statSync5(latestBackup).size;
6572
+ const backupSize = statSync6(latestBackup).size;
6223
6573
  const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
6224
6574
  if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
6225
6575
  const backupData = readFileSync13(latestBackup);
@@ -6890,7 +7240,7 @@ async function cloudPullDocuments(config) {
6890
7240
  }
6891
7241
  return { pulled };
6892
7242
  }
6893
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_RELINK_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
7243
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
6894
7244
  var init_cloud_sync = __esm({
6895
7245
  "src/lib/cloud-sync.ts"() {
6896
7246
  "use strict";
@@ -6909,7 +7259,7 @@ var init_cloud_sync = __esm({
6909
7259
  LOCK_STALE_MS = 3e4;
6910
7260
  _pgPromise = null;
6911
7261
  _pgFailed = false;
6912
- CLOUD_RELINK_REQUIRED_MESSAGE = "[cloud-sync] Paused after key rotation. Run `exe-os cloud relink --dry-run` for the safe relink checklist.";
7262
+ 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.";
6913
7263
  ROSTER_DELETIONS_PATH = path20.join(EXE_AI_DIR, "roster-deletions.json");
6914
7264
  }
6915
7265
  });
@@ -7107,7 +7457,7 @@ init_exe_daemon_client();
7107
7457
 
7108
7458
  // src/lib/memory-queue.ts
7109
7459
  init_config();
7110
- import { appendFileSync as appendFileSync2, readFileSync as readFileSync11, renameSync as renameSync5, unlinkSync as unlinkSync4, existsSync as existsSync15, statSync as statSync3 } from "fs";
7460
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync11, renameSync as renameSync5, unlinkSync as unlinkSync4, existsSync as existsSync15, statSync as statSync4 } from "fs";
7111
7461
  import path16 from "path";
7112
7462
  var QUEUE_PATH2 = path16.join(EXE_AI_DIR, "memory-queue.jsonl");
7113
7463
  var PROCESSING_PATH = QUEUE_PATH2 + ".processing";