@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
@@ -1045,7 +1045,7 @@ var init_daemon_auth = __esm({
1045
1045
  // src/lib/exe-daemon-client.ts
1046
1046
  import net from "net";
1047
1047
  import os4 from "os";
1048
- import { spawn } from "child_process";
1048
+ import { spawn, execSync as execSync2 } from "child_process";
1049
1049
  import { randomUUID } from "crypto";
1050
1050
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1051
1051
  import path5 from "path";
@@ -1075,6 +1075,14 @@ function handleData(chunk) {
1075
1075
  }
1076
1076
  }
1077
1077
  }
1078
+ function isZombie(pid) {
1079
+ try {
1080
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1081
+ return state.startsWith("Z");
1082
+ } catch {
1083
+ return false;
1084
+ }
1085
+ }
1078
1086
  function cleanupStaleFiles() {
1079
1087
  if (existsSync5(PID_PATH)) {
1080
1088
  try {
@@ -1082,7 +1090,11 @@ function cleanupStaleFiles() {
1082
1090
  if (pid > 0) {
1083
1091
  try {
1084
1092
  process.kill(pid, 0);
1085
- return;
1093
+ if (!isZombie(pid)) {
1094
+ return;
1095
+ }
1096
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1097
+ `);
1086
1098
  } catch {
1087
1099
  }
1088
1100
  }
@@ -1110,8 +1122,8 @@ function findPackageRoot() {
1110
1122
  function getAvailableMemoryGB() {
1111
1123
  if (process.platform === "darwin") {
1112
1124
  try {
1113
- const { execSync: execSync5 } = __require("child_process");
1114
- const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1125
+ const { execSync: execSync6 } = __require("child_process");
1126
+ const vmstat = execSync6("vm_stat", { encoding: "utf8" });
1115
1127
  const pageSize = 16384;
1116
1128
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1117
1129
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3046,6 +3058,12 @@ async function disposeDatabase() {
3046
3058
  clearInterval(_walCheckpointTimer);
3047
3059
  _walCheckpointTimer = null;
3048
3060
  }
3061
+ if (_client) {
3062
+ try {
3063
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3064
+ } catch {
3065
+ }
3066
+ }
3049
3067
  if (_daemonClient) {
3050
3068
  _daemonClient.close();
3051
3069
  _daemonClient = null;
@@ -3082,7 +3100,7 @@ var init_database = __esm({
3082
3100
  // src/lib/keychain.ts
3083
3101
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3084
3102
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3085
- import { execSync as execSync2 } from "child_process";
3103
+ import { execSync as execSync3 } from "child_process";
3086
3104
  import path6 from "path";
3087
3105
  import os5 from "os";
3088
3106
  function getKeyDir() {
@@ -3099,13 +3117,13 @@ function linuxSecretAvailable() {
3099
3117
  if (process.platform !== "linux") return false;
3100
3118
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3101
3119
  try {
3102
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3120
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3103
3121
  } catch {
3104
3122
  linuxSecretAvailability = false;
3105
3123
  return false;
3106
3124
  }
3107
3125
  try {
3108
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3126
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3109
3127
  linuxSecretAvailability = true;
3110
3128
  } catch {
3111
3129
  linuxSecretAvailability = false;
@@ -3129,7 +3147,7 @@ function macKeychainGet(service = SERVICE) {
3129
3147
  if (!nativeKeychainAllowed()) return null;
3130
3148
  if (process.platform !== "darwin") return null;
3131
3149
  try {
3132
- return execSync2(
3150
+ return execSync3(
3133
3151
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3134
3152
  { encoding: "utf-8", timeout: 5e3 }
3135
3153
  ).trim();
@@ -3142,13 +3160,13 @@ function macKeychainSet(value, service = SERVICE) {
3142
3160
  if (process.platform !== "darwin") return false;
3143
3161
  try {
3144
3162
  try {
3145
- execSync2(
3163
+ execSync3(
3146
3164
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3147
3165
  { timeout: 5e3 }
3148
3166
  );
3149
3167
  } catch {
3150
3168
  }
3151
- execSync2(
3169
+ execSync3(
3152
3170
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3153
3171
  { timeout: 5e3 }
3154
3172
  );
@@ -3161,7 +3179,7 @@ function macKeychainDelete(service = SERVICE) {
3161
3179
  if (!nativeKeychainAllowed()) return false;
3162
3180
  if (process.platform !== "darwin") return false;
3163
3181
  try {
3164
- execSync2(
3182
+ execSync3(
3165
3183
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3166
3184
  { timeout: 5e3 }
3167
3185
  );
@@ -3173,7 +3191,7 @@ function macKeychainDelete(service = SERVICE) {
3173
3191
  function linuxSecretGet(service = SERVICE) {
3174
3192
  if (!linuxSecretAvailable()) return null;
3175
3193
  try {
3176
- return execSync2(
3194
+ return execSync3(
3177
3195
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3178
3196
  { encoding: "utf-8", timeout: 5e3 }
3179
3197
  ).trim();
@@ -3184,7 +3202,7 @@ function linuxSecretGet(service = SERVICE) {
3184
3202
  function linuxSecretSet(value, service = SERVICE) {
3185
3203
  if (!linuxSecretAvailable()) return false;
3186
3204
  try {
3187
- execSync2(
3205
+ execSync3(
3188
3206
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3189
3207
  { timeout: 5e3 }
3190
3208
  );
@@ -3197,7 +3215,7 @@ function linuxSecretDelete(service = SERVICE) {
3197
3215
  if (!nativeKeychainAllowed()) return false;
3198
3216
  if (process.platform !== "linux") return false;
3199
3217
  try {
3200
- execSync2(
3218
+ execSync3(
3201
3219
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3202
3220
  { timeout: 5e3 }
3203
3221
  );
@@ -4134,6 +4152,24 @@ var init_platform_procedures = __esm({
4134
4152
  priority: "p0",
4135
4153
  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."
4136
4154
  },
4155
+ {
4156
+ title: "Bug report status check \u2014 surface available fixes on boot",
4157
+ domain: "support",
4158
+ priority: "p1",
4159
+ 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."
4160
+ },
4161
+ {
4162
+ title: "Feature request triage \u2014 upstream feature vs local customization",
4163
+ domain: "support",
4164
+ priority: "p0",
4165
+ 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."
4166
+ },
4167
+ {
4168
+ title: "Feature request status check \u2014 surface shipped features on boot",
4169
+ domain: "support",
4170
+ priority: "p1",
4171
+ 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."
4172
+ },
4137
4173
  // --- Operations ---
4138
4174
  {
4139
4175
  title: "Managers must supervise deployed workers",
@@ -5665,7 +5701,7 @@ __export(project_name_exports, {
5665
5701
  _resetCache: () => _resetCache,
5666
5702
  getProjectName: () => getProjectName
5667
5703
  });
5668
- import { execSync as execSync3 } from "child_process";
5704
+ import { execSync as execSync4 } from "child_process";
5669
5705
  import path9 from "path";
5670
5706
  function getProjectName(cwd) {
5671
5707
  const dir = cwd ?? process.cwd();
@@ -5673,7 +5709,7 @@ function getProjectName(cwd) {
5673
5709
  try {
5674
5710
  let repoRoot;
5675
5711
  try {
5676
- const gitCommonDir = execSync3("git rev-parse --path-format=absolute --git-common-dir", {
5712
+ const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
5677
5713
  cwd: dir,
5678
5714
  encoding: "utf8",
5679
5715
  timeout: 2e3,
@@ -5681,7 +5717,7 @@ function getProjectName(cwd) {
5681
5717
  }).trim();
5682
5718
  repoRoot = path9.dirname(gitCommonDir);
5683
5719
  } catch {
5684
- repoRoot = execSync3("git rev-parse --show-toplevel", {
5720
+ repoRoot = execSync4("git rev-parse --show-toplevel", {
5685
5721
  cwd: dir,
5686
5722
  encoding: "utf8",
5687
5723
  timeout: 2e3,
@@ -5715,14 +5751,14 @@ var file_grep_exports = {};
5715
5751
  __export(file_grep_exports, {
5716
5752
  grepProjectFiles: () => grepProjectFiles
5717
5753
  });
5718
- import { execSync as execSync4 } from "child_process";
5754
+ import { execSync as execSync5 } from "child_process";
5719
5755
  import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync4, existsSync as existsSync9 } from "fs";
5720
5756
  import path10 from "path";
5721
5757
  import crypto2 from "crypto";
5722
5758
  function hasRipgrep() {
5723
5759
  if (_hasRg === null) {
5724
5760
  try {
5725
- execSync4("rg --version", { stdio: "ignore", timeout: 2e3 });
5761
+ execSync5("rg --version", { stdio: "ignore", timeout: 2e3 });
5726
5762
  _hasRg = true;
5727
5763
  } catch {
5728
5764
  _hasRg = false;
@@ -5788,7 +5824,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
5788
5824
  const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
5789
5825
  const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
5790
5826
  const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
5791
- const output = execSync4(cmd, {
5827
+ const output = execSync5(cmd, {
5792
5828
  cwd: projectRoot,
5793
5829
  encoding: "utf8",
5794
5830
  timeout: 3e3,
@@ -5803,12 +5839,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
5803
5839
  const matchCount = parseInt(line.slice(colonIdx + 1));
5804
5840
  if (isNaN(matchCount) || matchCount === 0) continue;
5805
5841
  try {
5806
- const firstMatch = execSync4(
5842
+ const firstMatch = execSync5(
5807
5843
  `rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
5808
5844
  { cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
5809
5845
  ).trim();
5810
5846
  const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
5811
- const totalLines = execSync4(`wc -l < '${filePath}'`, {
5847
+ const totalLines = execSync5(`wc -l < '${filePath}'`, {
5812
5848
  cwd: projectRoot,
5813
5849
  encoding: "utf8",
5814
5850
  timeout: 1e3
@@ -72,12 +72,14 @@ On EVERY new conversation, before doing anything else:
72
72
  1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
73
73
  2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
74
74
  3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
75
- 4. **Present the brief**: Give the founder a concise status report:
75
+ 4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
76
+ 5. **Present the brief**: Give the founder a concise status report:
76
77
  - What's active and progressing
77
78
  - What's blocked and needs attention
78
79
  - What decisions are pending
80
+ - Available bug fixes (from step 4, if any)
79
81
  - What you recommend doing next
80
- 5. Then ask: "What's the priority?"
82
+ 6. Then ask: "What's the priority?"
81
83
 
82
84
  If this is your FIRST ever conversation (few or no prior memories):
83
85
  - Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
@@ -97,6 +99,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
97
99
  - **get_identity** \u2014 read any agent's identity for coordination
98
100
  - **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
99
101
  - **send_message** \u2014 direct intercom to employees
102
+ - **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
103
+ - **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
100
104
  ${PLAN_MODE_COMPAT}
101
105
  ## Completion Workflow
102
106
 
@@ -980,7 +980,7 @@ var init_daemon_auth = __esm({
980
980
  // src/lib/exe-daemon-client.ts
981
981
  import net from "net";
982
982
  import os4 from "os";
983
- import { spawn } from "child_process";
983
+ import { spawn, execSync as execSync2 } from "child_process";
984
984
  import { randomUUID } from "crypto";
985
985
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
986
986
  import path5 from "path";
@@ -1010,6 +1010,14 @@ function handleData(chunk) {
1010
1010
  }
1011
1011
  }
1012
1012
  }
1013
+ function isZombie(pid) {
1014
+ try {
1015
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1016
+ return state.startsWith("Z");
1017
+ } catch {
1018
+ return false;
1019
+ }
1020
+ }
1013
1021
  function cleanupStaleFiles() {
1014
1022
  if (existsSync5(PID_PATH)) {
1015
1023
  try {
@@ -1017,7 +1025,11 @@ function cleanupStaleFiles() {
1017
1025
  if (pid > 0) {
1018
1026
  try {
1019
1027
  process.kill(pid, 0);
1020
- return;
1028
+ if (!isZombie(pid)) {
1029
+ return;
1030
+ }
1031
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1032
+ `);
1021
1033
  } catch {
1022
1034
  }
1023
1035
  }
@@ -1045,8 +1057,8 @@ function findPackageRoot() {
1045
1057
  function getAvailableMemoryGB() {
1046
1058
  if (process.platform === "darwin") {
1047
1059
  try {
1048
- const { execSync: execSync4 } = __require("child_process");
1049
- const vmstat = execSync4("vm_stat", { encoding: "utf8" });
1060
+ const { execSync: execSync5 } = __require("child_process");
1061
+ const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1050
1062
  const pageSize = 16384;
1051
1063
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1052
1064
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -2832,6 +2844,12 @@ async function disposeDatabase() {
2832
2844
  clearInterval(_walCheckpointTimer);
2833
2845
  _walCheckpointTimer = null;
2834
2846
  }
2847
+ if (_client) {
2848
+ try {
2849
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
2850
+ } catch {
2851
+ }
2852
+ }
2835
2853
  if (_daemonClient) {
2836
2854
  _daemonClient.close();
2837
2855
  _daemonClient = null;
@@ -3363,6 +3381,24 @@ var init_platform_procedures = __esm({
3363
3381
  priority: "p0",
3364
3382
  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."
3365
3383
  },
3384
+ {
3385
+ title: "Bug report status check \u2014 surface available fixes on boot",
3386
+ domain: "support",
3387
+ priority: "p1",
3388
+ 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."
3389
+ },
3390
+ {
3391
+ title: "Feature request triage \u2014 upstream feature vs local customization",
3392
+ domain: "support",
3393
+ priority: "p0",
3394
+ 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."
3395
+ },
3396
+ {
3397
+ title: "Feature request status check \u2014 surface shipped features on boot",
3398
+ domain: "support",
3399
+ priority: "p1",
3400
+ 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."
3401
+ },
3366
3402
  // --- Operations ---
3367
3403
  {
3368
3404
  title: "Managers must supervise deployed workers",
@@ -3556,7 +3592,7 @@ ${p.content}`).join("\n\n");
3556
3592
  // src/lib/schedules.ts
3557
3593
  init_database();
3558
3594
  import crypto2 from "crypto";
3559
- import { execSync as execSync3 } from "child_process";
3595
+ import { execSync as execSync4 } from "child_process";
3560
3596
 
3561
3597
  // src/lib/store.ts
3562
3598
  init_memory();
@@ -3565,7 +3601,7 @@ init_database();
3565
3601
  // src/lib/keychain.ts
3566
3602
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3567
3603
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3568
- import { execSync as execSync2 } from "child_process";
3604
+ import { execSync as execSync3 } from "child_process";
3569
3605
  import path6 from "path";
3570
3606
  import os5 from "os";
3571
3607
  var SERVICE = "exe-os";
@@ -3586,13 +3622,13 @@ function linuxSecretAvailable() {
3586
3622
  if (process.platform !== "linux") return false;
3587
3623
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3588
3624
  try {
3589
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3625
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3590
3626
  } catch {
3591
3627
  linuxSecretAvailability = false;
3592
3628
  return false;
3593
3629
  }
3594
3630
  try {
3595
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3631
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3596
3632
  linuxSecretAvailability = true;
3597
3633
  } catch {
3598
3634
  linuxSecretAvailability = false;
@@ -3616,7 +3652,7 @@ function macKeychainGet(service = SERVICE) {
3616
3652
  if (!nativeKeychainAllowed()) return null;
3617
3653
  if (process.platform !== "darwin") return null;
3618
3654
  try {
3619
- return execSync2(
3655
+ return execSync3(
3620
3656
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3621
3657
  { encoding: "utf-8", timeout: 5e3 }
3622
3658
  ).trim();
@@ -3629,13 +3665,13 @@ function macKeychainSet(value, service = SERVICE) {
3629
3665
  if (process.platform !== "darwin") return false;
3630
3666
  try {
3631
3667
  try {
3632
- execSync2(
3668
+ execSync3(
3633
3669
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3634
3670
  { timeout: 5e3 }
3635
3671
  );
3636
3672
  } catch {
3637
3673
  }
3638
- execSync2(
3674
+ execSync3(
3639
3675
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3640
3676
  { timeout: 5e3 }
3641
3677
  );
@@ -3648,7 +3684,7 @@ function macKeychainDelete(service = SERVICE) {
3648
3684
  if (!nativeKeychainAllowed()) return false;
3649
3685
  if (process.platform !== "darwin") return false;
3650
3686
  try {
3651
- execSync2(
3687
+ execSync3(
3652
3688
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3653
3689
  { timeout: 5e3 }
3654
3690
  );
@@ -3660,7 +3696,7 @@ function macKeychainDelete(service = SERVICE) {
3660
3696
  function linuxSecretGet(service = SERVICE) {
3661
3697
  if (!linuxSecretAvailable()) return null;
3662
3698
  try {
3663
- return execSync2(
3699
+ return execSync3(
3664
3700
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3665
3701
  { encoding: "utf-8", timeout: 5e3 }
3666
3702
  ).trim();
@@ -3671,7 +3707,7 @@ function linuxSecretGet(service = SERVICE) {
3671
3707
  function linuxSecretSet(value, service = SERVICE) {
3672
3708
  if (!linuxSecretAvailable()) return false;
3673
3709
  try {
3674
- execSync2(
3710
+ execSync3(
3675
3711
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3676
3712
  { timeout: 5e3 }
3677
3713
  );
@@ -3684,7 +3720,7 @@ function linuxSecretDelete(service = SERVICE) {
3684
3720
  if (!nativeKeychainAllowed()) return false;
3685
3721
  if (process.platform !== "linux") return false;
3686
3722
  try {
3687
- execSync2(
3723
+ execSync3(
3688
3724
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3689
3725
  { timeout: 5e3 }
3690
3726
  );
@@ -4166,7 +4202,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
4166
4202
  const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
4167
4203
  const escapedPrompt = prompt.replace(/"/g, '\\"');
4168
4204
  const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
4169
- execSync3(
4205
+ execSync4(
4170
4206
  `(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
4171
4207
  { timeout: 5e3, stdio: "ignore" }
4172
4208
  );
@@ -4177,7 +4213,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
4177
4213
  function removeFromCrontab(id) {
4178
4214
  if (!isValidScheduleId(id)) return;
4179
4215
  try {
4180
- execSync3(
4216
+ execSync4(
4181
4217
  `crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
4182
4218
  { timeout: 5e3, stdio: "ignore" }
4183
4219
  );
@@ -368,7 +368,7 @@ var init_daemon_auth = __esm({
368
368
  // src/lib/exe-daemon-client.ts
369
369
  import net from "net";
370
370
  import os4 from "os";
371
- import { spawn } from "child_process";
371
+ import { spawn, execSync as execSync2 } from "child_process";
372
372
  import { randomUUID } from "crypto";
373
373
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
374
374
  import path5 from "path";
@@ -398,6 +398,14 @@ function handleData(chunk) {
398
398
  }
399
399
  }
400
400
  }
401
+ function isZombie(pid) {
402
+ try {
403
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
404
+ return state.startsWith("Z");
405
+ } catch {
406
+ return false;
407
+ }
408
+ }
401
409
  function cleanupStaleFiles() {
402
410
  if (existsSync5(PID_PATH)) {
403
411
  try {
@@ -405,7 +413,11 @@ function cleanupStaleFiles() {
405
413
  if (pid > 0) {
406
414
  try {
407
415
  process.kill(pid, 0);
408
- return;
416
+ if (!isZombie(pid)) {
417
+ return;
418
+ }
419
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
420
+ `);
409
421
  } catch {
410
422
  }
411
423
  }
@@ -433,8 +445,8 @@ function findPackageRoot() {
433
445
  function getAvailableMemoryGB() {
434
446
  if (process.platform === "darwin") {
435
447
  try {
436
- const { execSync: execSync2 } = __require("child_process");
437
- const vmstat = execSync2("vm_stat", { encoding: "utf8" });
448
+ const { execSync: execSync3 } = __require("child_process");
449
+ const vmstat = execSync3("vm_stat", { encoding: "utf8" });
438
450
  const pageSize = 16384;
439
451
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
440
452
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;