@askexenow/exe-os 0.9.114 → 0.9.115

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 (79) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +13 -1
  2. package/dist/bin/agentic-reflection-backfill.js +13 -1
  3. package/dist/bin/agentic-semantic-label.js +13 -1
  4. package/dist/bin/backfill-conversations.js +13 -1
  5. package/dist/bin/backfill-responses.js +13 -1
  6. package/dist/bin/backfill-vectors.js +13 -1
  7. package/dist/bin/bulk-sync-postgres.js +13 -1
  8. package/dist/bin/cleanup-stale-review-tasks.js +449 -104
  9. package/dist/bin/cli.js +317 -40
  10. package/dist/bin/exe-assign.js +13 -1
  11. package/dist/bin/exe-boot.js +202 -39
  12. package/dist/bin/exe-cloud.js +13 -1
  13. package/dist/bin/exe-dispatch.js +315 -38
  14. package/dist/bin/exe-doctor.js +28 -2
  15. package/dist/bin/exe-export-behaviors.js +14 -1
  16. package/dist/bin/exe-forget.js +13 -1
  17. package/dist/bin/exe-gateway.js +315 -38
  18. package/dist/bin/exe-heartbeat.js +450 -104
  19. package/dist/bin/exe-kill.js +13 -1
  20. package/dist/bin/exe-launch-agent.js +14 -1
  21. package/dist/bin/exe-pending-messages.js +429 -84
  22. package/dist/bin/exe-pending-notifications.js +429 -84
  23. package/dist/bin/exe-pending-reviews.js +429 -84
  24. package/dist/bin/exe-rename.js +13 -1
  25. package/dist/bin/exe-review.js +13 -1
  26. package/dist/bin/exe-search.js +14 -1
  27. package/dist/bin/exe-session-cleanup.js +315 -38
  28. package/dist/bin/exe-settings.js +12 -0
  29. package/dist/bin/exe-start-codex.js +14 -1
  30. package/dist/bin/exe-start-opencode.js +14 -1
  31. package/dist/bin/exe-status.js +439 -105
  32. package/dist/bin/exe-support.js +12 -0
  33. package/dist/bin/exe-team.js +13 -1
  34. package/dist/bin/git-sweep.js +315 -38
  35. package/dist/bin/graph-backfill.js +13 -1
  36. package/dist/bin/graph-export.js +13 -1
  37. package/dist/bin/intercom-check.js +222 -38
  38. package/dist/bin/scan-tasks.js +315 -38
  39. package/dist/bin/setup.js +14 -1
  40. package/dist/bin/shard-migrate.js +13 -1
  41. package/dist/gateway/index.js +315 -38
  42. package/dist/hooks/bug-report-worker.js +315 -38
  43. package/dist/hooks/codex-stop-task-finalizer.js +277 -26
  44. package/dist/hooks/commit-complete.js +315 -38
  45. package/dist/hooks/error-recall.js +14 -1
  46. package/dist/hooks/ingest.js +330 -39
  47. package/dist/hooks/instructions-loaded.js +13 -1
  48. package/dist/hooks/notification.js +13 -1
  49. package/dist/hooks/post-compact.js +403 -61
  50. package/dist/hooks/post-tool-combined.js +480 -137
  51. package/dist/hooks/pre-compact.js +315 -38
  52. package/dist/hooks/pre-tool-use.js +34 -2
  53. package/dist/hooks/prompt-submit.js +315 -38
  54. package/dist/hooks/session-end.js +125 -38
  55. package/dist/hooks/session-start.js +35 -2
  56. package/dist/hooks/stop.js +397 -61
  57. package/dist/hooks/subagent-stop.js +396 -61
  58. package/dist/hooks/summary-worker.js +410 -112
  59. package/dist/index.js +315 -38
  60. package/dist/lib/cloud-sync.js +1 -1
  61. package/dist/lib/config.js +13 -0
  62. package/dist/lib/embedder.js +13 -0
  63. package/dist/lib/exe-daemon.js +351 -44
  64. package/dist/lib/hybrid-search.js +14 -1
  65. package/dist/lib/keychain.js +1 -1
  66. package/dist/lib/messaging.js +395 -74
  67. package/dist/lib/schedules.js +13 -1
  68. package/dist/lib/skill-learning.js +13 -0
  69. package/dist/lib/store.js +13 -1
  70. package/dist/lib/tasks.js +314 -37
  71. package/dist/lib/tmux-routing.js +314 -37
  72. package/dist/mcp/server.js +331 -40
  73. package/dist/mcp/tools/create-task.js +314 -37
  74. package/dist/mcp/tools/list-tasks.js +406 -57
  75. package/dist/mcp/tools/send-message.js +395 -74
  76. package/dist/mcp/tools/update-task.js +314 -37
  77. package/dist/runtime/index.js +315 -38
  78. package/dist/tui/App.js +317 -40
  79. package/package.json +1 -1
@@ -155,6 +155,17 @@ function normalizeOrchestration(raw) {
155
155
  const userOrg = raw.orchestration ?? {};
156
156
  raw.orchestration = { ...defaultOrg, ...userOrg };
157
157
  }
158
+ function normalizeCloudEndpoint(raw) {
159
+ const cloud = raw.cloud;
160
+ if (!cloud?.endpoint) return;
161
+ const ep = String(cloud.endpoint);
162
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
163
+ cloud.endpoint = "https://cloud.askexe.com";
164
+ process.stderr.write(
165
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
166
+ );
167
+ }
168
+ }
158
169
  async function loadConfig() {
159
170
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
160
171
  await ensurePrivateDir(dir);
@@ -180,6 +191,7 @@ async function loadConfig() {
180
191
  normalizeSessionLifecycle(migratedCfg);
181
192
  normalizeAutoUpdate(migratedCfg);
182
193
  normalizeOrchestration(migratedCfg);
194
+ normalizeCloudEndpoint(migratedCfg);
183
195
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
184
196
  if (config.dbPath.startsWith("~")) {
185
197
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -208,6 +220,7 @@ function loadConfigSync() {
208
220
  normalizeSessionLifecycle(migratedCfg);
209
221
  normalizeAutoUpdate(migratedCfg);
210
222
  normalizeOrchestration(migratedCfg);
223
+ normalizeCloudEndpoint(migratedCfg);
211
224
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
212
225
  if (config.dbPath.startsWith("~")) {
213
226
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -4345,7 +4358,7 @@ async function getMasterKey() {
4345
4358
  b64Value = content;
4346
4359
  }
4347
4360
  const key = Buffer.from(b64Value, "base64");
4348
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
4361
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
4349
4362
  return key;
4350
4363
  }
4351
4364
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -5532,6 +5545,7 @@ var init_provider_table = __esm({
5532
5545
  // src/lib/intercom-queue.ts
5533
5546
  var intercom_queue_exports = {};
5534
5547
  __export(intercom_queue_exports, {
5548
+ _resetDrainGuard: () => _resetDrainGuard,
5535
5549
  clearQueueForAgent: () => clearQueueForAgent,
5536
5550
  drainForSession: () => drainForSession,
5537
5551
  drainQueue: () => drainQueue,
@@ -5577,38 +5591,47 @@ function queueIntercom(targetSession, reason) {
5577
5591
  writeQueue(queue);
5578
5592
  }
5579
5593
  function drainQueue(isSessionBusy2, sendKeys) {
5594
+ if (_draining) {
5595
+ logQueue("SKIP_DRAIN \u2014 previous drain still running (possible tmux hang)");
5596
+ return { drained: 0, failed: 0 };
5597
+ }
5580
5598
  const queue = readQueue();
5581
5599
  if (queue.length === 0) return { drained: 0, failed: 0 };
5600
+ _draining = true;
5582
5601
  const remaining = [];
5583
5602
  let drained = 0;
5584
5603
  let failed = 0;
5585
- for (const item of queue) {
5586
- const age = Date.now() - new Date(item.queuedAt).getTime();
5587
- if (age > TTL_MS) {
5588
- logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
5589
- failed++;
5590
- continue;
5591
- }
5592
- try {
5593
- if (!isSessionBusy2(item.targetSession)) {
5594
- const success = sendKeys(item.targetSession);
5595
- if (success) {
5596
- logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
5597
- drained++;
5598
- continue;
5604
+ try {
5605
+ for (const item of queue) {
5606
+ const age = Date.now() - new Date(item.queuedAt).getTime();
5607
+ if (age > TTL_MS) {
5608
+ logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
5609
+ failed++;
5610
+ continue;
5611
+ }
5612
+ try {
5613
+ if (!isSessionBusy2(item.targetSession)) {
5614
+ const success = sendKeys(item.targetSession);
5615
+ if (success) {
5616
+ logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
5617
+ drained++;
5618
+ continue;
5619
+ }
5599
5620
  }
5621
+ } catch {
5600
5622
  }
5601
- } catch {
5602
- }
5603
- item.attempts++;
5604
- if (item.attempts >= MAX_RETRIES2) {
5605
- logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
5606
- failed++;
5607
- continue;
5623
+ item.attempts++;
5624
+ if (item.attempts >= MAX_RETRIES2) {
5625
+ logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
5626
+ failed++;
5627
+ continue;
5628
+ }
5629
+ remaining.push(item);
5608
5630
  }
5609
- remaining.push(item);
5631
+ writeQueue(remaining);
5632
+ } finally {
5633
+ _draining = false;
5610
5634
  }
5611
- writeQueue(remaining);
5612
5635
  return { drained, failed };
5613
5636
  }
5614
5637
  function drainForSession(targetSession, sendKeys) {
@@ -5633,6 +5656,9 @@ function clearQueueForAgent(agentName) {
5633
5656
  logQueue(`CLEARED ${before - filtered.length} stale item(s) for ${agentName}`);
5634
5657
  }
5635
5658
  }
5659
+ function _resetDrainGuard() {
5660
+ _draining = false;
5661
+ }
5636
5662
  function logQueue(msg) {
5637
5663
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
5638
5664
  `;
@@ -5644,13 +5670,14 @@ function logQueue(msg) {
5644
5670
  } catch {
5645
5671
  }
5646
5672
  }
5647
- var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
5673
+ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, _draining, INTERCOM_LOG;
5648
5674
  var init_intercom_queue = __esm({
5649
5675
  "src/lib/intercom-queue.ts"() {
5650
5676
  "use strict";
5651
5677
  QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
5652
5678
  MAX_RETRIES2 = 5;
5653
5679
  TTL_MS = 60 * 60 * 1e3;
5680
+ _draining = false;
5654
5681
  INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
5655
5682
  }
5656
5683
  });
@@ -6265,6 +6292,17 @@ var init_agent_symlinks = __esm({
6265
6292
  });
6266
6293
 
6267
6294
  // src/lib/notifications.ts
6295
+ var notifications_exports = {};
6296
+ __export(notifications_exports, {
6297
+ cleanupOldNotifications: () => cleanupOldNotifications,
6298
+ formatNotifications: () => formatNotifications,
6299
+ markAsRead: () => markAsRead,
6300
+ markAsReadByTaskFile: () => markAsReadByTaskFile,
6301
+ markDoneTaskNotificationsAsRead: () => markDoneTaskNotificationsAsRead,
6302
+ migrateJsonNotifications: () => migrateJsonNotifications,
6303
+ readUnreadNotifications: () => readUnreadNotifications,
6304
+ writeNotification: () => writeNotification
6305
+ });
6268
6306
  import crypto2 from "crypto";
6269
6307
  import path14 from "path";
6270
6308
  import os10 from "os";
@@ -6394,6 +6432,29 @@ async function markDoneTaskNotificationsAsRead(sessionScope) {
6394
6432
  return 0;
6395
6433
  }
6396
6434
  }
6435
+ function formatNotifications(notifications) {
6436
+ if (notifications.length === 0) return "";
6437
+ const grouped = /* @__PURE__ */ new Map();
6438
+ for (const n of notifications) {
6439
+ const key = `${n.agentId}|${n.agentRole}`;
6440
+ if (!grouped.has(key)) grouped.set(key, []);
6441
+ grouped.get(key).push(n);
6442
+ }
6443
+ const lines = [];
6444
+ lines.push(`## Notifications (${notifications.length} unread)
6445
+ `);
6446
+ for (const [key, items] of grouped) {
6447
+ const [agentId, agentRole] = key.split("|");
6448
+ lines.push(`**${agentId}** (${agentRole}):`);
6449
+ for (const item of items) {
6450
+ const ago = formatTimeAgo(item.timestamp);
6451
+ const icon = eventIcon(item.event);
6452
+ lines.push(`- ${icon} ${item.summary} (${item.project}) \u2014 ${ago}`);
6453
+ }
6454
+ lines.push("");
6455
+ }
6456
+ return lines.join("\n");
6457
+ }
6397
6458
  async function migrateJsonNotifications() {
6398
6459
  const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path14.join(os10.homedir(), ".exe-os");
6399
6460
  const notifDir = path14.join(base, "notifications");
@@ -6439,6 +6500,34 @@ async function migrateJsonNotifications() {
6439
6500
  }
6440
6501
  return migrated;
6441
6502
  }
6503
+ function eventIcon(event) {
6504
+ switch (event) {
6505
+ case "task_complete":
6506
+ return "Completed:";
6507
+ case "task_needs_fix":
6508
+ return "Needs fix:";
6509
+ case "session_summary":
6510
+ return "Session:";
6511
+ case "error_spike":
6512
+ return "Errors:";
6513
+ case "orphan_task":
6514
+ return "Orphan:";
6515
+ case "subtasks_complete":
6516
+ return "Subtasks done:";
6517
+ case "capacity_relaunch":
6518
+ return "Relaunched:";
6519
+ }
6520
+ }
6521
+ function formatTimeAgo(timestamp) {
6522
+ const diffMs = Date.now() - new Date(timestamp).getTime();
6523
+ const mins = Math.floor(diffMs / 6e4);
6524
+ if (mins < 1) return "just now";
6525
+ if (mins < 60) return `${mins}m ago`;
6526
+ const hours = Math.floor(mins / 60);
6527
+ if (hours < 24) return `${hours}h ago`;
6528
+ const days = Math.floor(hours / 24);
6529
+ return `${days}d ago`;
6530
+ }
6442
6531
  var CLEANUP_DAYS;
6443
6532
  var init_notifications = __esm({
6444
6533
  "src/lib/notifications.ts"() {
@@ -7515,7 +7604,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
7515
7604
  taskFile
7516
7605
  });
7517
7606
  const originalPriority = String(row.priority).toLowerCase();
7518
- const autoApprove = originalPriority === "p2" && result?.toLowerCase().includes("tests pass");
7607
+ const resultLower = result?.toLowerCase() ?? "";
7608
+ const hasTestEvidence = (
7609
+ // Vitest/Jest output patterns (hard to fake without actually running tests)
7610
+ /\d+\s+pass(ed|ing)/.test(resultLower) || /test files?\s+\d+\s+passed/.test(resultLower) || /tests?\s+\d+\s+passed/.test(resultLower)
7611
+ );
7612
+ const hasNoFailures = !/fail(ed|ure|ing)|error/i.test(resultLower);
7613
+ const autoApprove = originalPriority === "p2" && hasTestEvidence && hasNoFailures;
7519
7614
  if (!autoApprove) {
7520
7615
  try {
7521
7616
  const key = getSessionKey();
@@ -7523,6 +7618,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
7523
7618
  if (exeSession) {
7524
7619
  sendIntercom(exeSession);
7525
7620
  }
7621
+ if (reviewer && reviewer !== coordinatorName && reviewer !== exeSession) {
7622
+ const { employeeSessionName: employeeSessionName2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
7623
+ if (exeSession) {
7624
+ const reviewerSession = employeeSessionName2(reviewer, exeSession);
7625
+ sendIntercom(reviewerSession);
7626
+ }
7627
+ }
7526
7628
  } catch {
7527
7629
  }
7528
7630
  }
@@ -8298,6 +8400,20 @@ async function updateTask(input) {
8298
8400
  notifyTaskDone();
8299
8401
  }
8300
8402
  await markTaskNotificationsRead(taskFile);
8403
+ if (input.status === "needs_review" && !isCoordinator) {
8404
+ try {
8405
+ const { writeNotification: writeNotification2 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
8406
+ await writeNotification2({
8407
+ agentId: String(row.assigned_to),
8408
+ agentRole: String(row.assigned_to),
8409
+ event: "task_complete",
8410
+ project: String(row.project_name),
8411
+ summary: `"${String(row.title)}" is ready for review`,
8412
+ taskFile
8413
+ });
8414
+ } catch {
8415
+ }
8416
+ }
8301
8417
  if (input.status === "done" || input.status === "closed") {
8302
8418
  try {
8303
8419
  await cascadeUnblock(taskId, input.baseDir, now);
@@ -8730,18 +8846,31 @@ function acquireSpawnLock2(sessionName) {
8730
8846
  mkdirSync9(SPAWN_LOCK_DIR, { recursive: true });
8731
8847
  }
8732
8848
  const lockFile = spawnLockPath(sessionName);
8733
- if (existsSync18(lockFile)) {
8734
- try {
8735
- const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
8736
- const age = Date.now() - lock.timestamp;
8737
- if (isProcessAlive(lock.pid) && age < 6e4) {
8738
- return false;
8739
- }
8740
- } catch {
8849
+ const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
8850
+ const { openSync: openSync4, closeSync: closeSync4, writeSync } = __require("fs");
8851
+ const { constants: constants2 } = __require("fs");
8852
+ try {
8853
+ const fd = openSync4(lockFile, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL, 420);
8854
+ writeSync(fd, lockData);
8855
+ closeSync4(fd);
8856
+ return true;
8857
+ } catch (err) {
8858
+ if (err?.code !== "EEXIST") {
8859
+ return true;
8741
8860
  }
8742
8861
  }
8743
- writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
8744
- return true;
8862
+ try {
8863
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
8864
+ const age = Date.now() - lock.timestamp;
8865
+ if (isProcessAlive(lock.pid) && age < 6e4) {
8866
+ return false;
8867
+ }
8868
+ writeFileSync8(lockFile, lockData);
8869
+ return true;
8870
+ } catch {
8871
+ writeFileSync8(lockFile, lockData);
8872
+ return true;
8873
+ }
8745
8874
  }
8746
8875
  function releaseSpawnLock2(sessionName) {
8747
8876
  try {
@@ -8820,6 +8949,21 @@ function parseParentExe(sessionName, agentId) {
8820
8949
  function extractRootExe(name) {
8821
8950
  if (!name) return null;
8822
8951
  if (!name.includes("-")) return name;
8952
+ try {
8953
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
8954
+ if (roster.length > 0) {
8955
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
8956
+ for (const agentName of sortedNames) {
8957
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8958
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
8959
+ const match = name.match(regex);
8960
+ if (match) {
8961
+ return extractRootExe(match[1]);
8962
+ }
8963
+ }
8964
+ }
8965
+ } catch {
8966
+ }
8823
8967
  const parts = name.split("-").filter(Boolean);
8824
8968
  return parts.length > 0 ? parts[parts.length - 1] : null;
8825
8969
  }
@@ -8838,6 +8982,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8838
8982
  function getParentExe(sessionKey) {
8839
8983
  try {
8840
8984
  const data = JSON.parse(readFileSync12(path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
8985
+ if (data.registeredAt) {
8986
+ const age = Date.now() - new Date(data.registeredAt).getTime();
8987
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
8988
+ }
8841
8989
  return data.parentExe || null;
8842
8990
  } catch {
8843
8991
  return null;
@@ -9386,7 +9534,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9386
9534
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
9387
9535
  } catch {
9388
9536
  }
9389
- let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName}`;
9537
+ let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName} EXE_SESSION_START_ISO=${(/* @__PURE__ */ new Date()).toISOString()}`;
9390
9538
  if (ccProvider !== DEFAULT_PROVIDER) {
9391
9539
  const cfg = PROVIDER_TABLE[ccProvider];
9392
9540
  if (cfg?.apiKeyEnv) {
@@ -9523,7 +9671,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9523
9671
  releaseSpawnLock2(sessionName);
9524
9672
  return { sessionName };
9525
9673
  }
9526
- var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
9674
+ var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, PARENT_EXE_CACHE_TTL_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
9527
9675
  var init_tmux_routing = __esm({
9528
9676
  "src/lib/tmux-routing.ts"() {
9529
9677
  "use strict";
@@ -9543,6 +9691,7 @@ var init_tmux_routing = __esm({
9543
9691
  SESSION_CACHE = path20.join(os12.homedir(), ".exe-os", "session-cache");
9544
9692
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
9545
9693
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
9694
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
9546
9695
  VERIFY_PANE_LINES = 200;
9547
9696
  INTERCOM_DEBOUNCE_MS = 3e4;
9548
9697
  CODEX_DEBOUNCE_MS = 12e4;
@@ -9626,7 +9775,21 @@ function tryAcquireWorkerSlot() {
9626
9775
  for (const f of files) {
9627
9776
  if (!f.endsWith(".pid")) continue;
9628
9777
  if (f.startsWith("res-")) {
9629
- alive++;
9778
+ const resParts = f.replace(".pid", "").split("-");
9779
+ const resPid = parseInt(resParts[1] ?? "", 10);
9780
+ if (!isNaN(resPid) && resPid > 0) {
9781
+ try {
9782
+ process.kill(resPid, 0);
9783
+ alive++;
9784
+ } catch {
9785
+ try {
9786
+ unlinkSync9(path22.join(WORKER_PID_DIR, f));
9787
+ } catch {
9788
+ }
9789
+ }
9790
+ } else {
9791
+ alive++;
9792
+ }
9630
9793
  continue;
9631
9794
  }
9632
9795
  const dashIdx = f.lastIndexOf("-");
@@ -302,7 +302,7 @@ async function getMasterKey() {
302
302
  b64Value = content;
303
303
  }
304
304
  const key = Buffer.from(b64Value, "base64");
305
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
305
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
306
306
  return key;
307
307
  }
308
308
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -637,6 +637,17 @@ function normalizeOrchestration(raw) {
637
637
  const userOrg = raw.orchestration ?? {};
638
638
  raw.orchestration = { ...defaultOrg, ...userOrg };
639
639
  }
640
+ function normalizeCloudEndpoint(raw) {
641
+ const cloud = raw.cloud;
642
+ if (!cloud?.endpoint) return;
643
+ const ep = String(cloud.endpoint);
644
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
645
+ cloud.endpoint = "https://cloud.askexe.com";
646
+ process.stderr.write(
647
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
648
+ );
649
+ }
650
+ }
640
651
  async function loadConfig() {
641
652
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
642
653
  await ensurePrivateDir(dir);
@@ -662,6 +673,7 @@ async function loadConfig() {
662
673
  normalizeSessionLifecycle(migratedCfg);
663
674
  normalizeAutoUpdate(migratedCfg);
664
675
  normalizeOrchestration(migratedCfg);
676
+ normalizeCloudEndpoint(migratedCfg);
665
677
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
666
678
  if (config.dbPath.startsWith("~")) {
667
679
  config.dbPath = config.dbPath.replace(/^~/, os2.homedir());