@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
@@ -195,6 +195,17 @@ function normalizeOrchestration(raw) {
195
195
  const userOrg = raw.orchestration ?? {};
196
196
  raw.orchestration = { ...defaultOrg, ...userOrg };
197
197
  }
198
+ function normalizeCloudEndpoint(raw) {
199
+ const cloud = raw.cloud;
200
+ if (!cloud?.endpoint) return;
201
+ const ep = String(cloud.endpoint);
202
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
203
+ cloud.endpoint = "https://cloud.askexe.com";
204
+ process.stderr.write(
205
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
206
+ );
207
+ }
208
+ }
198
209
  async function loadConfig() {
199
210
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
200
211
  await ensurePrivateDir(dir);
@@ -220,6 +231,7 @@ async function loadConfig() {
220
231
  normalizeSessionLifecycle(migratedCfg);
221
232
  normalizeAutoUpdate(migratedCfg);
222
233
  normalizeOrchestration(migratedCfg);
234
+ normalizeCloudEndpoint(migratedCfg);
223
235
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
224
236
  if (config.dbPath.startsWith("~")) {
225
237
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -321,11 +333,176 @@ var init_config = __esm({
321
333
  }
322
334
  });
323
335
 
336
+ // src/lib/runtime-table.ts
337
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
338
+ var init_runtime_table = __esm({
339
+ "src/lib/runtime-table.ts"() {
340
+ "use strict";
341
+ RUNTIME_TABLE = {
342
+ codex: {
343
+ binary: "codex",
344
+ launchMode: "interactive",
345
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
346
+ inlineFlag: "--no-alt-screen",
347
+ apiKeyEnv: "OPENAI_API_KEY",
348
+ defaultModel: "gpt-5.5"
349
+ },
350
+ opencode: {
351
+ binary: "opencode",
352
+ launchMode: "exec",
353
+ autoApproveFlag: "--dangerously-skip-permissions",
354
+ inlineFlag: "",
355
+ apiKeyEnv: "ANTHROPIC_API_KEY",
356
+ defaultModel: "anthropic/claude-sonnet-4-6"
357
+ }
358
+ };
359
+ DEFAULT_RUNTIME = "claude";
360
+ }
361
+ });
362
+
363
+ // src/lib/agent-config.ts
364
+ var agent_config_exports = {};
365
+ __export(agent_config_exports, {
366
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
367
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
368
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
369
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
370
+ clearAgentRuntime: () => clearAgentRuntime,
371
+ getAgentRuntime: () => getAgentRuntime,
372
+ loadAgentConfig: () => loadAgentConfig,
373
+ normalizeCcModelName: () => normalizeCcModelName,
374
+ saveAgentConfig: () => saveAgentConfig,
375
+ setAgentMcps: () => setAgentMcps,
376
+ setAgentRuntime: () => setAgentRuntime
377
+ });
378
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
379
+ import path2 from "path";
380
+ function loadAgentConfig() {
381
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
382
+ try {
383
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
384
+ } catch {
385
+ return {};
386
+ }
387
+ }
388
+ function saveAgentConfig(config) {
389
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
390
+ ensurePrivateDirSync(dir);
391
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
392
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
393
+ }
394
+ function getAgentRuntime(agentId) {
395
+ const config = loadAgentConfig();
396
+ const entry = config[agentId];
397
+ if (entry) return entry;
398
+ const orgDefault = config["default"];
399
+ if (orgDefault) return orgDefault;
400
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
401
+ }
402
+ function normalizeCcModelName(model) {
403
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
404
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
405
+ ccModel += "[1m]";
406
+ }
407
+ return ccModel;
408
+ }
409
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
410
+ const knownModels = KNOWN_RUNTIMES[runtime];
411
+ if (!knownModels) {
412
+ return {
413
+ ok: false,
414
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
415
+ };
416
+ }
417
+ if (!knownModels.includes(model)) {
418
+ return {
419
+ ok: false,
420
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
421
+ };
422
+ }
423
+ const config = loadAgentConfig();
424
+ const existing = config[agentId];
425
+ const entry = { runtime, model };
426
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
427
+ if (mcps !== void 0) {
428
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
429
+ } else if (existing?.mcps) {
430
+ entry.mcps = existing.mcps;
431
+ }
432
+ config[agentId] = entry;
433
+ saveAgentConfig(config);
434
+ return { ok: true };
435
+ }
436
+ function setAgentMcps(agentId, mcps) {
437
+ const config = loadAgentConfig();
438
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
439
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
440
+ config[agentId] = existing;
441
+ saveAgentConfig(config);
442
+ return { ok: true };
443
+ }
444
+ function clearAgentRuntime(agentId) {
445
+ const config = loadAgentConfig();
446
+ delete config[agentId];
447
+ saveAgentConfig(config);
448
+ }
449
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
450
+ var init_agent_config = __esm({
451
+ "src/lib/agent-config.ts"() {
452
+ "use strict";
453
+ init_config();
454
+ init_runtime_table();
455
+ init_secure_files();
456
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
457
+ KNOWN_RUNTIMES = {
458
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
459
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
460
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
461
+ };
462
+ RUNTIME_LABELS = {
463
+ claude: "Claude Code (Anthropic)",
464
+ codex: "Codex (OpenAI)",
465
+ opencode: "OpenCode (open source)"
466
+ };
467
+ DEFAULT_MODELS = {
468
+ claude: "claude-opus-4.6",
469
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
470
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
471
+ };
472
+ }
473
+ });
474
+
324
475
  // src/lib/employees.ts
476
+ var employees_exports = {};
477
+ __export(employees_exports, {
478
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
479
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
480
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
481
+ addEmployee: () => addEmployee,
482
+ baseAgentName: () => baseAgentName,
483
+ canCoordinate: () => canCoordinate,
484
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
485
+ getCoordinatorName: () => getCoordinatorName,
486
+ getEmployee: () => getEmployee,
487
+ getEmployeeByRole: () => getEmployeeByRole,
488
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
489
+ hasRole: () => hasRole,
490
+ hireEmployee: () => hireEmployee,
491
+ isCoordinatorName: () => isCoordinatorName,
492
+ isCoordinatorRole: () => isCoordinatorRole,
493
+ isMultiInstance: () => isMultiInstance,
494
+ loadEmployees: () => loadEmployees,
495
+ loadEmployeesSync: () => loadEmployeesSync,
496
+ normalizeRole: () => normalizeRole,
497
+ normalizeRosterCase: () => normalizeRosterCase,
498
+ registerBinSymlinks: () => registerBinSymlinks,
499
+ saveEmployees: () => saveEmployees,
500
+ validateEmployeeName: () => validateEmployeeName
501
+ });
325
502
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
326
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
503
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
327
504
  import { execSync } from "child_process";
328
- import path2 from "path";
505
+ import path3 from "path";
329
506
  import os2 from "os";
330
507
  function normalizeRole(role) {
331
508
  return (role ?? "").trim().toLowerCase();
@@ -339,29 +516,222 @@ function getCoordinatorEmployee(employees) {
339
516
  function getCoordinatorName(employees = loadEmployeesSync()) {
340
517
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
341
518
  }
519
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
520
+ if (!agentName) return false;
521
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
522
+ }
523
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
524
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
525
+ }
526
+ function validateEmployeeName(name) {
527
+ if (!name) {
528
+ return { valid: false, error: "Name is required" };
529
+ }
530
+ if (name.length > 32) {
531
+ return { valid: false, error: "Name must be 32 characters or fewer" };
532
+ }
533
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
534
+ return {
535
+ valid: false,
536
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
537
+ };
538
+ }
539
+ return { valid: true };
540
+ }
541
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
542
+ if (!existsSync4(employeesPath)) {
543
+ return [];
544
+ }
545
+ const raw = await readFile2(employeesPath, "utf-8");
546
+ try {
547
+ return JSON.parse(raw);
548
+ } catch {
549
+ return [];
550
+ }
551
+ }
552
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
553
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
554
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
555
+ }
342
556
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
343
- if (!existsSync3(employeesPath)) return [];
557
+ if (!existsSync4(employeesPath)) return [];
344
558
  try {
345
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
559
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
346
560
  } catch {
347
561
  return [];
348
562
  }
349
563
  }
350
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
564
+ function getEmployee(employees, name) {
565
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
566
+ }
567
+ function getEmployeeByRole(employees, role) {
568
+ const lower = role.toLowerCase();
569
+ return employees.find((e) => e.role.toLowerCase() === lower);
570
+ }
571
+ function getEmployeeNamesByRole(employees, role) {
572
+ const lower = role.toLowerCase();
573
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
574
+ }
575
+ function hasRole(agentName, role) {
576
+ const employees = loadEmployeesSync();
577
+ const emp = getEmployee(employees, agentName);
578
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
579
+ }
580
+ function baseAgentName(name, employees) {
581
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
582
+ if (!match) return name;
583
+ const base = match[1];
584
+ const roster = employees ?? loadEmployeesSync();
585
+ if (getEmployee(roster, base)) return base;
586
+ return name;
587
+ }
588
+ function isMultiInstance(agentName, employees) {
589
+ const roster = employees ?? loadEmployeesSync();
590
+ const emp = getEmployee(roster, agentName);
591
+ if (!emp) return false;
592
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
593
+ }
594
+ function addEmployee(employees, employee) {
595
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
596
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
597
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
598
+ throw new Error(`Employee '${normalized.name}' already exists`);
599
+ }
600
+ return [...employees, normalized];
601
+ }
602
+ function appendToCoordinatorTeam(employee) {
603
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
604
+ if (!coordinator) return;
605
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
606
+ if (!existsSync4(idPath)) return;
607
+ const content = readFileSync3(idPath, "utf-8");
608
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
609
+ const teamMatch = content.match(TEAM_SECTION_RE);
610
+ if (!teamMatch || teamMatch.index === void 0) return;
611
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
612
+ const nextHeading = afterTeam.match(/\n## /);
613
+ const entry = `
614
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
615
+ `;
616
+ let updated;
617
+ if (nextHeading && nextHeading.index !== void 0) {
618
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
619
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
620
+ } else {
621
+ updated = content.trimEnd() + "\n" + entry;
622
+ }
623
+ writeFileSync2(idPath, updated, "utf-8");
624
+ }
625
+ function capitalize(s) {
626
+ return s.charAt(0).toUpperCase() + s.slice(1);
627
+ }
628
+ async function hireEmployee(employee) {
629
+ const employees = await loadEmployees();
630
+ const updated = addEmployee(employees, employee);
631
+ await saveEmployees(updated);
632
+ try {
633
+ appendToCoordinatorTeam(employee);
634
+ } catch {
635
+ }
636
+ try {
637
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
638
+ const config = loadAgentConfig2();
639
+ const name = employee.name.toLowerCase();
640
+ if (!config[name] && config["default"]) {
641
+ config[name] = { ...config["default"] };
642
+ saveAgentConfig2(config);
643
+ }
644
+ } catch {
645
+ }
646
+ return updated;
647
+ }
648
+ async function normalizeRosterCase(rosterPath) {
649
+ const employees = await loadEmployees(rosterPath);
650
+ let changed = false;
651
+ for (const emp of employees) {
652
+ if (emp.name !== emp.name.toLowerCase()) {
653
+ const oldName = emp.name;
654
+ emp.name = emp.name.toLowerCase();
655
+ changed = true;
656
+ try {
657
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
658
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
659
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
660
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
661
+ renameSync2(oldPath, newPath);
662
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
663
+ const content = readFileSync3(oldPath, "utf-8");
664
+ writeFileSync2(newPath, content, "utf-8");
665
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
666
+ unlinkSync(oldPath);
667
+ }
668
+ }
669
+ } catch {
670
+ }
671
+ }
672
+ }
673
+ if (changed) {
674
+ await saveEmployees(employees, rosterPath);
675
+ }
676
+ return changed;
677
+ }
678
+ function findExeBin() {
679
+ try {
680
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
681
+ } catch {
682
+ return null;
683
+ }
684
+ }
685
+ function registerBinSymlinks(name) {
686
+ const created = [];
687
+ const skipped = [];
688
+ const errors = [];
689
+ const exeBinPath = findExeBin();
690
+ if (!exeBinPath) {
691
+ errors.push("Could not find 'exe-os' in PATH");
692
+ return { created, skipped, errors };
693
+ }
694
+ const binDir = path3.dirname(exeBinPath);
695
+ let target;
696
+ try {
697
+ target = readlinkSync(exeBinPath);
698
+ } catch {
699
+ errors.push("Could not read 'exe' symlink");
700
+ return { created, skipped, errors };
701
+ }
702
+ for (const suffix of ["", "-opencode"]) {
703
+ const linkName = `${name}${suffix}`;
704
+ const linkPath = path3.join(binDir, linkName);
705
+ if (existsSync4(linkPath)) {
706
+ skipped.push(linkName);
707
+ continue;
708
+ }
709
+ try {
710
+ symlinkSync(target, linkPath);
711
+ created.push(linkName);
712
+ } catch (err) {
713
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
714
+ }
715
+ }
716
+ return { created, skipped, errors };
717
+ }
718
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
351
719
  var init_employees = __esm({
352
720
  "src/lib/employees.ts"() {
353
721
  "use strict";
354
722
  init_config();
355
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
723
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
356
724
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
357
725
  COORDINATOR_ROLE = "COO";
358
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
726
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
727
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
728
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
359
729
  }
360
730
  });
361
731
 
362
732
  // src/lib/database-adapter.ts
363
733
  import os3 from "os";
364
- import path3 from "path";
734
+ import path4 from "path";
365
735
  import { createRequire } from "module";
366
736
  import { pathToFileURL } from "url";
367
737
  function quotedIdentifier(identifier) {
@@ -672,8 +1042,8 @@ async function loadPrismaClient() {
672
1042
  }
673
1043
  return new PrismaClient2();
674
1044
  }
675
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
676
- const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
1045
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1046
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
677
1047
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
678
1048
  const module = await import(pathToFileURL(prismaEntry).href);
679
1049
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -954,8 +1324,8 @@ var init_memory = __esm({
954
1324
 
955
1325
  // src/lib/daemon-auth.ts
956
1326
  import crypto from "crypto";
957
- import path4 from "path";
958
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1327
+ import path5 from "path";
1328
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
959
1329
  function normalizeToken(token) {
960
1330
  if (!token) return null;
961
1331
  const trimmed = token.trim();
@@ -963,8 +1333,8 @@ function normalizeToken(token) {
963
1333
  }
964
1334
  function readDaemonToken() {
965
1335
  try {
966
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
967
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1336
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1337
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
968
1338
  } catch {
969
1339
  return null;
970
1340
  }
@@ -974,7 +1344,7 @@ function ensureDaemonToken(seed) {
974
1344
  if (existing) return existing;
975
1345
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
976
1346
  ensurePrivateDirSync(EXE_AI_DIR);
977
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1347
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
978
1348
  `, "utf8");
979
1349
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
980
1350
  return token;
@@ -985,7 +1355,7 @@ var init_daemon_auth = __esm({
985
1355
  "use strict";
986
1356
  init_config();
987
1357
  init_secure_files();
988
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1358
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
989
1359
  }
990
1360
  });
991
1361
 
@@ -1005,8 +1375,8 @@ import net from "net";
1005
1375
  import os4 from "os";
1006
1376
  import { spawn, execSync as execSync2 } from "child_process";
1007
1377
  import { randomUUID } from "crypto";
1008
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1009
- import path5 from "path";
1378
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1379
+ import path6 from "path";
1010
1380
  import { fileURLToPath } from "url";
1011
1381
  function handleData(chunk) {
1012
1382
  _buffer += chunk.toString();
@@ -1042,9 +1412,9 @@ function isZombie(pid) {
1042
1412
  }
1043
1413
  }
1044
1414
  function cleanupStaleFiles() {
1045
- if (existsSync5(PID_PATH)) {
1415
+ if (existsSync6(PID_PATH)) {
1046
1416
  try {
1047
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1417
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1048
1418
  if (pid > 0) {
1049
1419
  try {
1050
1420
  process.kill(pid, 0);
@@ -1069,11 +1439,11 @@ function cleanupStaleFiles() {
1069
1439
  }
1070
1440
  }
1071
1441
  function findPackageRoot() {
1072
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1073
- const { root } = path5.parse(dir);
1442
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1443
+ const { root } = path6.parse(dir);
1074
1444
  while (dir !== root) {
1075
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1076
- dir = path5.dirname(dir);
1445
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1446
+ dir = path6.dirname(dir);
1077
1447
  }
1078
1448
  return null;
1079
1449
  }
@@ -1091,8 +1461,8 @@ function spawnDaemon() {
1091
1461
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1092
1462
  return;
1093
1463
  }
1094
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1095
- if (!existsSync5(daemonPath)) {
1464
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1465
+ if (!existsSync6(daemonPath)) {
1096
1466
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1097
1467
  `);
1098
1468
  return;
@@ -1101,7 +1471,7 @@ function spawnDaemon() {
1101
1471
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1102
1472
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1103
1473
  `);
1104
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1474
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1105
1475
  let stderrFd = "ignore";
1106
1476
  try {
1107
1477
  stderrFd = openSync(logPath, "a");
@@ -1266,9 +1636,9 @@ function killAndRespawnDaemon() {
1266
1636
  }
1267
1637
  try {
1268
1638
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1269
- if (existsSync5(PID_PATH)) {
1639
+ if (existsSync6(PID_PATH)) {
1270
1640
  try {
1271
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1641
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1272
1642
  if (pid > 0) {
1273
1643
  try {
1274
1644
  process.kill(pid, "SIGKILL");
@@ -1414,9 +1784,9 @@ var init_exe_daemon_client = __esm({
1414
1784
  "use strict";
1415
1785
  init_config();
1416
1786
  init_daemon_auth();
1417
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1418
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1419
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1787
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1788
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1789
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1420
1790
  SPAWN_LOCK_STALE_MS = 3e4;
1421
1791
  CONNECT_TIMEOUT_MS = 15e3;
1422
1792
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1684,7 +2054,7 @@ __export(database_exports, {
1684
2054
  isInitialized: () => isInitialized,
1685
2055
  setExternalClient: () => setExternalClient
1686
2056
  });
1687
- 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";
2057
+ 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";
1688
2058
  import { createClient } from "@libsql/client";
1689
2059
  import { homedir } from "os";
1690
2060
  import { join } from "path";
@@ -1737,11 +2107,11 @@ function releaseDbLock() {
1737
2107
  }
1738
2108
  async function initDatabase(config) {
1739
2109
  acquireDbLock();
1740
- if (existsSync6(config.dbPath)) {
2110
+ if (existsSync7(config.dbPath)) {
1741
2111
  const dbStat = statSync2(config.dbPath);
1742
2112
  if (dbStat.size === 0) {
1743
2113
  const walPath = config.dbPath + "-wal";
1744
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
2114
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1745
2115
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1746
2116
  copyFileSync(config.dbPath, backupPath);
1747
2117
  unlinkSync3(config.dbPath);
@@ -3327,16 +3697,16 @@ var init_database = __esm({
3327
3697
  });
3328
3698
 
3329
3699
  // src/lib/keychain.ts
3330
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3331
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3700
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3701
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
3332
3702
  import { execSync as execSync3 } from "child_process";
3333
- import path6 from "path";
3703
+ import path7 from "path";
3334
3704
  import os5 from "os";
3335
3705
  function getKeyDir() {
3336
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3706
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
3337
3707
  }
3338
3708
  function getKeyPath() {
3339
- return path6.join(getKeyDir(), "master.key");
3709
+ return path7.join(getKeyDir(), "master.key");
3340
3710
  }
3341
3711
  function nativeKeychainAllowed() {
3342
3712
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -3362,12 +3732,14 @@ function linuxSecretAvailable() {
3362
3732
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3363
3733
  if (process.platform !== "linux") return false;
3364
3734
  try {
3365
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3366
3735
  const st = statSync3(keyPath);
3367
3736
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3737
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3368
3738
  if (uid === 0) return true;
3369
3739
  const exeOsDir = process.env.EXE_OS_DIR;
3370
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3740
+ if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
3741
+ if (!linuxSecretAvailable()) return true;
3742
+ return false;
3371
3743
  } catch {
3372
3744
  return false;
3373
3745
  }
@@ -3517,15 +3889,25 @@ async function writeMachineBoundFileFallback(b64) {
3517
3889
  await mkdir3(dir, { recursive: true });
3518
3890
  const keyPath = getKeyPath();
3519
3891
  const machineKey = deriveMachineKey();
3520
- if (machineKey) {
3521
- const encrypted = encryptWithMachineKey(b64, machineKey);
3522
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3523
- await chmod2(keyPath, 384);
3524
- return "encrypted";
3892
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3893
+ const result = machineKey ? "encrypted" : "plaintext";
3894
+ const tmpPath = keyPath + ".tmp";
3895
+ try {
3896
+ if (existsSync8(keyPath)) {
3897
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3898
+ });
3899
+ }
3900
+ await writeFile3(tmpPath, content, "utf-8");
3901
+ await chmod2(tmpPath, 384);
3902
+ await rename(tmpPath, keyPath);
3903
+ } catch (err) {
3904
+ try {
3905
+ await unlink(tmpPath);
3906
+ } catch {
3907
+ }
3908
+ throw err;
3525
3909
  }
3526
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3527
- await chmod2(keyPath, 384);
3528
- return "plaintext";
3910
+ return result;
3529
3911
  }
3530
3912
  async function getMasterKey() {
3531
3913
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -3564,7 +3946,7 @@ async function getMasterKey() {
3564
3946
  }
3565
3947
  }
3566
3948
  const keyPath = getKeyPath();
3567
- if (!existsSync7(keyPath)) {
3949
+ if (!existsSync8(keyPath)) {
3568
3950
  process.stderr.write(
3569
3951
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3570
3952
  `
@@ -3592,7 +3974,7 @@ async function getMasterKey() {
3592
3974
  b64Value = content;
3593
3975
  }
3594
3976
  const key = Buffer.from(b64Value, "base64");
3595
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
3977
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3596
3978
  return key;
3597
3979
  }
3598
3980
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -3898,14 +4280,14 @@ __export(shard_manager_exports, {
3898
4280
  listShards: () => listShards,
3899
4281
  shardExists: () => shardExists
3900
4282
  });
3901
- import path7 from "path";
3902
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
4283
+ import path8 from "path";
4284
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3903
4285
  import { createClient as createClient2 } from "@libsql/client";
3904
4286
  function initShardManager(encryptionKey) {
3905
4287
  _encryptionKey = encryptionKey;
3906
4288
  _keyValidated = false;
3907
4289
  _keyValidationPromise = null;
3908
- if (!existsSync8(SHARDS_DIR)) {
4290
+ if (!existsSync9(SHARDS_DIR)) {
3909
4291
  mkdirSync3(SHARDS_DIR, { recursive: true });
3910
4292
  }
3911
4293
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3926,7 +4308,7 @@ async function validateEncryptionKey() {
3926
4308
  return true;
3927
4309
  }
3928
4310
  for (const shardFile of existingShards.slice(0, 3)) {
3929
- const dbPath = path7.join(SHARDS_DIR, shardFile);
4311
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3930
4312
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3931
4313
  try {
3932
4314
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -3969,7 +4351,7 @@ function getShardClient(projectName) {
3969
4351
  while (_shards.size >= MAX_OPEN_SHARDS) {
3970
4352
  evictLRU();
3971
4353
  }
3972
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4354
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3973
4355
  const client = createClient2({
3974
4356
  url: `file:${dbPath}`,
3975
4357
  encryptionKey: _encryptionKey
@@ -3980,13 +4362,13 @@ function getShardClient(projectName) {
3980
4362
  }
3981
4363
  function shardExists(projectName) {
3982
4364
  const safeName = safeShardName(projectName);
3983
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
4365
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
3984
4366
  }
3985
4367
  function safeShardName(projectName) {
3986
4368
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3987
4369
  }
3988
4370
  function listShards() {
3989
- if (!existsSync8(SHARDS_DIR)) return [];
4371
+ if (!existsSync9(SHARDS_DIR)) return [];
3990
4372
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3991
4373
  }
3992
4374
  async function auditShardHealth(options = {}) {
@@ -3998,7 +4380,7 @@ async function auditShardHealth(options = {}) {
3998
4380
  const names = listShards();
3999
4381
  const shards = [];
4000
4382
  for (const name of names) {
4001
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
4383
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
4002
4384
  const stat = statSync4(dbPath);
4003
4385
  const item = {
4004
4386
  name,
@@ -4033,7 +4415,7 @@ async function auditShardHealth(options = {}) {
4033
4415
  _shards.delete(name);
4034
4416
  _shardLastAccess.delete(name);
4035
4417
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4036
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4418
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4037
4419
  renameSync3(dbPath, archivedPath);
4038
4420
  item.archivedPath = archivedPath;
4039
4421
  }
@@ -4261,11 +4643,11 @@ async function getReadyShardClient(projectName) {
4261
4643
  client.close();
4262
4644
  _shards.delete(safeName);
4263
4645
  _shardLastAccess.delete(safeName);
4264
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4265
- if (existsSync8(dbPath)) {
4646
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4647
+ if (existsSync9(dbPath)) {
4266
4648
  const stat = statSync4(dbPath);
4267
4649
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4268
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4650
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4269
4651
  renameSync3(dbPath, archivedPath);
4270
4652
  process.stderr.write(
4271
4653
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -4333,7 +4715,7 @@ var init_shard_manager = __esm({
4333
4715
  "src/lib/shard-manager.ts"() {
4334
4716
  "use strict";
4335
4717
  init_config();
4336
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
4718
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
4337
4719
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4338
4720
  MAX_OPEN_SHARDS = 10;
4339
4721
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -5827,13 +6209,13 @@ var init_store = __esm({
5827
6209
  });
5828
6210
 
5829
6211
  // src/lib/session-registry.ts
5830
- import path8 from "path";
6212
+ import path9 from "path";
5831
6213
  import os6 from "os";
5832
6214
  var REGISTRY_PATH;
5833
6215
  var init_session_registry = __esm({
5834
6216
  "src/lib/session-registry.ts"() {
5835
6217
  "use strict";
5836
- REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
6218
+ REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
5837
6219
  }
5838
6220
  });
5839
6221
 
@@ -6051,51 +6433,6 @@ var init_provider_table = __esm({
6051
6433
  }
6052
6434
  });
6053
6435
 
6054
- // src/lib/runtime-table.ts
6055
- var RUNTIME_TABLE;
6056
- var init_runtime_table = __esm({
6057
- "src/lib/runtime-table.ts"() {
6058
- "use strict";
6059
- RUNTIME_TABLE = {
6060
- codex: {
6061
- binary: "codex",
6062
- launchMode: "interactive",
6063
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
6064
- inlineFlag: "--no-alt-screen",
6065
- apiKeyEnv: "OPENAI_API_KEY",
6066
- defaultModel: "gpt-5.5"
6067
- },
6068
- opencode: {
6069
- binary: "opencode",
6070
- launchMode: "exec",
6071
- autoApproveFlag: "--dangerously-skip-permissions",
6072
- inlineFlag: "",
6073
- apiKeyEnv: "ANTHROPIC_API_KEY",
6074
- defaultModel: "anthropic/claude-sonnet-4-6"
6075
- }
6076
- };
6077
- }
6078
- });
6079
-
6080
- // src/lib/agent-config.ts
6081
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
6082
- import path9 from "path";
6083
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
6084
- var init_agent_config = __esm({
6085
- "src/lib/agent-config.ts"() {
6086
- "use strict";
6087
- init_config();
6088
- init_runtime_table();
6089
- init_secure_files();
6090
- AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
6091
- DEFAULT_MODELS = {
6092
- claude: "claude-opus-4.6",
6093
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
6094
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
6095
- };
6096
- }
6097
- });
6098
-
6099
6436
  // src/lib/intercom-queue.ts
6100
6437
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
6101
6438
  import path10 from "path";
@@ -6176,6 +6513,21 @@ function isRootSession(name) {
6176
6513
  function extractRootExe(name) {
6177
6514
  if (!name) return null;
6178
6515
  if (!name.includes("-")) return name;
6516
+ try {
6517
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
6518
+ if (roster.length > 0) {
6519
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
6520
+ for (const agentName of sortedNames) {
6521
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6522
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
6523
+ const match = name.match(regex);
6524
+ if (match) {
6525
+ return extractRootExe(match[1]);
6526
+ }
6527
+ }
6528
+ }
6529
+ } catch {
6530
+ }
6179
6531
  const parts = name.split("-").filter(Boolean);
6180
6532
  return parts.length > 0 ? parts[parts.length - 1] : null;
6181
6533
  }
@@ -6194,6 +6546,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6194
6546
  function getParentExe(sessionKey) {
6195
6547
  try {
6196
6548
  const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6549
+ if (data.registeredAt) {
6550
+ const age = Date.now() - new Date(data.registeredAt).getTime();
6551
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
6552
+ }
6197
6553
  return data.parentExe || null;
6198
6554
  } catch {
6199
6555
  return null;
@@ -6266,7 +6622,7 @@ function resolveExeSession() {
6266
6622
  }
6267
6623
  return candidate;
6268
6624
  }
6269
- var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
6625
+ var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
6270
6626
  var init_tmux_routing = __esm({
6271
6627
  "src/lib/tmux-routing.ts"() {
6272
6628
  "use strict";
@@ -6284,6 +6640,7 @@ var init_tmux_routing = __esm({
6284
6640
  init_agent_symlinks();
6285
6641
  SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
6286
6642
  SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
6643
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
6287
6644
  INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
6288
6645
  DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
6289
6646
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;