@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
@@ -140,6 +140,17 @@ function normalizeOrchestration(raw) {
140
140
  const userOrg = raw.orchestration ?? {};
141
141
  raw.orchestration = { ...defaultOrg, ...userOrg };
142
142
  }
143
+ function normalizeCloudEndpoint(raw) {
144
+ const cloud = raw.cloud;
145
+ if (!cloud?.endpoint) return;
146
+ const ep = String(cloud.endpoint);
147
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
148
+ cloud.endpoint = "https://cloud.askexe.com";
149
+ process.stderr.write(
150
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
151
+ );
152
+ }
153
+ }
143
154
  async function loadConfig() {
144
155
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
145
156
  await ensurePrivateDir(dir);
@@ -165,6 +176,7 @@ async function loadConfig() {
165
176
  normalizeSessionLifecycle(migratedCfg);
166
177
  normalizeAutoUpdate(migratedCfg);
167
178
  normalizeOrchestration(migratedCfg);
179
+ normalizeCloudEndpoint(migratedCfg);
168
180
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
169
181
  if (config.dbPath.startsWith("~")) {
170
182
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -266,11 +278,176 @@ var init_config = __esm({
266
278
  }
267
279
  });
268
280
 
281
+ // src/lib/runtime-table.ts
282
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
283
+ var init_runtime_table = __esm({
284
+ "src/lib/runtime-table.ts"() {
285
+ "use strict";
286
+ RUNTIME_TABLE = {
287
+ codex: {
288
+ binary: "codex",
289
+ launchMode: "interactive",
290
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
291
+ inlineFlag: "--no-alt-screen",
292
+ apiKeyEnv: "OPENAI_API_KEY",
293
+ defaultModel: "gpt-5.5"
294
+ },
295
+ opencode: {
296
+ binary: "opencode",
297
+ launchMode: "exec",
298
+ autoApproveFlag: "--dangerously-skip-permissions",
299
+ inlineFlag: "",
300
+ apiKeyEnv: "ANTHROPIC_API_KEY",
301
+ defaultModel: "anthropic/claude-sonnet-4-6"
302
+ }
303
+ };
304
+ DEFAULT_RUNTIME = "claude";
305
+ }
306
+ });
307
+
308
+ // src/lib/agent-config.ts
309
+ var agent_config_exports = {};
310
+ __export(agent_config_exports, {
311
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
312
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
313
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
314
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
315
+ clearAgentRuntime: () => clearAgentRuntime,
316
+ getAgentRuntime: () => getAgentRuntime,
317
+ loadAgentConfig: () => loadAgentConfig,
318
+ normalizeCcModelName: () => normalizeCcModelName,
319
+ saveAgentConfig: () => saveAgentConfig,
320
+ setAgentMcps: () => setAgentMcps,
321
+ setAgentRuntime: () => setAgentRuntime
322
+ });
323
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
324
+ import path2 from "path";
325
+ function loadAgentConfig() {
326
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
327
+ try {
328
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
329
+ } catch {
330
+ return {};
331
+ }
332
+ }
333
+ function saveAgentConfig(config) {
334
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
335
+ ensurePrivateDirSync(dir);
336
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
337
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
338
+ }
339
+ function getAgentRuntime(agentId) {
340
+ const config = loadAgentConfig();
341
+ const entry = config[agentId];
342
+ if (entry) return entry;
343
+ const orgDefault = config["default"];
344
+ if (orgDefault) return orgDefault;
345
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
346
+ }
347
+ function normalizeCcModelName(model) {
348
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
349
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
350
+ ccModel += "[1m]";
351
+ }
352
+ return ccModel;
353
+ }
354
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
355
+ const knownModels = KNOWN_RUNTIMES[runtime];
356
+ if (!knownModels) {
357
+ return {
358
+ ok: false,
359
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
360
+ };
361
+ }
362
+ if (!knownModels.includes(model)) {
363
+ return {
364
+ ok: false,
365
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
366
+ };
367
+ }
368
+ const config = loadAgentConfig();
369
+ const existing = config[agentId];
370
+ const entry = { runtime, model };
371
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
372
+ if (mcps !== void 0) {
373
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
374
+ } else if (existing?.mcps) {
375
+ entry.mcps = existing.mcps;
376
+ }
377
+ config[agentId] = entry;
378
+ saveAgentConfig(config);
379
+ return { ok: true };
380
+ }
381
+ function setAgentMcps(agentId, mcps) {
382
+ const config = loadAgentConfig();
383
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
384
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
385
+ config[agentId] = existing;
386
+ saveAgentConfig(config);
387
+ return { ok: true };
388
+ }
389
+ function clearAgentRuntime(agentId) {
390
+ const config = loadAgentConfig();
391
+ delete config[agentId];
392
+ saveAgentConfig(config);
393
+ }
394
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
395
+ var init_agent_config = __esm({
396
+ "src/lib/agent-config.ts"() {
397
+ "use strict";
398
+ init_config();
399
+ init_runtime_table();
400
+ init_secure_files();
401
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
402
+ KNOWN_RUNTIMES = {
403
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
404
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
405
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
406
+ };
407
+ RUNTIME_LABELS = {
408
+ claude: "Claude Code (Anthropic)",
409
+ codex: "Codex (OpenAI)",
410
+ opencode: "OpenCode (open source)"
411
+ };
412
+ DEFAULT_MODELS = {
413
+ claude: "claude-opus-4.6",
414
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
415
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
416
+ };
417
+ }
418
+ });
419
+
269
420
  // src/lib/employees.ts
421
+ var employees_exports = {};
422
+ __export(employees_exports, {
423
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
424
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
425
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
426
+ addEmployee: () => addEmployee,
427
+ baseAgentName: () => baseAgentName,
428
+ canCoordinate: () => canCoordinate,
429
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
430
+ getCoordinatorName: () => getCoordinatorName,
431
+ getEmployee: () => getEmployee,
432
+ getEmployeeByRole: () => getEmployeeByRole,
433
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
434
+ hasRole: () => hasRole,
435
+ hireEmployee: () => hireEmployee,
436
+ isCoordinatorName: () => isCoordinatorName,
437
+ isCoordinatorRole: () => isCoordinatorRole,
438
+ isMultiInstance: () => isMultiInstance,
439
+ loadEmployees: () => loadEmployees,
440
+ loadEmployeesSync: () => loadEmployeesSync,
441
+ normalizeRole: () => normalizeRole,
442
+ normalizeRosterCase: () => normalizeRosterCase,
443
+ registerBinSymlinks: () => registerBinSymlinks,
444
+ saveEmployees: () => saveEmployees,
445
+ validateEmployeeName: () => validateEmployeeName
446
+ });
270
447
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
271
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
448
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
272
449
  import { execSync } from "child_process";
273
- import path2 from "path";
450
+ import path3 from "path";
274
451
  import os2 from "os";
275
452
  function normalizeRole(role) {
276
453
  return (role ?? "").trim().toLowerCase();
@@ -284,8 +461,30 @@ function getCoordinatorEmployee(employees) {
284
461
  function getCoordinatorName(employees = loadEmployeesSync()) {
285
462
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
286
463
  }
464
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
465
+ if (!agentName) return false;
466
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
467
+ }
468
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
469
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
470
+ }
471
+ function validateEmployeeName(name) {
472
+ if (!name) {
473
+ return { valid: false, error: "Name is required" };
474
+ }
475
+ if (name.length > 32) {
476
+ return { valid: false, error: "Name must be 32 characters or fewer" };
477
+ }
478
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
479
+ return {
480
+ valid: false,
481
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
482
+ };
483
+ }
484
+ return { valid: true };
485
+ }
287
486
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
288
- if (!existsSync3(employeesPath)) {
487
+ if (!existsSync4(employeesPath)) {
289
488
  return [];
290
489
  }
291
490
  const raw = await readFile2(employeesPath, "utf-8");
@@ -295,23 +494,183 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
295
494
  return [];
296
495
  }
297
496
  }
497
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
498
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
499
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
500
+ }
298
501
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
299
- if (!existsSync3(employeesPath)) return [];
502
+ if (!existsSync4(employeesPath)) return [];
300
503
  try {
301
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
504
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
302
505
  } catch {
303
506
  return [];
304
507
  }
305
508
  }
306
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
509
+ function getEmployee(employees, name) {
510
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
511
+ }
512
+ function getEmployeeByRole(employees, role) {
513
+ const lower = role.toLowerCase();
514
+ return employees.find((e) => e.role.toLowerCase() === lower);
515
+ }
516
+ function getEmployeeNamesByRole(employees, role) {
517
+ const lower = role.toLowerCase();
518
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
519
+ }
520
+ function hasRole(agentName, role) {
521
+ const employees = loadEmployeesSync();
522
+ const emp = getEmployee(employees, agentName);
523
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
524
+ }
525
+ function baseAgentName(name, employees) {
526
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
527
+ if (!match) return name;
528
+ const base = match[1];
529
+ const roster = employees ?? loadEmployeesSync();
530
+ if (getEmployee(roster, base)) return base;
531
+ return name;
532
+ }
533
+ function isMultiInstance(agentName, employees) {
534
+ const roster = employees ?? loadEmployeesSync();
535
+ const emp = getEmployee(roster, agentName);
536
+ if (!emp) return false;
537
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
538
+ }
539
+ function addEmployee(employees, employee) {
540
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
541
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
542
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
543
+ throw new Error(`Employee '${normalized.name}' already exists`);
544
+ }
545
+ return [...employees, normalized];
546
+ }
547
+ function appendToCoordinatorTeam(employee) {
548
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
549
+ if (!coordinator) return;
550
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
551
+ if (!existsSync4(idPath)) return;
552
+ const content = readFileSync3(idPath, "utf-8");
553
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
554
+ const teamMatch = content.match(TEAM_SECTION_RE);
555
+ if (!teamMatch || teamMatch.index === void 0) return;
556
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
557
+ const nextHeading = afterTeam.match(/\n## /);
558
+ const entry = `
559
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
560
+ `;
561
+ let updated;
562
+ if (nextHeading && nextHeading.index !== void 0) {
563
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
564
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
565
+ } else {
566
+ updated = content.trimEnd() + "\n" + entry;
567
+ }
568
+ writeFileSync2(idPath, updated, "utf-8");
569
+ }
570
+ function capitalize(s) {
571
+ return s.charAt(0).toUpperCase() + s.slice(1);
572
+ }
573
+ async function hireEmployee(employee) {
574
+ const employees = await loadEmployees();
575
+ const updated = addEmployee(employees, employee);
576
+ await saveEmployees(updated);
577
+ try {
578
+ appendToCoordinatorTeam(employee);
579
+ } catch {
580
+ }
581
+ try {
582
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
583
+ const config = loadAgentConfig2();
584
+ const name = employee.name.toLowerCase();
585
+ if (!config[name] && config["default"]) {
586
+ config[name] = { ...config["default"] };
587
+ saveAgentConfig2(config);
588
+ }
589
+ } catch {
590
+ }
591
+ return updated;
592
+ }
593
+ async function normalizeRosterCase(rosterPath) {
594
+ const employees = await loadEmployees(rosterPath);
595
+ let changed = false;
596
+ for (const emp of employees) {
597
+ if (emp.name !== emp.name.toLowerCase()) {
598
+ const oldName = emp.name;
599
+ emp.name = emp.name.toLowerCase();
600
+ changed = true;
601
+ try {
602
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
603
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
604
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
605
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
606
+ renameSync2(oldPath, newPath);
607
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
608
+ const content = readFileSync3(oldPath, "utf-8");
609
+ writeFileSync2(newPath, content, "utf-8");
610
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
611
+ unlinkSync(oldPath);
612
+ }
613
+ }
614
+ } catch {
615
+ }
616
+ }
617
+ }
618
+ if (changed) {
619
+ await saveEmployees(employees, rosterPath);
620
+ }
621
+ return changed;
622
+ }
623
+ function findExeBin() {
624
+ try {
625
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
626
+ } catch {
627
+ return null;
628
+ }
629
+ }
630
+ function registerBinSymlinks(name) {
631
+ const created = [];
632
+ const skipped = [];
633
+ const errors = [];
634
+ const exeBinPath = findExeBin();
635
+ if (!exeBinPath) {
636
+ errors.push("Could not find 'exe-os' in PATH");
637
+ return { created, skipped, errors };
638
+ }
639
+ const binDir = path3.dirname(exeBinPath);
640
+ let target;
641
+ try {
642
+ target = readlinkSync(exeBinPath);
643
+ } catch {
644
+ errors.push("Could not read 'exe' symlink");
645
+ return { created, skipped, errors };
646
+ }
647
+ for (const suffix of ["", "-opencode"]) {
648
+ const linkName = `${name}${suffix}`;
649
+ const linkPath = path3.join(binDir, linkName);
650
+ if (existsSync4(linkPath)) {
651
+ skipped.push(linkName);
652
+ continue;
653
+ }
654
+ try {
655
+ symlinkSync(target, linkPath);
656
+ created.push(linkName);
657
+ } catch (err) {
658
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
659
+ }
660
+ }
661
+ return { created, skipped, errors };
662
+ }
663
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
307
664
  var init_employees = __esm({
308
665
  "src/lib/employees.ts"() {
309
666
  "use strict";
310
667
  init_config();
311
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
668
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
312
669
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
313
670
  COORDINATOR_ROLE = "COO";
314
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
671
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
672
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
673
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
315
674
  }
316
675
  });
317
676
 
@@ -372,7 +731,7 @@ var init_db_retry = __esm({
372
731
 
373
732
  // src/lib/database-adapter.ts
374
733
  import os3 from "os";
375
- import path3 from "path";
734
+ import path4 from "path";
376
735
  import { createRequire } from "module";
377
736
  import { pathToFileURL } from "url";
378
737
  function quotedIdentifier(identifier) {
@@ -683,8 +1042,8 @@ async function loadPrismaClient() {
683
1042
  }
684
1043
  return new PrismaClient2();
685
1044
  }
686
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
687
- 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"));
688
1047
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
689
1048
  const module = await import(pathToFileURL(prismaEntry).href);
690
1049
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -965,8 +1324,8 @@ var init_memory = __esm({
965
1324
 
966
1325
  // src/lib/daemon-auth.ts
967
1326
  import crypto from "crypto";
968
- import path4 from "path";
969
- 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";
970
1329
  function normalizeToken(token) {
971
1330
  if (!token) return null;
972
1331
  const trimmed = token.trim();
@@ -974,8 +1333,8 @@ function normalizeToken(token) {
974
1333
  }
975
1334
  function readDaemonToken() {
976
1335
  try {
977
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
978
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1336
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1337
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
979
1338
  } catch {
980
1339
  return null;
981
1340
  }
@@ -985,7 +1344,7 @@ function ensureDaemonToken(seed) {
985
1344
  if (existing) return existing;
986
1345
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
987
1346
  ensurePrivateDirSync(EXE_AI_DIR);
988
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1347
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
989
1348
  `, "utf8");
990
1349
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
991
1350
  return token;
@@ -996,7 +1355,7 @@ var init_daemon_auth = __esm({
996
1355
  "use strict";
997
1356
  init_config();
998
1357
  init_secure_files();
999
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1358
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1000
1359
  }
1001
1360
  });
1002
1361
 
@@ -1016,8 +1375,8 @@ import net from "net";
1016
1375
  import os4 from "os";
1017
1376
  import { spawn, execSync as execSync2 } from "child_process";
1018
1377
  import { randomUUID } from "crypto";
1019
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1020
- 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";
1021
1380
  import { fileURLToPath } from "url";
1022
1381
  function handleData(chunk) {
1023
1382
  _buffer += chunk.toString();
@@ -1053,9 +1412,9 @@ function isZombie(pid) {
1053
1412
  }
1054
1413
  }
1055
1414
  function cleanupStaleFiles() {
1056
- if (existsSync5(PID_PATH)) {
1415
+ if (existsSync6(PID_PATH)) {
1057
1416
  try {
1058
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1417
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1059
1418
  if (pid > 0) {
1060
1419
  try {
1061
1420
  process.kill(pid, 0);
@@ -1080,11 +1439,11 @@ function cleanupStaleFiles() {
1080
1439
  }
1081
1440
  }
1082
1441
  function findPackageRoot() {
1083
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1084
- const { root } = path5.parse(dir);
1442
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1443
+ const { root } = path6.parse(dir);
1085
1444
  while (dir !== root) {
1086
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1087
- dir = path5.dirname(dir);
1445
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1446
+ dir = path6.dirname(dir);
1088
1447
  }
1089
1448
  return null;
1090
1449
  }
@@ -1102,8 +1461,8 @@ function spawnDaemon() {
1102
1461
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1103
1462
  return;
1104
1463
  }
1105
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1106
- if (!existsSync5(daemonPath)) {
1464
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1465
+ if (!existsSync6(daemonPath)) {
1107
1466
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1108
1467
  `);
1109
1468
  return;
@@ -1112,7 +1471,7 @@ function spawnDaemon() {
1112
1471
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1113
1472
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1114
1473
  `);
1115
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1474
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1116
1475
  let stderrFd = "ignore";
1117
1476
  try {
1118
1477
  stderrFd = openSync(logPath, "a");
@@ -1277,9 +1636,9 @@ function killAndRespawnDaemon() {
1277
1636
  }
1278
1637
  try {
1279
1638
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1280
- if (existsSync5(PID_PATH)) {
1639
+ if (existsSync6(PID_PATH)) {
1281
1640
  try {
1282
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1641
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1283
1642
  if (pid > 0) {
1284
1643
  try {
1285
1644
  process.kill(pid, "SIGKILL");
@@ -1425,9 +1784,9 @@ var init_exe_daemon_client = __esm({
1425
1784
  "use strict";
1426
1785
  init_config();
1427
1786
  init_daemon_auth();
1428
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1429
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1430
- 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");
1431
1790
  SPAWN_LOCK_STALE_MS = 3e4;
1432
1791
  CONNECT_TIMEOUT_MS = 15e3;
1433
1792
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1695,7 +2054,7 @@ __export(database_exports, {
1695
2054
  isInitialized: () => isInitialized,
1696
2055
  setExternalClient: () => setExternalClient
1697
2056
  });
1698
- 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";
1699
2058
  import { createClient } from "@libsql/client";
1700
2059
  import { homedir } from "os";
1701
2060
  import { join } from "path";
@@ -1748,11 +2107,11 @@ function releaseDbLock() {
1748
2107
  }
1749
2108
  async function initDatabase(config) {
1750
2109
  acquireDbLock();
1751
- if (existsSync6(config.dbPath)) {
2110
+ if (existsSync7(config.dbPath)) {
1752
2111
  const dbStat = statSync2(config.dbPath);
1753
2112
  if (dbStat.size === 0) {
1754
2113
  const walPath = config.dbPath + "-wal";
1755
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
2114
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1756
2115
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1757
2116
  copyFileSync(config.dbPath, backupPath);
1758
2117
  unlinkSync3(config.dbPath);
@@ -3338,16 +3697,16 @@ var init_database = __esm({
3338
3697
  });
3339
3698
 
3340
3699
  // src/lib/keychain.ts
3341
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3342
- 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";
3343
3702
  import { execSync as execSync3 } from "child_process";
3344
- import path6 from "path";
3703
+ import path7 from "path";
3345
3704
  import os5 from "os";
3346
3705
  function getKeyDir() {
3347
- 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");
3348
3707
  }
3349
3708
  function getKeyPath() {
3350
- return path6.join(getKeyDir(), "master.key");
3709
+ return path7.join(getKeyDir(), "master.key");
3351
3710
  }
3352
3711
  function nativeKeychainAllowed() {
3353
3712
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -3373,12 +3732,14 @@ function linuxSecretAvailable() {
3373
3732
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3374
3733
  if (process.platform !== "linux") return false;
3375
3734
  try {
3376
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3377
3735
  const st = statSync3(keyPath);
3378
3736
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3737
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3379
3738
  if (uid === 0) return true;
3380
3739
  const exeOsDir = process.env.EXE_OS_DIR;
3381
- 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;
3382
3743
  } catch {
3383
3744
  return false;
3384
3745
  }
@@ -3528,15 +3889,25 @@ async function writeMachineBoundFileFallback(b64) {
3528
3889
  await mkdir3(dir, { recursive: true });
3529
3890
  const keyPath = getKeyPath();
3530
3891
  const machineKey = deriveMachineKey();
3531
- if (machineKey) {
3532
- const encrypted = encryptWithMachineKey(b64, machineKey);
3533
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3534
- await chmod2(keyPath, 384);
3535
- 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;
3536
3909
  }
3537
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3538
- await chmod2(keyPath, 384);
3539
- return "plaintext";
3910
+ return result;
3540
3911
  }
3541
3912
  async function getMasterKey() {
3542
3913
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -3575,7 +3946,7 @@ async function getMasterKey() {
3575
3946
  }
3576
3947
  }
3577
3948
  const keyPath = getKeyPath();
3578
- if (!existsSync7(keyPath)) {
3949
+ if (!existsSync8(keyPath)) {
3579
3950
  process.stderr.write(
3580
3951
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3581
3952
  `
@@ -3603,7 +3974,7 @@ async function getMasterKey() {
3603
3974
  b64Value = content;
3604
3975
  }
3605
3976
  const key = Buffer.from(b64Value, "base64");
3606
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
3977
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3607
3978
  return key;
3608
3979
  }
3609
3980
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -3909,14 +4280,14 @@ __export(shard_manager_exports, {
3909
4280
  listShards: () => listShards,
3910
4281
  shardExists: () => shardExists
3911
4282
  });
3912
- import path7 from "path";
3913
- 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";
3914
4285
  import { createClient as createClient2 } from "@libsql/client";
3915
4286
  function initShardManager(encryptionKey) {
3916
4287
  _encryptionKey = encryptionKey;
3917
4288
  _keyValidated = false;
3918
4289
  _keyValidationPromise = null;
3919
- if (!existsSync8(SHARDS_DIR)) {
4290
+ if (!existsSync9(SHARDS_DIR)) {
3920
4291
  mkdirSync3(SHARDS_DIR, { recursive: true });
3921
4292
  }
3922
4293
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3937,7 +4308,7 @@ async function validateEncryptionKey() {
3937
4308
  return true;
3938
4309
  }
3939
4310
  for (const shardFile of existingShards.slice(0, 3)) {
3940
- const dbPath = path7.join(SHARDS_DIR, shardFile);
4311
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3941
4312
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3942
4313
  try {
3943
4314
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -3980,7 +4351,7 @@ function getShardClient(projectName) {
3980
4351
  while (_shards.size >= MAX_OPEN_SHARDS) {
3981
4352
  evictLRU();
3982
4353
  }
3983
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4354
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3984
4355
  const client = createClient2({
3985
4356
  url: `file:${dbPath}`,
3986
4357
  encryptionKey: _encryptionKey
@@ -3991,13 +4362,13 @@ function getShardClient(projectName) {
3991
4362
  }
3992
4363
  function shardExists(projectName) {
3993
4364
  const safeName = safeShardName(projectName);
3994
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
4365
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
3995
4366
  }
3996
4367
  function safeShardName(projectName) {
3997
4368
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3998
4369
  }
3999
4370
  function listShards() {
4000
- if (!existsSync8(SHARDS_DIR)) return [];
4371
+ if (!existsSync9(SHARDS_DIR)) return [];
4001
4372
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4002
4373
  }
4003
4374
  async function auditShardHealth(options = {}) {
@@ -4009,7 +4380,7 @@ async function auditShardHealth(options = {}) {
4009
4380
  const names = listShards();
4010
4381
  const shards = [];
4011
4382
  for (const name of names) {
4012
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
4383
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
4013
4384
  const stat = statSync4(dbPath);
4014
4385
  const item = {
4015
4386
  name,
@@ -4044,7 +4415,7 @@ async function auditShardHealth(options = {}) {
4044
4415
  _shards.delete(name);
4045
4416
  _shardLastAccess.delete(name);
4046
4417
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4047
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4418
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4048
4419
  renameSync3(dbPath, archivedPath);
4049
4420
  item.archivedPath = archivedPath;
4050
4421
  }
@@ -4272,11 +4643,11 @@ async function getReadyShardClient(projectName) {
4272
4643
  client.close();
4273
4644
  _shards.delete(safeName);
4274
4645
  _shardLastAccess.delete(safeName);
4275
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4276
- if (existsSync8(dbPath)) {
4646
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4647
+ if (existsSync9(dbPath)) {
4277
4648
  const stat = statSync4(dbPath);
4278
4649
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4279
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4650
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4280
4651
  renameSync3(dbPath, archivedPath);
4281
4652
  process.stderr.write(
4282
4653
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -4344,7 +4715,7 @@ var init_shard_manager = __esm({
4344
4715
  "src/lib/shard-manager.ts"() {
4345
4716
  "use strict";
4346
4717
  init_config();
4347
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
4718
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
4348
4719
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4349
4720
  MAX_OPEN_SHARDS = 10;
4350
4721
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -5838,13 +6209,13 @@ var init_store = __esm({
5838
6209
  });
5839
6210
 
5840
6211
  // src/lib/session-registry.ts
5841
- import path8 from "path";
6212
+ import path9 from "path";
5842
6213
  import os6 from "os";
5843
6214
  var REGISTRY_PATH;
5844
6215
  var init_session_registry = __esm({
5845
6216
  "src/lib/session-registry.ts"() {
5846
6217
  "use strict";
5847
- REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
6218
+ REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
5848
6219
  }
5849
6220
  });
5850
6221
 
@@ -6062,51 +6433,6 @@ var init_provider_table = __esm({
6062
6433
  }
6063
6434
  });
6064
6435
 
6065
- // src/lib/runtime-table.ts
6066
- var RUNTIME_TABLE;
6067
- var init_runtime_table = __esm({
6068
- "src/lib/runtime-table.ts"() {
6069
- "use strict";
6070
- RUNTIME_TABLE = {
6071
- codex: {
6072
- binary: "codex",
6073
- launchMode: "interactive",
6074
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
6075
- inlineFlag: "--no-alt-screen",
6076
- apiKeyEnv: "OPENAI_API_KEY",
6077
- defaultModel: "gpt-5.5"
6078
- },
6079
- opencode: {
6080
- binary: "opencode",
6081
- launchMode: "exec",
6082
- autoApproveFlag: "--dangerously-skip-permissions",
6083
- inlineFlag: "",
6084
- apiKeyEnv: "ANTHROPIC_API_KEY",
6085
- defaultModel: "anthropic/claude-sonnet-4-6"
6086
- }
6087
- };
6088
- }
6089
- });
6090
-
6091
- // src/lib/agent-config.ts
6092
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
6093
- import path9 from "path";
6094
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
6095
- var init_agent_config = __esm({
6096
- "src/lib/agent-config.ts"() {
6097
- "use strict";
6098
- init_config();
6099
- init_runtime_table();
6100
- init_secure_files();
6101
- AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
6102
- DEFAULT_MODELS = {
6103
- claude: "claude-opus-4.6",
6104
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
6105
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
6106
- };
6107
- }
6108
- });
6109
-
6110
6436
  // src/lib/intercom-queue.ts
6111
6437
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
6112
6438
  import path10 from "path";
@@ -6187,6 +6513,21 @@ function isRootSession(name) {
6187
6513
  function extractRootExe(name) {
6188
6514
  if (!name) return null;
6189
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
+ }
6190
6531
  const parts = name.split("-").filter(Boolean);
6191
6532
  return parts.length > 0 ? parts[parts.length - 1] : null;
6192
6533
  }
@@ -6205,6 +6546,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6205
6546
  function getParentExe(sessionKey) {
6206
6547
  try {
6207
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
+ }
6208
6553
  return data.parentExe || null;
6209
6554
  } catch {
6210
6555
  return null;
@@ -6277,7 +6622,7 @@ function resolveExeSession() {
6277
6622
  }
6278
6623
  return candidate;
6279
6624
  }
6280
- 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;
6281
6626
  var init_tmux_routing = __esm({
6282
6627
  "src/lib/tmux-routing.ts"() {
6283
6628
  "use strict";
@@ -6295,6 +6640,7 @@ var init_tmux_routing = __esm({
6295
6640
  init_agent_symlinks();
6296
6641
  SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
6297
6642
  SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
6643
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
6298
6644
  INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
6299
6645
  DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
6300
6646
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;