@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
@@ -218,6 +218,17 @@ function normalizeOrchestration(raw) {
218
218
  const userOrg = raw.orchestration ?? {};
219
219
  raw.orchestration = { ...defaultOrg, ...userOrg };
220
220
  }
221
+ function normalizeCloudEndpoint(raw) {
222
+ const cloud = raw.cloud;
223
+ if (!cloud?.endpoint) return;
224
+ const ep = String(cloud.endpoint);
225
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
226
+ cloud.endpoint = "https://cloud.askexe.com";
227
+ process.stderr.write(
228
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
229
+ );
230
+ }
231
+ }
221
232
  async function loadConfig() {
222
233
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
223
234
  await ensurePrivateDir(dir);
@@ -243,6 +254,7 @@ async function loadConfig() {
243
254
  normalizeSessionLifecycle(migratedCfg);
244
255
  normalizeAutoUpdate(migratedCfg);
245
256
  normalizeOrchestration(migratedCfg);
257
+ normalizeCloudEndpoint(migratedCfg);
246
258
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
247
259
  if (config.dbPath.startsWith("~")) {
248
260
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -271,6 +283,7 @@ function loadConfigSync() {
271
283
  normalizeSessionLifecycle(migratedCfg);
272
284
  normalizeAutoUpdate(migratedCfg);
273
285
  normalizeOrchestration(migratedCfg);
286
+ normalizeCloudEndpoint(migratedCfg);
274
287
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
275
288
  if (config.dbPath.startsWith("~")) {
276
289
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -394,11 +407,176 @@ var init_config = __esm({
394
407
  }
395
408
  });
396
409
 
410
+ // src/lib/runtime-table.ts
411
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
412
+ var init_runtime_table = __esm({
413
+ "src/lib/runtime-table.ts"() {
414
+ "use strict";
415
+ RUNTIME_TABLE = {
416
+ codex: {
417
+ binary: "codex",
418
+ launchMode: "interactive",
419
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
420
+ inlineFlag: "--no-alt-screen",
421
+ apiKeyEnv: "OPENAI_API_KEY",
422
+ defaultModel: "gpt-5.5"
423
+ },
424
+ opencode: {
425
+ binary: "opencode",
426
+ launchMode: "exec",
427
+ autoApproveFlag: "--dangerously-skip-permissions",
428
+ inlineFlag: "",
429
+ apiKeyEnv: "ANTHROPIC_API_KEY",
430
+ defaultModel: "anthropic/claude-sonnet-4-6"
431
+ }
432
+ };
433
+ DEFAULT_RUNTIME = "claude";
434
+ }
435
+ });
436
+
437
+ // src/lib/agent-config.ts
438
+ var agent_config_exports = {};
439
+ __export(agent_config_exports, {
440
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
441
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
442
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
443
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
444
+ clearAgentRuntime: () => clearAgentRuntime,
445
+ getAgentRuntime: () => getAgentRuntime,
446
+ loadAgentConfig: () => loadAgentConfig,
447
+ normalizeCcModelName: () => normalizeCcModelName,
448
+ saveAgentConfig: () => saveAgentConfig,
449
+ setAgentMcps: () => setAgentMcps,
450
+ setAgentRuntime: () => setAgentRuntime
451
+ });
452
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
453
+ import path2 from "path";
454
+ function loadAgentConfig() {
455
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
456
+ try {
457
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
458
+ } catch {
459
+ return {};
460
+ }
461
+ }
462
+ function saveAgentConfig(config) {
463
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
464
+ ensurePrivateDirSync(dir);
465
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
466
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
467
+ }
468
+ function getAgentRuntime(agentId) {
469
+ const config = loadAgentConfig();
470
+ const entry = config[agentId];
471
+ if (entry) return entry;
472
+ const orgDefault = config["default"];
473
+ if (orgDefault) return orgDefault;
474
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
475
+ }
476
+ function normalizeCcModelName(model) {
477
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
478
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
479
+ ccModel += "[1m]";
480
+ }
481
+ return ccModel;
482
+ }
483
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
484
+ const knownModels = KNOWN_RUNTIMES[runtime];
485
+ if (!knownModels) {
486
+ return {
487
+ ok: false,
488
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
489
+ };
490
+ }
491
+ if (!knownModels.includes(model)) {
492
+ return {
493
+ ok: false,
494
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
495
+ };
496
+ }
497
+ const config = loadAgentConfig();
498
+ const existing = config[agentId];
499
+ const entry = { runtime, model };
500
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
501
+ if (mcps !== void 0) {
502
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
503
+ } else if (existing?.mcps) {
504
+ entry.mcps = existing.mcps;
505
+ }
506
+ config[agentId] = entry;
507
+ saveAgentConfig(config);
508
+ return { ok: true };
509
+ }
510
+ function setAgentMcps(agentId, mcps) {
511
+ const config = loadAgentConfig();
512
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
513
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
514
+ config[agentId] = existing;
515
+ saveAgentConfig(config);
516
+ return { ok: true };
517
+ }
518
+ function clearAgentRuntime(agentId) {
519
+ const config = loadAgentConfig();
520
+ delete config[agentId];
521
+ saveAgentConfig(config);
522
+ }
523
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
524
+ var init_agent_config = __esm({
525
+ "src/lib/agent-config.ts"() {
526
+ "use strict";
527
+ init_config();
528
+ init_runtime_table();
529
+ init_secure_files();
530
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
531
+ KNOWN_RUNTIMES = {
532
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
533
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
534
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
535
+ };
536
+ RUNTIME_LABELS = {
537
+ claude: "Claude Code (Anthropic)",
538
+ codex: "Codex (OpenAI)",
539
+ opencode: "OpenCode (open source)"
540
+ };
541
+ DEFAULT_MODELS = {
542
+ claude: "claude-opus-4.6",
543
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
544
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
545
+ };
546
+ }
547
+ });
548
+
397
549
  // src/lib/employees.ts
550
+ var employees_exports = {};
551
+ __export(employees_exports, {
552
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
553
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
554
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
555
+ addEmployee: () => addEmployee,
556
+ baseAgentName: () => baseAgentName,
557
+ canCoordinate: () => canCoordinate,
558
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
559
+ getCoordinatorName: () => getCoordinatorName,
560
+ getEmployee: () => getEmployee,
561
+ getEmployeeByRole: () => getEmployeeByRole,
562
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
563
+ hasRole: () => hasRole,
564
+ hireEmployee: () => hireEmployee,
565
+ isCoordinatorName: () => isCoordinatorName,
566
+ isCoordinatorRole: () => isCoordinatorRole,
567
+ isMultiInstance: () => isMultiInstance,
568
+ loadEmployees: () => loadEmployees,
569
+ loadEmployeesSync: () => loadEmployeesSync,
570
+ normalizeRole: () => normalizeRole,
571
+ normalizeRosterCase: () => normalizeRosterCase,
572
+ registerBinSymlinks: () => registerBinSymlinks,
573
+ saveEmployees: () => saveEmployees,
574
+ validateEmployeeName: () => validateEmployeeName
575
+ });
398
576
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
399
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
577
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
400
578
  import { execSync } from "child_process";
401
- import path2 from "path";
579
+ import path3 from "path";
402
580
  import os2 from "os";
403
581
  function normalizeRole(role) {
404
582
  return (role ?? "").trim().toLowerCase();
@@ -419,8 +597,23 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
419
597
  function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
420
598
  return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
421
599
  }
600
+ function validateEmployeeName(name) {
601
+ if (!name) {
602
+ return { valid: false, error: "Name is required" };
603
+ }
604
+ if (name.length > 32) {
605
+ return { valid: false, error: "Name must be 32 characters or fewer" };
606
+ }
607
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
608
+ return {
609
+ valid: false,
610
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
611
+ };
612
+ }
613
+ return { valid: true };
614
+ }
422
615
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
423
- if (!existsSync3(employeesPath)) {
616
+ if (!existsSync4(employeesPath)) {
424
617
  return [];
425
618
  }
426
619
  const raw = await readFile2(employeesPath, "utf-8");
@@ -431,17 +624,131 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
431
624
  }
432
625
  }
433
626
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
434
- await mkdir2(path2.dirname(employeesPath), { recursive: true });
627
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
435
628
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
436
629
  }
437
630
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
438
- if (!existsSync3(employeesPath)) return [];
631
+ if (!existsSync4(employeesPath)) return [];
439
632
  try {
440
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
633
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
441
634
  } catch {
442
635
  return [];
443
636
  }
444
637
  }
638
+ function getEmployee(employees, name) {
639
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
640
+ }
641
+ function getEmployeeByRole(employees, role) {
642
+ const lower = role.toLowerCase();
643
+ return employees.find((e) => e.role.toLowerCase() === lower);
644
+ }
645
+ function getEmployeeNamesByRole(employees, role) {
646
+ const lower = role.toLowerCase();
647
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
648
+ }
649
+ function hasRole(agentName, role) {
650
+ const employees = loadEmployeesSync();
651
+ const emp = getEmployee(employees, agentName);
652
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
653
+ }
654
+ function baseAgentName(name, employees) {
655
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
656
+ if (!match) return name;
657
+ const base = match[1];
658
+ const roster = employees ?? loadEmployeesSync();
659
+ if (getEmployee(roster, base)) return base;
660
+ return name;
661
+ }
662
+ function isMultiInstance(agentName, employees) {
663
+ const roster = employees ?? loadEmployeesSync();
664
+ const emp = getEmployee(roster, agentName);
665
+ if (!emp) return false;
666
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
667
+ }
668
+ function addEmployee(employees, employee) {
669
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
670
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
671
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
672
+ throw new Error(`Employee '${normalized.name}' already exists`);
673
+ }
674
+ return [...employees, normalized];
675
+ }
676
+ function appendToCoordinatorTeam(employee) {
677
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
678
+ if (!coordinator) return;
679
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
680
+ if (!existsSync4(idPath)) return;
681
+ const content = readFileSync3(idPath, "utf-8");
682
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
683
+ const teamMatch = content.match(TEAM_SECTION_RE);
684
+ if (!teamMatch || teamMatch.index === void 0) return;
685
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
686
+ const nextHeading = afterTeam.match(/\n## /);
687
+ const entry = `
688
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
689
+ `;
690
+ let updated;
691
+ if (nextHeading && nextHeading.index !== void 0) {
692
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
693
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
694
+ } else {
695
+ updated = content.trimEnd() + "\n" + entry;
696
+ }
697
+ writeFileSync2(idPath, updated, "utf-8");
698
+ }
699
+ function capitalize(s) {
700
+ return s.charAt(0).toUpperCase() + s.slice(1);
701
+ }
702
+ async function hireEmployee(employee) {
703
+ const employees = await loadEmployees();
704
+ const updated = addEmployee(employees, employee);
705
+ await saveEmployees(updated);
706
+ try {
707
+ appendToCoordinatorTeam(employee);
708
+ } catch {
709
+ }
710
+ try {
711
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
712
+ const config = loadAgentConfig2();
713
+ const name = employee.name.toLowerCase();
714
+ if (!config[name] && config["default"]) {
715
+ config[name] = { ...config["default"] };
716
+ saveAgentConfig2(config);
717
+ }
718
+ } catch {
719
+ }
720
+ return updated;
721
+ }
722
+ async function normalizeRosterCase(rosterPath) {
723
+ const employees = await loadEmployees(rosterPath);
724
+ let changed = false;
725
+ for (const emp of employees) {
726
+ if (emp.name !== emp.name.toLowerCase()) {
727
+ const oldName = emp.name;
728
+ emp.name = emp.name.toLowerCase();
729
+ changed = true;
730
+ try {
731
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
732
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
733
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
734
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
735
+ renameSync2(oldPath, newPath);
736
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
737
+ const content = readFileSync3(oldPath, "utf-8");
738
+ writeFileSync2(newPath, content, "utf-8");
739
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
740
+ unlinkSync(oldPath);
741
+ }
742
+ }
743
+ } catch {
744
+ }
745
+ }
746
+ }
747
+ if (changed) {
748
+ await saveEmployees(employees, rosterPath);
749
+ }
750
+ return changed;
751
+ }
445
752
  function findExeBin() {
446
753
  try {
447
754
  return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
@@ -458,7 +765,7 @@ function registerBinSymlinks(name) {
458
765
  errors.push("Could not find 'exe-os' in PATH");
459
766
  return { created, skipped, errors };
460
767
  }
461
- const binDir = path2.dirname(exeBinPath);
768
+ const binDir = path3.dirname(exeBinPath);
462
769
  let target;
463
770
  try {
464
771
  target = readlinkSync(exeBinPath);
@@ -468,8 +775,8 @@ function registerBinSymlinks(name) {
468
775
  }
469
776
  for (const suffix of ["", "-opencode"]) {
470
777
  const linkName = `${name}${suffix}`;
471
- const linkPath = path2.join(binDir, linkName);
472
- if (existsSync3(linkPath)) {
778
+ const linkPath = path3.join(binDir, linkName);
779
+ if (existsSync4(linkPath)) {
473
780
  skipped.push(linkName);
474
781
  continue;
475
782
  }
@@ -482,21 +789,23 @@ function registerBinSymlinks(name) {
482
789
  }
483
790
  return { created, skipped, errors };
484
791
  }
485
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
792
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
486
793
  var init_employees = __esm({
487
794
  "src/lib/employees.ts"() {
488
795
  "use strict";
489
796
  init_config();
490
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
797
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
491
798
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
492
799
  COORDINATOR_ROLE = "COO";
493
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
800
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
801
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
802
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
494
803
  }
495
804
  });
496
805
 
497
806
  // src/lib/database-adapter.ts
498
807
  import os3 from "os";
499
- import path3 from "path";
808
+ import path4 from "path";
500
809
  import { createRequire } from "module";
501
810
  import { pathToFileURL } from "url";
502
811
  function quotedIdentifier(identifier) {
@@ -807,8 +1116,8 @@ async function loadPrismaClient() {
807
1116
  }
808
1117
  return new PrismaClient2();
809
1118
  }
810
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
811
- const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
1119
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1120
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
812
1121
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
813
1122
  const module = await import(pathToFileURL(prismaEntry).href);
814
1123
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -1080,8 +1389,8 @@ var init_database_adapter = __esm({
1080
1389
 
1081
1390
  // src/lib/daemon-auth.ts
1082
1391
  import crypto from "crypto";
1083
- import path4 from "path";
1084
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1392
+ import path5 from "path";
1393
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1085
1394
  function normalizeToken(token) {
1086
1395
  if (!token) return null;
1087
1396
  const trimmed = token.trim();
@@ -1089,8 +1398,8 @@ function normalizeToken(token) {
1089
1398
  }
1090
1399
  function readDaemonToken() {
1091
1400
  try {
1092
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
1093
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1401
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1402
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
1094
1403
  } catch {
1095
1404
  return null;
1096
1405
  }
@@ -1100,7 +1409,7 @@ function ensureDaemonToken(seed) {
1100
1409
  if (existing) return existing;
1101
1410
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1102
1411
  ensurePrivateDirSync(EXE_AI_DIR);
1103
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1412
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1104
1413
  `, "utf8");
1105
1414
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1106
1415
  return token;
@@ -1111,7 +1420,7 @@ var init_daemon_auth = __esm({
1111
1420
  "use strict";
1112
1421
  init_config();
1113
1422
  init_secure_files();
1114
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1423
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1115
1424
  }
1116
1425
  });
1117
1426
 
@@ -1120,8 +1429,8 @@ import net from "net";
1120
1429
  import os4 from "os";
1121
1430
  import { spawn, execSync as execSync2 } from "child_process";
1122
1431
  import { randomUUID } from "crypto";
1123
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1124
- import path5 from "path";
1432
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1433
+ import path6 from "path";
1125
1434
  import { fileURLToPath } from "url";
1126
1435
  function handleData(chunk) {
1127
1436
  _buffer += chunk.toString();
@@ -1157,9 +1466,9 @@ function isZombie(pid) {
1157
1466
  }
1158
1467
  }
1159
1468
  function cleanupStaleFiles() {
1160
- if (existsSync5(PID_PATH)) {
1469
+ if (existsSync6(PID_PATH)) {
1161
1470
  try {
1162
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1471
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1163
1472
  if (pid > 0) {
1164
1473
  try {
1165
1474
  process.kill(pid, 0);
@@ -1184,11 +1493,11 @@ function cleanupStaleFiles() {
1184
1493
  }
1185
1494
  }
1186
1495
  function findPackageRoot() {
1187
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1188
- const { root } = path5.parse(dir);
1496
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1497
+ const { root } = path6.parse(dir);
1189
1498
  while (dir !== root) {
1190
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1191
- dir = path5.dirname(dir);
1499
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1500
+ dir = path6.dirname(dir);
1192
1501
  }
1193
1502
  return null;
1194
1503
  }
@@ -1206,8 +1515,8 @@ function spawnDaemon() {
1206
1515
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1207
1516
  return;
1208
1517
  }
1209
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1210
- if (!existsSync5(daemonPath)) {
1518
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1519
+ if (!existsSync6(daemonPath)) {
1211
1520
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1212
1521
  `);
1213
1522
  return;
@@ -1216,7 +1525,7 @@ function spawnDaemon() {
1216
1525
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1217
1526
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1218
1527
  `);
1219
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1528
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1220
1529
  let stderrFd = "ignore";
1221
1530
  try {
1222
1531
  stderrFd = openSync(logPath, "a");
@@ -1381,9 +1690,9 @@ function killAndRespawnDaemon() {
1381
1690
  }
1382
1691
  try {
1383
1692
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1384
- if (existsSync5(PID_PATH)) {
1693
+ if (existsSync6(PID_PATH)) {
1385
1694
  try {
1386
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1695
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1387
1696
  if (pid > 0) {
1388
1697
  try {
1389
1698
  process.kill(pid, "SIGKILL");
@@ -1509,9 +1818,9 @@ var init_exe_daemon_client = __esm({
1509
1818
  "use strict";
1510
1819
  init_config();
1511
1820
  init_daemon_auth();
1512
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1513
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1514
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1821
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1822
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1823
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1515
1824
  SPAWN_LOCK_STALE_MS = 3e4;
1516
1825
  CONNECT_TIMEOUT_MS = 15e3;
1517
1826
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1744,7 +2053,7 @@ __export(database_exports, {
1744
2053
  isInitialized: () => isInitialized,
1745
2054
  setExternalClient: () => setExternalClient
1746
2055
  });
1747
- import { chmodSync as chmodSync2, existsSync as existsSync6, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
2056
+ import { chmodSync as chmodSync2, existsSync as existsSync7, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
1748
2057
  import { createClient } from "@libsql/client";
1749
2058
  import { homedir } from "os";
1750
2059
  import { join } from "path";
@@ -1797,11 +2106,11 @@ function releaseDbLock() {
1797
2106
  }
1798
2107
  async function initDatabase(config) {
1799
2108
  acquireDbLock();
1800
- if (existsSync6(config.dbPath)) {
2109
+ if (existsSync7(config.dbPath)) {
1801
2110
  const dbStat = statSync2(config.dbPath);
1802
2111
  if (dbStat.size === 0) {
1803
2112
  const walPath = config.dbPath + "-wal";
1804
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
2113
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1805
2114
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1806
2115
  copyFileSync(config.dbPath, backupPath);
1807
2116
  unlinkSync3(config.dbPath);
@@ -3396,16 +3705,16 @@ __export(keychain_exports, {
3396
3705
  importMnemonic: () => importMnemonic,
3397
3706
  setMasterKey: () => setMasterKey
3398
3707
  });
3399
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3400
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3708
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3709
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
3401
3710
  import { execSync as execSync3 } from "child_process";
3402
- import path6 from "path";
3711
+ import path7 from "path";
3403
3712
  import os5 from "os";
3404
3713
  function getKeyDir() {
3405
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3714
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
3406
3715
  }
3407
3716
  function getKeyPath() {
3408
- return path6.join(getKeyDir(), "master.key");
3717
+ return path7.join(getKeyDir(), "master.key");
3409
3718
  }
3410
3719
  function nativeKeychainAllowed() {
3411
3720
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -3431,12 +3740,14 @@ function linuxSecretAvailable() {
3431
3740
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3432
3741
  if (process.platform !== "linux") return false;
3433
3742
  try {
3434
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3435
3743
  const st = statSync3(keyPath);
3436
3744
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3745
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3437
3746
  if (uid === 0) return true;
3438
3747
  const exeOsDir = process.env.EXE_OS_DIR;
3439
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3748
+ if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
3749
+ if (!linuxSecretAvailable()) return true;
3750
+ return false;
3440
3751
  } catch {
3441
3752
  return false;
3442
3753
  }
@@ -3586,15 +3897,25 @@ async function writeMachineBoundFileFallback(b64) {
3586
3897
  await mkdir3(dir, { recursive: true });
3587
3898
  const keyPath = getKeyPath();
3588
3899
  const machineKey = deriveMachineKey();
3589
- if (machineKey) {
3590
- const encrypted = encryptWithMachineKey(b64, machineKey);
3591
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3592
- await chmod2(keyPath, 384);
3593
- return "encrypted";
3900
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3901
+ const result = machineKey ? "encrypted" : "plaintext";
3902
+ const tmpPath = keyPath + ".tmp";
3903
+ try {
3904
+ if (existsSync8(keyPath)) {
3905
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3906
+ });
3907
+ }
3908
+ await writeFile3(tmpPath, content, "utf-8");
3909
+ await chmod2(tmpPath, 384);
3910
+ await rename(tmpPath, keyPath);
3911
+ } catch (err) {
3912
+ try {
3913
+ await unlink(tmpPath);
3914
+ } catch {
3915
+ }
3916
+ throw err;
3594
3917
  }
3595
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3596
- await chmod2(keyPath, 384);
3597
- return "plaintext";
3918
+ return result;
3598
3919
  }
3599
3920
  async function getMasterKey() {
3600
3921
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -3633,7 +3954,7 @@ async function getMasterKey() {
3633
3954
  }
3634
3955
  }
3635
3956
  const keyPath = getKeyPath();
3636
- if (!existsSync7(keyPath)) {
3957
+ if (!existsSync8(keyPath)) {
3637
3958
  process.stderr.write(
3638
3959
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3639
3960
  `
@@ -3661,7 +3982,7 @@ async function getMasterKey() {
3661
3982
  b64Value = content;
3662
3983
  }
3663
3984
  const key = Buffer.from(b64Value, "base64");
3664
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
3985
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3665
3986
  return key;
3666
3987
  }
3667
3988
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -3741,7 +4062,7 @@ async function getKeyStorageInfo() {
3741
4062
  }
3742
4063
  }
3743
4064
  const keyPath = getKeyPath();
3744
- if (!existsSync7(keyPath)) {
4065
+ if (!existsSync8(keyPath)) {
3745
4066
  return {
3746
4067
  kind: "missing",
3747
4068
  secure: false,
@@ -3837,7 +4158,7 @@ async function deleteMasterKey() {
3837
4158
  }
3838
4159
  }
3839
4160
  const keyPath = getKeyPath();
3840
- if (existsSync7(keyPath)) {
4161
+ if (existsSync8(keyPath)) {
3841
4162
  await unlink(keyPath);
3842
4163
  }
3843
4164
  }
@@ -3952,14 +4273,14 @@ __export(shard_manager_exports, {
3952
4273
  listShards: () => listShards,
3953
4274
  shardExists: () => shardExists
3954
4275
  });
3955
- import path7 from "path";
3956
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
4276
+ import path8 from "path";
4277
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3957
4278
  import { createClient as createClient2 } from "@libsql/client";
3958
4279
  function initShardManager(encryptionKey) {
3959
4280
  _encryptionKey = encryptionKey;
3960
4281
  _keyValidated = false;
3961
4282
  _keyValidationPromise = null;
3962
- if (!existsSync8(SHARDS_DIR)) {
4283
+ if (!existsSync9(SHARDS_DIR)) {
3963
4284
  mkdirSync3(SHARDS_DIR, { recursive: true });
3964
4285
  }
3965
4286
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3980,7 +4301,7 @@ async function validateEncryptionKey() {
3980
4301
  return true;
3981
4302
  }
3982
4303
  for (const shardFile of existingShards.slice(0, 3)) {
3983
- const dbPath = path7.join(SHARDS_DIR, shardFile);
4304
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3984
4305
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3985
4306
  try {
3986
4307
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -4023,7 +4344,7 @@ function getShardClient(projectName) {
4023
4344
  while (_shards.size >= MAX_OPEN_SHARDS) {
4024
4345
  evictLRU();
4025
4346
  }
4026
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4347
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4027
4348
  const client = createClient2({
4028
4349
  url: `file:${dbPath}`,
4029
4350
  encryptionKey: _encryptionKey
@@ -4034,13 +4355,13 @@ function getShardClient(projectName) {
4034
4355
  }
4035
4356
  function shardExists(projectName) {
4036
4357
  const safeName = safeShardName(projectName);
4037
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
4358
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
4038
4359
  }
4039
4360
  function safeShardName(projectName) {
4040
4361
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4041
4362
  }
4042
4363
  function listShards() {
4043
- if (!existsSync8(SHARDS_DIR)) return [];
4364
+ if (!existsSync9(SHARDS_DIR)) return [];
4044
4365
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4045
4366
  }
4046
4367
  async function auditShardHealth(options = {}) {
@@ -4052,7 +4373,7 @@ async function auditShardHealth(options = {}) {
4052
4373
  const names = listShards();
4053
4374
  const shards = [];
4054
4375
  for (const name of names) {
4055
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
4376
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
4056
4377
  const stat = statSync4(dbPath);
4057
4378
  const item = {
4058
4379
  name,
@@ -4087,7 +4408,7 @@ async function auditShardHealth(options = {}) {
4087
4408
  _shards.delete(name);
4088
4409
  _shardLastAccess.delete(name);
4089
4410
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4090
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4411
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4091
4412
  renameSync3(dbPath, archivedPath);
4092
4413
  item.archivedPath = archivedPath;
4093
4414
  }
@@ -4315,11 +4636,11 @@ async function getReadyShardClient(projectName) {
4315
4636
  client.close();
4316
4637
  _shards.delete(safeName);
4317
4638
  _shardLastAccess.delete(safeName);
4318
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4319
- if (existsSync8(dbPath)) {
4639
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4640
+ if (existsSync9(dbPath)) {
4320
4641
  const stat = statSync4(dbPath);
4321
4642
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4322
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4643
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4323
4644
  renameSync3(dbPath, archivedPath);
4324
4645
  process.stderr.write(
4325
4646
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -4387,7 +4708,7 @@ var init_shard_manager = __esm({
4387
4708
  "src/lib/shard-manager.ts"() {
4388
4709
  "use strict";
4389
4710
  init_config();
4390
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
4711
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
4391
4712
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4392
4713
  MAX_OPEN_SHARDS = 10;
4393
4714
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -4777,13 +5098,13 @@ ${p.content}`).join("\n\n");
4777
5098
  });
4778
5099
 
4779
5100
  // src/lib/session-registry.ts
4780
- import path8 from "path";
5101
+ import path9 from "path";
4781
5102
  import os6 from "os";
4782
5103
  var REGISTRY_PATH;
4783
5104
  var init_session_registry = __esm({
4784
5105
  "src/lib/session-registry.ts"() {
4785
5106
  "use strict";
4786
- REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
5107
+ REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
4787
5108
  }
4788
5109
  });
4789
5110
 
@@ -5001,51 +5322,6 @@ var init_provider_table = __esm({
5001
5322
  }
5002
5323
  });
5003
5324
 
5004
- // src/lib/runtime-table.ts
5005
- var RUNTIME_TABLE;
5006
- var init_runtime_table = __esm({
5007
- "src/lib/runtime-table.ts"() {
5008
- "use strict";
5009
- RUNTIME_TABLE = {
5010
- codex: {
5011
- binary: "codex",
5012
- launchMode: "interactive",
5013
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
5014
- inlineFlag: "--no-alt-screen",
5015
- apiKeyEnv: "OPENAI_API_KEY",
5016
- defaultModel: "gpt-5.5"
5017
- },
5018
- opencode: {
5019
- binary: "opencode",
5020
- launchMode: "exec",
5021
- autoApproveFlag: "--dangerously-skip-permissions",
5022
- inlineFlag: "",
5023
- apiKeyEnv: "ANTHROPIC_API_KEY",
5024
- defaultModel: "anthropic/claude-sonnet-4-6"
5025
- }
5026
- };
5027
- }
5028
- });
5029
-
5030
- // src/lib/agent-config.ts
5031
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
5032
- import path9 from "path";
5033
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
5034
- var init_agent_config = __esm({
5035
- "src/lib/agent-config.ts"() {
5036
- "use strict";
5037
- init_config();
5038
- init_runtime_table();
5039
- init_secure_files();
5040
- AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
5041
- DEFAULT_MODELS = {
5042
- claude: "claude-opus-4.6",
5043
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
5044
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
5045
- };
5046
- }
5047
- });
5048
-
5049
5325
  // src/lib/intercom-queue.ts
5050
5326
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
5051
5327
  import path10 from "path";
@@ -5683,6 +5959,21 @@ function isRootSession(name) {
5683
5959
  function extractRootExe(name) {
5684
5960
  if (!name) return null;
5685
5961
  if (!name.includes("-")) return name;
5962
+ try {
5963
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
5964
+ if (roster.length > 0) {
5965
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
5966
+ for (const agentName of sortedNames) {
5967
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5968
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
5969
+ const match = name.match(regex);
5970
+ if (match) {
5971
+ return extractRootExe(match[1]);
5972
+ }
5973
+ }
5974
+ }
5975
+ } catch {
5976
+ }
5686
5977
  const parts = name.split("-").filter(Boolean);
5687
5978
  return parts.length > 0 ? parts[parts.length - 1] : null;
5688
5979
  }
@@ -5701,6 +5992,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5701
5992
  function getParentExe(sessionKey) {
5702
5993
  try {
5703
5994
  const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5995
+ if (data.registeredAt) {
5996
+ const age = Date.now() - new Date(data.registeredAt).getTime();
5997
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
5998
+ }
5704
5999
  return data.parentExe || null;
5705
6000
  } catch {
5706
6001
  return null;
@@ -5773,7 +6068,7 @@ function resolveExeSession() {
5773
6068
  }
5774
6069
  return candidate;
5775
6070
  }
5776
- var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
6071
+ var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
5777
6072
  var init_tmux_routing = __esm({
5778
6073
  "src/lib/tmux-routing.ts"() {
5779
6074
  "use strict";
@@ -5791,6 +6086,7 @@ var init_tmux_routing = __esm({
5791
6086
  init_agent_symlinks();
5792
6087
  SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
5793
6088
  SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
6089
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
5794
6090
  INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
5795
6091
  DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
5796
6092
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
@@ -5957,7 +6253,21 @@ function tryAcquireWorkerSlot() {
5957
6253
  for (const f of files) {
5958
6254
  if (!f.endsWith(".pid")) continue;
5959
6255
  if (f.startsWith("res-")) {
5960
- alive++;
6256
+ const resParts = f.replace(".pid", "").split("-");
6257
+ const resPid = parseInt(resParts[1] ?? "", 10);
6258
+ if (!isNaN(resPid) && resPid > 0) {
6259
+ try {
6260
+ process.kill(resPid, 0);
6261
+ alive++;
6262
+ } catch {
6263
+ try {
6264
+ unlinkSync6(path17.join(WORKER_PID_DIR, f));
6265
+ } catch {
6266
+ }
6267
+ }
6268
+ } else {
6269
+ alive++;
6270
+ }
5961
6271
  continue;
5962
6272
  }
5963
6273
  const dashIdx = f.lastIndexOf("-");
@@ -6500,6 +6810,7 @@ __export(cloud_sync_exports, {
6500
6810
  markCloudReuploadRequired: () => markCloudReuploadRequired,
6501
6811
  mergeConfig: () => mergeConfig,
6502
6812
  mergeRosterFromRemote: () => mergeRosterFromRemote,
6813
+ migrateEndpoint: () => migrateEndpoint,
6503
6814
  pushToPostgres: () => pushToPostgres,
6504
6815
  recordRosterDeletion: () => recordRosterDeletion
6505
6816
  });
@@ -6670,6 +6981,15 @@ async function fetchWithRetry(url, init) {
6670
6981
  }
6671
6982
  throw lastError;
6672
6983
  }
6984
+ function migrateEndpoint(endpoint) {
6985
+ if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
6986
+ process.stderr.write(
6987
+ "[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to cloud.askexe.com (bypasses Cloudflare WAF for datacenter IPs)\n"
6988
+ );
6989
+ return "https://cloud.askexe.com";
6990
+ }
6991
+ return endpoint;
6992
+ }
6673
6993
  function assertSecureEndpoint(endpoint) {
6674
6994
  if (endpoint.startsWith("https://")) return;
6675
6995
  if (endpoint.startsWith("http://")) {
@@ -6807,6 +7127,7 @@ async function markCloudReuploadRequired(client = getClient()) {
6807
7127
  await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
6808
7128
  }
6809
7129
  async function cloudSync(config) {
7130
+ config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
6810
7131
  if (!isSyncCryptoInitialized()) {
6811
7132
  try {
6812
7133
  const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));