@askexenow/exe-os 0.9.113 → 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 (86) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +36 -12
  2. package/dist/bin/agentic-reflection-backfill.js +36 -12
  3. package/dist/bin/agentic-semantic-label.js +36 -12
  4. package/dist/bin/backfill-conversations.js +36 -12
  5. package/dist/bin/backfill-responses.js +36 -12
  6. package/dist/bin/backfill-vectors.js +36 -12
  7. package/dist/bin/bulk-sync-postgres.js +36 -12
  8. package/dist/bin/cleanup-stale-review-tasks.js +470 -113
  9. package/dist/bin/cli.js +413 -62
  10. package/dist/bin/exe-agent.js +27 -0
  11. package/dist/bin/exe-assign.js +36 -12
  12. package/dist/bin/exe-boot.js +246 -54
  13. package/dist/bin/exe-call.js +8 -0
  14. package/dist/bin/exe-cloud.js +47 -12
  15. package/dist/bin/exe-dispatch.js +348 -53
  16. package/dist/bin/exe-doctor.js +51 -13
  17. package/dist/bin/exe-export-behaviors.js +37 -12
  18. package/dist/bin/exe-forget.js +36 -12
  19. package/dist/bin/exe-gateway.js +348 -53
  20. package/dist/bin/exe-heartbeat.js +471 -113
  21. package/dist/bin/exe-kill.js +36 -12
  22. package/dist/bin/exe-launch-agent.js +117 -18
  23. package/dist/bin/exe-new-employee.js +9 -1
  24. package/dist/bin/exe-pending-messages.js +452 -95
  25. package/dist/bin/exe-pending-notifications.js +452 -95
  26. package/dist/bin/exe-pending-reviews.js +452 -95
  27. package/dist/bin/exe-rename.js +36 -12
  28. package/dist/bin/exe-review.js +36 -12
  29. package/dist/bin/exe-search.js +37 -12
  30. package/dist/bin/exe-session-cleanup.js +348 -53
  31. package/dist/bin/exe-settings.js +12 -0
  32. package/dist/bin/exe-start-codex.js +46 -13
  33. package/dist/bin/exe-start-opencode.js +46 -13
  34. package/dist/bin/exe-status.js +460 -114
  35. package/dist/bin/exe-support.js +12 -0
  36. package/dist/bin/exe-team.js +36 -12
  37. package/dist/bin/git-sweep.js +348 -53
  38. package/dist/bin/graph-backfill.js +36 -12
  39. package/dist/bin/graph-export.js +36 -12
  40. package/dist/bin/install.js +9 -1
  41. package/dist/bin/intercom-check.js +255 -53
  42. package/dist/bin/scan-tasks.js +348 -53
  43. package/dist/bin/setup.js +74 -12
  44. package/dist/bin/shard-migrate.js +36 -12
  45. package/dist/gateway/index.js +348 -53
  46. package/dist/hooks/bug-report-worker.js +348 -53
  47. package/dist/hooks/codex-stop-task-finalizer.js +308 -37
  48. package/dist/hooks/commit-complete.js +348 -53
  49. package/dist/hooks/error-recall.js +37 -12
  50. package/dist/hooks/ingest.js +363 -54
  51. package/dist/hooks/instructions-loaded.js +36 -12
  52. package/dist/hooks/notification.js +36 -12
  53. package/dist/hooks/post-compact.js +426 -72
  54. package/dist/hooks/post-tool-combined.js +501 -146
  55. package/dist/hooks/pre-compact.js +348 -53
  56. package/dist/hooks/pre-tool-use.js +92 -13
  57. package/dist/hooks/prompt-submit.js +348 -53
  58. package/dist/hooks/session-end.js +158 -53
  59. package/dist/hooks/session-start.js +66 -13
  60. package/dist/hooks/stop.js +420 -72
  61. package/dist/hooks/subagent-stop.js +419 -72
  62. package/dist/hooks/summary-worker.js +442 -121
  63. package/dist/index.js +375 -53
  64. package/dist/lib/agent-config.js +8 -0
  65. package/dist/lib/cloud-sync.js +35 -12
  66. package/dist/lib/config.js +13 -0
  67. package/dist/lib/consolidation.js +9 -1
  68. package/dist/lib/embedder.js +13 -0
  69. package/dist/lib/employees.js +8 -0
  70. package/dist/lib/exe-daemon.js +524 -60
  71. package/dist/lib/hybrid-search.js +37 -12
  72. package/dist/lib/keychain.js +25 -13
  73. package/dist/lib/messaging.js +395 -74
  74. package/dist/lib/schedules.js +36 -12
  75. package/dist/lib/skill-learning.js +21 -0
  76. package/dist/lib/store.js +36 -12
  77. package/dist/lib/tasks.js +324 -41
  78. package/dist/lib/tmux-routing.js +324 -41
  79. package/dist/mcp/server.js +374 -54
  80. package/dist/mcp/tools/create-task.js +324 -41
  81. package/dist/mcp/tools/list-tasks.js +406 -57
  82. package/dist/mcp/tools/send-message.js +395 -74
  83. package/dist/mcp/tools/update-task.js +324 -41
  84. package/dist/runtime/index.js +375 -53
  85. package/dist/tui/App.js +377 -55
  86. package/package.json +1 -1
@@ -154,6 +154,17 @@ function normalizeOrchestration(raw) {
154
154
  const userOrg = raw.orchestration ?? {};
155
155
  raw.orchestration = { ...defaultOrg, ...userOrg };
156
156
  }
157
+ function normalizeCloudEndpoint(raw) {
158
+ const cloud = raw.cloud;
159
+ if (!cloud?.endpoint) return;
160
+ const ep = String(cloud.endpoint);
161
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
162
+ cloud.endpoint = "https://cloud.askexe.com";
163
+ process.stderr.write(
164
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
165
+ );
166
+ }
167
+ }
157
168
  async function loadConfig() {
158
169
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
159
170
  await ensurePrivateDir(dir);
@@ -179,6 +190,7 @@ async function loadConfig() {
179
190
  normalizeSessionLifecycle(migratedCfg);
180
191
  normalizeAutoUpdate(migratedCfg);
181
192
  normalizeOrchestration(migratedCfg);
193
+ normalizeCloudEndpoint(migratedCfg);
182
194
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
183
195
  if (config.dbPath.startsWith("~")) {
184
196
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -207,6 +219,7 @@ function loadConfigSync() {
207
219
  normalizeSessionLifecycle(migratedCfg);
208
220
  normalizeAutoUpdate(migratedCfg);
209
221
  normalizeOrchestration(migratedCfg);
222
+ normalizeCloudEndpoint(migratedCfg);
210
223
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
211
224
  if (config.dbPath.startsWith("~")) {
212
225
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -435,6 +448,7 @@ __export(agent_config_exports, {
435
448
  clearAgentRuntime: () => clearAgentRuntime,
436
449
  getAgentRuntime: () => getAgentRuntime,
437
450
  loadAgentConfig: () => loadAgentConfig,
451
+ normalizeCcModelName: () => normalizeCcModelName,
438
452
  saveAgentConfig: () => saveAgentConfig,
439
453
  setAgentMcps: () => setAgentMcps,
440
454
  setAgentRuntime: () => setAgentRuntime
@@ -463,6 +477,13 @@ function getAgentRuntime(agentId) {
463
477
  if (orgDefault) return orgDefault;
464
478
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
465
479
  }
480
+ function normalizeCcModelName(model) {
481
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
482
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
483
+ ccModel += "[1m]";
484
+ }
485
+ return ccModel;
486
+ }
466
487
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
467
488
  const knownModels = KNOWN_RUNTIMES[runtime];
468
489
  if (!knownModels) {
@@ -1013,6 +1034,7 @@ var init_provider_table = __esm({
1013
1034
  // src/lib/intercom-queue.ts
1014
1035
  var intercom_queue_exports = {};
1015
1036
  __export(intercom_queue_exports, {
1037
+ _resetDrainGuard: () => _resetDrainGuard,
1016
1038
  clearQueueForAgent: () => clearQueueForAgent,
1017
1039
  drainForSession: () => drainForSession,
1018
1040
  drainQueue: () => drainQueue,
@@ -1058,38 +1080,47 @@ function queueIntercom(targetSession, reason) {
1058
1080
  writeQueue(queue);
1059
1081
  }
1060
1082
  function drainQueue(isSessionBusy2, sendKeys) {
1083
+ if (_draining) {
1084
+ logQueue("SKIP_DRAIN \u2014 previous drain still running (possible tmux hang)");
1085
+ return { drained: 0, failed: 0 };
1086
+ }
1061
1087
  const queue = readQueue();
1062
1088
  if (queue.length === 0) return { drained: 0, failed: 0 };
1089
+ _draining = true;
1063
1090
  const remaining = [];
1064
1091
  let drained = 0;
1065
1092
  let failed = 0;
1066
- for (const item of queue) {
1067
- const age = Date.now() - new Date(item.queuedAt).getTime();
1068
- if (age > TTL_MS) {
1069
- logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
1070
- failed++;
1071
- continue;
1072
- }
1073
- try {
1074
- if (!isSessionBusy2(item.targetSession)) {
1075
- const success = sendKeys(item.targetSession);
1076
- if (success) {
1077
- logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
1078
- drained++;
1079
- continue;
1093
+ try {
1094
+ for (const item of queue) {
1095
+ const age = Date.now() - new Date(item.queuedAt).getTime();
1096
+ if (age > TTL_MS) {
1097
+ logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
1098
+ failed++;
1099
+ continue;
1100
+ }
1101
+ try {
1102
+ if (!isSessionBusy2(item.targetSession)) {
1103
+ const success = sendKeys(item.targetSession);
1104
+ if (success) {
1105
+ logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
1106
+ drained++;
1107
+ continue;
1108
+ }
1080
1109
  }
1110
+ } catch {
1081
1111
  }
1082
- } catch {
1083
- }
1084
- item.attempts++;
1085
- if (item.attempts >= MAX_RETRIES) {
1086
- logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES} retries exhausted, reason: ${item.reason})`);
1087
- failed++;
1088
- continue;
1112
+ item.attempts++;
1113
+ if (item.attempts >= MAX_RETRIES) {
1114
+ logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES} retries exhausted, reason: ${item.reason})`);
1115
+ failed++;
1116
+ continue;
1117
+ }
1118
+ remaining.push(item);
1089
1119
  }
1090
- remaining.push(item);
1120
+ writeQueue(remaining);
1121
+ } finally {
1122
+ _draining = false;
1091
1123
  }
1092
- writeQueue(remaining);
1093
1124
  return { drained, failed };
1094
1125
  }
1095
1126
  function drainForSession(targetSession, sendKeys) {
@@ -1114,6 +1145,9 @@ function clearQueueForAgent(agentName) {
1114
1145
  logQueue(`CLEARED ${before - filtered.length} stale item(s) for ${agentName}`);
1115
1146
  }
1116
1147
  }
1148
+ function _resetDrainGuard() {
1149
+ _draining = false;
1150
+ }
1117
1151
  function logQueue(msg) {
1118
1152
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
1119
1153
  `;
@@ -1125,13 +1159,14 @@ function logQueue(msg) {
1125
1159
  } catch {
1126
1160
  }
1127
1161
  }
1128
- var QUEUE_PATH, MAX_RETRIES, TTL_MS, INTERCOM_LOG;
1162
+ var QUEUE_PATH, MAX_RETRIES, TTL_MS, _draining, INTERCOM_LOG;
1129
1163
  var init_intercom_queue = __esm({
1130
1164
  "src/lib/intercom-queue.ts"() {
1131
1165
  "use strict";
1132
1166
  QUEUE_PATH = path7.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1133
1167
  MAX_RETRIES = 5;
1134
1168
  TTL_MS = 60 * 60 * 1e3;
1169
+ _draining = false;
1135
1170
  INTERCOM_LOG = path7.join(os5.homedir(), ".exe-os", "intercom.log");
1136
1171
  }
1137
1172
  });
@@ -6072,7 +6107,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
6072
6107
  taskFile
6073
6108
  });
6074
6109
  const originalPriority = String(row.priority).toLowerCase();
6075
- const autoApprove = originalPriority === "p2" && result?.toLowerCase().includes("tests pass");
6110
+ const resultLower = result?.toLowerCase() ?? "";
6111
+ const hasTestEvidence = (
6112
+ // Vitest/Jest output patterns (hard to fake without actually running tests)
6113
+ /\d+\s+pass(ed|ing)/.test(resultLower) || /test files?\s+\d+\s+passed/.test(resultLower) || /tests?\s+\d+\s+passed/.test(resultLower)
6114
+ );
6115
+ const hasNoFailures = !/fail(ed|ure|ing)|error/i.test(resultLower);
6116
+ const autoApprove = originalPriority === "p2" && hasTestEvidence && hasNoFailures;
6076
6117
  if (!autoApprove) {
6077
6118
  try {
6078
6119
  const key = getSessionKey();
@@ -6080,6 +6121,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
6080
6121
  if (exeSession) {
6081
6122
  sendIntercom(exeSession);
6082
6123
  }
6124
+ if (reviewer && reviewer !== coordinatorName && reviewer !== exeSession) {
6125
+ const { employeeSessionName: employeeSessionName2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
6126
+ if (exeSession) {
6127
+ const reviewerSession = employeeSessionName2(reviewer, exeSession);
6128
+ sendIntercom(reviewerSession);
6129
+ }
6130
+ }
6083
6131
  } catch {
6084
6132
  }
6085
6133
  }
@@ -6855,6 +6903,20 @@ async function updateTask(input2) {
6855
6903
  notifyTaskDone();
6856
6904
  }
6857
6905
  await markTaskNotificationsRead(taskFile);
6906
+ if (input2.status === "needs_review" && !isCoordinator) {
6907
+ try {
6908
+ const { writeNotification: writeNotification2 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
6909
+ await writeNotification2({
6910
+ agentId: String(row.assigned_to),
6911
+ agentRole: String(row.assigned_to),
6912
+ event: "task_complete",
6913
+ project: String(row.project_name),
6914
+ summary: `"${String(row.title)}" is ready for review`,
6915
+ taskFile
6916
+ });
6917
+ } catch {
6918
+ }
6919
+ }
6858
6920
  if (input2.status === "done" || input2.status === "closed") {
6859
6921
  try {
6860
6922
  await cascadeUnblock(taskId, input2.baseDir, now);
@@ -7287,18 +7349,31 @@ function acquireSpawnLock2(sessionName) {
7287
7349
  mkdirSync10(SPAWN_LOCK_DIR, { recursive: true });
7288
7350
  }
7289
7351
  const lockFile = spawnLockPath(sessionName);
7290
- if (existsSync17(lockFile)) {
7291
- try {
7292
- const lock = JSON.parse(readFileSync14(lockFile, "utf8"));
7293
- const age = Date.now() - lock.timestamp;
7294
- if (isProcessAlive(lock.pid) && age < 6e4) {
7295
- return false;
7296
- }
7297
- } catch {
7352
+ const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
7353
+ const { openSync: openSync3, closeSync: closeSync3, writeSync } = __require("fs");
7354
+ const { constants } = __require("fs");
7355
+ try {
7356
+ const fd = openSync3(lockFile, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 420);
7357
+ writeSync(fd, lockData);
7358
+ closeSync3(fd);
7359
+ return true;
7360
+ } catch (err) {
7361
+ if (err?.code !== "EEXIST") {
7362
+ return true;
7298
7363
  }
7299
7364
  }
7300
- writeFileSync10(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
7301
- return true;
7365
+ try {
7366
+ const lock = JSON.parse(readFileSync14(lockFile, "utf8"));
7367
+ const age = Date.now() - lock.timestamp;
7368
+ if (isProcessAlive(lock.pid) && age < 6e4) {
7369
+ return false;
7370
+ }
7371
+ writeFileSync10(lockFile, lockData);
7372
+ return true;
7373
+ } catch {
7374
+ writeFileSync10(lockFile, lockData);
7375
+ return true;
7376
+ }
7302
7377
  }
7303
7378
  function releaseSpawnLock2(sessionName) {
7304
7379
  try {
@@ -7377,6 +7452,21 @@ function parseParentExe(sessionName, agentId) {
7377
7452
  function extractRootExe(name) {
7378
7453
  if (!name) return null;
7379
7454
  if (!name.includes("-")) return name;
7455
+ try {
7456
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
7457
+ if (roster.length > 0) {
7458
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
7459
+ for (const agentName of sortedNames) {
7460
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7461
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
7462
+ const match = name.match(regex);
7463
+ if (match) {
7464
+ return extractRootExe(match[1]);
7465
+ }
7466
+ }
7467
+ }
7468
+ } catch {
7469
+ }
7380
7470
  const parts = name.split("-").filter(Boolean);
7381
7471
  return parts.length > 0 ? parts[parts.length - 1] : null;
7382
7472
  }
@@ -7395,6 +7485,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
7395
7485
  function getParentExe(sessionKey) {
7396
7486
  try {
7397
7487
  const data = JSON.parse(readFileSync14(path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
7488
+ if (data.registeredAt) {
7489
+ const age = Date.now() - new Date(data.registeredAt).getTime();
7490
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
7491
+ }
7398
7492
  return data.parentExe || null;
7399
7493
  } catch {
7400
7494
  return null;
@@ -7943,7 +8037,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7943
8037
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
7944
8038
  } catch {
7945
8039
  }
7946
- let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName}`;
8040
+ let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName} EXE_SESSION_START_ISO=${(/* @__PURE__ */ new Date()).toISOString()}`;
7947
8041
  if (ccProvider !== DEFAULT_PROVIDER) {
7948
8042
  const cfg = PROVIDER_TABLE[ccProvider];
7949
8043
  if (cfg?.apiKeyEnv) {
@@ -7978,10 +8072,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7978
8072
  }
7979
8073
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7980
8074
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7981
- let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
7982
- if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
7983
- ccModel += "[1m]";
7984
- }
8075
+ const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
8076
+ const ccModel = normalizeCcModelName2(agentRtConfig.model);
7985
8077
  envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
7986
8078
  }
7987
8079
  }
@@ -8082,7 +8174,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8082
8174
  releaseSpawnLock2(sessionName);
8083
8175
  return { sessionName };
8084
8176
  }
8085
- 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;
8177
+ 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;
8086
8178
  var init_tmux_routing = __esm({
8087
8179
  "src/lib/tmux-routing.ts"() {
8088
8180
  "use strict";
@@ -8102,6 +8194,7 @@ var init_tmux_routing = __esm({
8102
8194
  SESSION_CACHE = path20.join(os12.homedir(), ".exe-os", "session-cache");
8103
8195
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
8104
8196
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
8197
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
8105
8198
  VERIFY_PANE_LINES = 200;
8106
8199
  INTERCOM_DEBOUNCE_MS = 3e4;
8107
8200
  CODEX_DEBOUNCE_MS = 12e4;
@@ -8146,7 +8239,7 @@ var init_task_scope = __esm({
8146
8239
  });
8147
8240
 
8148
8241
  // src/lib/keychain.ts
8149
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
8242
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2, rename, copyFile } from "fs/promises";
8150
8243
  import { existsSync as existsSync19, statSync as statSync4 } from "fs";
8151
8244
  import { execSync as execSync9 } from "child_process";
8152
8245
  import path22 from "path";
@@ -8181,12 +8274,14 @@ function linuxSecretAvailable() {
8181
8274
  function isRootOnlyTrustedServerKeyFile(keyPath) {
8182
8275
  if (process.platform !== "linux") return false;
8183
8276
  try {
8184
- const uid = typeof os13.userInfo().uid === "number" ? os13.userInfo().uid : -1;
8185
8277
  const st = statSync4(keyPath);
8186
8278
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
8279
+ const uid = typeof os13.userInfo().uid === "number" ? os13.userInfo().uid : -1;
8187
8280
  if (uid === 0) return true;
8188
8281
  const exeOsDir = process.env.EXE_OS_DIR;
8189
- return Boolean(exeOsDir && path22.resolve(keyPath).startsWith(path22.resolve(exeOsDir) + path22.sep));
8282
+ if (exeOsDir && path22.resolve(keyPath).startsWith(path22.resolve(exeOsDir) + path22.sep)) return true;
8283
+ if (!linuxSecretAvailable()) return true;
8284
+ return false;
8190
8285
  } catch {
8191
8286
  return false;
8192
8287
  }
@@ -8336,15 +8431,25 @@ async function writeMachineBoundFileFallback(b64) {
8336
8431
  await mkdir4(dir, { recursive: true });
8337
8432
  const keyPath = getKeyPath();
8338
8433
  const machineKey = deriveMachineKey();
8339
- if (machineKey) {
8340
- const encrypted = encryptWithMachineKey(b64, machineKey);
8341
- await writeFile5(keyPath, encrypted + "\n", "utf-8");
8342
- await chmod2(keyPath, 384);
8343
- return "encrypted";
8434
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
8435
+ const result = machineKey ? "encrypted" : "plaintext";
8436
+ const tmpPath = keyPath + ".tmp";
8437
+ try {
8438
+ if (existsSync19(keyPath)) {
8439
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
8440
+ });
8441
+ }
8442
+ await writeFile5(tmpPath, content, "utf-8");
8443
+ await chmod2(tmpPath, 384);
8444
+ await rename(tmpPath, keyPath);
8445
+ } catch (err) {
8446
+ try {
8447
+ await unlink(tmpPath);
8448
+ } catch {
8449
+ }
8450
+ throw err;
8344
8451
  }
8345
- await writeFile5(keyPath, b64 + "\n", "utf-8");
8346
- await chmod2(keyPath, 384);
8347
- return "plaintext";
8452
+ return result;
8348
8453
  }
8349
8454
  async function getMasterKey() {
8350
8455
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -8411,7 +8516,7 @@ async function getMasterKey() {
8411
8516
  b64Value = content;
8412
8517
  }
8413
8518
  const key = Buffer.from(b64Value, "base64");
8414
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
8519
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
8415
8520
  return key;
8416
8521
  }
8417
8522
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -154,6 +154,17 @@ function normalizeOrchestration(raw) {
154
154
  const userOrg = raw.orchestration ?? {};
155
155
  raw.orchestration = { ...defaultOrg, ...userOrg };
156
156
  }
157
+ function normalizeCloudEndpoint(raw) {
158
+ const cloud = raw.cloud;
159
+ if (!cloud?.endpoint) return;
160
+ const ep = String(cloud.endpoint);
161
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
162
+ cloud.endpoint = "https://cloud.askexe.com";
163
+ process.stderr.write(
164
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
165
+ );
166
+ }
167
+ }
157
168
  async function loadConfig() {
158
169
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
159
170
  await ensurePrivateDir(dir);
@@ -179,6 +190,7 @@ async function loadConfig() {
179
190
  normalizeSessionLifecycle(migratedCfg);
180
191
  normalizeAutoUpdate(migratedCfg);
181
192
  normalizeOrchestration(migratedCfg);
193
+ normalizeCloudEndpoint(migratedCfg);
182
194
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
183
195
  if (config.dbPath.startsWith("~")) {
184
196
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -207,6 +219,7 @@ function loadConfigSync() {
207
219
  normalizeSessionLifecycle(migratedCfg);
208
220
  normalizeAutoUpdate(migratedCfg);
209
221
  normalizeOrchestration(migratedCfg);
222
+ normalizeCloudEndpoint(migratedCfg);
210
223
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
211
224
  if (config.dbPath.startsWith("~")) {
212
225
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -422,6 +435,7 @@ __export(agent_config_exports, {
422
435
  clearAgentRuntime: () => clearAgentRuntime,
423
436
  getAgentRuntime: () => getAgentRuntime,
424
437
  loadAgentConfig: () => loadAgentConfig,
438
+ normalizeCcModelName: () => normalizeCcModelName,
425
439
  saveAgentConfig: () => saveAgentConfig,
426
440
  setAgentMcps: () => setAgentMcps,
427
441
  setAgentRuntime: () => setAgentRuntime
@@ -450,6 +464,13 @@ function getAgentRuntime(agentId) {
450
464
  if (orgDefault) return orgDefault;
451
465
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
452
466
  }
467
+ function normalizeCcModelName(model) {
468
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
469
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
470
+ ccModel += "[1m]";
471
+ }
472
+ return ccModel;
473
+ }
453
474
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
454
475
  const knownModels = KNOWN_RUNTIMES[runtime];
455
476
  if (!knownModels) {
@@ -3741,7 +3762,7 @@ var init_database = __esm({
3741
3762
  });
3742
3763
 
3743
3764
  // src/lib/keychain.ts
3744
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3765
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3745
3766
  import { existsSync as existsSync8, statSync as statSync3 } from "fs";
3746
3767
  import { execSync as execSync3 } from "child_process";
3747
3768
  import path7 from "path";
@@ -3776,12 +3797,14 @@ function linuxSecretAvailable() {
3776
3797
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3777
3798
  if (process.platform !== "linux") return false;
3778
3799
  try {
3779
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3780
3800
  const st = statSync3(keyPath);
3781
3801
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3802
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3782
3803
  if (uid === 0) return true;
3783
3804
  const exeOsDir = process.env.EXE_OS_DIR;
3784
- return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
3805
+ if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
3806
+ if (!linuxSecretAvailable()) return true;
3807
+ return false;
3785
3808
  } catch {
3786
3809
  return false;
3787
3810
  }
@@ -3931,15 +3954,25 @@ async function writeMachineBoundFileFallback(b64) {
3931
3954
  await mkdir3(dir, { recursive: true });
3932
3955
  const keyPath = getKeyPath();
3933
3956
  const machineKey = deriveMachineKey();
3934
- if (machineKey) {
3935
- const encrypted = encryptWithMachineKey(b64, machineKey);
3936
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3937
- await chmod2(keyPath, 384);
3938
- return "encrypted";
3957
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3958
+ const result = machineKey ? "encrypted" : "plaintext";
3959
+ const tmpPath = keyPath + ".tmp";
3960
+ try {
3961
+ if (existsSync8(keyPath)) {
3962
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3963
+ });
3964
+ }
3965
+ await writeFile3(tmpPath, content, "utf-8");
3966
+ await chmod2(tmpPath, 384);
3967
+ await rename(tmpPath, keyPath);
3968
+ } catch (err) {
3969
+ try {
3970
+ await unlink(tmpPath);
3971
+ } catch {
3972
+ }
3973
+ throw err;
3939
3974
  }
3940
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3941
- await chmod2(keyPath, 384);
3942
- return "plaintext";
3975
+ return result;
3943
3976
  }
3944
3977
  async function getMasterKey() {
3945
3978
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -4006,7 +4039,7 @@ async function getMasterKey() {
4006
4039
  b64Value = content;
4007
4040
  }
4008
4041
  const key = Buffer.from(b64Value, "base64");
4009
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
4042
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
4010
4043
  return key;
4011
4044
  }
4012
4045
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -8583,6 +8616,21 @@ function isRootSession(name) {
8583
8616
  function extractRootExe(name) {
8584
8617
  if (!name) return null;
8585
8618
  if (!name.includes("-")) return name;
8619
+ try {
8620
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
8621
+ if (roster.length > 0) {
8622
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
8623
+ for (const agentName of sortedNames) {
8624
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8625
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
8626
+ const match = name.match(regex);
8627
+ if (match) {
8628
+ return extractRootExe(match[1]);
8629
+ }
8630
+ }
8631
+ }
8632
+ } catch {
8633
+ }
8586
8634
  const parts = name.split("-").filter(Boolean);
8587
8635
  return parts.length > 0 ? parts[parts.length - 1] : null;
8588
8636
  }
@@ -8601,6 +8649,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8601
8649
  function getParentExe(sessionKey) {
8602
8650
  try {
8603
8651
  const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
8652
+ if (data.registeredAt) {
8653
+ const age = Date.now() - new Date(data.registeredAt).getTime();
8654
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
8655
+ }
8604
8656
  return data.parentExe || null;
8605
8657
  } catch {
8606
8658
  return null;
@@ -8673,7 +8725,7 @@ function resolveExeSession() {
8673
8725
  }
8674
8726
  return candidate;
8675
8727
  }
8676
- var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
8728
+ var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
8677
8729
  var init_tmux_routing = __esm({
8678
8730
  "src/lib/tmux-routing.ts"() {
8679
8731
  "use strict";
@@ -8691,6 +8743,7 @@ var init_tmux_routing = __esm({
8691
8743
  init_agent_symlinks();
8692
8744
  SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
8693
8745
  SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
8746
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
8694
8747
  INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
8695
8748
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
8696
8749
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;