@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
@@ -1576,7 +1576,7 @@ __export(exe_daemon_client_exports, {
1576
1576
  });
1577
1577
  import net from "net";
1578
1578
  import os6 from "os";
1579
- import { spawn } from "child_process";
1579
+ import { spawn, execSync as execSync5 } from "child_process";
1580
1580
  import { randomUUID } from "crypto";
1581
1581
  import { existsSync as existsSync7, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
1582
1582
  import path9 from "path";
@@ -1606,6 +1606,14 @@ function handleData(chunk) {
1606
1606
  }
1607
1607
  }
1608
1608
  }
1609
+ function isZombie(pid) {
1610
+ try {
1611
+ const state = execSync5(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1612
+ return state.startsWith("Z");
1613
+ } catch {
1614
+ return false;
1615
+ }
1616
+ }
1609
1617
  function cleanupStaleFiles() {
1610
1618
  if (existsSync7(PID_PATH)) {
1611
1619
  try {
@@ -1613,7 +1621,11 @@ function cleanupStaleFiles() {
1613
1621
  if (pid > 0) {
1614
1622
  try {
1615
1623
  process.kill(pid, 0);
1616
- return;
1624
+ if (!isZombie(pid)) {
1625
+ return;
1626
+ }
1627
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1628
+ `);
1617
1629
  } catch {
1618
1630
  }
1619
1631
  }
@@ -1641,8 +1653,8 @@ function findPackageRoot() {
1641
1653
  function getAvailableMemoryGB() {
1642
1654
  if (process.platform === "darwin") {
1643
1655
  try {
1644
- const { execSync: execSync8 } = __require("child_process");
1645
- const vmstat = execSync8("vm_stat", { encoding: "utf8" });
1656
+ const { execSync: execSync9 } = __require("child_process");
1657
+ const vmstat = execSync9("vm_stat", { encoding: "utf8" });
1646
1658
  const pageSize = 16384;
1647
1659
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1648
1660
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3632,6 +3644,12 @@ async function disposeDatabase() {
3632
3644
  clearInterval(_walCheckpointTimer);
3633
3645
  _walCheckpointTimer = null;
3634
3646
  }
3647
+ if (_client) {
3648
+ try {
3649
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3650
+ } catch {
3651
+ }
3652
+ }
3635
3653
  if (_daemonClient) {
3636
3654
  _daemonClient.close();
3637
3655
  _daemonClient = null;
@@ -3995,7 +4013,7 @@ var init_cto_delegation_gate = __esm({
3995
4013
  // src/lib/keychain.ts
3996
4014
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3997
4015
  import { existsSync as existsSync13, statSync as statSync3 } from "fs";
3998
- import { execSync as execSync5 } from "child_process";
4016
+ import { execSync as execSync6 } from "child_process";
3999
4017
  import path15 from "path";
4000
4018
  import os11 from "os";
4001
4019
  function getKeyDir() {
@@ -4012,13 +4030,13 @@ function linuxSecretAvailable() {
4012
4030
  if (process.platform !== "linux") return false;
4013
4031
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
4014
4032
  try {
4015
- execSync5("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
4033
+ execSync6("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
4016
4034
  } catch {
4017
4035
  linuxSecretAvailability = false;
4018
4036
  return false;
4019
4037
  }
4020
4038
  try {
4021
- execSync5("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
4039
+ execSync6("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
4022
4040
  linuxSecretAvailability = true;
4023
4041
  } catch {
4024
4042
  linuxSecretAvailability = false;
@@ -4042,7 +4060,7 @@ function macKeychainGet(service = SERVICE) {
4042
4060
  if (!nativeKeychainAllowed()) return null;
4043
4061
  if (process.platform !== "darwin") return null;
4044
4062
  try {
4045
- return execSync5(
4063
+ return execSync6(
4046
4064
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
4047
4065
  { encoding: "utf-8", timeout: 5e3 }
4048
4066
  ).trim();
@@ -4055,13 +4073,13 @@ function macKeychainSet(value, service = SERVICE) {
4055
4073
  if (process.platform !== "darwin") return false;
4056
4074
  try {
4057
4075
  try {
4058
- execSync5(
4076
+ execSync6(
4059
4077
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
4060
4078
  { timeout: 5e3 }
4061
4079
  );
4062
4080
  } catch {
4063
4081
  }
4064
- execSync5(
4082
+ execSync6(
4065
4083
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
4066
4084
  { timeout: 5e3 }
4067
4085
  );
@@ -4074,7 +4092,7 @@ function macKeychainDelete(service = SERVICE) {
4074
4092
  if (!nativeKeychainAllowed()) return false;
4075
4093
  if (process.platform !== "darwin") return false;
4076
4094
  try {
4077
- execSync5(
4095
+ execSync6(
4078
4096
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
4079
4097
  { timeout: 5e3 }
4080
4098
  );
@@ -4086,7 +4104,7 @@ function macKeychainDelete(service = SERVICE) {
4086
4104
  function linuxSecretGet(service = SERVICE) {
4087
4105
  if (!linuxSecretAvailable()) return null;
4088
4106
  try {
4089
- return execSync5(
4107
+ return execSync6(
4090
4108
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
4091
4109
  { encoding: "utf-8", timeout: 5e3 }
4092
4110
  ).trim();
@@ -4097,7 +4115,7 @@ function linuxSecretGet(service = SERVICE) {
4097
4115
  function linuxSecretSet(value, service = SERVICE) {
4098
4116
  if (!linuxSecretAvailable()) return false;
4099
4117
  try {
4100
- execSync5(
4118
+ execSync6(
4101
4119
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
4102
4120
  { timeout: 5e3 }
4103
4121
  );
@@ -4110,7 +4128,7 @@ function linuxSecretDelete(service = SERVICE) {
4110
4128
  if (!nativeKeychainAllowed()) return false;
4111
4129
  if (process.platform !== "linux") return false;
4112
4130
  try {
4113
- execSync5(
4131
+ execSync6(
4114
4132
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
4115
4133
  { timeout: 5e3 }
4116
4134
  );
@@ -5047,6 +5065,24 @@ var init_platform_procedures = __esm({
5047
5065
  priority: "p0",
5048
5066
  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."
5049
5067
  },
5068
+ {
5069
+ title: "Bug report status check \u2014 surface available fixes on boot",
5070
+ domain: "support",
5071
+ priority: "p1",
5072
+ 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."
5073
+ },
5074
+ {
5075
+ title: "Feature request triage \u2014 upstream feature vs local customization",
5076
+ domain: "support",
5077
+ priority: "p0",
5078
+ 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."
5079
+ },
5080
+ {
5081
+ title: "Feature request status check \u2014 surface shipped features on boot",
5082
+ domain: "support",
5083
+ priority: "p1",
5084
+ 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."
5085
+ },
5050
5086
  // --- Operations ---
5051
5087
  {
5052
5088
  title: "Managers must supervise deployed workers",
@@ -6367,12 +6403,12 @@ __export(review_gate_exports, {
6367
6403
  checkTestCoverage: () => checkTestCoverage,
6368
6404
  runReviewGate: () => runReviewGate
6369
6405
  });
6370
- import { execSync as execSync6 } from "child_process";
6406
+ import { execSync as execSync7 } from "child_process";
6371
6407
  import { existsSync as existsSync15 } from "fs";
6372
6408
  function checkCommitsExist(taskCreatedAt) {
6373
6409
  try {
6374
6410
  const since = new Date(taskCreatedAt).toISOString();
6375
- const output = execSync6(
6411
+ const output = execSync7(
6376
6412
  `git log --oneline --since="${since}" --no-merges 2>/dev/null`,
6377
6413
  { encoding: "utf8", timeout: 5e3 }
6378
6414
  ).trim();
@@ -6389,7 +6425,7 @@ function checkLayerBoundaries(taskCreatedAt) {
6389
6425
  const violations = [];
6390
6426
  try {
6391
6427
  const since = new Date(taskCreatedAt).toISOString();
6392
- const filesOutput = execSync6(
6428
+ const filesOutput = execSync7(
6393
6429
  `git log --since="${since}" --no-merges --name-only --pretty=format: 2>/dev/null`,
6394
6430
  { encoding: "utf8", timeout: 5e3 }
6395
6431
  ).trim();
@@ -6398,7 +6434,7 @@ function checkLayerBoundaries(taskCreatedAt) {
6398
6434
  const libFiles = changedFiles.filter((f) => f.startsWith("src/lib/"));
6399
6435
  for (const file of libFiles) {
6400
6436
  try {
6401
- const grepResult = execSync6(
6437
+ const grepResult = execSync7(
6402
6438
  `grep -nE "from ['"]\\.\\./(adapters|tui|runtime)/" "${file}" 2>/dev/null`,
6403
6439
  { encoding: "utf8", timeout: 3e3 }
6404
6440
  ).trim();
@@ -6418,7 +6454,7 @@ function checkTestCoverage(taskCreatedAt) {
6418
6454
  const warnings = [];
6419
6455
  try {
6420
6456
  const since = new Date(taskCreatedAt).toISOString();
6421
- const output = execSync6(
6457
+ const output = execSync7(
6422
6458
  `git log --since="${since}" --no-merges --diff-filter=A --name-only --pretty=format: 2>/dev/null`,
6423
6459
  { encoding: "utf8", timeout: 5e3 }
6424
6460
  ).trim();
@@ -6434,7 +6470,7 @@ function checkTestCoverage(taskCreatedAt) {
6434
6470
  const hasDirectTest = existsSync15(testPath);
6435
6471
  let hasRelatedTest = false;
6436
6472
  try {
6437
- const related = execSync6(
6473
+ const related = execSync7(
6438
6474
  `find "${testDir}" -name "*${baseName}*" -name "*.test.ts" 2>/dev/null`,
6439
6475
  { encoding: "utf8", timeout: 3e3 }
6440
6476
  ).trim();
@@ -6486,7 +6522,7 @@ var init_review_gate = __esm({
6486
6522
 
6487
6523
  // src/adapters/claude/hooks/pre-tool-use.ts
6488
6524
  import { existsSync as existsSync16, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
6489
- import { execSync as execSync7 } from "child_process";
6525
+ import { execSync as execSync8 } from "child_process";
6490
6526
  import path17 from "path";
6491
6527
 
6492
6528
  // src/lib/active-agent.ts
@@ -6636,7 +6672,7 @@ function markDelegationFired() {
6636
6672
  }
6637
6673
  function countEngineerSessions() {
6638
6674
  try {
6639
- const output = execSync7("tmux list-sessions 2>/dev/null", {
6675
+ const output = execSync8("tmux list-sessions 2>/dev/null", {
6640
6676
  encoding: "utf8",
6641
6677
  timeout: 2e3
6642
6678
  });
@@ -1402,7 +1402,7 @@ __export(exe_daemon_client_exports, {
1402
1402
  });
1403
1403
  import net from "net";
1404
1404
  import os4 from "os";
1405
- import { spawn } from "child_process";
1405
+ import { spawn, execSync as execSync2 } from "child_process";
1406
1406
  import { randomUUID } from "crypto";
1407
1407
  import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1408
1408
  import path6 from "path";
@@ -1432,6 +1432,14 @@ function handleData(chunk) {
1432
1432
  }
1433
1433
  }
1434
1434
  }
1435
+ function isZombie(pid) {
1436
+ try {
1437
+ const state = execSync2(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
1438
+ return state.startsWith("Z");
1439
+ } catch {
1440
+ return false;
1441
+ }
1442
+ }
1435
1443
  function cleanupStaleFiles() {
1436
1444
  if (existsSync6(PID_PATH)) {
1437
1445
  try {
@@ -1439,7 +1447,11 @@ function cleanupStaleFiles() {
1439
1447
  if (pid > 0) {
1440
1448
  try {
1441
1449
  process.kill(pid, 0);
1442
- return;
1450
+ if (!isZombie(pid)) {
1451
+ return;
1452
+ }
1453
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
1454
+ `);
1443
1455
  } catch {
1444
1456
  }
1445
1457
  }
@@ -1467,8 +1479,8 @@ function findPackageRoot() {
1467
1479
  function getAvailableMemoryGB() {
1468
1480
  if (process.platform === "darwin") {
1469
1481
  try {
1470
- const { execSync: execSync11 } = __require("child_process");
1471
- const vmstat = execSync11("vm_stat", { encoding: "utf8" });
1482
+ const { execSync: execSync12 } = __require("child_process");
1483
+ const vmstat = execSync12("vm_stat", { encoding: "utf8" });
1472
1484
  const pageSize = 16384;
1473
1485
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1474
1486
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3458,6 +3470,12 @@ async function disposeDatabase() {
3458
3470
  clearInterval(_walCheckpointTimer);
3459
3471
  _walCheckpointTimer = null;
3460
3472
  }
3473
+ if (_client) {
3474
+ try {
3475
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3476
+ } catch {
3477
+ }
3478
+ }
3461
3479
  if (_daemonClient) {
3462
3480
  _daemonClient.close();
3463
3481
  _daemonClient = null;
@@ -3494,7 +3512,7 @@ var init_database = __esm({
3494
3512
  // src/lib/keychain.ts
3495
3513
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3496
3514
  import { existsSync as existsSync7, statSync as statSync2 } from "fs";
3497
- import { execSync as execSync2 } from "child_process";
3515
+ import { execSync as execSync3 } from "child_process";
3498
3516
  import path7 from "path";
3499
3517
  import os5 from "os";
3500
3518
  function getKeyDir() {
@@ -3511,13 +3529,13 @@ function linuxSecretAvailable() {
3511
3529
  if (process.platform !== "linux") return false;
3512
3530
  if (linuxSecretAvailability !== null) return linuxSecretAvailability;
3513
3531
  try {
3514
- execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3532
+ execSync3("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
3515
3533
  } catch {
3516
3534
  linuxSecretAvailability = false;
3517
3535
  return false;
3518
3536
  }
3519
3537
  try {
3520
- execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3538
+ execSync3("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
3521
3539
  linuxSecretAvailability = true;
3522
3540
  } catch {
3523
3541
  linuxSecretAvailability = false;
@@ -3541,7 +3559,7 @@ function macKeychainGet(service = SERVICE) {
3541
3559
  if (!nativeKeychainAllowed()) return null;
3542
3560
  if (process.platform !== "darwin") return null;
3543
3561
  try {
3544
- return execSync2(
3562
+ return execSync3(
3545
3563
  `security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
3546
3564
  { encoding: "utf-8", timeout: 5e3 }
3547
3565
  ).trim();
@@ -3554,13 +3572,13 @@ function macKeychainSet(value, service = SERVICE) {
3554
3572
  if (process.platform !== "darwin") return false;
3555
3573
  try {
3556
3574
  try {
3557
- execSync2(
3575
+ execSync3(
3558
3576
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3559
3577
  { timeout: 5e3 }
3560
3578
  );
3561
3579
  } catch {
3562
3580
  }
3563
- execSync2(
3581
+ execSync3(
3564
3582
  `security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
3565
3583
  { timeout: 5e3 }
3566
3584
  );
@@ -3573,7 +3591,7 @@ function macKeychainDelete(service = SERVICE) {
3573
3591
  if (!nativeKeychainAllowed()) return false;
3574
3592
  if (process.platform !== "darwin") return false;
3575
3593
  try {
3576
- execSync2(
3594
+ execSync3(
3577
3595
  `security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
3578
3596
  { timeout: 5e3 }
3579
3597
  );
@@ -3585,7 +3603,7 @@ function macKeychainDelete(service = SERVICE) {
3585
3603
  function linuxSecretGet(service = SERVICE) {
3586
3604
  if (!linuxSecretAvailable()) return null;
3587
3605
  try {
3588
- return execSync2(
3606
+ return execSync3(
3589
3607
  `secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3590
3608
  { encoding: "utf-8", timeout: 5e3 }
3591
3609
  ).trim();
@@ -3596,7 +3614,7 @@ function linuxSecretGet(service = SERVICE) {
3596
3614
  function linuxSecretSet(value, service = SERVICE) {
3597
3615
  if (!linuxSecretAvailable()) return false;
3598
3616
  try {
3599
- execSync2(
3617
+ execSync3(
3600
3618
  `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3601
3619
  { timeout: 5e3 }
3602
3620
  );
@@ -3609,7 +3627,7 @@ function linuxSecretDelete(service = SERVICE) {
3609
3627
  if (!nativeKeychainAllowed()) return false;
3610
3628
  if (process.platform !== "linux") return false;
3611
3629
  try {
3612
- execSync2(
3630
+ execSync3(
3613
3631
  `secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
3614
3632
  { timeout: 5e3 }
3615
3633
  );
@@ -4546,6 +4564,24 @@ var init_platform_procedures = __esm({
4546
4564
  priority: "p0",
4547
4565
  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."
4548
4566
  },
4567
+ {
4568
+ title: "Bug report status check \u2014 surface available fixes on boot",
4569
+ domain: "support",
4570
+ priority: "p1",
4571
+ 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."
4572
+ },
4573
+ {
4574
+ title: "Feature request triage \u2014 upstream feature vs local customization",
4575
+ domain: "support",
4576
+ priority: "p0",
4577
+ 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."
4578
+ },
4579
+ {
4580
+ title: "Feature request status check \u2014 surface shipped features on boot",
4581
+ domain: "support",
4582
+ priority: "p1",
4583
+ 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."
4584
+ },
4549
4585
  // --- Operations ---
4550
4586
  {
4551
4587
  title: "Managers must supervise deployed workers",
@@ -6077,7 +6113,7 @@ __export(project_name_exports, {
6077
6113
  _resetCache: () => _resetCache,
6078
6114
  getProjectName: () => getProjectName
6079
6115
  });
6080
- import { execSync as execSync3 } from "child_process";
6116
+ import { execSync as execSync4 } from "child_process";
6081
6117
  import path10 from "path";
6082
6118
  function getProjectName(cwd) {
6083
6119
  const dir = cwd ?? process.cwd();
@@ -6085,7 +6121,7 @@ function getProjectName(cwd) {
6085
6121
  try {
6086
6122
  let repoRoot;
6087
6123
  try {
6088
- const gitCommonDir = execSync3("git rev-parse --path-format=absolute --git-common-dir", {
6124
+ const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
6089
6125
  cwd: dir,
6090
6126
  encoding: "utf8",
6091
6127
  timeout: 2e3,
@@ -6093,7 +6129,7 @@ function getProjectName(cwd) {
6093
6129
  }).trim();
6094
6130
  repoRoot = path10.dirname(gitCommonDir);
6095
6131
  } catch {
6096
- repoRoot = execSync3("git rev-parse --show-toplevel", {
6132
+ repoRoot = execSync4("git rev-parse --show-toplevel", {
6097
6133
  cwd: dir,
6098
6134
  encoding: "utf8",
6099
6135
  timeout: 2e3,
@@ -6127,14 +6163,14 @@ var file_grep_exports = {};
6127
6163
  __export(file_grep_exports, {
6128
6164
  grepProjectFiles: () => grepProjectFiles
6129
6165
  });
6130
- import { execSync as execSync4 } from "child_process";
6166
+ import { execSync as execSync5 } from "child_process";
6131
6167
  import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync4, existsSync as existsSync10 } from "fs";
6132
6168
  import path11 from "path";
6133
6169
  import crypto2 from "crypto";
6134
6170
  function hasRipgrep() {
6135
6171
  if (_hasRg === null) {
6136
6172
  try {
6137
- execSync4("rg --version", { stdio: "ignore", timeout: 2e3 });
6173
+ execSync5("rg --version", { stdio: "ignore", timeout: 2e3 });
6138
6174
  _hasRg = true;
6139
6175
  } catch {
6140
6176
  _hasRg = false;
@@ -6200,7 +6236,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
6200
6236
  const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
6201
6237
  const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
6202
6238
  const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
6203
- const output = execSync4(cmd, {
6239
+ const output = execSync5(cmd, {
6204
6240
  cwd: projectRoot,
6205
6241
  encoding: "utf8",
6206
6242
  timeout: 3e3,
@@ -6215,12 +6251,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
6215
6251
  const matchCount = parseInt(line.slice(colonIdx + 1));
6216
6252
  if (isNaN(matchCount) || matchCount === 0) continue;
6217
6253
  try {
6218
- const firstMatch = execSync4(
6254
+ const firstMatch = execSync5(
6219
6255
  `rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
6220
6256
  { cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
6221
6257
  ).trim();
6222
6258
  const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
6223
- const totalLines = execSync4(`wc -l < '${filePath}'`, {
6259
+ const totalLines = execSync5(`wc -l < '${filePath}'`, {
6224
6260
  cwd: projectRoot,
6225
6261
  encoding: "utf8",
6226
6262
  timeout: 1e3
@@ -6924,7 +6960,7 @@ var init_entity_boost = __esm({
6924
6960
  });
6925
6961
 
6926
6962
  // src/lib/session-key.ts
6927
- import { execSync as execSync5 } from "child_process";
6963
+ import { execSync as execSync6 } from "child_process";
6928
6964
  function normalizeCommand(command) {
6929
6965
  const trimmed = command.trim().toLowerCase();
6930
6966
  const parts = trimmed.split(/[\\/]/);
@@ -6943,7 +6979,7 @@ function resolveRuntimeProcess() {
6943
6979
  let pid = process.ppid;
6944
6980
  for (let i = 0; i < 10; i++) {
6945
6981
  try {
6946
- const info = execSync5(`ps -p ${pid} -o ppid=,comm=`, {
6982
+ const info = execSync6(`ps -p ${pid} -o ppid=,comm=`, {
6947
6983
  encoding: "utf8",
6948
6984
  timeout: 2e3
6949
6985
  }).trim();
@@ -7000,7 +7036,7 @@ __export(session_registry_exports, {
7000
7036
  registerSession: () => registerSession
7001
7037
  });
7002
7038
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync12 } from "fs";
7003
- import { execSync as execSync7 } from "child_process";
7039
+ import { execSync as execSync8 } from "child_process";
7004
7040
  import path14 from "path";
7005
7041
  import os7 from "os";
7006
7042
  function registerSession(entry) {
@@ -7040,7 +7076,7 @@ function pruneStaleSessions() {
7040
7076
  if (sessions.length === 0) return 0;
7041
7077
  let liveSessions = [];
7042
7078
  try {
7043
- liveSessions = execSync7("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
7079
+ liveSessions = execSync8("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
7044
7080
  encoding: "utf8"
7045
7081
  }).trim().split("\n").filter(Boolean);
7046
7082
  } catch {
@@ -7188,14 +7224,14 @@ var init_transport = __esm({
7188
7224
  });
7189
7225
 
7190
7226
  // src/lib/cc-agent-support.ts
7191
- import { execSync as execSync8 } from "child_process";
7227
+ import { execSync as execSync9 } from "child_process";
7192
7228
  function _resetCcAgentSupportCache() {
7193
7229
  _cachedSupport = null;
7194
7230
  }
7195
7231
  function claudeSupportsAgentFlag() {
7196
7232
  if (_cachedSupport !== null) return _cachedSupport;
7197
7233
  try {
7198
- const helpOutput = execSync8("claude --help 2>&1", {
7234
+ const helpOutput = execSync9("claude --help 2>&1", {
7199
7235
  encoding: "utf-8",
7200
7236
  timeout: 5e3
7201
7237
  });
@@ -7744,7 +7780,7 @@ var init_session_scope = __esm({
7744
7780
  import crypto5 from "crypto";
7745
7781
  import path20 from "path";
7746
7782
  import os12 from "os";
7747
- import { execSync as execSync9 } from "child_process";
7783
+ import { execSync as execSync10 } from "child_process";
7748
7784
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
7749
7785
  import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
7750
7786
  async function writeCheckpoint(input2) {
@@ -8089,14 +8125,14 @@ function isTmuxSessionAlive(identifier) {
8089
8125
  if (!identifier || identifier === "unknown") return true;
8090
8126
  try {
8091
8127
  if (identifier.startsWith("%")) {
8092
- const output = execSync9("tmux list-panes -a -F '#{pane_id}'", {
8128
+ const output = execSync10("tmux list-panes -a -F '#{pane_id}'", {
8093
8129
  timeout: 2e3,
8094
8130
  encoding: "utf8",
8095
8131
  stdio: ["pipe", "pipe", "pipe"]
8096
8132
  });
8097
8133
  return output.split("\n").some((l) => l.trim() === identifier);
8098
8134
  } else {
8099
- execSync9(`tmux has-session -t ${JSON.stringify(identifier)}`, {
8135
+ execSync10(`tmux has-session -t ${JSON.stringify(identifier)}`, {
8100
8136
  timeout: 2e3,
8101
8137
  stdio: ["pipe", "pipe", "pipe"]
8102
8138
  });
@@ -8105,7 +8141,7 @@ function isTmuxSessionAlive(identifier) {
8105
8141
  } catch {
8106
8142
  if (identifier.startsWith("%")) return true;
8107
8143
  try {
8108
- execSync9("tmux list-sessions", {
8144
+ execSync10("tmux list-sessions", {
8109
8145
  timeout: 2e3,
8110
8146
  stdio: ["pipe", "pipe", "pipe"]
8111
8147
  });
@@ -8120,12 +8156,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
8120
8156
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
8121
8157
  try {
8122
8158
  const since = new Date(taskCreatedAt).toISOString();
8123
- const branch = execSync9(
8159
+ const branch = execSync10(
8124
8160
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
8125
8161
  { encoding: "utf8", timeout: 3e3 }
8126
8162
  ).trim();
8127
8163
  const branchArg = branch && branch !== "HEAD" ? branch : "";
8128
- const commitCount = execSync9(
8164
+ const commitCount = execSync10(
8129
8165
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
8130
8166
  { encoding: "utf8", timeout: 5e3 }
8131
8167
  ).trim();
@@ -9727,7 +9763,7 @@ __export(tmux_routing_exports, {
9727
9763
  spawnEmployee: () => spawnEmployee,
9728
9764
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
9729
9765
  });
9730
- import { execFileSync as execFileSync2, execSync as execSync10 } from "child_process";
9766
+ import { execFileSync as execFileSync2, execSync as execSync11 } from "child_process";
9731
9767
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10, existsSync as existsSync20, appendFileSync, readdirSync as readdirSync6 } from "fs";
9732
9768
  import path24 from "path";
9733
9769
  import os13 from "os";
@@ -10448,7 +10484,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
10448
10484
  let booted = false;
10449
10485
  for (let i = 0; i < 30; i++) {
10450
10486
  try {
10451
- execSync10("sleep 0.5");
10487
+ execSync11("sleep 0.5");
10452
10488
  } catch {
10453
10489
  }
10454
10490
  try {
@@ -11739,7 +11775,7 @@ function isCacheCold(sessionKey) {
11739
11775
  init_config();
11740
11776
  init_session_key();
11741
11777
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
11742
- import { execSync as execSync6 } from "child_process";
11778
+ import { execSync as execSync7 } from "child_process";
11743
11779
  import path13 from "path";
11744
11780
 
11745
11781
  // src/mcp/agent-context.ts
@@ -11829,7 +11865,7 @@ function getActiveAgent() {
11829
11865
  } catch {
11830
11866
  }
11831
11867
  try {
11832
- const sessionName = execSync6(
11868
+ const sessionName = execSync7(
11833
11869
  "tmux display-message -p '#{session_name}' 2>/dev/null",
11834
11870
  { encoding: "utf8", timeout: 2e3 }
11835
11871
  ).trim();
@@ -11917,8 +11953,8 @@ process.stdin.on("end", async () => {
11917
11953
  }
11918
11954
  }
11919
11955
  try {
11920
- const { execSync: execSync11 } = await import("child_process");
11921
- const currentSession = execSync11("tmux display-message -p '#{session_name}' 2>/dev/null", {
11956
+ const { execSync: execSync12 } = await import("child_process");
11957
+ const currentSession = execSync12("tmux display-message -p '#{session_name}' 2>/dev/null", {
11922
11958
  encoding: "utf8",
11923
11959
  timeout: 2e3
11924
11960
  }).trim();
@@ -12055,8 +12091,8 @@ ${lines.join("\n")}`;
12055
12091
  const lastCheckPath = path26.join(CACHE_DIR3, `review-lastcheck-${sessionKey}.json`);
12056
12092
  let sessionScope;
12057
12093
  try {
12058
- const { execSync: execSync11 } = await import("child_process");
12059
- const tmuxSession = execSync11("tmux display-message -p '#{session_name}'", { encoding: "utf8", timeout: 2e3 }).trim();
12094
+ const { execSync: execSync12 } = await import("child_process");
12095
+ const tmuxSession = execSync12("tmux display-message -p '#{session_name}'", { encoding: "utf8", timeout: 2e3 }).trim();
12060
12096
  if (isExeSession(tmuxSession)) sessionScope = tmuxSession;
12061
12097
  } catch {
12062
12098
  }