@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
@@ -1101,7 +1101,7 @@ __export(exe_daemon_client_exports, {
1101
1101
  });
1102
1102
  import net from "net";
1103
1103
  import os4 from "os";
1104
- import { spawn } from "child_process";
1104
+ import { spawn, execSync as execSync2 } from "child_process";
1105
1105
  import { randomUUID } from "crypto";
1106
1106
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1107
1107
  import path5 from "path";
@@ -1131,6 +1131,14 @@ function handleData(chunk) {
1131
1131
  }
1132
1132
  }
1133
1133
  }
1134
+ function isZombie(pid) {
1135
+ try {
1136
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1137
+ return state.startsWith("Z");
1138
+ } catch {
1139
+ return false;
1140
+ }
1141
+ }
1134
1142
  function cleanupStaleFiles() {
1135
1143
  if (existsSync5(PID_PATH)) {
1136
1144
  try {
@@ -1138,7 +1146,11 @@ function cleanupStaleFiles() {
1138
1146
  if (pid > 0) {
1139
1147
  try {
1140
1148
  process.kill(pid, 0);
1141
- return;
1149
+ if (!isZombie(pid)) {
1150
+ return;
1151
+ }
1152
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1153
+ `);
1142
1154
  } catch {
1143
1155
  }
1144
1156
  }
@@ -1166,8 +1178,8 @@ function findPackageRoot() {
1166
1178
  function getAvailableMemoryGB() {
1167
1179
  if (process.platform === "darwin") {
1168
1180
  try {
1169
- const { execSync: execSync8 } = __require("child_process");
1170
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1181
+ const { execSync: execSync9 } = __require("child_process");
1182
+ const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1171
1183
  const pageSize = 16384;
1172
1184
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1173
1185
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3157,6 +3169,12 @@ async function disposeDatabase() {
3157
3169
  clearInterval(_walCheckpointTimer);
3158
3170
  _walCheckpointTimer = null;
3159
3171
  }
3172
+ if (_client) {
3173
+ try {
3174
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3175
+ } catch {
3176
+ }
3177
+ }
3160
3178
  if (_daemonClient) {
3161
3179
  _daemonClient.close();
3162
3180
  _daemonClient = null;
@@ -3193,7 +3211,7 @@ var init_database = __esm({
3193
3211
  // src/lib/keychain.ts
3194
3212
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3195
3213
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3196
- import { execSync as execSync2 } from "child_process";
3214
+ import { execSync as execSync3 } from "child_process";
3197
3215
  import path6 from "path";
3198
3216
  import os5 from "os";
3199
3217
  function getKeyDir() {
@@ -3210,13 +3228,13 @@ function linuxSecretAvailable() {
3210
3228
  if (process.platform !== "linux") return false;
3211
3229
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3212
3230
  try {
3213
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3231
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3214
3232
  } catch {
3215
3233
  linuxSecretAvailability = false;
3216
3234
  return false;
3217
3235
  }
3218
3236
  try {
3219
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3237
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3220
3238
  linuxSecretAvailability = true;
3221
3239
  } catch {
3222
3240
  linuxSecretAvailability = false;
@@ -3240,7 +3258,7 @@ function macKeychainGet(service = SERVICE) {
3240
3258
  if (!nativeKeychainAllowed()) return null;
3241
3259
  if (process.platform !== "darwin") return null;
3242
3260
  try {
3243
- return execSync2(
3261
+ return execSync3(
3244
3262
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3245
3263
  { encoding: "utf-8", timeout: 5e3 }
3246
3264
  ).trim();
@@ -3253,13 +3271,13 @@ function macKeychainSet(value, service = SERVICE) {
3253
3271
  if (process.platform !== "darwin") return false;
3254
3272
  try {
3255
3273
  try {
3256
- execSync2(
3274
+ execSync3(
3257
3275
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3258
3276
  { timeout: 5e3 }
3259
3277
  );
3260
3278
  } catch {
3261
3279
  }
3262
- execSync2(
3280
+ execSync3(
3263
3281
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3264
3282
  { timeout: 5e3 }
3265
3283
  );
@@ -3272,7 +3290,7 @@ function macKeychainDelete(service = SERVICE) {
3272
3290
  if (!nativeKeychainAllowed()) return false;
3273
3291
  if (process.platform !== "darwin") return false;
3274
3292
  try {
3275
- execSync2(
3293
+ execSync3(
3276
3294
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3277
3295
  { timeout: 5e3 }
3278
3296
  );
@@ -3284,7 +3302,7 @@ function macKeychainDelete(service = SERVICE) {
3284
3302
  function linuxSecretGet(service = SERVICE) {
3285
3303
  if (!linuxSecretAvailable()) return null;
3286
3304
  try {
3287
- return execSync2(
3305
+ return execSync3(
3288
3306
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3289
3307
  { encoding: "utf-8", timeout: 5e3 }
3290
3308
  ).trim();
@@ -3295,7 +3313,7 @@ function linuxSecretGet(service = SERVICE) {
3295
3313
  function linuxSecretSet(value, service = SERVICE) {
3296
3314
  if (!linuxSecretAvailable()) return false;
3297
3315
  try {
3298
- execSync2(
3316
+ execSync3(
3299
3317
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3300
3318
  { timeout: 5e3 }
3301
3319
  );
@@ -3308,7 +3326,7 @@ function linuxSecretDelete(service = SERVICE) {
3308
3326
  if (!nativeKeychainAllowed()) return false;
3309
3327
  if (process.platform !== "linux") return false;
3310
3328
  try {
3311
- execSync2(
3329
+ execSync3(
3312
3330
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3313
3331
  { timeout: 5e3 }
3314
3332
  );
@@ -4245,6 +4263,24 @@ var init_platform_procedures = __esm({
4245
4263
  priority: "p0",
4246
4264
  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."
4247
4265
  },
4266
+ {
4267
+ title: "Bug report status check \u2014 surface available fixes on boot",
4268
+ domain: "support",
4269
+ priority: "p1",
4270
+ 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."
4271
+ },
4272
+ {
4273
+ title: "Feature request triage \u2014 upstream feature vs local customization",
4274
+ domain: "support",
4275
+ priority: "p0",
4276
+ 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."
4277
+ },
4278
+ {
4279
+ title: "Feature request status check \u2014 surface shipped features on boot",
4280
+ domain: "support",
4281
+ priority: "p1",
4282
+ 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."
4283
+ },
4248
4284
  // --- Operations ---
4249
4285
  {
4250
4286
  title: "Managers must supervise deployed workers",
@@ -5591,7 +5627,7 @@ var init_session_registry = __esm({
5591
5627
  });
5592
5628
 
5593
5629
  // src/lib/session-key.ts
5594
- import { execSync as execSync3 } from "child_process";
5630
+ import { execSync as execSync4 } from "child_process";
5595
5631
  function normalizeCommand(command) {
5596
5632
  const trimmed = command.trim().toLowerCase();
5597
5633
  const parts = trimmed.split(/[\\/]/);
@@ -5610,7 +5646,7 @@ function resolveRuntimeProcess() {
5610
5646
  let pid = process.ppid;
5611
5647
  for (let i = 0; i < 10; i++) {
5612
5648
  try {
5613
- const info = execSync3(`ps -p ${pid} -o ppid=,comm=`, {
5649
+ const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
5614
5650
  encoding: "utf8",
5615
5651
  timeout: 2e3
5616
5652
  }).trim();
@@ -5788,14 +5824,14 @@ var init_transport = __esm({
5788
5824
  });
5789
5825
 
5790
5826
  // src/lib/cc-agent-support.ts
5791
- import { execSync as execSync4 } from "child_process";
5827
+ import { execSync as execSync5 } from "child_process";
5792
5828
  function _resetCcAgentSupportCache() {
5793
5829
  _cachedSupport = null;
5794
5830
  }
5795
5831
  function claudeSupportsAgentFlag() {
5796
5832
  if (_cachedSupport !== null) return _cachedSupport;
5797
5833
  try {
5798
- const helpOutput = execSync4("claude --help 2>&1", {
5834
+ const helpOutput = execSync5("claude --help 2>&1", {
5799
5835
  encoding: "utf-8",
5800
5836
  timeout: 5e3
5801
5837
  });
@@ -6256,7 +6292,7 @@ __export(project_name_exports, {
6256
6292
  _resetCache: () => _resetCache,
6257
6293
  getProjectName: () => getProjectName
6258
6294
  });
6259
- import { execSync as execSync5 } from "child_process";
6295
+ import { execSync as execSync6 } from "child_process";
6260
6296
  import path14 from "path";
6261
6297
  function getProjectName(cwd) {
6262
6298
  const dir = cwd ?? process.cwd();
@@ -6264,7 +6300,7 @@ function getProjectName(cwd) {
6264
6300
  try {
6265
6301
  let repoRoot;
6266
6302
  try {
6267
- const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
6303
+ const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
6268
6304
  cwd: dir,
6269
6305
  encoding: "utf8",
6270
6306
  timeout: 2e3,
@@ -6272,7 +6308,7 @@ function getProjectName(cwd) {
6272
6308
  }).trim();
6273
6309
  repoRoot = path14.dirname(gitCommonDir);
6274
6310
  } catch {
6275
- repoRoot = execSync5("git rev-parse --show-toplevel", {
6311
+ repoRoot = execSync6("git rev-parse --show-toplevel", {
6276
6312
  cwd: dir,
6277
6313
  encoding: "utf8",
6278
6314
  timeout: 2e3,
@@ -6367,7 +6403,7 @@ var init_session_scope = __esm({
6367
6403
  import crypto3 from "crypto";
6368
6404
  import path15 from "path";
6369
6405
  import os10 from "os";
6370
- import { execSync as execSync6 } from "child_process";
6406
+ import { execSync as execSync7 } from "child_process";
6371
6407
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
6372
6408
  import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
6373
6409
  async function writeCheckpoint(input) {
@@ -6712,14 +6748,14 @@ function isTmuxSessionAlive(identifier) {
6712
6748
  if (!identifier || identifier === "unknown") return true;
6713
6749
  try {
6714
6750
  if (identifier.startsWith("%")) {
6715
- const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
6751
+ const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
6716
6752
  timeout: 2e3,
6717
6753
  encoding: "utf8",
6718
6754
  stdio: ["pipe", "pipe", "pipe"]
6719
6755
  });
6720
6756
  return output.split("\n").some((l) => l.trim() === identifier);
6721
6757
  } else {
6722
- execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
6758
+ execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
6723
6759
  timeout: 2e3,
6724
6760
  stdio: ["pipe", "pipe", "pipe"]
6725
6761
  });
@@ -6728,7 +6764,7 @@ function isTmuxSessionAlive(identifier) {
6728
6764
  } catch {
6729
6765
  if (identifier.startsWith("%")) return true;
6730
6766
  try {
6731
- execSync6("tmux list-sessions", {
6767
+ execSync7("tmux list-sessions", {
6732
6768
  timeout: 2e3,
6733
6769
  stdio: ["pipe", "pipe", "pipe"]
6734
6770
  });
@@ -6743,12 +6779,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
6743
6779
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
6744
6780
  try {
6745
6781
  const since = new Date(taskCreatedAt).toISOString();
6746
- const branch = execSync6(
6782
+ const branch = execSync7(
6747
6783
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
6748
6784
  { encoding: "utf8", timeout: 3e3 }
6749
6785
  ).trim();
6750
6786
  const branchArg = branch && branch !== "HEAD" ? branch : "";
6751
- const commitCount = execSync6(
6787
+ const commitCount = execSync7(
6752
6788
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
6753
6789
  { encoding: "utf8", timeout: 5e3 }
6754
6790
  ).trim();
@@ -8039,7 +8075,7 @@ __export(tmux_routing_exports, {
8039
8075
  spawnEmployee: () => spawnEmployee,
8040
8076
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
8041
8077
  });
8042
- import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
8078
+ import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
8043
8079
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync2 } from "fs";
8044
8080
  import path18 from "path";
8045
8081
  import os11 from "os";
@@ -8760,7 +8796,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8760
8796
  let booted = false;
8761
8797
  for (let i = 0; i < 30; i++) {
8762
8798
  try {
8763
- execSync7("sleep 0.5");
8799
+ execSync8("sleep 0.5");
8764
8800
  } catch {
8765
8801
  }
8766
8802
  try {
@@ -9633,8 +9669,8 @@ async function main() {
9633
9669
  const agentId = folderArg.startsWith("exe/") ? folderArg.slice(4) : folderArg;
9634
9670
  let tmuxSession;
9635
9671
  try {
9636
- const { execSync: execSync8 } = await import("child_process");
9637
- const out = execSync8("tmux display-message -p '#{session_name}' 2>/dev/null", {
9672
+ const { execSync: execSync9 } = await import("child_process");
9673
+ const out = execSync9("tmux display-message -p '#{session_name}' 2>/dev/null", {
9638
9674
  encoding: "utf8",
9639
9675
  timeout: 1e3
9640
9676
  }).trim();
@@ -1593,7 +1593,7 @@ __export(exe_daemon_client_exports, {
1593
1593
  });
1594
1594
  import net from "net";
1595
1595
  import os6 from "os";
1596
- import { spawn } from "child_process";
1596
+ import { spawn, execSync as execSync4 } from "child_process";
1597
1597
  import { randomUUID } from "crypto";
1598
1598
  import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
1599
1599
  import path8 from "path";
@@ -1623,6 +1623,14 @@ function handleData(chunk) {
1623
1623
  }
1624
1624
  }
1625
1625
  }
1626
+ function isZombie(pid) {
1627
+ try {
1628
+ const state = execSync4(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1629
+ return state.startsWith("Z");
1630
+ } catch {
1631
+ return false;
1632
+ }
1633
+ }
1626
1634
  function cleanupStaleFiles() {
1627
1635
  if (existsSync8(PID_PATH)) {
1628
1636
  try {
@@ -1630,7 +1638,11 @@ function cleanupStaleFiles() {
1630
1638
  if (pid > 0) {
1631
1639
  try {
1632
1640
  process.kill(pid, 0);
1633
- return;
1641
+ if (!isZombie(pid)) {
1642
+ return;
1643
+ }
1644
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1645
+ `);
1634
1646
  } catch {
1635
1647
  }
1636
1648
  }
@@ -1658,8 +1670,8 @@ function findPackageRoot() {
1658
1670
  function getAvailableMemoryGB() {
1659
1671
  if (process.platform === "darwin") {
1660
1672
  try {
1661
- const { execSync: execSync8 } = __require("child_process");
1662
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1673
+ const { execSync: execSync9 } = __require("child_process");
1674
+ const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1663
1675
  const pageSize = 16384;
1664
1676
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1665
1677
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3649,6 +3661,12 @@ async function disposeDatabase() {
3649
3661
  clearInterval(_walCheckpointTimer);
3650
3662
  _walCheckpointTimer = null;
3651
3663
  }
3664
+ if (_client) {
3665
+ try {
3666
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3667
+ } catch {
3668
+ }
3669
+ }
3652
3670
  if (_daemonClient) {
3653
3671
  _daemonClient.close();
3654
3672
  _daemonClient = null;
@@ -4002,7 +4020,7 @@ __export(project_name_exports, {
4002
4020
  _resetCache: () => _resetCache,
4003
4021
  getProjectName: () => getProjectName
4004
4022
  });
4005
- import { execSync as execSync4 } from "child_process";
4023
+ import { execSync as execSync5 } from "child_process";
4006
4024
  import path13 from "path";
4007
4025
  function getProjectName(cwd) {
4008
4026
  const dir = cwd ?? process.cwd();
@@ -4010,7 +4028,7 @@ function getProjectName(cwd) {
4010
4028
  try {
4011
4029
  let repoRoot;
4012
4030
  try {
4013
- const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
4031
+ const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
4014
4032
  cwd: dir,
4015
4033
  encoding: "utf8",
4016
4034
  timeout: 2e3,
@@ -4018,7 +4036,7 @@ function getProjectName(cwd) {
4018
4036
  }).trim();
4019
4037
  repoRoot = path13.dirname(gitCommonDir);
4020
4038
  } catch {
4021
- repoRoot = execSync4("git rev-parse --show-toplevel", {
4039
+ repoRoot = execSync5("git rev-parse --show-toplevel", {
4022
4040
  cwd: dir,
4023
4041
  encoding: "utf8",
4024
4042
  timeout: 2e3,
@@ -4113,7 +4131,7 @@ var init_session_scope = __esm({
4113
4131
  import crypto4 from "crypto";
4114
4132
  import path14 from "path";
4115
4133
  import os10 from "os";
4116
- import { execSync as execSync5 } from "child_process";
4134
+ import { execSync as execSync6 } from "child_process";
4117
4135
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
4118
4136
  import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
4119
4137
  async function writeCheckpoint(input) {
@@ -4458,14 +4476,14 @@ function isTmuxSessionAlive(identifier) {
4458
4476
  if (!identifier || identifier === "unknown") return true;
4459
4477
  try {
4460
4478
  if (identifier.startsWith("%")) {
4461
- const output = execSync5("tmux list-panes -a -F '#{pane_id}'", {
4479
+ const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
4462
4480
  timeout: 2e3,
4463
4481
  encoding: "utf8",
4464
4482
  stdio: ["pipe", "pipe", "pipe"]
4465
4483
  });
4466
4484
  return output.split("\n").some((l) => l.trim() === identifier);
4467
4485
  } else {
4468
- execSync5(`tmux has-session -t ${JSON.stringify(identifier)}`, {
4486
+ execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
4469
4487
  timeout: 2e3,
4470
4488
  stdio: ["pipe", "pipe", "pipe"]
4471
4489
  });
@@ -4474,7 +4492,7 @@ function isTmuxSessionAlive(identifier) {
4474
4492
  } catch {
4475
4493
  if (identifier.startsWith("%")) return true;
4476
4494
  try {
4477
- execSync5("tmux list-sessions", {
4495
+ execSync6("tmux list-sessions", {
4478
4496
  timeout: 2e3,
4479
4497
  stdio: ["pipe", "pipe", "pipe"]
4480
4498
  });
@@ -4489,12 +4507,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
4489
4507
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
4490
4508
  try {
4491
4509
  const since = new Date(taskCreatedAt).toISOString();
4492
- const branch = execSync5(
4510
+ const branch = execSync6(
4493
4511
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
4494
4512
  { encoding: "utf8", timeout: 3e3 }
4495
4513
  ).trim();
4496
4514
  const branchArg = branch && branch !== "HEAD" ? branch : "";
4497
- const commitCount = execSync5(
4515
+ const commitCount = execSync6(
4498
4516
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
4499
4517
  { encoding: "utf8", timeout: 5e3 }
4500
4518
  ).trim();
@@ -6093,7 +6111,7 @@ __export(tmux_routing_exports, {
6093
6111
  spawnEmployee: () => spawnEmployee,
6094
6112
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
6095
6113
  });
6096
- import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
6114
+ import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
6097
6115
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync3 } from "fs";
6098
6116
  import path18 from "path";
6099
6117
  import os11 from "os";
@@ -6814,7 +6832,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6814
6832
  let booted = false;
6815
6833
  for (let i = 0; i < 30; i++) {
6816
6834
  try {
6817
- execSync6("sleep 0.5");
6835
+ execSync7("sleep 0.5");
6818
6836
  } catch {
6819
6837
  }
6820
6838
  try {
@@ -6926,7 +6944,7 @@ var init_task_scope = __esm({
6926
6944
  // src/lib/keychain.ts
6927
6945
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
6928
6946
  import { existsSync as existsSync16, statSync as statSync2 } from "fs";
6929
- import { execSync as execSync7 } from "child_process";
6947
+ import { execSync as execSync8 } from "child_process";
6930
6948
  import path19 from "path";
6931
6949
  import os12 from "os";
6932
6950
  function getKeyDir() {
@@ -6943,13 +6961,13 @@ function linuxSecretAvailable() {
6943
6961
  if (process.platform !== "linux") return false;
6944
6962
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
6945
6963
  try {
6946
- execSync7("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
6964
+ execSync8("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
6947
6965
  } catch {
6948
6966
  linuxSecretAvailability = false;
6949
6967
  return false;
6950
6968
  }
6951
6969
  try {
6952
- execSync7("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
6970
+ execSync8("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
6953
6971
  linuxSecretAvailability = true;
6954
6972
  } catch {
6955
6973
  linuxSecretAvailability = false;
@@ -6973,7 +6991,7 @@ function macKeychainGet(service = SERVICE) {
6973
6991
  if (!nativeKeychainAllowed()) return null;
6974
6992
  if (process.platform !== "darwin") return null;
6975
6993
  try {
6976
- return execSync7(
6994
+ return execSync8(
6977
6995
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
6978
6996
  { encoding: "utf-8", timeout: 5e3 }
6979
6997
  ).trim();
@@ -6986,13 +7004,13 @@ function macKeychainSet(value, service = SERVICE) {
6986
7004
  if (process.platform !== "darwin") return false;
6987
7005
  try {
6988
7006
  try {
6989
- execSync7(
7007
+ execSync8(
6990
7008
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
6991
7009
  { timeout: 5e3 }
6992
7010
  );
6993
7011
  } catch {
6994
7012
  }
6995
- execSync7(
7013
+ execSync8(
6996
7014
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
6997
7015
  { timeout: 5e3 }
6998
7016
  );
@@ -7005,7 +7023,7 @@ function macKeychainDelete(service = SERVICE) {
7005
7023
  if (!nativeKeychainAllowed()) return false;
7006
7024
  if (process.platform !== "darwin") return false;
7007
7025
  try {
7008
- execSync7(
7026
+ execSync8(
7009
7027
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
7010
7028
  { timeout: 5e3 }
7011
7029
  );
@@ -7017,7 +7035,7 @@ function macKeychainDelete(service = SERVICE) {
7017
7035
  function linuxSecretGet(service = SERVICE) {
7018
7036
  if (!linuxSecretAvailable()) return null;
7019
7037
  try {
7020
- return execSync7(
7038
+ return execSync8(
7021
7039
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
7022
7040
  { encoding: "utf-8", timeout: 5e3 }
7023
7041
  ).trim();
@@ -7028,7 +7046,7 @@ function linuxSecretGet(service = SERVICE) {
7028
7046
  function linuxSecretSet(value, service = SERVICE) {
7029
7047
  if (!linuxSecretAvailable()) return false;
7030
7048
  try {
7031
- execSync7(
7049
+ execSync8(
7032
7050
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
7033
7051
  { timeout: 5e3 }
7034
7052
  );
@@ -7041,7 +7059,7 @@ function linuxSecretDelete(service = SERVICE) {
7041
7059
  if (!nativeKeychainAllowed()) return false;
7042
7060
  if (process.platform !== "linux") return false;
7043
7061
  try {
7044
- execSync7(
7062
+ execSync8(
7045
7063
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
7046
7064
  { timeout: 5e3 }
7047
7065
  );
@@ -7923,6 +7941,24 @@ var init_platform_procedures = __esm({
7923
7941
  priority: "p0",
7924
7942
  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."
7925
7943
  },
7944
+ {
7945
+ title: "Bug report status check \u2014 surface available fixes on boot",
7946
+ domain: "support",
7947
+ priority: "p1",
7948
+ 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."
7949
+ },
7950
+ {
7951
+ title: "Feature request triage \u2014 upstream feature vs local customization",
7952
+ domain: "support",
7953
+ priority: "p0",
7954
+ 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."
7955
+ },
7956
+ {
7957
+ title: "Feature request status check \u2014 surface shipped features on boot",
7958
+ domain: "support",
7959
+ priority: "p1",
7960
+ 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."
7961
+ },
7926
7962
  // --- Operations ---
7927
7963
  {
7928
7964
  title: "Managers must supervise deployed workers",
@@ -9437,8 +9473,8 @@ async function main() {
9437
9473
  const agentId = folderArg.startsWith("exe/") ? folderArg.slice(4) : folderArg;
9438
9474
  let tmuxSession;
9439
9475
  try {
9440
- const { execSync: execSync8 } = await import("child_process");
9441
- const out = execSync8("tmux display-message -p '#{session_name}' 2>/dev/null", {
9476
+ const { execSync: execSync9 } = await import("child_process");
9477
+ const out = execSync9("tmux display-message -p '#{session_name}' 2>/dev/null", {
9442
9478
  encoding: "utf8",
9443
9479
  timeout: 1e3
9444
9480
  }).trim();