@askexenow/exe-os 0.9.85 → 0.9.87

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 (86) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +50 -14
  2. package/dist/bin/agentic-reflection-backfill.js +50 -14
  3. package/dist/bin/agentic-semantic-label.js +50 -14
  4. package/dist/bin/backfill-conversations.js +50 -14
  5. package/dist/bin/backfill-responses.js +50 -14
  6. package/dist/bin/backfill-vectors.js +50 -14
  7. package/dist/bin/bulk-sync-postgres.js +50 -14
  8. package/dist/bin/cleanup-stale-review-tasks.js +53 -17
  9. package/dist/bin/cli.js +339 -81
  10. package/dist/bin/exe-agent.js +18 -0
  11. package/dist/bin/exe-assign.js +50 -14
  12. package/dist/bin/exe-boot.js +75 -39
  13. package/dist/bin/exe-call.js +18 -0
  14. package/dist/bin/exe-cloud.js +40 -4
  15. package/dist/bin/exe-dispatch.js +61 -25
  16. package/dist/bin/exe-doctor.js +40 -4
  17. package/dist/bin/exe-export-behaviors.js +50 -14
  18. package/dist/bin/exe-forget.js +50 -14
  19. package/dist/bin/exe-gateway.js +65 -29
  20. package/dist/bin/exe-heartbeat.js +55 -19
  21. package/dist/bin/exe-kill.js +54 -18
  22. package/dist/bin/exe-launch-agent.js +58 -22
  23. package/dist/bin/exe-new-employee.js +33 -2
  24. package/dist/bin/exe-pending-messages.js +53 -17
  25. package/dist/bin/exe-pending-notifications.js +53 -17
  26. package/dist/bin/exe-pending-reviews.js +55 -19
  27. package/dist/bin/exe-rename.js +52 -16
  28. package/dist/bin/exe-review.js +50 -14
  29. package/dist/bin/exe-search.js +58 -22
  30. package/dist/bin/exe-session-cleanup.js +85 -44
  31. package/dist/bin/exe-start-codex.js +57 -21
  32. package/dist/bin/exe-start-opencode.js +55 -19
  33. package/dist/bin/exe-status.js +62 -26
  34. package/dist/bin/exe-team.js +50 -14
  35. package/dist/bin/git-sweep.js +63 -27
  36. package/dist/bin/graph-backfill.js +50 -14
  37. package/dist/bin/graph-export.js +50 -14
  38. package/dist/bin/install.js +9 -0
  39. package/dist/bin/intercom-check.js +67 -31
  40. package/dist/bin/scan-tasks.js +63 -27
  41. package/dist/bin/setup.js +53 -13
  42. package/dist/bin/shard-migrate.js +50 -14
  43. package/dist/bin/stack-update.js +59 -2
  44. package/dist/bin/update.js +1 -1
  45. package/dist/gateway/index.js +65 -29
  46. package/dist/hooks/bug-report-worker.js +65 -29
  47. package/dist/hooks/codex-stop-task-finalizer.js +59 -23
  48. package/dist/hooks/commit-complete.js +64 -28
  49. package/dist/hooks/error-recall.js +62 -26
  50. package/dist/hooks/ingest-worker.js +4 -4
  51. package/dist/hooks/ingest.js +56 -20
  52. package/dist/hooks/instructions-loaded.js +50 -14
  53. package/dist/hooks/notification.js +50 -14
  54. package/dist/hooks/post-compact.js +50 -14
  55. package/dist/hooks/post-tool-combined.js +63 -27
  56. package/dist/hooks/pre-compact.js +61 -25
  57. package/dist/hooks/pre-tool-use.js +58 -22
  58. package/dist/hooks/prompt-submit.js +78 -42
  59. package/dist/hooks/session-end.js +66 -30
  60. package/dist/hooks/session-start.js +68 -32
  61. package/dist/hooks/stop.js +53 -17
  62. package/dist/hooks/subagent-stop.js +50 -14
  63. package/dist/hooks/summary-worker.js +55 -19
  64. package/dist/index.js +61 -25
  65. package/dist/lib/cloud-sync.js +32 -14
  66. package/dist/lib/database.js +22 -4
  67. package/dist/lib/db-daemon-client.js +16 -4
  68. package/dist/lib/db.js +22 -4
  69. package/dist/lib/device-registry.js +22 -4
  70. package/dist/lib/embedder.js +16 -4
  71. package/dist/lib/employee-templates.js +18 -0
  72. package/dist/lib/exe-daemon-client.js +16 -4
  73. package/dist/lib/exe-daemon.js +874 -232
  74. package/dist/lib/hybrid-search.js +58 -22
  75. package/dist/lib/identity-templates.js +6 -2
  76. package/dist/lib/schedules.js +53 -17
  77. package/dist/lib/skill-learning.js +16 -4
  78. package/dist/lib/store.js +50 -14
  79. package/dist/lib/tasks.js +16 -4
  80. package/dist/lib/tmux-routing.js +18 -6
  81. package/dist/mcp/server.js +809 -200
  82. package/dist/mcp/tools/create-task.js +24 -8
  83. package/dist/mcp/tools/update-task.js +18 -6
  84. package/dist/runtime/index.js +61 -25
  85. package/dist/tui/App.js +91 -55
  86. package/package.json +1 -1
@@ -1003,7 +1003,7 @@ __export(exe_daemon_client_exports, {
1003
1003
  });
1004
1004
  import net from "net";
1005
1005
  import os4 from "os";
1006
- import { spawn } from "child_process";
1006
+ import { spawn, execSync as execSync2 } from "child_process";
1007
1007
  import { randomUUID } from "crypto";
1008
1008
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1009
1009
  import path5 from "path";
@@ -1033,6 +1033,14 @@ function handleData(chunk) {
1033
1033
  }
1034
1034
  }
1035
1035
  }
1036
+ function isZombie(pid) {
1037
+ try {
1038
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1039
+ return state.startsWith("Z");
1040
+ } catch {
1041
+ return false;
1042
+ }
1043
+ }
1036
1044
  function cleanupStaleFiles() {
1037
1045
  if (existsSync5(PID_PATH)) {
1038
1046
  try {
@@ -1040,7 +1048,11 @@ function cleanupStaleFiles() {
1040
1048
  if (pid > 0) {
1041
1049
  try {
1042
1050
  process.kill(pid, 0);
1043
- return;
1051
+ if (!isZombie(pid)) {
1052
+ return;
1053
+ }
1054
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1055
+ `);
1044
1056
  } catch {
1045
1057
  }
1046
1058
  }
@@ -1068,8 +1080,8 @@ function findPackageRoot() {
1068
1080
  function getAvailableMemoryGB() {
1069
1081
  if (process.platform === "darwin") {
1070
1082
  try {
1071
- const { execSync: execSync5 } = __require("child_process");
1072
- const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1083
+ const { execSync: execSync6 } = __require("child_process");
1084
+ const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1073
1085
  const pageSize = 16384;
1074
1086
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1075
1087
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3059,6 +3071,12 @@ async function disposeDatabase() {
3059
3071
  clearInterval(_walCheckpointTimer);
3060
3072
  _walCheckpointTimer = null;
3061
3073
  }
3074
+ if (_client) {
3075
+ try {
3076
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3077
+ } catch {
3078
+ }
3079
+ }
3062
3080
  if (_daemonClient) {
3063
3081
  _daemonClient.close();
3064
3082
  _daemonClient = null;
@@ -3104,7 +3122,7 @@ var init_session_registry = __esm({
3104
3122
  });
3105
3123
 
3106
3124
  // src/lib/session-key.ts
3107
- import { execSync as execSync2 } from "child_process";
3125
+ import { execSync as execSync3 } from "child_process";
3108
3126
  function normalizeCommand(command) {
3109
3127
  const trimmed = command.trim().toLowerCase();
3110
3128
  const parts = trimmed.split(/[\\/]/);
@@ -3123,7 +3141,7 @@ function resolveRuntimeProcess() {
3123
3141
  let pid = process.ppid;
3124
3142
  for (let i = 0; i < 10; i++) {
3125
3143
  try {
3126
- const info = execSync2(`ps -p ${pid} -o ppid=,comm=`, {
3144
+ const info = execSync3(`ps -p ${pid} -o ppid=,comm=`, {
3127
3145
  encoding: "utf8",
3128
3146
  timeout: 2e3
3129
3147
  }).trim();
@@ -3289,7 +3307,7 @@ var init_transport = __esm({
3289
3307
  });
3290
3308
 
3291
3309
  // src/lib/cc-agent-support.ts
3292
- import { execSync as execSync3 } from "child_process";
3310
+ import { execSync as execSync4 } from "child_process";
3293
3311
  var init_cc_agent_support = __esm({
3294
3312
  "src/lib/cc-agent-support.ts"() {
3295
3313
  "use strict";
@@ -3681,7 +3699,7 @@ var init_tasks_review = __esm({
3681
3699
  // src/lib/keychain.ts
3682
3700
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3683
3701
  import { existsSync as existsSync14, statSync as statSync2 } from "fs";
3684
- import { execSync as execSync4 } from "child_process";
3702
+ import { execSync as execSync5 } from "child_process";
3685
3703
  import path15 from "path";
3686
3704
  import os11 from "os";
3687
3705
  function getKeyDir() {
@@ -3698,13 +3716,13 @@ function linuxSecretAvailable() {
3698
3716
  if (process.platform !== "linux") return false;
3699
3717
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3700
3718
  try {
3701
- execSync4("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3719
+ execSync5("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3702
3720
  } catch {
3703
3721
  linuxSecretAvailability = false;
3704
3722
  return false;
3705
3723
  }
3706
3724
  try {
3707
- execSync4("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3725
+ execSync5("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3708
3726
  linuxSecretAvailability = true;
3709
3727
  } catch {
3710
3728
  linuxSecretAvailability = false;
@@ -3728,7 +3746,7 @@ function macKeychainGet(service = SERVICE) {
3728
3746
  if (!nativeKeychainAllowed()) return null;
3729
3747
  if (process.platform !== "darwin") return null;
3730
3748
  try {
3731
- return execSync4(
3749
+ return execSync5(
3732
3750
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3733
3751
  { encoding: "utf-8", timeout: 5e3 }
3734
3752
  ).trim();
@@ -3741,13 +3759,13 @@ function macKeychainSet(value, service = SERVICE) {
3741
3759
  if (process.platform !== "darwin") return false;
3742
3760
  try {
3743
3761
  try {
3744
- execSync4(
3762
+ execSync5(
3745
3763
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3746
3764
  { timeout: 5e3 }
3747
3765
  );
3748
3766
  } catch {
3749
3767
  }
3750
- execSync4(
3768
+ execSync5(
3751
3769
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3752
3770
  { timeout: 5e3 }
3753
3771
  );
@@ -3760,7 +3778,7 @@ function macKeychainDelete(service = SERVICE) {
3760
3778
  if (!nativeKeychainAllowed()) return false;
3761
3779
  if (process.platform !== "darwin") return false;
3762
3780
  try {
3763
- execSync4(
3781
+ execSync5(
3764
3782
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3765
3783
  { timeout: 5e3 }
3766
3784
  );
@@ -3772,7 +3790,7 @@ function macKeychainDelete(service = SERVICE) {
3772
3790
  function linuxSecretGet(service = SERVICE) {
3773
3791
  if (!linuxSecretAvailable()) return null;
3774
3792
  try {
3775
- return execSync4(
3793
+ return execSync5(
3776
3794
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3777
3795
  { encoding: "utf-8", timeout: 5e3 }
3778
3796
  ).trim();
@@ -3783,7 +3801,7 @@ function linuxSecretGet(service = SERVICE) {
3783
3801
  function linuxSecretSet(value, service = SERVICE) {
3784
3802
  if (!linuxSecretAvailable()) return false;
3785
3803
  try {
3786
- execSync4(
3804
+ execSync5(
3787
3805
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3788
3806
  { timeout: 5e3 }
3789
3807
  );
@@ -3796,7 +3814,7 @@ function linuxSecretDelete(service = SERVICE) {
3796
3814
  if (!nativeKeychainAllowed()) return false;
3797
3815
  if (process.platform !== "linux") return false;
3798
3816
  try {
3799
- execSync4(
3817
+ execSync5(
3800
3818
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3801
3819
  { timeout: 5e3 }
3802
3820
  );
@@ -4678,6 +4696,24 @@ var init_platform_procedures = __esm({
4678
4696
  priority: "p0",
4679
4697
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
4680
4698
  },
4699
+ {
4700
+ title: "Bug report status check \u2014 surface available fixes on boot",
4701
+ domain: "support",
4702
+ priority: "p1",
4703
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4704
+ },
4705
+ {
4706
+ title: "Feature request triage \u2014 upstream feature vs local customization",
4707
+ domain: "support",
4708
+ priority: "p0",
4709
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
4710
+ },
4711
+ {
4712
+ title: "Feature request status check \u2014 surface shipped features on boot",
4713
+ domain: "support",
4714
+ priority: "p1",
4715
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4716
+ },
4681
4717
  // --- Operations ---
4682
4718
  {
4683
4719
  title: "Managers must supervise deployed workers",
@@ -6007,8 +6043,8 @@ async function main() {
6007
6043
  let sessionScope = process.env.EXE_SESSION ? extractRootExe(process.env.EXE_SESSION) ?? void 0 : void 0;
6008
6044
  if (!sessionScope) {
6009
6045
  try {
6010
- const { execSync: execSync5 } = await import("child_process");
6011
- const tmuxSession = execSync5("tmux display-message -p '#{session_name}'", { encoding: "utf8", timeout: 2e3 }).trim();
6046
+ const { execSync: execSync6 } = await import("child_process");
6047
+ const tmuxSession = execSync6("tmux display-message -p '#{session_name}'", { encoding: "utf8", timeout: 2e3 }).trim();
6012
6048
  if (isExeSession(tmuxSession)) sessionScope = tmuxSession;
6013
6049
  } catch {
6014
6050
  }
@@ -1051,7 +1051,7 @@ var init_daemon_auth = __esm({
1051
1051
  // src/lib/exe-daemon-client.ts
1052
1052
  import net from "net";
1053
1053
  import os4 from "os";
1054
- import { spawn } from "child_process";
1054
+ import { spawn, execSync as execSync2 } from "child_process";
1055
1055
  import { randomUUID } from "crypto";
1056
1056
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1057
1057
  import path5 from "path";
@@ -1081,6 +1081,14 @@ function handleData(chunk) {
1081
1081
  }
1082
1082
  }
1083
1083
  }
1084
+ function isZombie(pid) {
1085
+ try {
1086
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1087
+ return state.startsWith("Z");
1088
+ } catch {
1089
+ return false;
1090
+ }
1091
+ }
1084
1092
  function cleanupStaleFiles() {
1085
1093
  if (existsSync5(PID_PATH)) {
1086
1094
  try {
@@ -1088,7 +1096,11 @@ function cleanupStaleFiles() {
1088
1096
  if (pid > 0) {
1089
1097
  try {
1090
1098
  process.kill(pid, 0);
1091
- return;
1099
+ if (!isZombie(pid)) {
1100
+ return;
1101
+ }
1102
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1103
+ `);
1092
1104
  } catch {
1093
1105
  }
1094
1106
  }
@@ -1116,8 +1128,8 @@ function findPackageRoot() {
1116
1128
  function getAvailableMemoryGB() {
1117
1129
  if (process.platform === "darwin") {
1118
1130
  try {
1119
- const { execSync: execSync4 } = __require("child_process");
1120
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1131
+ const { execSync: execSync5 } = __require("child_process");
1132
+ const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1121
1133
  const pageSize = 16384;
1122
1134
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1123
1135
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -2903,6 +2915,12 @@ async function disposeDatabase() {
2903
2915
  clearInterval(_walCheckpointTimer);
2904
2916
  _walCheckpointTimer = null;
2905
2917
  }
2918
+ if (_client) {
2919
+ try {
2920
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
2921
+ } catch {
2922
+ }
2923
+ }
2906
2924
  if (_daemonClient) {
2907
2925
  _daemonClient.close();
2908
2926
  _daemonClient = null;
@@ -3019,6 +3037,24 @@ var init_platform_procedures = __esm({
3019
3037
  priority: "p0",
3020
3038
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
3021
3039
  },
3040
+ {
3041
+ title: "Bug report status check \u2014 surface available fixes on boot",
3042
+ domain: "support",
3043
+ priority: "p1",
3044
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
3045
+ },
3046
+ {
3047
+ title: "Feature request triage \u2014 upstream feature vs local customization",
3048
+ domain: "support",
3049
+ priority: "p0",
3050
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
3051
+ },
3052
+ {
3053
+ title: "Feature request status check \u2014 surface shipped features on boot",
3054
+ domain: "support",
3055
+ priority: "p1",
3056
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
3057
+ },
3022
3058
  // --- Operations ---
3023
3059
  {
3024
3060
  title: "Managers must supervise deployed workers",
@@ -3212,7 +3248,7 @@ ${p.content}`).join("\n\n");
3212
3248
  // src/lib/keychain.ts
3213
3249
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3214
3250
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3215
- import { execSync as execSync2 } from "child_process";
3251
+ import { execSync as execSync3 } from "child_process";
3216
3252
  import path6 from "path";
3217
3253
  import os5 from "os";
3218
3254
  function getKeyDir() {
@@ -3229,13 +3265,13 @@ function linuxSecretAvailable() {
3229
3265
  if (process.platform !== "linux") return false;
3230
3266
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3231
3267
  try {
3232
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3268
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3233
3269
  } catch {
3234
3270
  linuxSecretAvailability = false;
3235
3271
  return false;
3236
3272
  }
3237
3273
  try {
3238
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3274
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3239
3275
  linuxSecretAvailability = true;
3240
3276
  } catch {
3241
3277
  linuxSecretAvailability = false;
@@ -3259,7 +3295,7 @@ function macKeychainGet(service = SERVICE) {
3259
3295
  if (!nativeKeychainAllowed()) return null;
3260
3296
  if (process.platform !== "darwin") return null;
3261
3297
  try {
3262
- return execSync2(
3298
+ return execSync3(
3263
3299
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3264
3300
  { encoding: "utf-8", timeout: 5e3 }
3265
3301
  ).trim();
@@ -3272,13 +3308,13 @@ function macKeychainSet(value, service = SERVICE) {
3272
3308
  if (process.platform !== "darwin") return false;
3273
3309
  try {
3274
3310
  try {
3275
- execSync2(
3311
+ execSync3(
3276
3312
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3277
3313
  { timeout: 5e3 }
3278
3314
  );
3279
3315
  } catch {
3280
3316
  }
3281
- execSync2(
3317
+ execSync3(
3282
3318
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3283
3319
  { timeout: 5e3 }
3284
3320
  );
@@ -3291,7 +3327,7 @@ function macKeychainDelete(service = SERVICE) {
3291
3327
  if (!nativeKeychainAllowed()) return false;
3292
3328
  if (process.platform !== "darwin") return false;
3293
3329
  try {
3294
- execSync2(
3330
+ execSync3(
3295
3331
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3296
3332
  { timeout: 5e3 }
3297
3333
  );
@@ -3303,7 +3339,7 @@ function macKeychainDelete(service = SERVICE) {
3303
3339
  function linuxSecretGet(service = SERVICE) {
3304
3340
  if (!linuxSecretAvailable()) return null;
3305
3341
  try {
3306
- return execSync2(
3342
+ return execSync3(
3307
3343
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3308
3344
  { encoding: "utf-8", timeout: 5e3 }
3309
3345
  ).trim();
@@ -3314,7 +3350,7 @@ function linuxSecretGet(service = SERVICE) {
3314
3350
  function linuxSecretSet(value, service = SERVICE) {
3315
3351
  if (!linuxSecretAvailable()) return false;
3316
3352
  try {
3317
- execSync2(
3353
+ execSync3(
3318
3354
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3319
3355
  { timeout: 5e3 }
3320
3356
  );
@@ -3327,7 +3363,7 @@ function linuxSecretDelete(service = SERVICE) {
3327
3363
  if (!nativeKeychainAllowed()) return false;
3328
3364
  if (process.platform !== "linux") return false;
3329
3365
  try {
3330
- execSync2(
3366
+ execSync3(
3331
3367
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3332
3368
  { timeout: 5e3 }
3333
3369
  );
@@ -5233,7 +5269,7 @@ var init_store = __esm({
5233
5269
  // src/bin/exe-rename.ts
5234
5270
  init_employees();
5235
5271
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync4, unlinkSync as unlinkSync3, existsSync as existsSync8 } from "fs";
5236
- import { execSync as execSync3 } from "child_process";
5272
+ import { execSync as execSync4 } from "child_process";
5237
5273
  import path8 from "path";
5238
5274
  import { homedir } from "os";
5239
5275
 
@@ -5399,7 +5435,7 @@ function rewriteRenamedEmployeeContent(content, oldName, newName) {
5399
5435
  }
5400
5436
  function findExeBin2() {
5401
5437
  try {
5402
- return execSync3(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
5438
+ return execSync4(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
5403
5439
  } catch {
5404
5440
  return null;
5405
5441
  }
@@ -1006,7 +1006,7 @@ __export(exe_daemon_client_exports, {
1006
1006
  });
1007
1007
  import net from "net";
1008
1008
  import os4 from "os";
1009
- import { spawn } from "child_process";
1009
+ import { spawn, execSync as execSync2 } from "child_process";
1010
1010
  import { randomUUID } from "crypto";
1011
1011
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1012
1012
  import path5 from "path";
@@ -1036,6 +1036,14 @@ function handleData(chunk) {
1036
1036
  }
1037
1037
  }
1038
1038
  }
1039
+ function isZombie(pid) {
1040
+ try {
1041
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1042
+ return state.startsWith("Z");
1043
+ } catch {
1044
+ return false;
1045
+ }
1046
+ }
1039
1047
  function cleanupStaleFiles() {
1040
1048
  if (existsSync5(PID_PATH)) {
1041
1049
  try {
@@ -1043,7 +1051,11 @@ function cleanupStaleFiles() {
1043
1051
  if (pid > 0) {
1044
1052
  try {
1045
1053
  process.kill(pid, 0);
1046
- return;
1054
+ if (!isZombie(pid)) {
1055
+ return;
1056
+ }
1057
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1058
+ `);
1047
1059
  } catch {
1048
1060
  }
1049
1061
  }
@@ -1071,8 +1083,8 @@ function findPackageRoot() {
1071
1083
  function getAvailableMemoryGB() {
1072
1084
  if (process.platform === "darwin") {
1073
1085
  try {
1074
- const { execSync: execSync3 } = __require("child_process");
1075
- const vmstat = execSync3("vm_stat", { encoding: "utf8" });
1086
+ const { execSync: execSync4 } = __require("child_process");
1087
+ const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1076
1088
  const pageSize = 16384;
1077
1089
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1078
1090
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3062,6 +3074,12 @@ async function disposeDatabase() {
3062
3074
  clearInterval(_walCheckpointTimer);
3063
3075
  _walCheckpointTimer = null;
3064
3076
  }
3077
+ if (_client) {
3078
+ try {
3079
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3080
+ } catch {
3081
+ }
3082
+ }
3065
3083
  if (_daemonClient) {
3066
3084
  _daemonClient.close();
3067
3085
  _daemonClient = null;
@@ -3098,7 +3116,7 @@ var init_database = __esm({
3098
3116
  // src/lib/keychain.ts
3099
3117
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3100
3118
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3101
- import { execSync as execSync2 } from "child_process";
3119
+ import { execSync as execSync3 } from "child_process";
3102
3120
  import path6 from "path";
3103
3121
  import os5 from "os";
3104
3122
  function getKeyDir() {
@@ -3115,13 +3133,13 @@ function linuxSecretAvailable() {
3115
3133
  if (process.platform !== "linux") return false;
3116
3134
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3117
3135
  try {
3118
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3136
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3119
3137
  } catch {
3120
3138
  linuxSecretAvailability = false;
3121
3139
  return false;
3122
3140
  }
3123
3141
  try {
3124
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3142
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3125
3143
  linuxSecretAvailability = true;
3126
3144
  } catch {
3127
3145
  linuxSecretAvailability = false;
@@ -3145,7 +3163,7 @@ function macKeychainGet(service = SERVICE) {
3145
3163
  if (!nativeKeychainAllowed()) return null;
3146
3164
  if (process.platform !== "darwin") return null;
3147
3165
  try {
3148
- return execSync2(
3166
+ return execSync3(
3149
3167
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3150
3168
  { encoding: "utf-8", timeout: 5e3 }
3151
3169
  ).trim();
@@ -3158,13 +3176,13 @@ function macKeychainSet(value, service = SERVICE) {
3158
3176
  if (process.platform !== "darwin") return false;
3159
3177
  try {
3160
3178
  try {
3161
- execSync2(
3179
+ execSync3(
3162
3180
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3163
3181
  { timeout: 5e3 }
3164
3182
  );
3165
3183
  } catch {
3166
3184
  }
3167
- execSync2(
3185
+ execSync3(
3168
3186
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3169
3187
  { timeout: 5e3 }
3170
3188
  );
@@ -3177,7 +3195,7 @@ function macKeychainDelete(service = SERVICE) {
3177
3195
  if (!nativeKeychainAllowed()) return false;
3178
3196
  if (process.platform !== "darwin") return false;
3179
3197
  try {
3180
- execSync2(
3198
+ execSync3(
3181
3199
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3182
3200
  { timeout: 5e3 }
3183
3201
  );
@@ -3189,7 +3207,7 @@ function macKeychainDelete(service = SERVICE) {
3189
3207
  function linuxSecretGet(service = SERVICE) {
3190
3208
  if (!linuxSecretAvailable()) return null;
3191
3209
  try {
3192
- return execSync2(
3210
+ return execSync3(
3193
3211
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3194
3212
  { encoding: "utf-8", timeout: 5e3 }
3195
3213
  ).trim();
@@ -3200,7 +3218,7 @@ function linuxSecretGet(service = SERVICE) {
3200
3218
  function linuxSecretSet(value, service = SERVICE) {
3201
3219
  if (!linuxSecretAvailable()) return false;
3202
3220
  try {
3203
- execSync2(
3221
+ execSync3(
3204
3222
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3205
3223
  { timeout: 5e3 }
3206
3224
  );
@@ -3213,7 +3231,7 @@ function linuxSecretDelete(service = SERVICE) {
3213
3231
  if (!nativeKeychainAllowed()) return false;
3214
3232
  if (process.platform !== "linux") return false;
3215
3233
  try {
3216
- execSync2(
3234
+ execSync3(
3217
3235
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3218
3236
  { timeout: 5e3 }
3219
3237
  );
@@ -4150,6 +4168,24 @@ var init_platform_procedures = __esm({
4150
4168
  priority: "p0",
4151
4169
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
4152
4170
  },
4171
+ {
4172
+ title: "Bug report status check \u2014 surface available fixes on boot",
4173
+ domain: "support",
4174
+ priority: "p1",
4175
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4176
+ },
4177
+ {
4178
+ title: "Feature request triage \u2014 upstream feature vs local customization",
4179
+ domain: "support",
4180
+ priority: "p0",
4181
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
4182
+ },
4183
+ {
4184
+ title: "Feature request status check \u2014 surface shipped features on boot",
4185
+ domain: "support",
4186
+ priority: "p1",
4187
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4188
+ },
4153
4189
  // --- Operations ---
4154
4190
  {
4155
4191
  title: "Managers must supervise deployed workers",