@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
@@ -1055,40 +1055,11 @@ function findPackageRoot() {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function getAvailableMemoryGB() {
1059
- if (process.platform === "darwin") {
1060
- try {
1061
- const { execSync: execSync4 } = __require("child_process");
1062
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1063
- const pageSize = 16384;
1064
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1065
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1066
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1067
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1068
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1069
- const freePages = free ? parseInt(free[1], 10) : 0;
1070
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1071
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1072
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1073
- } catch {
1074
- return os4.freemem() / (1024 * 1024 * 1024);
1075
- }
1076
- }
1077
- return os4.freemem() / (1024 * 1024 * 1024);
1078
- }
1079
1058
  function spawnDaemon() {
1080
- const freeGB = getAvailableMemoryGB();
1081
1059
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1082
1060
  if (totalGB <= 8) {
1083
1061
  process.stderr.write(
1084
1062
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1085
- `
1086
- );
1087
- return;
1088
- }
1089
- if (totalGB <= 16 && freeGB < 2) {
1090
- process.stderr.write(
1091
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1092
1063
  `
1093
1064
  );
1094
1065
  return;
@@ -3545,6 +3516,13 @@ var init_platform_procedures = __esm({
3545
3516
  priority: "p0",
3546
3517
  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."
3547
3518
  },
3519
+ // --- Encryption key + cloud sync ---
3520
+ {
3521
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3522
+ domain: "security",
3523
+ priority: "p0",
3524
+ 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."
3525
+ },
3548
3526
  // --- MCP is the ONLY data interface ---
3549
3527
  {
3550
3528
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1055,40 +1055,11 @@ function findPackageRoot() {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function getAvailableMemoryGB() {
1059
- if (process.platform === "darwin") {
1060
- try {
1061
- const { execSync: execSync4 } = __require("child_process");
1062
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1063
- const pageSize = 16384;
1064
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1065
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1066
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1067
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1068
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1069
- const freePages = free ? parseInt(free[1], 10) : 0;
1070
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1071
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1072
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1073
- } catch {
1074
- return os4.freemem() / (1024 * 1024 * 1024);
1075
- }
1076
- }
1077
- return os4.freemem() / (1024 * 1024 * 1024);
1078
- }
1079
1058
  function spawnDaemon() {
1080
- const freeGB = getAvailableMemoryGB();
1081
1059
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1082
1060
  if (totalGB <= 8) {
1083
1061
  process.stderr.write(
1084
1062
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1085
- `
1086
- );
1087
- return;
1088
- }
1089
- if (totalGB <= 16 && freeGB < 2) {
1090
- process.stderr.write(
1091
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1092
1063
  `
1093
1064
  );
1094
1065
  return;
@@ -3545,6 +3516,13 @@ var init_platform_procedures = __esm({
3545
3516
  priority: "p0",
3546
3517
  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."
3547
3518
  },
3519
+ // --- Encryption key + cloud sync ---
3520
+ {
3521
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3522
+ domain: "security",
3523
+ priority: "p0",
3524
+ 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."
3525
+ },
3548
3526
  // --- MCP is the ONLY data interface ---
3549
3527
  {
3550
3528
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1055,40 +1055,11 @@ function findPackageRoot() {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function getAvailableMemoryGB() {
1059
- if (process.platform === "darwin") {
1060
- try {
1061
- const { execSync: execSync4 } = __require("child_process");
1062
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1063
- const pageSize = 16384;
1064
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1065
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1066
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1067
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1068
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1069
- const freePages = free ? parseInt(free[1], 10) : 0;
1070
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1071
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1072
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1073
- } catch {
1074
- return os4.freemem() / (1024 * 1024 * 1024);
1075
- }
1076
- }
1077
- return os4.freemem() / (1024 * 1024 * 1024);
1078
- }
1079
1058
  function spawnDaemon() {
1080
- const freeGB = getAvailableMemoryGB();
1081
1059
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1082
1060
  if (totalGB <= 8) {
1083
1061
  process.stderr.write(
1084
1062
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1085
- `
1086
- );
1087
- return;
1088
- }
1089
- if (totalGB <= 16 && freeGB < 2) {
1090
- process.stderr.write(
1091
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1092
1063
  `
1093
1064
  );
1094
1065
  return;
@@ -3545,6 +3516,13 @@ var init_platform_procedures = __esm({
3545
3516
  priority: "p0",
3546
3517
  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."
3547
3518
  },
3519
+ // --- Encryption key + cloud sync ---
3520
+ {
3521
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3522
+ domain: "security",
3523
+ priority: "p0",
3524
+ 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."
3525
+ },
3548
3526
  // --- MCP is the ONLY data interface ---
3549
3527
  {
3550
3528
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1059,40 +1059,11 @@ function findPackageRoot() {
1059
1059
  }
1060
1060
  return null;
1061
1061
  }
1062
- function getAvailableMemoryGB() {
1063
- if (process.platform === "darwin") {
1064
- try {
1065
- const { execSync: execSync4 } = __require("child_process");
1066
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1067
- const pageSize = 16384;
1068
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1069
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1070
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1071
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1072
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1073
- const freePages = free ? parseInt(free[1], 10) : 0;
1074
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1075
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1076
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1077
- } catch {
1078
- return os4.freemem() / (1024 * 1024 * 1024);
1079
- }
1080
- }
1081
- return os4.freemem() / (1024 * 1024 * 1024);
1082
- }
1083
1062
  function spawnDaemon() {
1084
- const freeGB = getAvailableMemoryGB();
1085
1063
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1086
1064
  if (totalGB <= 8) {
1087
1065
  process.stderr.write(
1088
1066
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1089
- `
1090
- );
1091
- return;
1092
- }
1093
- if (totalGB <= 16 && freeGB < 2) {
1094
- process.stderr.write(
1095
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1096
1067
  `
1097
1068
  );
1098
1069
  return;
@@ -3685,6 +3656,13 @@ var init_platform_procedures = __esm({
3685
3656
  priority: "p0",
3686
3657
  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."
3687
3658
  },
3659
+ // --- Encryption key + cloud sync ---
3660
+ {
3661
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3662
+ domain: "security",
3663
+ priority: "p0",
3664
+ 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."
3665
+ },
3688
3666
  // --- MCP is the ONLY data interface ---
3689
3667
  {
3690
3668
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1059,40 +1059,11 @@ function findPackageRoot() {
1059
1059
  }
1060
1060
  return null;
1061
1061
  }
1062
- function getAvailableMemoryGB() {
1063
- if (process.platform === "darwin") {
1064
- try {
1065
- const { execSync: execSync4 } = __require("child_process");
1066
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1067
- const pageSize = 16384;
1068
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1069
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1070
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1071
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1072
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1073
- const freePages = free ? parseInt(free[1], 10) : 0;
1074
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1075
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1076
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1077
- } catch {
1078
- return os4.freemem() / (1024 * 1024 * 1024);
1079
- }
1080
- }
1081
- return os4.freemem() / (1024 * 1024 * 1024);
1082
- }
1083
1062
  function spawnDaemon() {
1084
- const freeGB = getAvailableMemoryGB();
1085
1063
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1086
1064
  if (totalGB <= 8) {
1087
1065
  process.stderr.write(
1088
1066
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1089
- `
1090
- );
1091
- return;
1092
- }
1093
- if (totalGB <= 16 && freeGB < 2) {
1094
- process.stderr.write(
1095
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1096
1067
  `
1097
1068
  );
1098
1069
  return;
@@ -3685,6 +3656,13 @@ var init_platform_procedures = __esm({
3685
3656
  priority: "p0",
3686
3657
  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."
3687
3658
  },
3659
+ // --- Encryption key + cloud sync ---
3660
+ {
3661
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3662
+ domain: "security",
3663
+ priority: "p0",
3664
+ 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."
3665
+ },
3688
3666
  // --- MCP is the ONLY data interface ---
3689
3667
  {
3690
3668
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1055,40 +1055,11 @@ function findPackageRoot() {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function getAvailableMemoryGB() {
1059
- if (process.platform === "darwin") {
1060
- try {
1061
- const { execSync: execSync4 } = __require("child_process");
1062
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1063
- const pageSize = 16384;
1064
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1065
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1066
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1067
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1068
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1069
- const freePages = free ? parseInt(free[1], 10) : 0;
1070
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1071
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1072
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1073
- } catch {
1074
- return os4.freemem() / (1024 * 1024 * 1024);
1075
- }
1076
- }
1077
- return os4.freemem() / (1024 * 1024 * 1024);
1078
- }
1079
1058
  function spawnDaemon() {
1080
- const freeGB = getAvailableMemoryGB();
1081
1059
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1082
1060
  if (totalGB <= 8) {
1083
1061
  process.stderr.write(
1084
1062
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1085
- `
1086
- );
1087
- return;
1088
- }
1089
- if (totalGB <= 16 && freeGB < 2) {
1090
- process.stderr.write(
1091
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1092
1063
  `
1093
1064
  );
1094
1065
  return;
@@ -3681,6 +3652,13 @@ var init_platform_procedures = __esm({
3681
3652
  priority: "p0",
3682
3653
  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."
3683
3654
  },
3655
+ // --- Encryption key + cloud sync ---
3656
+ {
3657
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3658
+ domain: "security",
3659
+ priority: "p0",
3660
+ 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."
3661
+ },
3684
3662
  // --- MCP is the ONLY data interface ---
3685
3663
  {
3686
3664
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1055,40 +1055,11 @@ function findPackageRoot() {
1055
1055
  }
1056
1056
  return null;
1057
1057
  }
1058
- function getAvailableMemoryGB() {
1059
- if (process.platform === "darwin") {
1060
- try {
1061
- const { execSync: execSync4 } = __require("child_process");
1062
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1063
- const pageSize = 16384;
1064
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1065
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1066
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1067
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1068
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1069
- const freePages = free ? parseInt(free[1], 10) : 0;
1070
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1071
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1072
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1073
- } catch {
1074
- return os4.freemem() / (1024 * 1024 * 1024);
1075
- }
1076
- }
1077
- return os4.freemem() / (1024 * 1024 * 1024);
1078
- }
1079
1058
  function spawnDaemon() {
1080
- const freeGB = getAvailableMemoryGB();
1081
1059
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1082
1060
  if (totalGB <= 8) {
1083
1061
  process.stderr.write(
1084
1062
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1085
- `
1086
- );
1087
- return;
1088
- }
1089
- if (totalGB <= 16 && freeGB < 2) {
1090
- process.stderr.write(
1091
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1092
1063
  `
1093
1064
  );
1094
1065
  return;
@@ -3853,6 +3824,13 @@ var init_platform_procedures = __esm({
3853
3824
  priority: "p0",
3854
3825
  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."
3855
3826
  },
3827
+ // --- Encryption key + cloud sync ---
3828
+ {
3829
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3830
+ domain: "security",
3831
+ priority: "p0",
3832
+ 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."
3833
+ },
3856
3834
  // --- MCP is the ONLY data interface ---
3857
3835
  {
3858
3836
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1077,40 +1077,11 @@ function findPackageRoot() {
1077
1077
  }
1078
1078
  return null;
1079
1079
  }
1080
- function getAvailableMemoryGB() {
1081
- if (process.platform === "darwin") {
1082
- try {
1083
- const { execSync: execSync6 } = __require("child_process");
1084
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1085
- const pageSize = 16384;
1086
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1087
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1088
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1089
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1090
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1091
- const freePages = free ? parseInt(free[1], 10) : 0;
1092
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1093
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1094
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1095
- } catch {
1096
- return os4.freemem() / (1024 * 1024 * 1024);
1097
- }
1098
- }
1099
- return os4.freemem() / (1024 * 1024 * 1024);
1100
- }
1101
1080
  function spawnDaemon() {
1102
- const freeGB = getAvailableMemoryGB();
1103
1081
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1104
1082
  if (totalGB <= 8) {
1105
1083
  process.stderr.write(
1106
1084
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1107
- `
1108
- );
1109
- return;
1110
- }
1111
- if (totalGB <= 16 && freeGB < 2) {
1112
- process.stderr.write(
1113
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1114
1085
  `
1115
1086
  );
1116
1087
  return;
@@ -4328,6 +4299,13 @@ var init_platform_procedures = __esm({
4328
4299
  priority: "p0",
4329
4300
  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."
4330
4301
  },
4302
+ // --- Encryption key + cloud sync ---
4303
+ {
4304
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4305
+ domain: "security",
4306
+ priority: "p0",
4307
+ 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."
4308
+ },
4331
4309
  // --- MCP is the ONLY data interface ---
4332
4310
  {
4333
4311
  title: "MCP disconnect \u2014 ask the user, never work around it",
package/dist/bin/cli.js CHANGED
@@ -3748,40 +3748,11 @@ function findPackageRoot() {
3748
3748
  }
3749
3749
  return null;
3750
3750
  }
3751
- function getAvailableMemoryGB() {
3752
- if (process.platform === "darwin") {
3753
- try {
3754
- const { execSync: execSync20 } = __require("child_process");
3755
- const vmstat = execSync20("vm_stat", { encoding: "utf8" });
3756
- const pageSize = 16384;
3757
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
3758
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
3759
- const free = vmstat.match(/Pages free:\s+(\d+)/);
3760
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
3761
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
3762
- const freePages = free ? parseInt(free[1], 10) : 0;
3763
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
3764
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
3765
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
3766
- } catch {
3767
- return os10.freemem() / (1024 * 1024 * 1024);
3768
- }
3769
- }
3770
- return os10.freemem() / (1024 * 1024 * 1024);
3771
- }
3772
3751
  function spawnDaemon() {
3773
- const freeGB = getAvailableMemoryGB();
3774
3752
  const totalGB = os10.totalmem() / (1024 * 1024 * 1024);
3775
3753
  if (totalGB <= 8) {
3776
3754
  process.stderr.write(
3777
3755
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
3778
- `
3779
- );
3780
- return;
3781
- }
3782
- if (totalGB <= 16 && freeGB < 2) {
3783
- process.stderr.write(
3784
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
3785
3756
  `
3786
3757
  );
3787
3758
  return;
@@ -9015,6 +8986,13 @@ var init_platform_procedures = __esm({
9015
8986
  priority: "p0",
9016
8987
  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."
9017
8988
  },
8989
+ // --- Encryption key + cloud sync ---
8990
+ {
8991
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
8992
+ domain: "security",
8993
+ priority: "p0",
8994
+ 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."
8995
+ },
9018
8996
  // --- MCP is the ONLY data interface ---
9019
8997
  {
9020
8998
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -19950,7 +19928,7 @@ function ask3(rl, prompt) {
19950
19928
  doAsk();
19951
19929
  });
19952
19930
  }
19953
- function getAvailableMemoryGB2() {
19931
+ function getAvailableMemoryGB() {
19954
19932
  if (process.platform === "darwin") {
19955
19933
  try {
19956
19934
  const { execSync: execSync20 } = __require("child_process");
@@ -19974,11 +19952,11 @@ function getTotalMemoryGB() {
19974
19952
  return os19.totalmem() / (1024 * 1024 * 1024);
19975
19953
  }
19976
19954
  function isLowMemory() {
19977
- return getAvailableMemoryGB2() < 2;
19955
+ return getAvailableMemoryGB() < 2;
19978
19956
  }
19979
19957
  async function validateModel(log) {
19980
19958
  const totalGB = getTotalMemoryGB();
19981
- const freeGB = getAvailableMemoryGB2();
19959
+ const freeGB = getAvailableMemoryGB();
19982
19960
  if (totalGB <= 8 || isLowMemory()) {
19983
19961
  log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
19984
19962
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
@@ -20223,7 +20201,7 @@ async function runSetupWizard(opts = {}) {
20223
20201
  skipModel = true;
20224
20202
  }
20225
20203
  if (!skipModel) {
20226
- const freeGB = getAvailableMemoryGB2();
20204
+ const freeGB = getAvailableMemoryGB();
20227
20205
  if (freeGB < 2) {
20228
20206
  log(`\u26A0 Low memory detected: ${freeGB.toFixed(1)}GB free of ${totalGB.toFixed(0)}GB total`);
20229
20207
  log(" Close other applications (browser, Slack, etc.) before continuing.");
@@ -1493,6 +1493,13 @@ var PLATFORM_PROCEDURES = [
1493
1493
  priority: "p0",
1494
1494
  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."
1495
1495
  },
1496
+ // --- Encryption key + cloud sync ---
1497
+ {
1498
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
1499
+ domain: "security",
1500
+ priority: "p0",
1501
+ 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."
1502
+ },
1496
1503
  // --- MCP is the ONLY data interface ---
1497
1504
  {
1498
1505
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -430,40 +430,11 @@ function findPackageRoot() {
430
430
  }
431
431
  return null;
432
432
  }
433
- function getAvailableMemoryGB() {
434
- if (process.platform === "darwin") {
435
- try {
436
- const { execSync: execSync4 } = __require("child_process");
437
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
438
- const pageSize = 16384;
439
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
440
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
441
- const free = vmstat.match(/Pages free:\s+(\d+)/);
442
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
443
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
444
- const freePages = free ? parseInt(free[1], 10) : 0;
445
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
446
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
447
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
448
- } catch {
449
- return os3.freemem() / (1024 * 1024 * 1024);
450
- }
451
- }
452
- return os3.freemem() / (1024 * 1024 * 1024);
453
- }
454
433
  function spawnDaemon() {
455
- const freeGB = getAvailableMemoryGB();
456
434
  const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
457
435
  if (totalGB <= 8) {
458
436
  process.stderr.write(
459
437
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
460
- `
461
- );
462
- return;
463
- }
464
- if (totalGB <= 16 && freeGB < 2) {
465
- process.stderr.write(
466
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
467
438
  `
468
439
  );
469
440
  return;
@@ -3695,6 +3666,13 @@ var init_platform_procedures = __esm({
3695
3666
  priority: "p0",
3696
3667
  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."
3697
3668
  },
3669
+ // --- Encryption key + cloud sync ---
3670
+ {
3671
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3672
+ domain: "security",
3673
+ priority: "p0",
3674
+ 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."
3675
+ },
3698
3676
  // --- MCP is the ONLY data interface ---
3699
3677
  {
3700
3678
  title: "MCP disconnect \u2014 ask the user, never work around it",