@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
@@ -1208,40 +1208,11 @@ function findPackageRoot() {
1208
1208
  }
1209
1209
  return null;
1210
1210
  }
1211
- function getAvailableMemoryGB() {
1212
- if (process.platform === "darwin") {
1213
- try {
1214
- const { execSync: execSync12 } = __require("child_process");
1215
- const vmstat = execSync12("vm_stat", { encoding: "utf8" });
1216
- const pageSize = 16384;
1217
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1218
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1219
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1220
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1221
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1222
- const freePages = free ? parseInt(free[1], 10) : 0;
1223
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1224
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1225
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1226
- } catch {
1227
- return os4.freemem() / (1024 * 1024 * 1024);
1228
- }
1229
- }
1230
- return os4.freemem() / (1024 * 1024 * 1024);
1231
- }
1232
1211
  function spawnDaemon() {
1233
- const freeGB = getAvailableMemoryGB();
1234
1212
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1235
1213
  if (totalGB <= 8) {
1236
1214
  process.stderr.write(
1237
1215
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1238
- `
1239
- );
1240
- return;
1241
- }
1242
- if (totalGB <= 16 && freeGB < 2) {
1243
- process.stderr.write(
1244
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1245
1216
  `
1246
1217
  );
1247
1218
  return;
@@ -3432,6 +3403,13 @@ var init_platform_procedures = __esm({
3432
3403
  priority: "p0",
3433
3404
  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."
3434
3405
  },
3406
+ // --- Encryption key + cloud sync ---
3407
+ {
3408
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3409
+ domain: "security",
3410
+ priority: "p0",
3411
+ 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."
3412
+ },
3435
3413
  // --- MCP is the ONLY data interface ---
3436
3414
  {
3437
3415
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -431,6 +431,13 @@ var init_platform_procedures = __esm({
431
431
  priority: "p0",
432
432
  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."
433
433
  },
434
+ // --- Encryption key + cloud sync ---
435
+ {
436
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
437
+ domain: "security",
438
+ priority: "p0",
439
+ 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."
440
+ },
434
441
  // --- MCP is the ONLY data interface ---
435
442
  {
436
443
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1605,40 +1605,11 @@ function findPackageRoot() {
1605
1605
  }
1606
1606
  return null;
1607
1607
  }
1608
- function getAvailableMemoryGB() {
1609
- if (process.platform === "darwin") {
1610
- try {
1611
- const { execSync: execSync4 } = __require("child_process");
1612
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1613
- const pageSize = 16384;
1614
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1615
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1616
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1617
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1618
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1619
- const freePages = free ? parseInt(free[1], 10) : 0;
1620
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1621
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1622
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1623
- } catch {
1624
- return os5.freemem() / (1024 * 1024 * 1024);
1625
- }
1626
- }
1627
- return os5.freemem() / (1024 * 1024 * 1024);
1628
- }
1629
1608
  function spawnDaemon() {
1630
- const freeGB = getAvailableMemoryGB();
1631
1609
  const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1632
1610
  if (totalGB <= 8) {
1633
1611
  process.stderr.write(
1634
1612
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1635
- `
1636
- );
1637
- return;
1638
- }
1639
- if (totalGB <= 16 && freeGB < 2) {
1640
- process.stderr.write(
1641
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1642
1613
  `
1643
1614
  );
1644
1615
  return;
@@ -6824,6 +6795,13 @@ var init_platform_procedures = __esm({
6824
6795
  priority: "p0",
6825
6796
  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."
6826
6797
  },
6798
+ // --- Encryption key + cloud sync ---
6799
+ {
6800
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
6801
+ domain: "security",
6802
+ priority: "p0",
6803
+ 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."
6804
+ },
6827
6805
  // --- MCP is the ONLY data interface ---
6828
6806
  {
6829
6807
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1655,40 +1655,11 @@ function findPackageRoot() {
1655
1655
  }
1656
1656
  return null;
1657
1657
  }
1658
- function getAvailableMemoryGB() {
1659
- if (process.platform === "darwin") {
1660
- try {
1661
- const { execSync: execSync9 } = __require("child_process");
1662
- const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1663
- const pageSize = 16384;
1664
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1665
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1666
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1667
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1668
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1669
- const freePages = free ? parseInt(free[1], 10) : 0;
1670
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1671
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1672
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1673
- } catch {
1674
- return os6.freemem() / (1024 * 1024 * 1024);
1675
- }
1676
- }
1677
- return os6.freemem() / (1024 * 1024 * 1024);
1678
- }
1679
1658
  function spawnDaemon() {
1680
- const freeGB = getAvailableMemoryGB();
1681
1659
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1682
1660
  if (totalGB <= 8) {
1683
1661
  process.stderr.write(
1684
1662
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1685
- `
1686
- );
1687
- return;
1688
- }
1689
- if (totalGB <= 16 && freeGB < 2) {
1690
- process.stderr.write(
1691
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1692
1663
  `
1693
1664
  );
1694
1665
  return;
@@ -8135,6 +8106,13 @@ var init_platform_procedures = __esm({
8135
8106
  priority: "p0",
8136
8107
  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."
8137
8108
  },
8109
+ // --- Encryption key + cloud sync ---
8110
+ {
8111
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
8112
+ domain: "security",
8113
+ priority: "p0",
8114
+ 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."
8115
+ },
8138
8116
  // --- MCP is the ONLY data interface ---
8139
8117
  {
8140
8118
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -2109,40 +2109,11 @@ function findPackageRoot() {
2109
2109
  }
2110
2110
  return null;
2111
2111
  }
2112
- function getAvailableMemoryGB() {
2113
- if (process.platform === "darwin") {
2114
- try {
2115
- const { execSync: execSync4 } = __require("child_process");
2116
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
2117
- const pageSize = 16384;
2118
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
2119
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
2120
- const free = vmstat.match(/Pages free:\s+(\d+)/);
2121
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
2122
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
2123
- const freePages = free ? parseInt(free[1], 10) : 0;
2124
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
2125
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
2126
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
2127
- } catch {
2128
- return os5.freemem() / (1024 * 1024 * 1024);
2129
- }
2130
- }
2131
- return os5.freemem() / (1024 * 1024 * 1024);
2132
- }
2133
2112
  function spawnDaemon() {
2134
- const freeGB = getAvailableMemoryGB();
2135
2113
  const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
2136
2114
  if (totalGB <= 8) {
2137
2115
  process.stderr.write(
2138
2116
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
2139
- `
2140
- );
2141
- return;
2142
- }
2143
- if (totalGB <= 16 && freeGB < 2) {
2144
- process.stderr.write(
2145
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
2146
2117
  `
2147
2118
  );
2148
2119
  return;
@@ -4637,6 +4608,13 @@ var init_platform_procedures = __esm({
4637
4608
  priority: "p0",
4638
4609
  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."
4639
4610
  },
4611
+ // --- Encryption key + cloud sync ---
4612
+ {
4613
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4614
+ domain: "security",
4615
+ priority: "p0",
4616
+ 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."
4617
+ },
4640
4618
  // --- MCP is the ONLY data interface ---
4641
4619
  {
4642
4620
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1142,40 +1142,11 @@ function findPackageRoot() {
1142
1142
  }
1143
1143
  return null;
1144
1144
  }
1145
- function getAvailableMemoryGB() {
1146
- if (process.platform === "darwin") {
1147
- try {
1148
- const { execSync: execSync4 } = __require("child_process");
1149
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1150
- const pageSize = 16384;
1151
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1152
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1153
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1154
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1155
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1156
- const freePages = free ? parseInt(free[1], 10) : 0;
1157
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1158
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1159
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1160
- } catch {
1161
- return os4.freemem() / (1024 * 1024 * 1024);
1162
- }
1163
- }
1164
- return os4.freemem() / (1024 * 1024 * 1024);
1165
- }
1166
1145
  function spawnDaemon() {
1167
- const freeGB = getAvailableMemoryGB();
1168
1146
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1169
1147
  if (totalGB <= 8) {
1170
1148
  process.stderr.write(
1171
1149
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1172
- `
1173
- );
1174
- return;
1175
- }
1176
- if (totalGB <= 16 && freeGB < 2) {
1177
- process.stderr.write(
1178
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1179
1150
  `
1180
1151
  );
1181
1152
  return;
@@ -4393,6 +4364,13 @@ var init_platform_procedures = __esm({
4393
4364
  priority: "p0",
4394
4365
  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."
4395
4366
  },
4367
+ // --- Encryption key + cloud sync ---
4368
+ {
4369
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4370
+ domain: "security",
4371
+ priority: "p0",
4372
+ 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."
4373
+ },
4396
4374
  // --- MCP is the ONLY data interface ---
4397
4375
  {
4398
4376
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1066,40 +1066,11 @@ function findPackageRoot() {
1066
1066
  }
1067
1067
  return null;
1068
1068
  }
1069
- function getAvailableMemoryGB() {
1070
- if (process.platform === "darwin") {
1071
- try {
1072
- const { execSync: execSync4 } = __require("child_process");
1073
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1074
- const pageSize = 16384;
1075
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1076
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1077
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1078
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1079
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1080
- const freePages = free ? parseInt(free[1], 10) : 0;
1081
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1082
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1083
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1084
- } catch {
1085
- return os4.freemem() / (1024 * 1024 * 1024);
1086
- }
1087
- }
1088
- return os4.freemem() / (1024 * 1024 * 1024);
1089
- }
1090
1069
  function spawnDaemon() {
1091
- const freeGB = getAvailableMemoryGB();
1092
1070
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1093
1071
  if (totalGB <= 8) {
1094
1072
  process.stderr.write(
1095
1073
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1096
- `
1097
- );
1098
- return;
1099
- }
1100
- if (totalGB <= 16 && freeGB < 2) {
1101
- process.stderr.write(
1102
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1103
1074
  `
1104
1075
  );
1105
1076
  return;
@@ -4317,6 +4288,13 @@ var init_platform_procedures = __esm({
4317
4288
  priority: "p0",
4318
4289
  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."
4319
4290
  },
4291
+ // --- Encryption key + cloud sync ---
4292
+ {
4293
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4294
+ domain: "security",
4295
+ priority: "p0",
4296
+ 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."
4297
+ },
4320
4298
  // --- MCP is the ONLY data interface ---
4321
4299
  {
4322
4300
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1792,40 +1792,11 @@ function findPackageRoot() {
1792
1792
  }
1793
1793
  return null;
1794
1794
  }
1795
- function getAvailableMemoryGB() {
1796
- if (process.platform === "darwin") {
1797
- try {
1798
- const { execSync: execSync9 } = __require("child_process");
1799
- const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1800
- const pageSize = 16384;
1801
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1802
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1803
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1804
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1805
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1806
- const freePages = free ? parseInt(free[1], 10) : 0;
1807
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1808
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1809
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1810
- } catch {
1811
- return os5.freemem() / (1024 * 1024 * 1024);
1812
- }
1813
- }
1814
- return os5.freemem() / (1024 * 1024 * 1024);
1815
- }
1816
1795
  function spawnDaemon() {
1817
- const freeGB = getAvailableMemoryGB();
1818
1796
  const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1819
1797
  if (totalGB <= 8) {
1820
1798
  process.stderr.write(
1821
1799
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1822
- `
1823
- );
1824
- return;
1825
- }
1826
- if (totalGB <= 16 && freeGB < 2) {
1827
- process.stderr.write(
1828
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1829
1800
  `
1830
1801
  );
1831
1802
  return;
@@ -5001,6 +4972,13 @@ var init_platform_procedures = __esm({
5001
4972
  priority: "p0",
5002
4973
  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."
5003
4974
  },
4975
+ // --- Encryption key + cloud sync ---
4976
+ {
4977
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4978
+ domain: "security",
4979
+ priority: "p0",
4980
+ 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."
4981
+ },
5004
4982
  // --- MCP is the ONLY data interface ---
5005
4983
  {
5006
4984
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1105,40 +1105,11 @@ function findPackageRoot() {
1105
1105
  }
1106
1106
  return null;
1107
1107
  }
1108
- function getAvailableMemoryGB() {
1109
- if (process.platform === "darwin") {
1110
- try {
1111
- const { execSync: execSync6 } = __require("child_process");
1112
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1113
- const pageSize = 16384;
1114
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1115
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1116
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1117
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1118
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1119
- const freePages = free ? parseInt(free[1], 10) : 0;
1120
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1121
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1122
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1123
- } catch {
1124
- return os4.freemem() / (1024 * 1024 * 1024);
1125
- }
1126
- }
1127
- return os4.freemem() / (1024 * 1024 * 1024);
1128
- }
1129
1108
  function spawnDaemon() {
1130
- const freeGB = getAvailableMemoryGB();
1131
1109
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1132
1110
  if (totalGB <= 8) {
1133
1111
  process.stderr.write(
1134
1112
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1135
- `
1136
- );
1137
- return;
1138
- }
1139
- if (totalGB <= 16 && freeGB < 2) {
1140
- process.stderr.write(
1141
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1142
1113
  `
1143
1114
  );
1144
1115
  return;
@@ -4356,6 +4327,13 @@ var init_platform_procedures = __esm({
4356
4327
  priority: "p0",
4357
4328
  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."
4358
4329
  },
4330
+ // --- Encryption key + cloud sync ---
4331
+ {
4332
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4333
+ domain: "security",
4334
+ priority: "p0",
4335
+ 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."
4336
+ },
4359
4337
  // --- MCP is the ONLY data interface ---
4360
4338
  {
4361
4339
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1066,40 +1066,11 @@ function findPackageRoot() {
1066
1066
  }
1067
1067
  return null;
1068
1068
  }
1069
- function getAvailableMemoryGB() {
1070
- if (process.platform === "darwin") {
1071
- try {
1072
- const { execSync: execSync5 } = __require("child_process");
1073
- const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1074
- const pageSize = 16384;
1075
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1076
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1077
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1078
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1079
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1080
- const freePages = free ? parseInt(free[1], 10) : 0;
1081
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1082
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1083
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1084
- } catch {
1085
- return os4.freemem() / (1024 * 1024 * 1024);
1086
- }
1087
- }
1088
- return os4.freemem() / (1024 * 1024 * 1024);
1089
- }
1090
1069
  function spawnDaemon() {
1091
- const freeGB = getAvailableMemoryGB();
1092
1070
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1093
1071
  if (totalGB <= 8) {
1094
1072
  process.stderr.write(
1095
1073
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1096
- `
1097
- );
1098
- return;
1099
- }
1100
- if (totalGB <= 16 && freeGB < 2) {
1101
- process.stderr.write(
1102
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1103
1074
  `
1104
1075
  );
1105
1076
  return;
@@ -4317,6 +4288,13 @@ var init_platform_procedures = __esm({
4317
4288
  priority: "p0",
4318
4289
  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."
4319
4290
  },
4291
+ // --- Encryption key + cloud sync ---
4292
+ {
4293
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4294
+ domain: "security",
4295
+ priority: "p0",
4296
+ 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."
4297
+ },
4320
4298
  // --- MCP is the ONLY data interface ---
4321
4299
  {
4322
4300
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1164,40 +1164,11 @@ function findPackageRoot() {
1164
1164
  }
1165
1165
  return null;
1166
1166
  }
1167
- function getAvailableMemoryGB() {
1168
- if (process.platform === "darwin") {
1169
- try {
1170
- const { execSync: execSync8 } = __require("child_process");
1171
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1172
- const pageSize = 16384;
1173
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1174
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1175
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1176
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1177
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1178
- const freePages = free ? parseInt(free[1], 10) : 0;
1179
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1180
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1181
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1182
- } catch {
1183
- return os4.freemem() / (1024 * 1024 * 1024);
1184
- }
1185
- }
1186
- return os4.freemem() / (1024 * 1024 * 1024);
1187
- }
1188
1167
  function spawnDaemon() {
1189
- const freeGB = getAvailableMemoryGB();
1190
1168
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1191
1169
  if (totalGB <= 8) {
1192
1170
  process.stderr.write(
1193
1171
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1194
- `
1195
- );
1196
- return;
1197
- }
1198
- if (totalGB <= 16 && freeGB < 2) {
1199
- process.stderr.write(
1200
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1201
1172
  `
1202
1173
  );
1203
1174
  return;
@@ -4415,6 +4386,13 @@ var init_platform_procedures = __esm({
4415
4386
  priority: "p0",
4416
4387
  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."
4417
4388
  },
4389
+ // --- Encryption key + cloud sync ---
4390
+ {
4391
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4392
+ domain: "security",
4393
+ priority: "p0",
4394
+ 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."
4395
+ },
4418
4396
  // --- MCP is the ONLY data interface ---
4419
4397
  {
4420
4398
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -2808,6 +2808,13 @@ var PLATFORM_PROCEDURES = [
2808
2808
  priority: "p0",
2809
2809
  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."
2810
2810
  },
2811
+ // --- Encryption key + cloud sync ---
2812
+ {
2813
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
2814
+ domain: "security",
2815
+ priority: "p0",
2816
+ 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."
2817
+ },
2811
2818
  // --- MCP is the ONLY data interface ---
2812
2819
  {
2813
2820
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1076,40 +1076,11 @@ function findPackageRoot() {
1076
1076
  }
1077
1077
  return null;
1078
1078
  }
1079
- function getAvailableMemoryGB() {
1080
- if (process.platform === "darwin") {
1081
- try {
1082
- const { execSync: execSync6 } = __require("child_process");
1083
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1084
- const pageSize = 16384;
1085
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1086
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1087
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1088
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1089
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1090
- const freePages = free ? parseInt(free[1], 10) : 0;
1091
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1092
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1093
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1094
- } catch {
1095
- return os4.freemem() / (1024 * 1024 * 1024);
1096
- }
1097
- }
1098
- return os4.freemem() / (1024 * 1024 * 1024);
1099
- }
1100
1079
  function spawnDaemon() {
1101
- const freeGB = getAvailableMemoryGB();
1102
1080
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1103
1081
  if (totalGB <= 8) {
1104
1082
  process.stderr.write(
1105
1083
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1106
- `
1107
- );
1108
- return;
1109
- }
1110
- if (totalGB <= 16 && freeGB < 2) {
1111
- process.stderr.write(
1112
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1113
1084
  `
1114
1085
  );
1115
1086
  return;
@@ -4796,6 +4767,13 @@ var init_platform_procedures = __esm({
4796
4767
  priority: "p0",
4797
4768
  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."
4798
4769
  },
4770
+ // --- Encryption key + cloud sync ---
4771
+ {
4772
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4773
+ domain: "security",
4774
+ priority: "p0",
4775
+ 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."
4776
+ },
4799
4777
  // --- MCP is the ONLY data interface ---
4800
4778
  {
4801
4779
  title: "MCP disconnect \u2014 ask the user, never work around it",