@askexenow/exe-os 0.9.97 → 0.9.99

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 (81) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +7 -29
  2. package/dist/bin/agentic-reflection-backfill.js +7 -29
  3. package/dist/bin/agentic-semantic-label.js +7 -29
  4. package/dist/bin/backfill-conversations.js +7 -29
  5. package/dist/bin/backfill-responses.js +7 -29
  6. package/dist/bin/backfill-vectors.js +7 -29
  7. package/dist/bin/bulk-sync-postgres.js +7 -29
  8. package/dist/bin/cleanup-stale-review-tasks.js +7 -29
  9. package/dist/bin/cli.js +11 -33
  10. package/dist/bin/exe-agent.js +7 -0
  11. package/dist/bin/exe-assign.js +7 -29
  12. package/dist/bin/exe-boot.js +7 -29
  13. package/dist/bin/exe-call.js +7 -0
  14. package/dist/bin/exe-cloud.js +7 -29
  15. package/dist/bin/exe-dispatch.js +7 -29
  16. package/dist/bin/exe-doctor.js +7 -29
  17. package/dist/bin/exe-export-behaviors.js +7 -29
  18. package/dist/bin/exe-forget.js +7 -29
  19. package/dist/bin/exe-gateway.js +7 -29
  20. package/dist/bin/exe-heartbeat.js +7 -29
  21. package/dist/bin/exe-kill.js +7 -29
  22. package/dist/bin/exe-launch-agent.js +7 -29
  23. package/dist/bin/exe-new-employee.js +7 -0
  24. package/dist/bin/exe-pending-messages.js +7 -29
  25. package/dist/bin/exe-pending-notifications.js +7 -29
  26. package/dist/bin/exe-pending-reviews.js +7 -29
  27. package/dist/bin/exe-rename.js +7 -29
  28. package/dist/bin/exe-review.js +7 -29
  29. package/dist/bin/exe-search.js +7 -29
  30. package/dist/bin/exe-session-cleanup.js +7 -29
  31. package/dist/bin/exe-start-codex.js +7 -29
  32. package/dist/bin/exe-start-opencode.js +7 -29
  33. package/dist/bin/exe-status.js +7 -29
  34. package/dist/bin/exe-team.js +7 -29
  35. package/dist/bin/git-sweep.js +7 -29
  36. package/dist/bin/graph-backfill.js +7 -29
  37. package/dist/bin/graph-export.js +7 -29
  38. package/dist/bin/intercom-check.js +7 -29
  39. package/dist/bin/scan-tasks.js +7 -29
  40. package/dist/bin/setup.js +11 -33
  41. package/dist/bin/shard-migrate.js +7 -29
  42. package/dist/gateway/index.js +7 -29
  43. package/dist/hooks/bug-report-worker.js +7 -29
  44. package/dist/hooks/codex-stop-task-finalizer.js +7 -29
  45. package/dist/hooks/commit-complete.js +7 -29
  46. package/dist/hooks/error-recall.js +7 -29
  47. package/dist/hooks/ingest.js +7 -29
  48. package/dist/hooks/instructions-loaded.js +7 -29
  49. package/dist/hooks/notification.js +7 -29
  50. package/dist/hooks/post-compact.js +7 -29
  51. package/dist/hooks/post-tool-combined.js +7 -29
  52. package/dist/hooks/pre-compact.js +7 -29
  53. package/dist/hooks/pre-tool-use.js +7 -29
  54. package/dist/hooks/prompt-submit.js +7 -29
  55. package/dist/hooks/session-end.js +7 -29
  56. package/dist/hooks/session-start.js +7 -29
  57. package/dist/hooks/stop.js +7 -29
  58. package/dist/hooks/subagent-stop.js +7 -29
  59. package/dist/hooks/summary-worker.js +7 -29
  60. package/dist/index.js +7 -29
  61. package/dist/lib/cloud-sync.js +0 -29
  62. package/dist/lib/database.js +0 -35
  63. package/dist/lib/db-daemon-client.js +0 -36
  64. package/dist/lib/db.js +0 -35
  65. package/dist/lib/device-registry.js +0 -35
  66. package/dist/lib/embedder.js +0 -35
  67. package/dist/lib/employee-templates.js +7 -0
  68. package/dist/lib/exe-daemon-client.js +0 -36
  69. package/dist/lib/exe-daemon.js +7 -29
  70. package/dist/lib/hybrid-search.js +7 -29
  71. package/dist/lib/schedules.js +7 -29
  72. package/dist/lib/skill-learning.js +0 -35
  73. package/dist/lib/store.js +7 -29
  74. package/dist/lib/tasks.js +0 -29
  75. package/dist/lib/tmux-routing.js +0 -29
  76. package/dist/mcp/server.js +7 -29
  77. package/dist/mcp/tools/create-task.js +0 -29
  78. package/dist/mcp/tools/update-task.js +0 -29
  79. package/dist/runtime/index.js +7 -29
  80. package/dist/tui/App.js +7 -29
  81. package/package.json +1 -1
@@ -1447,40 +1447,11 @@ function findPackageRoot() {
1447
1447
  }
1448
1448
  return null;
1449
1449
  }
1450
- function getAvailableMemoryGB() {
1451
- if (process.platform === "darwin") {
1452
- try {
1453
- const { execSync: execSync8 } = __require("child_process");
1454
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1455
- const pageSize = 16384;
1456
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1457
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1458
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1459
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1460
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1461
- const freePages = free ? parseInt(free[1], 10) : 0;
1462
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1463
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1464
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1465
- } catch {
1466
- return os5.freemem() / (1024 * 1024 * 1024);
1467
- }
1468
- }
1469
- return os5.freemem() / (1024 * 1024 * 1024);
1470
- }
1471
1450
  function spawnDaemon() {
1472
- const freeGB = getAvailableMemoryGB();
1473
1451
  const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1474
1452
  if (totalGB <= 8) {
1475
1453
  process.stderr.write(
1476
1454
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1477
- `
1478
- );
1479
- return;
1480
- }
1481
- if (totalGB <= 16 && freeGB < 2) {
1482
- process.stderr.write(
1483
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1484
1455
  `
1485
1456
  );
1486
1457
  return;
@@ -4494,6 +4465,13 @@ var init_platform_procedures = __esm({
4494
4465
  priority: "p0",
4495
4466
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4496
4467
  },
4468
+ // --- Encryption key + cloud sync ---
4469
+ {
4470
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4471
+ domain: "security",
4472
+ priority: "p0",
4473
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4474
+ },
4497
4475
  // --- MCP is the ONLY data interface ---
4498
4476
  {
4499
4477
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1068,40 +1068,11 @@ function findPackageRoot() {
1068
1068
  }
1069
1069
  return null;
1070
1070
  }
1071
- function getAvailableMemoryGB() {
1072
- if (process.platform === "darwin") {
1073
- try {
1074
- const { execSync: execSync6 } = __require("child_process");
1075
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1076
- const pageSize = 16384;
1077
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1078
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1079
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1080
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1081
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1082
- const freePages = free ? parseInt(free[1], 10) : 0;
1083
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1084
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1085
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1086
- } catch {
1087
- return os4.freemem() / (1024 * 1024 * 1024);
1088
- }
1089
- }
1090
- return os4.freemem() / (1024 * 1024 * 1024);
1091
- }
1092
1071
  function spawnDaemon() {
1093
- const freeGB = getAvailableMemoryGB();
1094
1072
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1095
1073
  if (totalGB <= 8) {
1096
1074
  process.stderr.write(
1097
1075
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1098
- `
1099
- );
1100
- return;
1101
- }
1102
- if (totalGB <= 16 && freeGB < 2) {
1103
- process.stderr.write(
1104
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1105
1076
  `
1106
1077
  );
1107
1078
  return;
@@ -4319,6 +4290,13 @@ var init_platform_procedures = __esm({
4319
4290
  priority: "p0",
4320
4291
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4321
4292
  },
4293
+ // --- Encryption key + cloud sync ---
4294
+ {
4295
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4296
+ domain: "security",
4297
+ priority: "p0",
4298
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4299
+ },
4322
4300
  // --- MCP is the ONLY data interface ---
4323
4301
  {
4324
4302
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1068,40 +1068,11 @@ function findPackageRoot() {
1068
1068
  }
1069
1069
  return null;
1070
1070
  }
1071
- function getAvailableMemoryGB() {
1072
- if (process.platform === "darwin") {
1073
- try {
1074
- const { execSync: execSync6 } = __require("child_process");
1075
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1076
- const pageSize = 16384;
1077
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1078
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1079
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1080
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1081
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1082
- const freePages = free ? parseInt(free[1], 10) : 0;
1083
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1084
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1085
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1086
- } catch {
1087
- return os4.freemem() / (1024 * 1024 * 1024);
1088
- }
1089
- }
1090
- return os4.freemem() / (1024 * 1024 * 1024);
1091
- }
1092
1071
  function spawnDaemon() {
1093
- const freeGB = getAvailableMemoryGB();
1094
1072
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1095
1073
  if (totalGB <= 8) {
1096
1074
  process.stderr.write(
1097
1075
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1098
- `
1099
- );
1100
- return;
1101
- }
1102
- if (totalGB <= 16 && freeGB < 2) {
1103
- process.stderr.write(
1104
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1105
1076
  `
1106
1077
  );
1107
1078
  return;
@@ -4319,6 +4290,13 @@ var init_platform_procedures = __esm({
4319
4290
  priority: "p0",
4320
4291
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4321
4292
  },
4293
+ // --- Encryption key + cloud sync ---
4294
+ {
4295
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4296
+ domain: "security",
4297
+ priority: "p0",
4298
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4299
+ },
4322
4300
  // --- MCP is the ONLY data interface ---
4323
4301
  {
4324
4302
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1363,40 +1363,11 @@ function findPackageRoot() {
1363
1363
  }
1364
1364
  return null;
1365
1365
  }
1366
- function getAvailableMemoryGB() {
1367
- if (process.platform === "darwin") {
1368
- try {
1369
- const { execSync: execSync7 } = __require("child_process");
1370
- const vmstat = execSync7("vm_stat", { encoding: "utf8" });
1371
- const pageSize = 16384;
1372
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1373
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1374
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1375
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1376
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1377
- const freePages = free ? parseInt(free[1], 10) : 0;
1378
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1379
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1380
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1381
- } catch {
1382
- return os6.freemem() / (1024 * 1024 * 1024);
1383
- }
1384
- }
1385
- return os6.freemem() / (1024 * 1024 * 1024);
1386
- }
1387
1366
  function spawnDaemon() {
1388
- const freeGB = getAvailableMemoryGB();
1389
1367
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1390
1368
  if (totalGB <= 8) {
1391
1369
  process.stderr.write(
1392
1370
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1393
- `
1394
- );
1395
- return;
1396
- }
1397
- if (totalGB <= 16 && freeGB < 2) {
1398
- process.stderr.write(
1399
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1400
1371
  `
1401
1372
  );
1402
1373
  return;
@@ -4939,6 +4910,13 @@ var init_platform_procedures = __esm({
4939
4910
  priority: "p0",
4940
4911
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4941
4912
  },
4913
+ // --- Encryption key + cloud sync ---
4914
+ {
4915
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4916
+ domain: "security",
4917
+ priority: "p0",
4918
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4919
+ },
4942
4920
  // --- MCP is the ONLY data interface ---
4943
4921
  {
4944
4922
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1319,40 +1319,11 @@ function findPackageRoot() {
1319
1319
  }
1320
1320
  return null;
1321
1321
  }
1322
- function getAvailableMemoryGB() {
1323
- if (process.platform === "darwin") {
1324
- try {
1325
- const { execSync: execSync9 } = __require("child_process");
1326
- const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1327
- const pageSize = 16384;
1328
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1329
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1330
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1331
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1332
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1333
- const freePages = free ? parseInt(free[1], 10) : 0;
1334
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1335
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1336
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1337
- } catch {
1338
- return os4.freemem() / (1024 * 1024 * 1024);
1339
- }
1340
- }
1341
- return os4.freemem() / (1024 * 1024 * 1024);
1342
- }
1343
1322
  function spawnDaemon() {
1344
- const freeGB = getAvailableMemoryGB();
1345
1323
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1346
1324
  if (totalGB <= 8) {
1347
1325
  process.stderr.write(
1348
1326
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1349
- `
1350
- );
1351
- return;
1352
- }
1353
- if (totalGB <= 16 && freeGB < 2) {
1354
- process.stderr.write(
1355
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1356
1327
  `
1357
1328
  );
1358
1329
  return;
@@ -4570,6 +4541,13 @@ var init_platform_procedures = __esm({
4570
4541
  priority: "p0",
4571
4542
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4572
4543
  },
4544
+ // --- Encryption key + cloud sync ---
4545
+ {
4546
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4547
+ domain: "security",
4548
+ priority: "p0",
4549
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4550
+ },
4573
4551
  // --- MCP is the ONLY data interface ---
4574
4552
  {
4575
4553
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1654,40 +1654,11 @@ function findPackageRoot() {
1654
1654
  }
1655
1655
  return null;
1656
1656
  }
1657
- function getAvailableMemoryGB() {
1658
- if (process.platform === "darwin") {
1659
- try {
1660
- const { execSync: execSync10 } = __require("child_process");
1661
- const vmstat = execSync10("vm_stat", { encoding: "utf8" });
1662
- const pageSize = 16384;
1663
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1664
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1665
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1666
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1667
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1668
- const freePages = free ? parseInt(free[1], 10) : 0;
1669
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1670
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1671
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1672
- } catch {
1673
- return os6.freemem() / (1024 * 1024 * 1024);
1674
- }
1675
- }
1676
- return os6.freemem() / (1024 * 1024 * 1024);
1677
- }
1678
1657
  function spawnDaemon() {
1679
- const freeGB = getAvailableMemoryGB();
1680
1658
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1681
1659
  if (totalGB <= 8) {
1682
1660
  process.stderr.write(
1683
1661
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1684
- `
1685
- );
1686
- return;
1687
- }
1688
- if (totalGB <= 16 && freeGB < 2) {
1689
- process.stderr.write(
1690
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1691
1662
  `
1692
1663
  );
1693
1664
  return;
@@ -8134,6 +8105,13 @@ var init_platform_procedures = __esm({
8134
8105
  priority: "p0",
8135
8106
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
8136
8107
  },
8108
+ // --- Encryption key + cloud sync ---
8109
+ {
8110
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
8111
+ domain: "security",
8112
+ priority: "p0",
8113
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
8114
+ },
8137
8115
  // --- MCP is the ONLY data interface ---
8138
8116
  {
8139
8117
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1650,40 +1650,11 @@ function findPackageRoot() {
1650
1650
  }
1651
1651
  return null;
1652
1652
  }
1653
- function getAvailableMemoryGB() {
1654
- if (process.platform === "darwin") {
1655
- try {
1656
- const { execSync: execSync9 } = __require("child_process");
1657
- const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1658
- const pageSize = 16384;
1659
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1660
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1661
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1662
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1663
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1664
- const freePages = free ? parseInt(free[1], 10) : 0;
1665
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1666
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1667
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1668
- } catch {
1669
- return os6.freemem() / (1024 * 1024 * 1024);
1670
- }
1671
- }
1672
- return os6.freemem() / (1024 * 1024 * 1024);
1673
- }
1674
1653
  function spawnDaemon() {
1675
- const freeGB = getAvailableMemoryGB();
1676
1654
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1677
1655
  if (totalGB <= 8) {
1678
1656
  process.stderr.write(
1679
1657
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1680
- `
1681
- );
1682
- return;
1683
- }
1684
- if (totalGB <= 16 && freeGB < 2) {
1685
- process.stderr.write(
1686
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1687
1658
  `
1688
1659
  );
1689
1660
  return;
@@ -5270,6 +5241,13 @@ var init_platform_procedures = __esm({
5270
5241
  priority: "p0",
5271
5242
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
5272
5243
  },
5244
+ // --- Encryption key + cloud sync ---
5245
+ {
5246
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
5247
+ domain: "security",
5248
+ priority: "p0",
5249
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
5250
+ },
5273
5251
  // --- MCP is the ONLY data interface ---
5274
5252
  {
5275
5253
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1476,40 +1476,11 @@ function findPackageRoot() {
1476
1476
  }
1477
1477
  return null;
1478
1478
  }
1479
- function getAvailableMemoryGB() {
1480
- if (process.platform === "darwin") {
1481
- try {
1482
- const { execSync: execSync12 } = __require("child_process");
1483
- const vmstat = execSync12("vm_stat", { encoding: "utf8" });
1484
- const pageSize = 16384;
1485
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1486
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1487
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1488
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1489
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1490
- const freePages = free ? parseInt(free[1], 10) : 0;
1491
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1492
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1493
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1494
- } catch {
1495
- return os4.freemem() / (1024 * 1024 * 1024);
1496
- }
1497
- }
1498
- return os4.freemem() / (1024 * 1024 * 1024);
1499
- }
1500
1479
  function spawnDaemon() {
1501
- const freeGB = getAvailableMemoryGB();
1502
1480
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1503
1481
  if (totalGB <= 8) {
1504
1482
  process.stderr.write(
1505
1483
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1506
- `
1507
- );
1508
- return;
1509
- }
1510
- if (totalGB <= 16 && freeGB < 2) {
1511
- process.stderr.write(
1512
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1513
1484
  `
1514
1485
  );
1515
1486
  return;
@@ -4727,6 +4698,13 @@ var init_platform_procedures = __esm({
4727
4698
  priority: "p0",
4728
4699
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4729
4700
  },
4701
+ // --- Encryption key + cloud sync ---
4702
+ {
4703
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4704
+ domain: "security",
4705
+ priority: "p0",
4706
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4707
+ },
4730
4708
  // --- MCP is the ONLY data interface ---
4731
4709
  {
4732
4710
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1657,40 +1657,11 @@ function findPackageRoot() {
1657
1657
  }
1658
1658
  return null;
1659
1659
  }
1660
- function getAvailableMemoryGB() {
1661
- if (process.platform === "darwin") {
1662
- try {
1663
- const { execSync: execSync12 } = __require("child_process");
1664
- const vmstat = execSync12("vm_stat", { encoding: "utf8" });
1665
- const pageSize = 16384;
1666
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1667
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1668
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1669
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1670
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1671
- const freePages = free ? parseInt(free[1], 10) : 0;
1672
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1673
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1674
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1675
- } catch {
1676
- return os7.freemem() / (1024 * 1024 * 1024);
1677
- }
1678
- }
1679
- return os7.freemem() / (1024 * 1024 * 1024);
1680
- }
1681
1660
  function spawnDaemon() {
1682
- const freeGB = getAvailableMemoryGB();
1683
1661
  const totalGB = os7.totalmem() / (1024 * 1024 * 1024);
1684
1662
  if (totalGB <= 8) {
1685
1663
  process.stderr.write(
1686
1664
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1687
- `
1688
- );
1689
- return;
1690
- }
1691
- if (totalGB <= 16 && freeGB < 2) {
1692
- process.stderr.write(
1693
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1694
1665
  `
1695
1666
  );
1696
1667
  return;
@@ -8343,6 +8314,13 @@ var init_platform_procedures = __esm({
8343
8314
  priority: "p0",
8344
8315
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
8345
8316
  },
8317
+ // --- Encryption key + cloud sync ---
8318
+ {
8319
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
8320
+ domain: "security",
8321
+ priority: "p0",
8322
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
8323
+ },
8346
8324
  // --- MCP is the ONLY data interface ---
8347
8325
  {
8348
8326
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1476,40 +1476,11 @@ function findPackageRoot() {
1476
1476
  }
1477
1477
  return null;
1478
1478
  }
1479
- function getAvailableMemoryGB() {
1480
- if (process.platform === "darwin") {
1481
- try {
1482
- const { execSync: execSync11 } = __require("child_process");
1483
- const vmstat = execSync11("vm_stat", { encoding: "utf8" });
1484
- const pageSize = 16384;
1485
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1486
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1487
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1488
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1489
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1490
- const freePages = free ? parseInt(free[1], 10) : 0;
1491
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1492
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1493
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1494
- } catch {
1495
- return os4.freemem() / (1024 * 1024 * 1024);
1496
- }
1497
- }
1498
- return os4.freemem() / (1024 * 1024 * 1024);
1499
- }
1500
1479
  function spawnDaemon() {
1501
- const freeGB = getAvailableMemoryGB();
1502
1480
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1503
1481
  if (totalGB <= 8) {
1504
1482
  process.stderr.write(
1505
1483
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1506
- `
1507
- );
1508
- return;
1509
- }
1510
- if (totalGB <= 16 && freeGB < 2) {
1511
- process.stderr.write(
1512
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1513
1484
  `
1514
1485
  );
1515
1486
  return;
@@ -4727,6 +4698,13 @@ var init_platform_procedures = __esm({
4727
4698
  priority: "p0",
4728
4699
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4729
4700
  },
4701
+ // --- Encryption key + cloud sync ---
4702
+ {
4703
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4704
+ domain: "security",
4705
+ priority: "p0",
4706
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4707
+ },
4730
4708
  // --- MCP is the ONLY data interface ---
4731
4709
  {
4732
4710
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1398,40 +1398,11 @@ function findPackageRoot() {
1398
1398
  }
1399
1399
  return null;
1400
1400
  }
1401
- function getAvailableMemoryGB() {
1402
- if (process.platform === "darwin") {
1403
- try {
1404
- const { execSync: execSync8 } = __require("child_process");
1405
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1406
- const pageSize = 16384;
1407
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1408
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1409
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1410
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1411
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1412
- const freePages = free ? parseInt(free[1], 10) : 0;
1413
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1414
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1415
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1416
- } catch {
1417
- return os6.freemem() / (1024 * 1024 * 1024);
1418
- }
1419
- }
1420
- return os6.freemem() / (1024 * 1024 * 1024);
1421
- }
1422
1401
  function spawnDaemon() {
1423
- const freeGB = getAvailableMemoryGB();
1424
1402
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1425
1403
  if (totalGB <= 8) {
1426
1404
  process.stderr.write(
1427
1405
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1428
- `
1429
- );
1430
- return;
1431
- }
1432
- if (totalGB <= 16 && freeGB < 2) {
1433
- process.stderr.write(
1434
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1435
1406
  `
1436
1407
  );
1437
1408
  return;
@@ -4843,6 +4814,13 @@ var init_platform_procedures = __esm({
4843
4814
  priority: "p0",
4844
4815
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
4845
4816
  },
4817
+ // --- Encryption key + cloud sync ---
4818
+ {
4819
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4820
+ domain: "security",
4821
+ priority: "p0",
4822
+ content: "The master encryption key is stored in macOS Keychain (Secure Enclave) or Linux secret-tool \u2014 NOT as a file. There is no ~/.exe-os/master.key on modern installs. If an older install had one, it was auto-migrated to Keychain and the file deleted. Device linking uses a 24-word BIP39 recovery phrase: Device 1 runs `exe-os cloud link --show-full` in their local Terminal to reveal it, Device 2 runs `exe-os cloud` and pastes the phrase to import the key into its own Keychain, then cloud sync pulls encrypted memories. NEVER display, log, or return the recovery phrase in agent output. MCP tools are hardened \u2014 they cannot reveal it. If the user needs the phrase, tell them: 'Run exe-os cloud link --show-full in your Terminal.' If searching for master.key returns nothing, that is CORRECT \u2014 the key is in Keychain."
4823
+ },
4846
4824
  // --- MCP is the ONLY data interface ---
4847
4825
  {
4848
4826
  title: "MCP disconnect \u2014 ask the user, never work around it",