@askexenow/exe-os 0.9.113 → 0.9.114

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 +24 -12
  2. package/dist/bin/agentic-reflection-backfill.js +24 -12
  3. package/dist/bin/agentic-semantic-label.js +24 -12
  4. package/dist/bin/backfill-conversations.js +24 -12
  5. package/dist/bin/backfill-responses.js +24 -12
  6. package/dist/bin/backfill-vectors.js +24 -12
  7. package/dist/bin/bulk-sync-postgres.js +24 -12
  8. package/dist/bin/cleanup-stale-review-tasks.js +24 -12
  9. package/dist/bin/cli.js +96 -22
  10. package/dist/bin/exe-agent.js +27 -0
  11. package/dist/bin/exe-assign.js +24 -12
  12. package/dist/bin/exe-boot.js +44 -15
  13. package/dist/bin/exe-call.js +8 -0
  14. package/dist/bin/exe-cloud.js +34 -11
  15. package/dist/bin/exe-dispatch.js +34 -16
  16. package/dist/bin/exe-doctor.js +24 -12
  17. package/dist/bin/exe-export-behaviors.js +24 -12
  18. package/dist/bin/exe-forget.js +24 -12
  19. package/dist/bin/exe-gateway.js +33 -15
  20. package/dist/bin/exe-heartbeat.js +24 -12
  21. package/dist/bin/exe-kill.js +24 -12
  22. package/dist/bin/exe-launch-agent.js +103 -17
  23. package/dist/bin/exe-new-employee.js +9 -1
  24. package/dist/bin/exe-pending-messages.js +24 -12
  25. package/dist/bin/exe-pending-notifications.js +24 -12
  26. package/dist/bin/exe-pending-reviews.js +24 -12
  27. package/dist/bin/exe-rename.js +24 -12
  28. package/dist/bin/exe-review.js +24 -12
  29. package/dist/bin/exe-search.js +24 -12
  30. package/dist/bin/exe-session-cleanup.js +33 -15
  31. package/dist/bin/exe-start-codex.js +33 -13
  32. package/dist/bin/exe-start-opencode.js +33 -13
  33. package/dist/bin/exe-status.js +24 -12
  34. package/dist/bin/exe-team.js +24 -12
  35. package/dist/bin/git-sweep.js +34 -16
  36. package/dist/bin/graph-backfill.js +24 -12
  37. package/dist/bin/graph-export.js +24 -12
  38. package/dist/bin/install.js +9 -1
  39. package/dist/bin/intercom-check.js +33 -15
  40. package/dist/bin/scan-tasks.js +34 -16
  41. package/dist/bin/setup.js +60 -11
  42. package/dist/bin/shard-migrate.js +24 -12
  43. package/dist/gateway/index.js +33 -15
  44. package/dist/hooks/bug-report-worker.js +33 -15
  45. package/dist/hooks/codex-stop-task-finalizer.js +32 -12
  46. package/dist/hooks/commit-complete.js +34 -16
  47. package/dist/hooks/error-recall.js +24 -12
  48. package/dist/hooks/ingest.js +33 -15
  49. package/dist/hooks/instructions-loaded.js +24 -12
  50. package/dist/hooks/notification.js +24 -12
  51. package/dist/hooks/post-compact.js +24 -12
  52. package/dist/hooks/post-tool-combined.js +24 -12
  53. package/dist/hooks/pre-compact.js +34 -16
  54. package/dist/hooks/pre-tool-use.js +58 -11
  55. package/dist/hooks/prompt-submit.js +33 -15
  56. package/dist/hooks/session-end.js +34 -16
  57. package/dist/hooks/session-start.js +32 -12
  58. package/dist/hooks/stop.js +24 -12
  59. package/dist/hooks/subagent-stop.js +24 -12
  60. package/dist/hooks/summary-worker.js +34 -11
  61. package/dist/index.js +60 -15
  62. package/dist/lib/agent-config.js +8 -0
  63. package/dist/lib/cloud-sync.js +34 -11
  64. package/dist/lib/consolidation.js +9 -1
  65. package/dist/lib/employees.js +8 -0
  66. package/dist/lib/exe-daemon.js +174 -17
  67. package/dist/lib/hybrid-search.js +24 -12
  68. package/dist/lib/keychain.js +24 -12
  69. package/dist/lib/schedules.js +24 -12
  70. package/dist/lib/skill-learning.js +8 -0
  71. package/dist/lib/store.js +24 -12
  72. package/dist/lib/tasks.js +10 -4
  73. package/dist/lib/tmux-routing.js +10 -4
  74. package/dist/mcp/server.js +44 -15
  75. package/dist/mcp/tools/create-task.js +10 -4
  76. package/dist/mcp/tools/update-task.js +10 -4
  77. package/dist/runtime/index.js +60 -15
  78. package/dist/tui/App.js +61 -16
  79. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -367,6 +367,7 @@ __export(agent_config_exports, {
367
367
  clearAgentRuntime: () => clearAgentRuntime,
368
368
  getAgentRuntime: () => getAgentRuntime,
369
369
  loadAgentConfig: () => loadAgentConfig,
370
+ normalizeCcModelName: () => normalizeCcModelName,
370
371
  saveAgentConfig: () => saveAgentConfig,
371
372
  setAgentMcps: () => setAgentMcps,
372
373
  setAgentRuntime: () => setAgentRuntime
@@ -395,6 +396,13 @@ function getAgentRuntime(agentId) {
395
396
  if (orgDefault) return orgDefault;
396
397
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
397
398
  }
399
+ function normalizeCcModelName(model) {
400
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
401
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
402
+ ccModel += "[1m]";
403
+ }
404
+ return ccModel;
405
+ }
398
406
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
399
407
  const knownModels = KNOWN_RUNTIMES[runtime];
400
408
  if (!knownModels) {
@@ -7869,10 +7877,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7869
7877
  }
7870
7878
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7871
7879
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7872
- let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
7873
- if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
7874
- ccModel += "[1m]";
7875
- }
7880
+ const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
7881
+ const ccModel = normalizeCcModelName2(agentRtConfig.model);
7876
7882
  envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
7877
7883
  }
7878
7884
  }
@@ -8004,7 +8010,7 @@ var init_tmux_routing = __esm({
8004
8010
  });
8005
8011
 
8006
8012
  // src/lib/keychain.ts
8007
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
8013
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2, rename, copyFile } from "fs/promises";
8008
8014
  import { existsSync as existsSync17, statSync as statSync3 } from "fs";
8009
8015
  import { execSync as execSync9 } from "child_process";
8010
8016
  import path20 from "path";
@@ -8039,12 +8045,14 @@ function linuxSecretAvailable() {
8039
8045
  function isRootOnlyTrustedServerKeyFile(keyPath) {
8040
8046
  if (process.platform !== "linux") return false;
8041
8047
  try {
8042
- const uid = typeof os13.userInfo().uid === "number" ? os13.userInfo().uid : -1;
8043
8048
  const st = statSync3(keyPath);
8044
8049
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
8050
+ const uid = typeof os13.userInfo().uid === "number" ? os13.userInfo().uid : -1;
8045
8051
  if (uid === 0) return true;
8046
8052
  const exeOsDir = process.env.EXE_OS_DIR;
8047
- return Boolean(exeOsDir && path20.resolve(keyPath).startsWith(path20.resolve(exeOsDir) + path20.sep));
8053
+ if (exeOsDir && path20.resolve(keyPath).startsWith(path20.resolve(exeOsDir) + path20.sep)) return true;
8054
+ if (!linuxSecretAvailable()) return true;
8055
+ return false;
8048
8056
  } catch {
8049
8057
  return false;
8050
8058
  }
@@ -8194,15 +8202,25 @@ async function writeMachineBoundFileFallback(b64) {
8194
8202
  await mkdir4(dir, { recursive: true });
8195
8203
  const keyPath = getKeyPath();
8196
8204
  const machineKey = deriveMachineKey();
8197
- if (machineKey) {
8198
- const encrypted = encryptWithMachineKey(b64, machineKey);
8199
- await writeFile5(keyPath, encrypted + "\n", "utf-8");
8200
- await chmod2(keyPath, 384);
8201
- return "encrypted";
8205
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
8206
+ const result = machineKey ? "encrypted" : "plaintext";
8207
+ const tmpPath = keyPath + ".tmp";
8208
+ try {
8209
+ if (existsSync17(keyPath)) {
8210
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
8211
+ });
8212
+ }
8213
+ await writeFile5(tmpPath, content, "utf-8");
8214
+ await chmod2(tmpPath, 384);
8215
+ await rename(tmpPath, keyPath);
8216
+ } catch (err) {
8217
+ try {
8218
+ await unlink(tmpPath);
8219
+ } catch {
8220
+ }
8221
+ throw err;
8202
8222
  }
8203
- await writeFile5(keyPath, b64 + "\n", "utf-8");
8204
- await chmod2(keyPath, 384);
8205
- return "plaintext";
8223
+ return result;
8206
8224
  }
8207
8225
  async function getMasterKey() {
8208
8226
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -13555,6 +13573,33 @@ var DANGEROUS_PATTERNS = [
13555
13573
  regex: /\bkill\s+-9\b/,
13556
13574
  severity: "warning",
13557
13575
  reason: "Force kill signal"
13576
+ },
13577
+ // MCP bypass — agents must use MCP tools, never access the DB directly.
13578
+ // These patterns catch attempts to work around a disconnected MCP server.
13579
+ {
13580
+ regex: /\bsqlite3\b.*\bmemories\.db\b/,
13581
+ severity: "critical",
13582
+ reason: "Direct SQLite access bypasses MCP contract boundary \u2014 use MCP tools"
13583
+ },
13584
+ {
13585
+ regex: /\bsqlite3\b.*\.exe-os\b/,
13586
+ severity: "critical",
13587
+ reason: "Direct SQLite access to exe-os database \u2014 use MCP tools"
13588
+ },
13589
+ {
13590
+ regex: /\bnode\s+-e\b.*\b(better-sqlite3|libsql|sqlite3)\b/,
13591
+ severity: "critical",
13592
+ reason: "Inline Node.js script accessing SQLite directly \u2014 use MCP tools"
13593
+ },
13594
+ {
13595
+ regex: /\brequire\s*\(\s*['"].*memories\.db['"]\s*\)/,
13596
+ severity: "critical",
13597
+ reason: "Direct require of memories database \u2014 use MCP tools"
13598
+ },
13599
+ {
13600
+ regex: /\bcat\b.*\bmemories\.db\b/,
13601
+ severity: "warning",
13602
+ reason: "Reading raw database file \u2014 encrypted data, use MCP tools instead"
13558
13603
  }
13559
13604
  ];
13560
13605
  function checkDangerousPatterns(command) {
@@ -175,6 +175,13 @@ function getAgentRuntime(agentId) {
175
175
  if (orgDefault) return orgDefault;
176
176
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
177
177
  }
178
+ function normalizeCcModelName(model) {
179
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
180
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
181
+ ccModel += "[1m]";
182
+ }
183
+ return ccModel;
184
+ }
178
185
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
179
186
  const knownModels = KNOWN_RUNTIMES[runtime];
180
187
  if (!knownModels) {
@@ -223,6 +230,7 @@ export {
223
230
  clearAgentRuntime,
224
231
  getAgentRuntime,
225
232
  loadAgentConfig,
233
+ normalizeCcModelName,
226
234
  saveAgentConfig,
227
235
  setAgentMcps,
228
236
  setAgentRuntime
@@ -3289,7 +3289,7 @@ __export(keychain_exports, {
3289
3289
  importMnemonic: () => importMnemonic,
3290
3290
  setMasterKey: () => setMasterKey
3291
3291
  });
3292
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3292
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3293
3293
  import { existsSync as existsSync9, statSync as statSync3 } from "fs";
3294
3294
  import { execSync as execSync3 } from "child_process";
3295
3295
  import path8 from "path";
@@ -3324,12 +3324,14 @@ function linuxSecretAvailable() {
3324
3324
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3325
3325
  if (process.platform !== "linux") return false;
3326
3326
  try {
3327
- const uid = typeof os6.userInfo().uid === "number" ? os6.userInfo().uid : -1;
3328
3327
  const st = statSync3(keyPath);
3329
3328
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3329
+ const uid = typeof os6.userInfo().uid === "number" ? os6.userInfo().uid : -1;
3330
3330
  if (uid === 0) return true;
3331
3331
  const exeOsDir = process.env.EXE_OS_DIR;
3332
- return Boolean(exeOsDir && path8.resolve(keyPath).startsWith(path8.resolve(exeOsDir) + path8.sep));
3332
+ if (exeOsDir && path8.resolve(keyPath).startsWith(path8.resolve(exeOsDir) + path8.sep)) return true;
3333
+ if (!linuxSecretAvailable()) return true;
3334
+ return false;
3333
3335
  } catch {
3334
3336
  return false;
3335
3337
  }
@@ -3479,15 +3481,25 @@ async function writeMachineBoundFileFallback(b64) {
3479
3481
  await mkdir3(dir, { recursive: true });
3480
3482
  const keyPath = getKeyPath();
3481
3483
  const machineKey = deriveMachineKey();
3482
- if (machineKey) {
3483
- const encrypted = encryptWithMachineKey(b64, machineKey);
3484
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3485
- await chmod2(keyPath, 384);
3486
- return "encrypted";
3484
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3485
+ const result = machineKey ? "encrypted" : "plaintext";
3486
+ const tmpPath = keyPath + ".tmp";
3487
+ try {
3488
+ if (existsSync9(keyPath)) {
3489
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3490
+ });
3491
+ }
3492
+ await writeFile3(tmpPath, content, "utf-8");
3493
+ await chmod2(tmpPath, 384);
3494
+ await rename(tmpPath, keyPath);
3495
+ } catch (err) {
3496
+ try {
3497
+ await unlink(tmpPath);
3498
+ } catch {
3499
+ }
3500
+ throw err;
3487
3501
  }
3488
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3489
- await chmod2(keyPath, 384);
3490
- return "plaintext";
3502
+ return result;
3491
3503
  }
3492
3504
  async function getMasterKey() {
3493
3505
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -4156,6 +4168,15 @@ async function fetchWithRetry(url, init) {
4156
4168
  }
4157
4169
  throw lastError;
4158
4170
  }
4171
+ function migrateEndpoint(endpoint) {
4172
+ if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
4173
+ process.stderr.write(
4174
+ "[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
4175
+ );
4176
+ return "https://cloud.askexe.com";
4177
+ }
4178
+ return endpoint;
4179
+ }
4159
4180
  function assertSecureEndpoint(endpoint) {
4160
4181
  if (endpoint.startsWith("https://")) return;
4161
4182
  if (endpoint.startsWith("http://")) {
@@ -4294,6 +4315,7 @@ async function markCloudReuploadRequired(client = getClient()) {
4294
4315
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
4295
4316
  }
4296
4317
  async function cloudSync(config) {
4318
+ config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
4297
4319
  if (!isSyncCryptoInitialized()) {
4298
4320
  try {
4299
4321
  const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
@@ -5404,6 +5426,7 @@ export {
5404
5426
  markCloudReuploadRequired,
5405
5427
  mergeConfig,
5406
5428
  mergeRosterFromRemote,
5429
+ migrateEndpoint,
5407
5430
  pushToPostgres,
5408
5431
  recordRosterDeletion
5409
5432
  };
@@ -183,6 +183,7 @@ __export(agent_config_exports, {
183
183
  clearAgentRuntime: () => clearAgentRuntime,
184
184
  getAgentRuntime: () => getAgentRuntime,
185
185
  loadAgentConfig: () => loadAgentConfig,
186
+ normalizeCcModelName: () => normalizeCcModelName,
186
187
  saveAgentConfig: () => saveAgentConfig,
187
188
  setAgentMcps: () => setAgentMcps,
188
189
  setAgentRuntime: () => setAgentRuntime
@@ -211,6 +212,13 @@ function getAgentRuntime(agentId) {
211
212
  if (orgDefault) return orgDefault;
212
213
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
213
214
  }
215
+ function normalizeCcModelName(model) {
216
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
217
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
218
+ ccModel += "[1m]";
219
+ }
220
+ return ccModel;
221
+ }
214
222
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
215
223
  const knownModels = KNOWN_RUNTIMES[runtime];
216
224
  if (!knownModels) {
@@ -581,7 +589,7 @@ init_memory();
581
589
  init_database();
582
590
 
583
591
  // src/lib/keychain.ts
584
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
592
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
585
593
  import { existsSync as existsSync6, statSync as statSync2 } from "fs";
586
594
  import { execSync as execSync2 } from "child_process";
587
595
  import path5 from "path";
@@ -169,6 +169,7 @@ __export(agent_config_exports, {
169
169
  clearAgentRuntime: () => clearAgentRuntime,
170
170
  getAgentRuntime: () => getAgentRuntime,
171
171
  loadAgentConfig: () => loadAgentConfig,
172
+ normalizeCcModelName: () => normalizeCcModelName,
172
173
  saveAgentConfig: () => saveAgentConfig,
173
174
  setAgentMcps: () => setAgentMcps,
174
175
  setAgentRuntime: () => setAgentRuntime
@@ -197,6 +198,13 @@ function getAgentRuntime(agentId) {
197
198
  if (orgDefault) return orgDefault;
198
199
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
199
200
  }
201
+ function normalizeCcModelName(model) {
202
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
203
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
204
+ ccModel += "[1m]";
205
+ }
206
+ return ccModel;
207
+ }
200
208
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
201
209
  const knownModels = KNOWN_RUNTIMES[runtime];
202
210
  if (!knownModels) {
@@ -567,6 +567,7 @@ __export(agent_config_exports, {
567
567
  clearAgentRuntime: () => clearAgentRuntime,
568
568
  getAgentRuntime: () => getAgentRuntime,
569
569
  loadAgentConfig: () => loadAgentConfig,
570
+ normalizeCcModelName: () => normalizeCcModelName,
570
571
  saveAgentConfig: () => saveAgentConfig,
571
572
  setAgentMcps: () => setAgentMcps,
572
573
  setAgentRuntime: () => setAgentRuntime
@@ -595,6 +596,13 @@ function getAgentRuntime(agentId) {
595
596
  if (orgDefault) return orgDefault;
596
597
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
597
598
  }
599
+ function normalizeCcModelName(model) {
600
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
601
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
602
+ ccModel += "[1m]";
603
+ }
604
+ return ccModel;
605
+ }
598
606
  function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
599
607
  const knownModels = KNOWN_RUNTIMES[runtime];
600
608
  if (!knownModels) {
@@ -5510,7 +5518,7 @@ __export(keychain_exports, {
5510
5518
  importMnemonic: () => importMnemonic,
5511
5519
  setMasterKey: () => setMasterKey
5512
5520
  });
5513
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5521
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
5514
5522
  import { existsSync as existsSync12, statSync as statSync5 } from "fs";
5515
5523
  import { execSync as execSync3 } from "child_process";
5516
5524
  import path11 from "path";
@@ -5545,12 +5553,14 @@ function linuxSecretAvailable() {
5545
5553
  function isRootOnlyTrustedServerKeyFile(keyPath) {
5546
5554
  if (process.platform !== "linux") return false;
5547
5555
  try {
5548
- const uid = typeof os6.userInfo().uid === "number" ? os6.userInfo().uid : -1;
5549
5556
  const st = statSync5(keyPath);
5550
5557
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
5558
+ const uid = typeof os6.userInfo().uid === "number" ? os6.userInfo().uid : -1;
5551
5559
  if (uid === 0) return true;
5552
5560
  const exeOsDir = process.env.EXE_OS_DIR;
5553
- return Boolean(exeOsDir && path11.resolve(keyPath).startsWith(path11.resolve(exeOsDir) + path11.sep));
5561
+ if (exeOsDir && path11.resolve(keyPath).startsWith(path11.resolve(exeOsDir) + path11.sep)) return true;
5562
+ if (!linuxSecretAvailable()) return true;
5563
+ return false;
5554
5564
  } catch {
5555
5565
  return false;
5556
5566
  }
@@ -5700,15 +5710,25 @@ async function writeMachineBoundFileFallback(b64) {
5700
5710
  await mkdir3(dir, { recursive: true });
5701
5711
  const keyPath = getKeyPath();
5702
5712
  const machineKey = deriveMachineKey();
5703
- if (machineKey) {
5704
- const encrypted = encryptWithMachineKey(b64, machineKey);
5705
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
5706
- await chmod2(keyPath, 384);
5707
- return "encrypted";
5713
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
5714
+ const result3 = machineKey ? "encrypted" : "plaintext";
5715
+ const tmpPath = keyPath + ".tmp";
5716
+ try {
5717
+ if (existsSync12(keyPath)) {
5718
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
5719
+ });
5720
+ }
5721
+ await writeFile3(tmpPath, content, "utf-8");
5722
+ await chmod2(tmpPath, 384);
5723
+ await rename(tmpPath, keyPath);
5724
+ } catch (err) {
5725
+ try {
5726
+ await unlink(tmpPath);
5727
+ } catch {
5728
+ }
5729
+ throw err;
5708
5730
  }
5709
- await writeFile3(keyPath, b64 + "\n", "utf-8");
5710
- await chmod2(keyPath, 384);
5711
- return "plaintext";
5731
+ return result3;
5712
5732
  }
5713
5733
  async function getMasterKey() {
5714
5734
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -13968,10 +13988,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
13968
13988
  }
13969
13989
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
13970
13990
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
13971
- let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
13972
- if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
13973
- ccModel += "[1m]";
13974
- }
13991
+ const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
13992
+ const ccModel = normalizeCcModelName2(agentRtConfig.model);
13975
13993
  envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
13976
13994
  }
13977
13995
  }
@@ -22316,6 +22334,7 @@ __export(daemon_orchestration_exports, {
22316
22334
  REVIEW_NUDGE_COOLDOWN_MS: () => REVIEW_NUDGE_COOLDOWN_MS,
22317
22335
  SESSION_CONTEXT_THRESHOLD_PCT: () => SESSION_CONTEXT_THRESHOLD_PCT,
22318
22336
  SESSION_TTL_HOURS: () => SESSION_TTL_HOURS,
22337
+ STUCK_TASK_GRACE_MS: () => STUCK_TASK_GRACE_MS,
22319
22338
  _resetAutoWakeState: () => _resetAutoWakeState,
22320
22339
  checkSessionTTL: () => checkSessionTTL,
22321
22340
  classifyTtlKillReason: () => classifyTtlKillReason,
@@ -22325,12 +22344,14 @@ __export(daemon_orchestration_exports, {
22325
22344
  createOrphanReaperRealDeps: () => createOrphanReaperRealDeps,
22326
22345
  createReviewNudgeRealDeps: () => createReviewNudgeRealDeps,
22327
22346
  createSessionTTLRealDeps: () => createSessionTTLRealDeps,
22347
+ createStuckTaskRealDeps: () => createStuckTaskRealDeps,
22328
22348
  loadNudgeState: () => loadNudgeState,
22329
22349
  pollIdleEmployees: () => pollIdleEmployees,
22330
22350
  pollIdleKill: () => pollIdleKill,
22331
22351
  pollOrphanedTasks: () => pollOrphanedTasks,
22332
22352
  pollReviewNudge: () => pollReviewNudge,
22333
22353
  reapOrphanedMcpProcesses: () => reapOrphanedMcpProcesses,
22354
+ releaseStuckTasks: () => releaseStuckTasks,
22334
22355
  saveNudgeState: () => saveNudgeState,
22335
22356
  shouldAutoWake: () => shouldAutoWake,
22336
22357
  shouldKillIdleSession: () => shouldKillIdleSession,
@@ -22818,6 +22839,86 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
22818
22839
  }
22819
22840
  return woken;
22820
22841
  }
22842
+ async function releaseStuckTasks(deps, nowMs = Date.now()) {
22843
+ let liveSessions;
22844
+ try {
22845
+ liveSessions = deps.listTmuxSessions();
22846
+ } catch {
22847
+ return [];
22848
+ }
22849
+ const liveAgents = /* @__PURE__ */ new Set();
22850
+ for (const session of liveSessions) {
22851
+ const agent = deps.parseAgentFromSession(session);
22852
+ if (agent) liveAgents.add(agent);
22853
+ }
22854
+ for (const session of liveSessions) {
22855
+ if (isExeSession(session)) liveAgents.add(session);
22856
+ }
22857
+ let tasks;
22858
+ try {
22859
+ tasks = await deps.queryInProgressTasks();
22860
+ } catch {
22861
+ return [];
22862
+ }
22863
+ const released = [];
22864
+ for (const t of tasks) {
22865
+ if (liveAgents.has(t.agentId)) continue;
22866
+ const updatedMs = new Date(t.updatedAt).getTime();
22867
+ if (isNaN(updatedMs) || nowMs - updatedMs < STUCK_TASK_GRACE_MS) continue;
22868
+ const ageMinutes = Math.round((nowMs - updatedMs) / 6e4);
22869
+ const reason = `[auto-release] Agent "${t.agentId}" session is dead and task has been in_progress for ${ageMinutes}m with no update. Marked blocked for triage.`;
22870
+ try {
22871
+ await deps.markTaskBlocked(t.taskId, reason);
22872
+ released.push(t.taskId);
22873
+ process.stderr.write(
22874
+ `[stuck-release] Task ${t.taskId} (${t.agentId}) \u2014 in_progress for ${ageMinutes}m, agent dead \u2192 blocked
22875
+ `
22876
+ );
22877
+ } catch (err) {
22878
+ process.stderr.write(
22879
+ `[stuck-release] Failed to release ${t.taskId}: ${err instanceof Error ? err.message : String(err)}
22880
+ `
22881
+ );
22882
+ }
22883
+ }
22884
+ return released;
22885
+ }
22886
+ function createStuckTaskRealDeps(getClient2) {
22887
+ return {
22888
+ listTmuxSessions: () => {
22889
+ const { listTmuxSessions: listTmuxSessions2 } = (init_tmux_status(), __toCommonJS(tmux_status_exports));
22890
+ return listTmuxSessions2();
22891
+ },
22892
+ queryInProgressTasks: async () => {
22893
+ const client = getClient2();
22894
+ const result3 = await client.execute({
22895
+ sql: `SELECT id, assigned_to, session_scope, updated_at FROM tasks
22896
+ WHERE status = 'in_progress'
22897
+ ORDER BY updated_at ASC`,
22898
+ args: []
22899
+ });
22900
+ return result3.rows.map((r) => ({
22901
+ taskId: String(r.id),
22902
+ agentId: String(r.assigned_to),
22903
+ sessionScope: r.session_scope ? String(r.session_scope) : null,
22904
+ updatedAt: String(r.updated_at)
22905
+ }));
22906
+ },
22907
+ markTaskBlocked: async (taskId, reason) => {
22908
+ const client = getClient2();
22909
+ await client.execute({
22910
+ sql: `UPDATE tasks SET status = 'blocked', result = ?, updated_at = ? WHERE id = ?`,
22911
+ args: [reason, (/* @__PURE__ */ new Date()).toISOString(), taskId]
22912
+ });
22913
+ },
22914
+ parseAgentFromSession: (sessionName) => {
22915
+ const { baseAgentName: baseAgentName2 } = (init_employees(), __toCommonJS(employees_exports));
22916
+ if (!sessionName.includes("-")) return null;
22917
+ const agentPart = sessionName.split("-")[0];
22918
+ return baseAgentName2(agentPart);
22919
+ }
22920
+ };
22921
+ }
22821
22922
  function createAutoWakeRealDeps(getClient2, projectDir) {
22822
22923
  return {
22823
22924
  listTmuxSessions: () => {
@@ -22910,7 +23011,7 @@ function createOrphanReaperRealDeps() {
22910
23011
  selfPid: process.pid
22911
23012
  };
22912
23013
  }
22913
- var IDLE_NUDGE_DEDUP_MS, SESSION_TTL_HOURS, SESSION_CONTEXT_THRESHOLD_PCT, IDLE_KILL_INTERCOM_ACK_WINDOW_MS, REVIEW_NUDGE_COOLDOWN_MS, NUDGE_STATE_PATH, AUTO_WAKE_COOLDOWN_MS, AUTO_WAKE_MAX_RETRIES, _autoWakeLastSpawn, _autoWakeTaskRetries, ORPHAN_SIGKILL_DELAY_MS, ORPHAN_PATTERNS;
23014
+ var IDLE_NUDGE_DEDUP_MS, SESSION_TTL_HOURS, SESSION_CONTEXT_THRESHOLD_PCT, IDLE_KILL_INTERCOM_ACK_WINDOW_MS, REVIEW_NUDGE_COOLDOWN_MS, NUDGE_STATE_PATH, AUTO_WAKE_COOLDOWN_MS, AUTO_WAKE_MAX_RETRIES, _autoWakeLastSpawn, _autoWakeTaskRetries, STUCK_TASK_GRACE_MS, ORPHAN_SIGKILL_DELAY_MS, ORPHAN_PATTERNS;
22914
23015
  var init_daemon_orchestration = __esm({
22915
23016
  "src/lib/daemon-orchestration.ts"() {
22916
23017
  "use strict";
@@ -22927,6 +23028,7 @@ var init_daemon_orchestration = __esm({
22927
23028
  AUTO_WAKE_MAX_RETRIES = 3;
22928
23029
  _autoWakeLastSpawn = /* @__PURE__ */ new Map();
22929
23030
  _autoWakeTaskRetries = /* @__PURE__ */ new Map();
23031
+ STUCK_TASK_GRACE_MS = 30 * 60 * 1e3;
22930
23032
  ORPHAN_SIGKILL_DELAY_MS = 5e3;
22931
23033
  ORPHAN_PATTERNS = [
22932
23034
  "exe-os/dist/mcp/server.js",
@@ -25089,6 +25191,7 @@ __export(cloud_sync_exports, {
25089
25191
  markCloudReuploadRequired: () => markCloudReuploadRequired,
25090
25192
  mergeConfig: () => mergeConfig,
25091
25193
  mergeRosterFromRemote: () => mergeRosterFromRemote,
25194
+ migrateEndpoint: () => migrateEndpoint,
25092
25195
  pushToPostgres: () => pushToPostgres,
25093
25196
  recordRosterDeletion: () => recordRosterDeletion
25094
25197
  });
@@ -25259,6 +25362,15 @@ async function fetchWithRetry(url, init) {
25259
25362
  }
25260
25363
  throw lastError;
25261
25364
  }
25365
+ function migrateEndpoint(endpoint2) {
25366
+ if (endpoint2 === "https://askexe.com/cloud" || endpoint2 === "https://askexe.com/cloud/") {
25367
+ process.stderr.write(
25368
+ "[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
25369
+ );
25370
+ return "https://cloud.askexe.com";
25371
+ }
25372
+ return endpoint2;
25373
+ }
25262
25374
  function assertSecureEndpoint(endpoint2) {
25263
25375
  if (endpoint2.startsWith("https://")) return;
25264
25376
  if (endpoint2.startsWith("http://")) {
@@ -25396,6 +25508,7 @@ async function markCloudReuploadRequired(client = getClient()) {
25396
25508
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
25397
25509
  }
25398
25510
  async function cloudSync(config2) {
25511
+ config2 = { ...config2, endpoint: migrateEndpoint(config2.endpoint) };
25399
25512
  if (!isSyncCryptoInitialized()) {
25400
25513
  try {
25401
25514
  const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
@@ -37163,7 +37276,25 @@ async function startMcpHttpServer() {
37163
37276
  }
37164
37277
  const agentId = req.headers["x-agent-id"] || "default";
37165
37278
  const agentRole = req.headers["x-agent-role"] || "employee";
37166
- const sessionHint = req.headers["x-exe-session"] || "";
37279
+ let sessionHint = req.headers["x-exe-session"] || "";
37280
+ if (sessionHint.includes("$(") || sessionHint.includes("#{") || sessionHint.includes("tmux")) {
37281
+ sessionHint = "";
37282
+ }
37283
+ if (!sessionHint) {
37284
+ try {
37285
+ const { getTransport: getTransport2 } = (init_transport(), __toCommonJS(transport_exports));
37286
+ const transport2 = getTransport2();
37287
+ const liveSessions = transport2.listSessions();
37288
+ const agentSessions = liveSessions.filter((s) => s.startsWith(agentId + "-"));
37289
+ if (agentSessions.length === 1) {
37290
+ sessionHint = agentSessions[0];
37291
+ } else {
37292
+ const roots = liveSessions.filter((s) => !s.includes("-") && /^[a-z]+\d*$/.test(s));
37293
+ if (roots.length === 1) sessionHint = roots[0];
37294
+ }
37295
+ } catch {
37296
+ }
37297
+ }
37167
37298
  const runtime = inferMcpRuntime({
37168
37299
  runtimeHeader: req.headers["x-agent-runtime"] || req.headers["x-runtime"],
37169
37300
  userAgent: req.headers["user-agent"],
@@ -37871,6 +38002,31 @@ function startAutoWake() {
37871
38002
  process.stderr.write(`[exed] Auto-wake started (every ${AUTO_WAKE_INTERVAL_MS / 1e3}s)
37872
38003
  `);
37873
38004
  }
38005
+ var STUCK_TASK_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
38006
+ function startStuckTaskRelease() {
38007
+ const tick = async () => {
38008
+ fired("stuck_task_release");
38009
+ if (!await ensureStoreForPolling()) return;
38010
+ try {
38011
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
38012
+ const { releaseStuckTasks: releaseStuckTasks2, createStuckTaskRealDeps: createStuckTaskRealDeps2 } = await Promise.resolve().then(() => (init_daemon_orchestration(), daemon_orchestration_exports));
38013
+ const deps = createStuckTaskRealDeps2(getClient2);
38014
+ const released = await releaseStuckTasks2(deps);
38015
+ if (released.length > 0) {
38016
+ acted("stuck_task_release");
38017
+ process.stderr.write(`[exed] Stuck-release: ${released.length} task(s) moved to blocked
38018
+ `);
38019
+ }
38020
+ } catch (err) {
38021
+ process.stderr.write(`[exed] Stuck-release error: ${err instanceof Error ? err.message : String(err)}
38022
+ `);
38023
+ }
38024
+ };
38025
+ const timer = setInterval(() => void tick(), STUCK_TASK_CHECK_INTERVAL_MS);
38026
+ timer.unref();
38027
+ process.stderr.write(`[exed] Stuck-task release started (every ${STUCK_TASK_CHECK_INTERVAL_MS / 1e3}s)
38028
+ `);
38029
+ }
37874
38030
  var TOTAL_MEM_GB = os24.totalmem() / 1024 ** 3;
37875
38031
  var RSS_WARN_BYTES = Number(process.env.EXE_RSS_WARN_MB) * 1024 * 1024 || (TOTAL_MEM_GB >= 64 ? 6 * 1024 ** 3 : TOTAL_MEM_GB >= 32 ? 4 * 1024 ** 3 : TOTAL_MEM_GB >= 16 ? 2.5 * 1024 ** 3 : 1.5 * 1024 ** 3);
37876
38032
  var RSS_RESTART_BYTES = Number(process.env.EXE_RSS_RESTART_MB) * 1024 * 1024 || (TOTAL_MEM_GB >= 64 ? 8 * 1024 ** 3 : TOTAL_MEM_GB >= 32 ? 6 * 1024 ** 3 : TOTAL_MEM_GB >= 16 ? 4 * 1024 ** 3 : 3 * 1024 ** 3);
@@ -38211,6 +38367,7 @@ try {
38211
38367
  startOrphanReaper();
38212
38368
  startAgentStats();
38213
38369
  startAutoWake();
38370
+ startStuckTaskRelease();
38214
38371
  startGraphExtraction();
38215
38372
  startMemoryQueueDrain();
38216
38373
  startIntercomQueueDrain();
@@ -3314,7 +3314,7 @@ var init_database = __esm({
3314
3314
  });
3315
3315
 
3316
3316
  // src/lib/keychain.ts
3317
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3317
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3318
3318
  import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3319
3319
  import { execSync as execSync3 } from "child_process";
3320
3320
  import path6 from "path";
@@ -3349,12 +3349,14 @@ function linuxSecretAvailable() {
3349
3349
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3350
3350
  if (process.platform !== "linux") return false;
3351
3351
  try {
3352
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3353
3352
  const st = statSync3(keyPath);
3354
3353
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3354
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3355
3355
  if (uid === 0) return true;
3356
3356
  const exeOsDir = process.env.EXE_OS_DIR;
3357
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3357
+ if (exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep)) return true;
3358
+ if (!linuxSecretAvailable()) return true;
3359
+ return false;
3358
3360
  } catch {
3359
3361
  return false;
3360
3362
  }
@@ -3504,15 +3506,25 @@ async function writeMachineBoundFileFallback(b64) {
3504
3506
  await mkdir3(dir, { recursive: true });
3505
3507
  const keyPath = getKeyPath();
3506
3508
  const machineKey = deriveMachineKey();
3507
- if (machineKey) {
3508
- const encrypted = encryptWithMachineKey(b64, machineKey);
3509
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3510
- await chmod2(keyPath, 384);
3511
- return "encrypted";
3512
- }
3513
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3514
- await chmod2(keyPath, 384);
3515
- return "plaintext";
3509
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3510
+ const result = machineKey ? "encrypted" : "plaintext";
3511
+ const tmpPath = keyPath + ".tmp";
3512
+ try {
3513
+ if (existsSync7(keyPath)) {
3514
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3515
+ });
3516
+ }
3517
+ await writeFile3(tmpPath, content, "utf-8");
3518
+ await chmod2(tmpPath, 384);
3519
+ await rename(tmpPath, keyPath);
3520
+ } catch (err) {
3521
+ try {
3522
+ await unlink(tmpPath);
3523
+ } catch {
3524
+ }
3525
+ throw err;
3526
+ }
3527
+ return result;
3516
3528
  }
3517
3529
  async function getMasterKey() {
3518
3530
  let nativeValue = macKeychainGet() ?? linuxSecretGet();