@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
@@ -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;
@@ -4862,6 +4833,13 @@ var init_platform_procedures = __esm({
4862
4833
  priority: "p0",
4863
4834
  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."
4864
4835
  },
4836
+ // --- Encryption key + cloud sync ---
4837
+ {
4838
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4839
+ domain: "security",
4840
+ priority: "p0",
4841
+ 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."
4842
+ },
4865
4843
  // --- MCP is the ONLY data interface ---
4866
4844
  {
4867
4845
  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;
@@ -4901,6 +4872,13 @@ var init_platform_procedures = __esm({
4901
4872
  priority: "p0",
4902
4873
  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."
4903
4874
  },
4875
+ // --- Encryption key + cloud sync ---
4876
+ {
4877
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4878
+ domain: "security",
4879
+ priority: "p0",
4880
+ 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."
4881
+ },
4904
4882
  // --- MCP is the ONLY data interface ---
4905
4883
  {
4906
4884
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1125,40 +1125,11 @@ function findPackageRoot() {
1125
1125
  }
1126
1126
  return null;
1127
1127
  }
1128
- function getAvailableMemoryGB() {
1129
- if (process.platform === "darwin") {
1130
- try {
1131
- const { execSync: execSync5 } = __require("child_process");
1132
- const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1133
- const pageSize = 16384;
1134
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1135
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1136
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1137
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1138
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1139
- const freePages = free ? parseInt(free[1], 10) : 0;
1140
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1141
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1142
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1143
- } catch {
1144
- return os4.freemem() / (1024 * 1024 * 1024);
1145
- }
1146
- }
1147
- return os4.freemem() / (1024 * 1024 * 1024);
1148
- }
1149
1128
  function spawnDaemon() {
1150
- const freeGB = getAvailableMemoryGB();
1151
1129
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1152
1130
  if (totalGB <= 8) {
1153
1131
  process.stderr.write(
1154
1132
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1155
- `
1156
- );
1157
- return;
1158
- }
1159
- if (totalGB <= 16 && freeGB < 2) {
1160
- process.stderr.write(
1161
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1162
1133
  `
1163
1134
  );
1164
1135
  return;
@@ -3200,6 +3171,13 @@ var init_platform_procedures = __esm({
3200
3171
  priority: "p0",
3201
3172
  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."
3202
3173
  },
3174
+ // --- Encryption key + cloud sync ---
3175
+ {
3176
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3177
+ domain: "security",
3178
+ priority: "p0",
3179
+ 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."
3180
+ },
3203
3181
  // --- MCP is the ONLY data interface ---
3204
3182
  {
3205
3183
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1080,40 +1080,11 @@ function findPackageRoot() {
1080
1080
  }
1081
1081
  return null;
1082
1082
  }
1083
- function getAvailableMemoryGB() {
1084
- if (process.platform === "darwin") {
1085
- try {
1086
- const { execSync: execSync4 } = __require("child_process");
1087
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1088
- const pageSize = 16384;
1089
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1090
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1091
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1092
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1093
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1094
- const freePages = free ? parseInt(free[1], 10) : 0;
1095
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1096
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1097
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1098
- } catch {
1099
- return os4.freemem() / (1024 * 1024 * 1024);
1100
- }
1101
- }
1102
- return os4.freemem() / (1024 * 1024 * 1024);
1103
- }
1104
1083
  function spawnDaemon() {
1105
- const freeGB = getAvailableMemoryGB();
1106
1084
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1107
1085
  if (totalGB <= 8) {
1108
1086
  process.stderr.write(
1109
1087
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1110
- `
1111
- );
1112
- return;
1113
- }
1114
- if (totalGB <= 16 && freeGB < 2) {
1115
- process.stderr.write(
1116
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1117
1088
  `
1118
1089
  );
1119
1090
  return;
@@ -4331,6 +4302,13 @@ var init_platform_procedures = __esm({
4331
4302
  priority: "p0",
4332
4303
  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."
4333
4304
  },
4305
+ // --- Encryption key + cloud sync ---
4306
+ {
4307
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4308
+ domain: "security",
4309
+ priority: "p0",
4310
+ 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."
4311
+ },
4334
4312
  // --- MCP is the ONLY data interface ---
4335
4313
  {
4336
4314
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1120,40 +1120,11 @@ function findPackageRoot() {
1120
1120
  }
1121
1121
  return null;
1122
1122
  }
1123
- function getAvailableMemoryGB() {
1124
- if (process.platform === "darwin") {
1125
- try {
1126
- const { execSync: execSync6 } = __require("child_process");
1127
- const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1128
- const pageSize = 16384;
1129
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1130
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1131
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1132
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1133
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1134
- const freePages = free ? parseInt(free[1], 10) : 0;
1135
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1136
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1137
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1138
- } catch {
1139
- return os4.freemem() / (1024 * 1024 * 1024);
1140
- }
1141
- }
1142
- return os4.freemem() / (1024 * 1024 * 1024);
1143
- }
1144
1123
  function spawnDaemon() {
1145
- const freeGB = getAvailableMemoryGB();
1146
1124
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1147
1125
  if (totalGB <= 8) {
1148
1126
  process.stderr.write(
1149
1127
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1150
- `
1151
- );
1152
- return;
1153
- }
1154
- if (totalGB <= 16 && freeGB < 2) {
1155
- process.stderr.write(
1156
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1157
1128
  `
1158
1129
  );
1159
1130
  return;
@@ -4316,6 +4287,13 @@ var init_platform_procedures = __esm({
4316
4287
  priority: "p0",
4317
4288
  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."
4318
4289
  },
4290
+ // --- Encryption key + cloud sync ---
4291
+ {
4292
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4293
+ domain: "security",
4294
+ priority: "p0",
4295
+ 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."
4296
+ },
4319
4297
  // --- MCP is the ONLY data interface ---
4320
4298
  {
4321
4299
  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: execSync12 } = __require("child_process");
1171
- const vmstat = execSync12("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;
@@ -4360,6 +4331,13 @@ var init_platform_procedures = __esm({
4360
4331
  priority: "p0",
4361
4332
  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."
4362
4333
  },
4334
+ // --- Encryption key + cloud sync ---
4335
+ {
4336
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4337
+ domain: "security",
4338
+ priority: "p0",
4339
+ 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."
4340
+ },
4363
4341
  // --- MCP is the ONLY data interface ---
4364
4342
  {
4365
4343
  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: execSync7 } = __require("child_process");
1149
- const vmstat = execSync7("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;
@@ -3632,6 +3603,13 @@ var init_platform_procedures = __esm({
3632
3603
  priority: "p0",
3633
3604
  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."
3634
3605
  },
3606
+ // --- Encryption key + cloud sync ---
3607
+ {
3608
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3609
+ domain: "security",
3610
+ priority: "p0",
3611
+ 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."
3612
+ },
3635
3613
  // --- MCP is the ONLY data interface ---
3636
3614
  {
3637
3615
  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: execSync7 } = __require("child_process");
1149
- const vmstat = execSync7("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;
@@ -3632,6 +3603,13 @@ var init_platform_procedures = __esm({
3632
3603
  priority: "p0",
3633
3604
  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."
3634
3605
  },
3606
+ // --- Encryption key + cloud sync ---
3607
+ {
3608
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
3609
+ domain: "security",
3610
+ priority: "p0",
3611
+ 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."
3612
+ },
3635
3613
  // --- MCP is the ONLY data interface ---
3636
3614
  {
3637
3615
  title: "MCP disconnect \u2014 ask the user, never work around it",
@@ -1088,40 +1088,11 @@ function findPackageRoot() {
1088
1088
  }
1089
1089
  return null;
1090
1090
  }
1091
- function getAvailableMemoryGB() {
1092
- if (process.platform === "darwin") {
1093
- try {
1094
- const { execSync: execSync7 } = __require("child_process");
1095
- const vmstat = execSync7("vm_stat", { encoding: "utf8" });
1096
- const pageSize = 16384;
1097
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1098
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1099
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1100
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1101
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1102
- const freePages = free ? parseInt(free[1], 10) : 0;
1103
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1104
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1105
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1106
- } catch {
1107
- return os4.freemem() / (1024 * 1024 * 1024);
1108
- }
1109
- }
1110
- return os4.freemem() / (1024 * 1024 * 1024);
1111
- }
1112
1091
  function spawnDaemon() {
1113
- const freeGB = getAvailableMemoryGB();
1114
1092
  const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1115
1093
  if (totalGB <= 8) {
1116
1094
  process.stderr.write(
1117
1095
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1118
- `
1119
- );
1120
- return;
1121
- }
1122
- if (totalGB <= 16 && freeGB < 2) {
1123
- process.stderr.write(
1124
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1125
1096
  `
1126
1097
  );
1127
1098
  return;
@@ -4339,6 +4310,13 @@ var init_platform_procedures = __esm({
4339
4310
  priority: "p0",
4340
4311
  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."
4341
4312
  },
4313
+ // --- Encryption key + cloud sync ---
4314
+ {
4315
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
4316
+ domain: "security",
4317
+ priority: "p0",
4318
+ 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."
4319
+ },
4342
4320
  // --- MCP is the ONLY data interface ---
4343
4321
  {
4344
4322
  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: execSync4 } = __require("child_process");
1084
- const vmstat = execSync4("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",
@@ -1644,40 +1644,11 @@ function findPackageRoot() {
1644
1644
  }
1645
1645
  return null;
1646
1646
  }
1647
- function getAvailableMemoryGB() {
1648
- if (process.platform === "darwin") {
1649
- try {
1650
- const { execSync: execSync10 } = __require("child_process");
1651
- const vmstat = execSync10("vm_stat", { encoding: "utf8" });
1652
- const pageSize = 16384;
1653
- const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1654
- const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1655
- const free = vmstat.match(/Pages free:\s+(\d+)/);
1656
- const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1657
- const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1658
- const freePages = free ? parseInt(free[1], 10) : 0;
1659
- const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1660
- const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1661
- return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1662
- } catch {
1663
- return os6.freemem() / (1024 * 1024 * 1024);
1664
- }
1665
- }
1666
- return os6.freemem() / (1024 * 1024 * 1024);
1667
- }
1668
1647
  function spawnDaemon() {
1669
- const freeGB = getAvailableMemoryGB();
1670
1648
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1671
1649
  if (totalGB <= 8) {
1672
1650
  process.stderr.write(
1673
1651
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1674
- `
1675
- );
1676
- return;
1677
- }
1678
- if (totalGB <= 16 && freeGB < 2) {
1679
- process.stderr.write(
1680
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1681
1652
  `
1682
1653
  );
1683
1654
  return;
@@ -8085,6 +8056,13 @@ var init_platform_procedures = __esm({
8085
8056
  priority: "p0",
8086
8057
  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."
8087
8058
  },
8059
+ // --- Encryption key + cloud sync ---
8060
+ {
8061
+ title: "Encryption key lives in Keychain, not on disk \u2014 never expose the recovery phrase",
8062
+ domain: "security",
8063
+ priority: "p0",
8064
+ 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."
8065
+ },
8088
8066
  // --- MCP is the ONLY data interface ---
8089
8067
  {
8090
8068
  title: "MCP disconnect \u2014 ask the user, never work around it",