@askexenow/exe-os 0.9.112 → 0.9.113

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 (95) hide show
  1. package/README.md +9 -7
  2. package/dist/bin/agentic-ontology-backfill.js +54 -11
  3. package/dist/bin/agentic-reflection-backfill.js +29 -1
  4. package/dist/bin/agentic-semantic-label.js +29 -1
  5. package/dist/bin/backfill-conversations.js +53 -10
  6. package/dist/bin/backfill-responses.js +54 -11
  7. package/dist/bin/backfill-vectors.js +29 -1
  8. package/dist/bin/bulk-sync-postgres.js +55 -12
  9. package/dist/bin/cleanup-stale-review-tasks.js +75 -15
  10. package/dist/bin/cli.js +293 -76
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +28 -2
  13. package/dist/bin/exe-assign.js +54 -11
  14. package/dist/bin/exe-boot.js +481 -147
  15. package/dist/bin/exe-call.js +45 -4
  16. package/dist/bin/exe-cloud.js +93 -15
  17. package/dist/bin/exe-dispatch.js +369 -24
  18. package/dist/bin/exe-doctor.js +53 -10
  19. package/dist/bin/exe-export-behaviors.js +54 -11
  20. package/dist/bin/exe-forget.js +54 -11
  21. package/dist/bin/exe-gateway.js +128 -23
  22. package/dist/bin/exe-heartbeat.js +75 -15
  23. package/dist/bin/exe-kill.js +54 -11
  24. package/dist/bin/exe-launch-agent.js +70 -12
  25. package/dist/bin/exe-new-employee.js +175 -7
  26. package/dist/bin/exe-pending-messages.js +75 -15
  27. package/dist/bin/exe-pending-notifications.js +75 -15
  28. package/dist/bin/exe-pending-reviews.js +75 -15
  29. package/dist/bin/exe-rename.js +54 -11
  30. package/dist/bin/exe-review.js +54 -11
  31. package/dist/bin/exe-search.js +54 -11
  32. package/dist/bin/exe-session-cleanup.js +491 -146
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +524 -245
  35. package/dist/bin/exe-start-opencode.js +534 -165
  36. package/dist/bin/exe-status.js +75 -15
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +54 -11
  39. package/dist/bin/git-sweep.js +369 -24
  40. package/dist/bin/graph-backfill.js +54 -11
  41. package/dist/bin/graph-export.js +54 -11
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +491 -146
  44. package/dist/bin/pre-publish.js +13 -1
  45. package/dist/bin/scan-tasks.js +369 -24
  46. package/dist/bin/setup.js +91 -13
  47. package/dist/bin/shard-migrate.js +54 -11
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +128 -23
  51. package/dist/hooks/bug-report-worker.js +128 -23
  52. package/dist/hooks/codex-stop-task-finalizer.js +512 -140
  53. package/dist/hooks/commit-complete.js +369 -24
  54. package/dist/hooks/error-recall.js +54 -11
  55. package/dist/hooks/ingest.js +4575 -252
  56. package/dist/hooks/instructions-loaded.js +54 -11
  57. package/dist/hooks/notification.js +54 -11
  58. package/dist/hooks/post-compact.js +75 -15
  59. package/dist/hooks/post-tool-combined.js +75 -15
  60. package/dist/hooks/pre-compact.js +449 -104
  61. package/dist/hooks/pre-tool-use.js +90 -15
  62. package/dist/hooks/prompt-submit.js +129 -24
  63. package/dist/hooks/session-end.js +451 -109
  64. package/dist/hooks/session-start.js +104 -16
  65. package/dist/hooks/stop.js +74 -14
  66. package/dist/hooks/subagent-stop.js +75 -15
  67. package/dist/hooks/summary-worker.js +73 -7
  68. package/dist/index.js +128 -23
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +38 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +16 -0
  73. package/dist/lib/db.js +16 -0
  74. package/dist/lib/device-registry.js +16 -0
  75. package/dist/lib/employee-templates.js +29 -3
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +268 -42
  78. package/dist/lib/hybrid-search.js +54 -11
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +29 -1
  82. package/dist/lib/skill-learning.js +458 -70
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +54 -11
  85. package/dist/lib/tasks.js +393 -91
  86. package/dist/lib/tmux-routing.js +316 -14
  87. package/dist/mcp/server.js +169 -30
  88. package/dist/mcp/tools/create-task.js +75 -13
  89. package/dist/mcp/tools/deactivate-behavior.js +33 -24
  90. package/dist/mcp/tools/list-tasks.js +21 -4
  91. package/dist/mcp/tools/send-message.js +21 -4
  92. package/dist/mcp/tools/update-task.js +390 -91
  93. package/dist/runtime/index.js +446 -101
  94. package/dist/tui/App.js +208 -54
  95. package/package.json +1 -1
@@ -395,11 +395,168 @@ var init_config = __esm({
395
395
  }
396
396
  });
397
397
 
398
+ // src/lib/runtime-table.ts
399
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
400
+ var init_runtime_table = __esm({
401
+ "src/lib/runtime-table.ts"() {
402
+ "use strict";
403
+ RUNTIME_TABLE = {
404
+ codex: {
405
+ binary: "codex",
406
+ launchMode: "interactive",
407
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
408
+ inlineFlag: "--no-alt-screen",
409
+ apiKeyEnv: "OPENAI_API_KEY",
410
+ defaultModel: "gpt-5.5"
411
+ },
412
+ opencode: {
413
+ binary: "opencode",
414
+ launchMode: "exec",
415
+ autoApproveFlag: "--dangerously-skip-permissions",
416
+ inlineFlag: "",
417
+ apiKeyEnv: "ANTHROPIC_API_KEY",
418
+ defaultModel: "anthropic/claude-sonnet-4-6"
419
+ }
420
+ };
421
+ DEFAULT_RUNTIME = "claude";
422
+ }
423
+ });
424
+
425
+ // src/lib/agent-config.ts
426
+ var agent_config_exports = {};
427
+ __export(agent_config_exports, {
428
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
429
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
430
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
431
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
432
+ clearAgentRuntime: () => clearAgentRuntime,
433
+ getAgentRuntime: () => getAgentRuntime,
434
+ loadAgentConfig: () => loadAgentConfig,
435
+ saveAgentConfig: () => saveAgentConfig,
436
+ setAgentMcps: () => setAgentMcps,
437
+ setAgentRuntime: () => setAgentRuntime
438
+ });
439
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
440
+ import path2 from "path";
441
+ function loadAgentConfig() {
442
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
443
+ try {
444
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
445
+ } catch {
446
+ return {};
447
+ }
448
+ }
449
+ function saveAgentConfig(config) {
450
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
451
+ ensurePrivateDirSync(dir);
452
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
453
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
454
+ }
455
+ function getAgentRuntime(agentId) {
456
+ const config = loadAgentConfig();
457
+ const entry = config[agentId];
458
+ if (entry) return entry;
459
+ const orgDefault = config["default"];
460
+ if (orgDefault) return orgDefault;
461
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
462
+ }
463
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
464
+ const knownModels = KNOWN_RUNTIMES[runtime];
465
+ if (!knownModels) {
466
+ return {
467
+ ok: false,
468
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
469
+ };
470
+ }
471
+ if (!knownModels.includes(model)) {
472
+ return {
473
+ ok: false,
474
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
475
+ };
476
+ }
477
+ const config = loadAgentConfig();
478
+ const existing = config[agentId];
479
+ const entry = { runtime, model };
480
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
481
+ if (mcps !== void 0) {
482
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
483
+ } else if (existing?.mcps) {
484
+ entry.mcps = existing.mcps;
485
+ }
486
+ config[agentId] = entry;
487
+ saveAgentConfig(config);
488
+ return { ok: true };
489
+ }
490
+ function setAgentMcps(agentId, mcps) {
491
+ const config = loadAgentConfig();
492
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
493
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
494
+ config[agentId] = existing;
495
+ saveAgentConfig(config);
496
+ return { ok: true };
497
+ }
498
+ function clearAgentRuntime(agentId) {
499
+ const config = loadAgentConfig();
500
+ delete config[agentId];
501
+ saveAgentConfig(config);
502
+ }
503
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
504
+ var init_agent_config = __esm({
505
+ "src/lib/agent-config.ts"() {
506
+ "use strict";
507
+ init_config();
508
+ init_runtime_table();
509
+ init_secure_files();
510
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
511
+ KNOWN_RUNTIMES = {
512
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
513
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
514
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
515
+ };
516
+ RUNTIME_LABELS = {
517
+ claude: "Claude Code (Anthropic)",
518
+ codex: "Codex (OpenAI)",
519
+ opencode: "OpenCode (open source)"
520
+ };
521
+ DEFAULT_MODELS = {
522
+ claude: "claude-opus-4.6",
523
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
524
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
525
+ };
526
+ }
527
+ });
528
+
398
529
  // src/lib/employees.ts
530
+ var employees_exports = {};
531
+ __export(employees_exports, {
532
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
533
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
534
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
535
+ addEmployee: () => addEmployee,
536
+ baseAgentName: () => baseAgentName,
537
+ canCoordinate: () => canCoordinate,
538
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
539
+ getCoordinatorName: () => getCoordinatorName,
540
+ getEmployee: () => getEmployee,
541
+ getEmployeeByRole: () => getEmployeeByRole,
542
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
543
+ hasRole: () => hasRole,
544
+ hireEmployee: () => hireEmployee,
545
+ isCoordinatorName: () => isCoordinatorName,
546
+ isCoordinatorRole: () => isCoordinatorRole,
547
+ isMultiInstance: () => isMultiInstance,
548
+ loadEmployees: () => loadEmployees,
549
+ loadEmployeesSync: () => loadEmployeesSync,
550
+ normalizeRole: () => normalizeRole,
551
+ normalizeRosterCase: () => normalizeRosterCase,
552
+ registerBinSymlinks: () => registerBinSymlinks,
553
+ saveEmployees: () => saveEmployees,
554
+ validateEmployeeName: () => validateEmployeeName
555
+ });
399
556
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
400
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
557
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
401
558
  import { execSync } from "child_process";
402
- import path2 from "path";
559
+ import path3 from "path";
403
560
  import os2 from "os";
404
561
  function normalizeRole(role) {
405
562
  return (role ?? "").trim().toLowerCase();
@@ -413,10 +570,47 @@ function getCoordinatorEmployee(employees) {
413
570
  function getCoordinatorName(employees = loadEmployeesSync()) {
414
571
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
415
572
  }
573
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
574
+ if (!agentName) return false;
575
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
576
+ }
577
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
578
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
579
+ }
580
+ function validateEmployeeName(name) {
581
+ if (!name) {
582
+ return { valid: false, error: "Name is required" };
583
+ }
584
+ if (name.length > 32) {
585
+ return { valid: false, error: "Name must be 32 characters or fewer" };
586
+ }
587
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
588
+ return {
589
+ valid: false,
590
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
591
+ };
592
+ }
593
+ return { valid: true };
594
+ }
595
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
596
+ if (!existsSync4(employeesPath)) {
597
+ return [];
598
+ }
599
+ const raw = await readFile2(employeesPath, "utf-8");
600
+ try {
601
+ return JSON.parse(raw);
602
+ } catch {
603
+ return [];
604
+ }
605
+ }
606
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
607
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
608
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
609
+ }
416
610
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
417
- if (!existsSync3(employeesPath)) return [];
611
+ if (!existsSync4(employeesPath)) return [];
418
612
  try {
419
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
613
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
420
614
  } catch {
421
615
  return [];
422
616
  }
@@ -424,6 +618,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
424
618
  function getEmployee(employees, name) {
425
619
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
426
620
  }
621
+ function getEmployeeByRole(employees, role) {
622
+ const lower = role.toLowerCase();
623
+ return employees.find((e) => e.role.toLowerCase() === lower);
624
+ }
625
+ function getEmployeeNamesByRole(employees, role) {
626
+ const lower = role.toLowerCase();
627
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
628
+ }
629
+ function hasRole(agentName, role) {
630
+ const employees = loadEmployeesSync();
631
+ const emp = getEmployee(employees, agentName);
632
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
633
+ }
427
634
  function baseAgentName(name, employees) {
428
635
  const match = name.match(/^([a-zA-Z]+)\d+$/);
429
636
  if (!match) return name;
@@ -432,21 +639,153 @@ function baseAgentName(name, employees) {
432
639
  if (getEmployee(roster, base)) return base;
433
640
  return name;
434
641
  }
435
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
642
+ function isMultiInstance(agentName, employees) {
643
+ const roster = employees ?? loadEmployeesSync();
644
+ const emp = getEmployee(roster, agentName);
645
+ if (!emp) return false;
646
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
647
+ }
648
+ function addEmployee(employees, employee) {
649
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
650
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
651
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
652
+ throw new Error(`Employee '${normalized.name}' already exists`);
653
+ }
654
+ return [...employees, normalized];
655
+ }
656
+ function appendToCoordinatorTeam(employee) {
657
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
658
+ if (!coordinator) return;
659
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
660
+ if (!existsSync4(idPath)) return;
661
+ const content = readFileSync3(idPath, "utf-8");
662
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
663
+ const teamMatch = content.match(TEAM_SECTION_RE);
664
+ if (!teamMatch || teamMatch.index === void 0) return;
665
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
666
+ const nextHeading = afterTeam.match(/\n## /);
667
+ const entry = `
668
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
669
+ `;
670
+ let updated;
671
+ if (nextHeading && nextHeading.index !== void 0) {
672
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
673
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
674
+ } else {
675
+ updated = content.trimEnd() + "\n" + entry;
676
+ }
677
+ writeFileSync2(idPath, updated, "utf-8");
678
+ }
679
+ function capitalize(s) {
680
+ return s.charAt(0).toUpperCase() + s.slice(1);
681
+ }
682
+ async function hireEmployee(employee) {
683
+ const employees = await loadEmployees();
684
+ const updated = addEmployee(employees, employee);
685
+ await saveEmployees(updated);
686
+ try {
687
+ appendToCoordinatorTeam(employee);
688
+ } catch {
689
+ }
690
+ try {
691
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
692
+ const config = loadAgentConfig2();
693
+ const name = employee.name.toLowerCase();
694
+ if (!config[name] && config["default"]) {
695
+ config[name] = { ...config["default"] };
696
+ saveAgentConfig2(config);
697
+ }
698
+ } catch {
699
+ }
700
+ return updated;
701
+ }
702
+ async function normalizeRosterCase(rosterPath) {
703
+ const employees = await loadEmployees(rosterPath);
704
+ let changed = false;
705
+ for (const emp of employees) {
706
+ if (emp.name !== emp.name.toLowerCase()) {
707
+ const oldName = emp.name;
708
+ emp.name = emp.name.toLowerCase();
709
+ changed = true;
710
+ try {
711
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
712
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
713
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
714
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
715
+ renameSync2(oldPath, newPath);
716
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
717
+ const content = readFileSync3(oldPath, "utf-8");
718
+ writeFileSync2(newPath, content, "utf-8");
719
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
720
+ unlinkSync(oldPath);
721
+ }
722
+ }
723
+ } catch {
724
+ }
725
+ }
726
+ }
727
+ if (changed) {
728
+ await saveEmployees(employees, rosterPath);
729
+ }
730
+ return changed;
731
+ }
732
+ function findExeBin() {
733
+ try {
734
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
735
+ } catch {
736
+ return null;
737
+ }
738
+ }
739
+ function registerBinSymlinks(name) {
740
+ const created = [];
741
+ const skipped = [];
742
+ const errors = [];
743
+ const exeBinPath = findExeBin();
744
+ if (!exeBinPath) {
745
+ errors.push("Could not find 'exe-os' in PATH");
746
+ return { created, skipped, errors };
747
+ }
748
+ const binDir = path3.dirname(exeBinPath);
749
+ let target;
750
+ try {
751
+ target = readlinkSync(exeBinPath);
752
+ } catch {
753
+ errors.push("Could not read 'exe' symlink");
754
+ return { created, skipped, errors };
755
+ }
756
+ for (const suffix of ["", "-opencode"]) {
757
+ const linkName = `${name}${suffix}`;
758
+ const linkPath = path3.join(binDir, linkName);
759
+ if (existsSync4(linkPath)) {
760
+ skipped.push(linkName);
761
+ continue;
762
+ }
763
+ try {
764
+ symlinkSync(target, linkPath);
765
+ created.push(linkName);
766
+ } catch (err) {
767
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
768
+ }
769
+ }
770
+ return { created, skipped, errors };
771
+ }
772
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
436
773
  var init_employees = __esm({
437
774
  "src/lib/employees.ts"() {
438
775
  "use strict";
439
776
  init_config();
440
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
777
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
441
778
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
442
779
  COORDINATOR_ROLE = "COO";
443
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
780
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
781
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
782
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
444
783
  }
445
784
  });
446
785
 
447
786
  // src/lib/database-adapter.ts
448
787
  import os3 from "os";
449
- import path3 from "path";
788
+ import path4 from "path";
450
789
  import { createRequire } from "module";
451
790
  import { pathToFileURL } from "url";
452
791
  function quotedIdentifier(identifier) {
@@ -757,8 +1096,8 @@ async function loadPrismaClient() {
757
1096
  }
758
1097
  return new PrismaClient2();
759
1098
  }
760
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
761
- const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
1099
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1100
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
762
1101
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
763
1102
  const module = await import(pathToFileURL(prismaEntry).href);
764
1103
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -1030,8 +1369,8 @@ var init_database_adapter = __esm({
1030
1369
 
1031
1370
  // src/lib/daemon-auth.ts
1032
1371
  import crypto from "crypto";
1033
- import path4 from "path";
1034
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1372
+ import path5 from "path";
1373
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1035
1374
  function normalizeToken(token) {
1036
1375
  if (!token) return null;
1037
1376
  const trimmed = token.trim();
@@ -1039,8 +1378,8 @@ function normalizeToken(token) {
1039
1378
  }
1040
1379
  function readDaemonToken() {
1041
1380
  try {
1042
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
1043
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1381
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1382
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
1044
1383
  } catch {
1045
1384
  return null;
1046
1385
  }
@@ -1050,7 +1389,7 @@ function ensureDaemonToken(seed) {
1050
1389
  if (existing) return existing;
1051
1390
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1052
1391
  ensurePrivateDirSync(EXE_AI_DIR);
1053
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1392
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1054
1393
  `, "utf8");
1055
1394
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1056
1395
  return token;
@@ -1061,7 +1400,7 @@ var init_daemon_auth = __esm({
1061
1400
  "use strict";
1062
1401
  init_config();
1063
1402
  init_secure_files();
1064
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1403
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1065
1404
  }
1066
1405
  });
1067
1406
 
@@ -1070,8 +1409,8 @@ import net from "net";
1070
1409
  import os4 from "os";
1071
1410
  import { spawn, execSync as execSync2 } from "child_process";
1072
1411
  import { randomUUID } from "crypto";
1073
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1074
- import path5 from "path";
1412
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1413
+ import path6 from "path";
1075
1414
  import { fileURLToPath } from "url";
1076
1415
  function handleData(chunk) {
1077
1416
  _buffer += chunk.toString();
@@ -1107,9 +1446,9 @@ function isZombie(pid) {
1107
1446
  }
1108
1447
  }
1109
1448
  function cleanupStaleFiles() {
1110
- if (existsSync5(PID_PATH)) {
1449
+ if (existsSync6(PID_PATH)) {
1111
1450
  try {
1112
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1451
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1113
1452
  if (pid > 0) {
1114
1453
  try {
1115
1454
  process.kill(pid, 0);
@@ -1134,11 +1473,11 @@ function cleanupStaleFiles() {
1134
1473
  }
1135
1474
  }
1136
1475
  function findPackageRoot() {
1137
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1138
- const { root } = path5.parse(dir);
1476
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1477
+ const { root } = path6.parse(dir);
1139
1478
  while (dir !== root) {
1140
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1141
- dir = path5.dirname(dir);
1479
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1480
+ dir = path6.dirname(dir);
1142
1481
  }
1143
1482
  return null;
1144
1483
  }
@@ -1156,8 +1495,8 @@ function spawnDaemon() {
1156
1495
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1157
1496
  return;
1158
1497
  }
1159
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1160
- if (!existsSync5(daemonPath)) {
1498
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1499
+ if (!existsSync6(daemonPath)) {
1161
1500
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1162
1501
  `);
1163
1502
  return;
@@ -1166,7 +1505,7 @@ function spawnDaemon() {
1166
1505
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1167
1506
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1168
1507
  `);
1169
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1508
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1170
1509
  let stderrFd = "ignore";
1171
1510
  try {
1172
1511
  stderrFd = openSync(logPath, "a");
@@ -1316,9 +1655,9 @@ var init_exe_daemon_client = __esm({
1316
1655
  "use strict";
1317
1656
  init_config();
1318
1657
  init_daemon_auth();
1319
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1320
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1321
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1658
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1659
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1660
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1322
1661
  SPAWN_LOCK_STALE_MS = 3e4;
1323
1662
  CONNECT_TIMEOUT_MS = 15e3;
1324
1663
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1545,7 +1884,7 @@ __export(database_exports, {
1545
1884
  isInitialized: () => isInitialized,
1546
1885
  setExternalClient: () => setExternalClient
1547
1886
  });
1548
- 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";
1887
+ 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";
1549
1888
  import { createClient } from "@libsql/client";
1550
1889
  import { homedir } from "os";
1551
1890
  import { join } from "path";
@@ -1598,11 +1937,11 @@ function releaseDbLock() {
1598
1937
  }
1599
1938
  async function initDatabase(config) {
1600
1939
  acquireDbLock();
1601
- if (existsSync6(config.dbPath)) {
1940
+ if (existsSync7(config.dbPath)) {
1602
1941
  const dbStat = statSync2(config.dbPath);
1603
1942
  if (dbStat.size === 0) {
1604
1943
  const walPath = config.dbPath + "-wal";
1605
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
1944
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1606
1945
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1607
1946
  copyFileSync(config.dbPath, backupPath);
1608
1947
  unlinkSync3(config.dbPath);
@@ -3121,6 +3460,22 @@ async function ensureSchema() {
3121
3460
  } catch (e) {
3122
3461
  logCatchDebug("migration", e);
3123
3462
  }
3463
+ try {
3464
+ await client.execute({
3465
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
3466
+ args: []
3467
+ });
3468
+ } catch (e) {
3469
+ logCatchDebug("migration", e);
3470
+ }
3471
+ try {
3472
+ await client.execute({
3473
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
3474
+ args: []
3475
+ });
3476
+ } catch (e) {
3477
+ logCatchDebug("migration", e);
3478
+ }
3124
3479
  }
3125
3480
  async function disposeDatabase() {
3126
3481
  if (_walCheckpointTimer) {
@@ -3186,14 +3541,14 @@ __export(shard_manager_exports, {
3186
3541
  listShards: () => listShards,
3187
3542
  shardExists: () => shardExists
3188
3543
  });
3189
- import path7 from "path";
3190
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3544
+ import path8 from "path";
3545
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3191
3546
  import { createClient as createClient2 } from "@libsql/client";
3192
3547
  function initShardManager(encryptionKey) {
3193
3548
  _encryptionKey = encryptionKey;
3194
3549
  _keyValidated = false;
3195
3550
  _keyValidationPromise = null;
3196
- if (!existsSync8(SHARDS_DIR)) {
3551
+ if (!existsSync9(SHARDS_DIR)) {
3197
3552
  mkdirSync3(SHARDS_DIR, { recursive: true });
3198
3553
  }
3199
3554
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3214,7 +3569,7 @@ async function validateEncryptionKey() {
3214
3569
  return true;
3215
3570
  }
3216
3571
  for (const shardFile of existingShards.slice(0, 3)) {
3217
- const dbPath = path7.join(SHARDS_DIR, shardFile);
3572
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3218
3573
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3219
3574
  try {
3220
3575
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -3257,7 +3612,7 @@ function getShardClient(projectName) {
3257
3612
  while (_shards.size >= MAX_OPEN_SHARDS) {
3258
3613
  evictLRU();
3259
3614
  }
3260
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3615
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3261
3616
  const client = createClient2({
3262
3617
  url: `file:${dbPath}`,
3263
3618
  encryptionKey: _encryptionKey
@@ -3268,13 +3623,13 @@ function getShardClient(projectName) {
3268
3623
  }
3269
3624
  function shardExists(projectName) {
3270
3625
  const safeName = safeShardName(projectName);
3271
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
3626
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
3272
3627
  }
3273
3628
  function safeShardName(projectName) {
3274
3629
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3275
3630
  }
3276
3631
  function listShards() {
3277
- if (!existsSync8(SHARDS_DIR)) return [];
3632
+ if (!existsSync9(SHARDS_DIR)) return [];
3278
3633
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3279
3634
  }
3280
3635
  async function auditShardHealth(options = {}) {
@@ -3286,7 +3641,7 @@ async function auditShardHealth(options = {}) {
3286
3641
  const names = listShards();
3287
3642
  const shards = [];
3288
3643
  for (const name of names) {
3289
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3644
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
3290
3645
  const stat = statSync4(dbPath);
3291
3646
  const item = {
3292
3647
  name,
@@ -3321,7 +3676,7 @@ async function auditShardHealth(options = {}) {
3321
3676
  _shards.delete(name);
3322
3677
  _shardLastAccess.delete(name);
3323
3678
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3324
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3679
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3325
3680
  renameSync3(dbPath, archivedPath);
3326
3681
  item.archivedPath = archivedPath;
3327
3682
  }
@@ -3549,11 +3904,11 @@ async function getReadyShardClient(projectName) {
3549
3904
  client.close();
3550
3905
  _shards.delete(safeName);
3551
3906
  _shardLastAccess.delete(safeName);
3552
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3553
- if (existsSync8(dbPath)) {
3907
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3908
+ if (existsSync9(dbPath)) {
3554
3909
  const stat = statSync4(dbPath);
3555
3910
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3556
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3911
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3557
3912
  renameSync3(dbPath, archivedPath);
3558
3913
  process.stderr.write(
3559
3914
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -3621,7 +3976,7 @@ var init_shard_manager = __esm({
3621
3976
  "src/lib/shard-manager.ts"() {
3622
3977
  "use strict";
3623
3978
  init_config();
3624
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
3979
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
3625
3980
  SHARD_IDLE_MS = 5 * 60 * 1e3;
3626
3981
  MAX_OPEN_SHARDS = 10;
3627
3982
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -3687,11 +4042,17 @@ var init_platform_procedures = __esm({
3687
4042
  content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3688
4043
  },
3689
4044
  {
3690
- title: "Customer orchestration maturity \u2014 recommend, never trap",
4045
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
3691
4046
  domain: "workflow",
3692
4047
  priority: "p1",
3693
4048
  content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
3694
4049
  },
4050
+ {
4051
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
4052
+ domain: "identity",
4053
+ priority: "p0",
4054
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
4055
+ },
3695
4056
  {
3696
4057
  title: "Single dispatch path \u2014 create_task only",
3697
4058
  domain: "workflow",
@@ -3725,6 +4086,12 @@ var init_platform_procedures = __esm({
3725
4086
  priority: "p0",
3726
4087
  content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
3727
4088
  },
4089
+ {
4090
+ title: "Destructive operations \u2014 mandatory reviewer gate",
4091
+ domain: "security",
4092
+ priority: "p0",
4093
+ content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
4094
+ },
3728
4095
  {
3729
4096
  title: "Customer patch triage \u2014 upstream bug vs customization",
3730
4097
  domain: "support",
@@ -4010,10 +4377,24 @@ function stableId(memoryId, type, content) {
4010
4377
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4011
4378
  }
4012
4379
  function cleanText(text) {
4013
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4380
+ let cleaned = text.replace(
4381
+ /```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
4382
+ (_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
4383
+ );
4384
+ cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4385
+ return cleaned;
4014
4386
  }
4015
- function splitSentences(text) {
4016
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4387
+ function splitSegments(text) {
4388
+ const cleaned = cleanText(text);
4389
+ const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
4390
+ if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
4391
+ const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
4392
+ if (lines.length > 0) return lines;
4393
+ if (cleaned.length >= MIN_SEGMENT_CHARS) {
4394
+ return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
4395
+ }
4396
+ }
4397
+ return segments;
4017
4398
  }
4018
4399
  function inferCardType(sentence, toolName) {
4019
4400
  const lower = sentence.toLowerCase();
@@ -4045,12 +4426,12 @@ function predicateFor(type) {
4045
4426
  }
4046
4427
  }
4047
4428
  function extractMemoryCards(row) {
4048
- const sentences = splitSentences(row.raw_text);
4429
+ const segments = splitSegments(row.raw_text);
4049
4430
  const cards = [];
4050
- for (const sentence of sentences) {
4431
+ for (const sentence of segments) {
4051
4432
  const type = inferCardType(sentence, row.tool_name);
4052
4433
  const subject = extractSubject(sentence, row.agent_id);
4053
- const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4434
+ const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
4054
4435
  cards.push({
4055
4436
  id: stableId(row.id, type, content),
4056
4437
  memory_id: row.id,
@@ -4146,13 +4527,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4146
4527
  last_accessed: String(row.timestamp)
4147
4528
  }));
4148
4529
  }
4149
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4530
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
4150
4531
  var init_memory_cards = __esm({
4151
4532
  "src/lib/memory-cards.ts"() {
4152
4533
  "use strict";
4153
4534
  init_database();
4154
- MAX_CARDS_PER_MEMORY = 6;
4155
- MAX_SENTENCE_CHARS = 360;
4535
+ MAX_CARDS_PER_MEMORY = 8;
4536
+ MAX_SEGMENT_CHARS = 500;
4537
+ MIN_SEGMENT_CHARS = 20;
4156
4538
  }
4157
4539
  });
4158
4540
 
@@ -4424,33 +4806,6 @@ var init_agentic_ontology = __esm({
4424
4806
  }
4425
4807
  });
4426
4808
 
4427
- // src/lib/runtime-table.ts
4428
- var RUNTIME_TABLE, DEFAULT_RUNTIME;
4429
- var init_runtime_table = __esm({
4430
- "src/lib/runtime-table.ts"() {
4431
- "use strict";
4432
- RUNTIME_TABLE = {
4433
- codex: {
4434
- binary: "codex",
4435
- launchMode: "interactive",
4436
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
4437
- inlineFlag: "--no-alt-screen",
4438
- apiKeyEnv: "OPENAI_API_KEY",
4439
- defaultModel: "gpt-5.5"
4440
- },
4441
- opencode: {
4442
- binary: "opencode",
4443
- launchMode: "exec",
4444
- autoApproveFlag: "--dangerously-skip-permissions",
4445
- inlineFlag: "",
4446
- apiKeyEnv: "ANTHROPIC_API_KEY",
4447
- defaultModel: "anthropic/claude-sonnet-4-6"
4448
- }
4449
- };
4450
- DEFAULT_RUNTIME = "claude";
4451
- }
4452
- });
4453
-
4454
4809
  // src/lib/session-key.ts
4455
4810
  import { execSync as execSync4 } from "child_process";
4456
4811
  function normalizeCommand(command) {
@@ -4542,9 +4897,9 @@ __export(active_agent_exports, {
4542
4897
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
4543
4898
  writeActiveAgent: () => writeActiveAgent
4544
4899
  });
4545
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
4900
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
4546
4901
  import { execSync as execSync5 } from "child_process";
4547
- import path9 from "path";
4902
+ import path10 from "path";
4548
4903
  function isNameWithOptionalInstance(candidate, baseName) {
4549
4904
  if (candidate === baseName) return true;
4550
4905
  if (!candidate.startsWith(baseName)) return false;
@@ -4588,12 +4943,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
4588
4943
  return null;
4589
4944
  }
4590
4945
  function getMarkerPath() {
4591
- return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
4946
+ return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
4592
4947
  }
4593
4948
  function writeActiveAgent(agentId, agentRole) {
4594
4949
  try {
4595
4950
  mkdirSync5(CACHE_DIR, { recursive: true });
4596
- writeFileSync4(
4951
+ writeFileSync5(
4597
4952
  getMarkerPath(),
4598
4953
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
4599
4954
  );
@@ -4611,7 +4966,7 @@ function getActiveAgent() {
4611
4966
  if (httpCtx) return httpCtx;
4612
4967
  try {
4613
4968
  const markerPath = getMarkerPath();
4614
- const raw = readFileSync5(markerPath, "utf8");
4969
+ const raw = readFileSync6(markerPath, "utf8");
4615
4970
  const data = JSON.parse(raw);
4616
4971
  if (data.agentId) {
4617
4972
  if (data.startedAt) {
@@ -4659,14 +5014,14 @@ function getAllActiveAgents() {
4659
5014
  const key = file.slice("active-agent-".length, -".json".length);
4660
5015
  if (key === "undefined") continue;
4661
5016
  try {
4662
- const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
5017
+ const raw = readFileSync6(path10.join(CACHE_DIR, file), "utf8");
4663
5018
  const data = JSON.parse(raw);
4664
5019
  if (!data.agentId) continue;
4665
5020
  if (data.startedAt) {
4666
5021
  const age = Date.now() - new Date(data.startedAt).getTime();
4667
5022
  if (age > STALE_MS) {
4668
5023
  try {
4669
- unlinkSync5(path9.join(CACHE_DIR, file));
5024
+ unlinkSync5(path10.join(CACHE_DIR, file));
4670
5025
  } catch {
4671
5026
  }
4672
5027
  continue;
@@ -4689,11 +5044,11 @@ function getAllActiveAgents() {
4689
5044
  function cleanupSessionMarkers() {
4690
5045
  const key = getSessionKey();
4691
5046
  try {
4692
- unlinkSync5(path9.join(CACHE_DIR, `active-agent-${key}.json`));
5047
+ unlinkSync5(path10.join(CACHE_DIR, `active-agent-${key}.json`));
4693
5048
  } catch {
4694
5049
  }
4695
5050
  try {
4696
- unlinkSync5(path9.join(CACHE_DIR, "active-agent-undefined.json"));
5051
+ unlinkSync5(path10.join(CACHE_DIR, "active-agent-undefined.json"));
4697
5052
  } catch {
4698
5053
  }
4699
5054
  }
@@ -4705,16 +5060,16 @@ var init_active_agent = __esm({
4705
5060
  init_session_key();
4706
5061
  init_agent_context();
4707
5062
  init_employees();
4708
- CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
5063
+ CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
4709
5064
  STALE_MS = 24 * 60 * 60 * 1e3;
4710
5065
  }
4711
5066
  });
4712
5067
 
4713
5068
  // src/lib/agent-symlinks.ts
4714
5069
  import os7 from "os";
4715
- import path10 from "path";
5070
+ import path11 from "path";
4716
5071
  import {
4717
- existsSync as existsSync10,
5072
+ existsSync as existsSync11,
4718
5073
  lstatSync,
4719
5074
  mkdirSync as mkdirSync6,
4720
5075
  readlinkSync as readlinkSync2,
@@ -4742,14 +5097,14 @@ var init_mcp_prefix = __esm({
4742
5097
  });
4743
5098
 
4744
5099
  // src/lib/preferences.ts
4745
- import { existsSync as existsSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4746
- import path11 from "path";
5100
+ import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
5101
+ import path12 from "path";
4747
5102
  import os8 from "os";
4748
5103
  function loadPreferences(homeDir = os8.homedir()) {
4749
- const configPath = path11.join(homeDir, ".exe-os", "config.json");
4750
- if (!existsSync11(configPath)) return {};
5104
+ const configPath = path12.join(homeDir, ".exe-os", "config.json");
5105
+ if (!existsSync12(configPath)) return {};
4751
5106
  try {
4752
- const config = JSON.parse(readFileSync6(configPath, "utf-8"));
5107
+ const config = JSON.parse(readFileSync7(configPath, "utf-8"));
4753
5108
  return config.preferences ?? {};
4754
5109
  } catch {
4755
5110
  return {};
@@ -4763,10 +5118,20 @@ var init_preferences = __esm({
4763
5118
  });
4764
5119
 
4765
5120
  // src/adapters/mcp-http-config.ts
4766
- import { chmodSync as chmodSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
5121
+ import { chmodSync as chmodSync3, existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4767
5122
  import { randomBytes } from "crypto";
4768
- import path12 from "path";
5123
+ import path13 from "path";
4769
5124
  import os9 from "os";
5125
+ function resolveDefaultAgentId() {
5126
+ if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
5127
+ try {
5128
+ const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
5129
+ const coo = loadEmployeesSync2().find((e) => e.role === "COO");
5130
+ if (coo) return coo.name;
5131
+ } catch {
5132
+ }
5133
+ return "exe";
5134
+ }
4770
5135
  function mcpHttpPort() {
4771
5136
  return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
4772
5137
  }
@@ -4774,18 +5139,18 @@ function mcpHttpUrl() {
4774
5139
  return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
4775
5140
  }
4776
5141
  function readOrCreateDaemonToken(homeDir = os9.homedir()) {
4777
- const exeDir = path12.join(homeDir, ".exe-os");
4778
- const tokenPath = path12.join(exeDir, "exed.token");
4779
- if (existsSync12(tokenPath)) {
5142
+ const exeDir = path13.join(homeDir, ".exe-os");
5143
+ const tokenPath = path13.join(exeDir, "exed.token");
5144
+ if (existsSync13(tokenPath)) {
4780
5145
  try {
4781
- const token2 = readFileSync7(tokenPath, "utf-8").trim();
5146
+ const token2 = readFileSync8(tokenPath, "utf-8").trim();
4782
5147
  if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
4783
5148
  } catch {
4784
5149
  }
4785
5150
  }
4786
5151
  const token = randomBytes(32).toString("hex");
4787
5152
  mkdirSync7(exeDir, { recursive: true });
4788
- writeFileSync6(tokenPath, `${token}
5153
+ writeFileSync7(tokenPath, `${token}
4789
5154
  `, "utf-8");
4790
5155
  try {
4791
5156
  chmodSync3(tokenPath, 384);
@@ -4796,18 +5161,21 @@ function readOrCreateDaemonToken(homeDir = os9.homedir()) {
4796
5161
  function buildMcpHttpHeaders(homeDir = os9.homedir(), opts = {}) {
4797
5162
  const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
4798
5163
  const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
4799
- return {
5164
+ const sessionName = opts.useShellPlaceholders ? "$(tmux display-message -p '#{session_name}' 2>/dev/null || echo '')" : process.env.EXE_SESSION_NAME ?? "";
5165
+ const headers = {
4800
5166
  Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
4801
5167
  "X-Agent-Id": agentId,
4802
5168
  "X-Agent-Role": agentRole
4803
5169
  };
5170
+ if (sessionName) headers["X-Exe-Session"] = sessionName;
5171
+ return headers;
4804
5172
  }
4805
5173
  var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
4806
5174
  var init_mcp_http_config = __esm({
4807
5175
  "src/adapters/mcp-http-config.ts"() {
4808
5176
  "use strict";
4809
5177
  DEFAULT_MCP_HTTP_PORT = "48739";
4810
- DEFAULT_MCP_HTTP_AGENT_ID = "exe";
5178
+ DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
4811
5179
  DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
4812
5180
  }
4813
5181
  });
@@ -4957,28 +5325,28 @@ var init_runtime_hook_manifest = __esm({
4957
5325
 
4958
5326
  // src/adapters/claude/installer.ts
4959
5327
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir, rm } from "fs/promises";
4960
- import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync7, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
5328
+ import { existsSync as existsSync14, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
4961
5329
  import { createHash as createHash4 } from "crypto";
4962
- import path13 from "path";
5330
+ import path14 from "path";
4963
5331
  import os10 from "os";
4964
5332
  import { execSync as execSync6 } from "child_process";
4965
5333
  import { fileURLToPath as fileURLToPath2 } from "url";
4966
5334
  function resolvePackageRoot() {
4967
5335
  const thisFile = fileURLToPath2(import.meta.url);
4968
- let dir = path13.dirname(thisFile);
4969
- const root = path13.parse(dir).root;
5336
+ let dir = path14.dirname(thisFile);
5337
+ const root = path14.parse(dir).root;
4970
5338
  while (dir !== root) {
4971
- const pkgPath = path13.join(dir, "package.json");
4972
- if (existsSync13(pkgPath)) {
5339
+ const pkgPath = path14.join(dir, "package.json");
5340
+ if (existsSync14(pkgPath)) {
4973
5341
  try {
4974
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
5342
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
4975
5343
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
4976
5344
  } catch {
4977
5345
  }
4978
5346
  }
4979
- dir = path13.dirname(dir);
5347
+ dir = path14.dirname(dir);
4980
5348
  }
4981
- return path13.resolve(path13.dirname(thisFile), "..", "..", "..");
5349
+ return path14.resolve(path14.dirname(thisFile), "..", "..", "..");
4982
5350
  }
4983
5351
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
4984
5352
  var init_installer = __esm({
@@ -5016,19 +5384,19 @@ __export(installer_exports, {
5016
5384
  verifyCodexHooks: () => verifyCodexHooks
5017
5385
  });
5018
5386
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
5019
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
5020
- import path14 from "path";
5387
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
5388
+ import path15 from "path";
5021
5389
  import os11 from "os";
5022
5390
  async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5023
- const codexDir = path14.join(homeDir, ".codex");
5024
- const hooksPath = path14.join(codexDir, "hooks.json");
5025
- const logsDir = path14.join(homeDir, ".exe-os", "logs");
5026
- const hookLogPath = path14.join(logsDir, "hooks.log");
5391
+ const codexDir = path15.join(homeDir, ".codex");
5392
+ const hooksPath = path15.join(codexDir, "hooks.json");
5393
+ const logsDir = path15.join(homeDir, ".exe-os", "logs");
5394
+ const hookLogPath = path15.join(logsDir, "hooks.log");
5027
5395
  const logSuffix = ` 2>> "${hookLogPath}"`;
5028
5396
  await mkdir5(codexDir, { recursive: true });
5029
5397
  await mkdir5(logsDir, { recursive: true });
5030
5398
  let hooksJson = {};
5031
- if (existsSync14(hooksPath)) {
5399
+ if (existsSync15(hooksPath)) {
5032
5400
  try {
5033
5401
  hooksJson = JSON.parse(await readFile5(hooksPath, "utf-8"));
5034
5402
  } catch {
@@ -5048,7 +5416,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5048
5416
  // Combined hook: runs ingest + error-recall in one Node process.
5049
5417
  // Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
5050
5418
  type: "command",
5051
- command: `node "${path14.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
5419
+ command: `node "${path15.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
5052
5420
  }
5053
5421
  ]
5054
5422
  },
@@ -5062,7 +5430,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5062
5430
  // Single hook: prompt-submit handles memory retrieval + entity boost.
5063
5431
  // exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
5064
5432
  type: "command",
5065
- command: `node "${path14.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
5433
+ command: `node "${path15.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
5066
5434
  }
5067
5435
  ]
5068
5436
  },
@@ -5074,7 +5442,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5074
5442
  hooks: [
5075
5443
  {
5076
5444
  type: "command",
5077
- command: `node "${path14.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
5445
+ command: `node "${path15.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
5078
5446
  }
5079
5447
  ]
5080
5448
  },
@@ -5087,7 +5455,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5087
5455
  hooks: [
5088
5456
  {
5089
5457
  type: "command",
5090
- command: `node "${path14.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
5458
+ command: `node "${path15.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
5091
5459
  }
5092
5460
  ]
5093
5461
  },
@@ -5136,10 +5504,10 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
5136
5504
  return { added, skipped };
5137
5505
  }
5138
5506
  function verifyCodexHooks(homeDir = os11.homedir()) {
5139
- const hooksPath = path14.join(homeDir, ".codex", "hooks.json");
5140
- if (!existsSync14(hooksPath)) return false;
5507
+ const hooksPath = path15.join(homeDir, ".codex", "hooks.json");
5508
+ if (!existsSync15(hooksPath)) return false;
5141
5509
  try {
5142
- const hooksJson = JSON.parse(readFileSync9(hooksPath, "utf-8"));
5510
+ const hooksJson = JSON.parse(readFileSync10(hooksPath, "utf-8"));
5143
5511
  if (!hooksJson.hooks) return false;
5144
5512
  const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
5145
5513
  for (const event of required) {
@@ -5171,11 +5539,11 @@ function verifyCodexHooks(homeDir = os11.homedir()) {
5171
5539
  async function installCodexStatusLine(homeDir = os11.homedir()) {
5172
5540
  const prefs = loadPreferences(homeDir);
5173
5541
  if (prefs.codexStatusLine === false) return "opted-out";
5174
- const codexDir = path14.join(homeDir, ".codex");
5175
- const configPath = path14.join(codexDir, "config.toml");
5542
+ const codexDir = path15.join(homeDir, ".codex");
5543
+ const configPath = path15.join(codexDir, "config.toml");
5176
5544
  await mkdir5(codexDir, { recursive: true });
5177
5545
  let content = "";
5178
- if (existsSync14(configPath)) {
5546
+ if (existsSync15(configPath)) {
5179
5547
  content = await readFile5(configPath, "utf-8");
5180
5548
  if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
5181
5549
  return "already-configured";
@@ -5227,12 +5595,12 @@ ${line}
5227
5595
  return { content: next, changed: next !== sectionContent };
5228
5596
  }
5229
5597
  async function registerCodexMcpServer(packageRoot, homeDir = os11.homedir()) {
5230
- const codexDir = path14.join(homeDir, ".codex");
5231
- const configPath = path14.join(codexDir, "config.toml");
5598
+ const codexDir = path15.join(homeDir, ".codex");
5599
+ const configPath = path15.join(codexDir, "config.toml");
5232
5600
  void packageRoot;
5233
5601
  await mkdir5(codexDir, { recursive: true });
5234
5602
  let content = "";
5235
- if (existsSync14(configPath)) {
5603
+ if (existsSync15(configPath)) {
5236
5604
  content = await readFile5(configPath, "utf-8");
5237
5605
  }
5238
5606
  const sectionHeader = "[mcp_servers.exe-os]";
@@ -5257,10 +5625,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os11.homedir()) {
5257
5625
  return "registered";
5258
5626
  }
5259
5627
  async function ensureCodexHooksFeature(homeDir = os11.homedir()) {
5260
- const configPath = path14.join(homeDir, ".codex", "config.toml");
5261
- await mkdir5(path14.join(homeDir, ".codex"), { recursive: true });
5628
+ const configPath = path15.join(homeDir, ".codex", "config.toml");
5629
+ await mkdir5(path15.join(homeDir, ".codex"), { recursive: true });
5262
5630
  let content = "";
5263
- if (existsSync14(configPath)) {
5631
+ if (existsSync15(configPath)) {
5264
5632
  content = await readFile5(configPath, "utf-8");
5265
5633
  }
5266
5634
  if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
@@ -5329,95 +5697,6 @@ var init_installer2 = __esm({
5329
5697
  }
5330
5698
  });
5331
5699
 
5332
- // src/lib/agent-config.ts
5333
- var agent_config_exports = {};
5334
- __export(agent_config_exports, {
5335
- AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
5336
- DEFAULT_MODELS: () => DEFAULT_MODELS,
5337
- KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
5338
- RUNTIME_LABELS: () => RUNTIME_LABELS,
5339
- clearAgentRuntime: () => clearAgentRuntime,
5340
- getAgentRuntime: () => getAgentRuntime,
5341
- loadAgentConfig: () => loadAgentConfig,
5342
- saveAgentConfig: () => saveAgentConfig,
5343
- setAgentRuntime: () => setAgentRuntime
5344
- });
5345
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync15 } from "fs";
5346
- import path15 from "path";
5347
- function loadAgentConfig() {
5348
- if (!existsSync15(AGENT_CONFIG_PATH)) return {};
5349
- try {
5350
- return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
5351
- } catch {
5352
- return {};
5353
- }
5354
- }
5355
- function saveAgentConfig(config) {
5356
- const dir = path15.dirname(AGENT_CONFIG_PATH);
5357
- ensurePrivateDirSync(dir);
5358
- writeFileSync8(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
5359
- enforcePrivateFileSync(AGENT_CONFIG_PATH);
5360
- }
5361
- function getAgentRuntime(agentId) {
5362
- const config = loadAgentConfig();
5363
- const entry = config[agentId];
5364
- if (entry) return entry;
5365
- const orgDefault = config["default"];
5366
- if (orgDefault) return orgDefault;
5367
- return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
5368
- }
5369
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
5370
- const knownModels = KNOWN_RUNTIMES[runtime];
5371
- if (!knownModels) {
5372
- return {
5373
- ok: false,
5374
- error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
5375
- };
5376
- }
5377
- if (!knownModels.includes(model)) {
5378
- return {
5379
- ok: false,
5380
- error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
5381
- };
5382
- }
5383
- const config = loadAgentConfig();
5384
- const entry = { runtime, model };
5385
- if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
5386
- config[agentId] = entry;
5387
- saveAgentConfig(config);
5388
- return { ok: true };
5389
- }
5390
- function clearAgentRuntime(agentId) {
5391
- const config = loadAgentConfig();
5392
- delete config[agentId];
5393
- saveAgentConfig(config);
5394
- }
5395
- var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
5396
- var init_agent_config = __esm({
5397
- "src/lib/agent-config.ts"() {
5398
- "use strict";
5399
- init_config();
5400
- init_runtime_table();
5401
- init_secure_files();
5402
- AGENT_CONFIG_PATH = path15.join(EXE_AI_DIR, "agent-config.json");
5403
- KNOWN_RUNTIMES = {
5404
- claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
5405
- codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
5406
- opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
5407
- };
5408
- RUNTIME_LABELS = {
5409
- claude: "Claude Code (Anthropic)",
5410
- codex: "Codex (OpenAI)",
5411
- opencode: "OpenCode (open source)"
5412
- };
5413
- DEFAULT_MODELS = {
5414
- claude: "claude-opus-4.6",
5415
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
5416
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
5417
- };
5418
- }
5419
- });
5420
-
5421
5700
  // src/bin/exe-start-codex.ts
5422
5701
  import os12 from "os";
5423
5702
  import path16 from "path";
@@ -5436,18 +5715,18 @@ init_database();
5436
5715
 
5437
5716
  // src/lib/keychain.ts
5438
5717
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5439
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
5718
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
5440
5719
  import { execSync as execSync3 } from "child_process";
5441
- import path6 from "path";
5720
+ import path7 from "path";
5442
5721
  import os5 from "os";
5443
5722
  var SERVICE = "exe-os";
5444
5723
  var LEGACY_SERVICE = "exe-mem";
5445
5724
  var ACCOUNT = "master-key";
5446
5725
  function getKeyDir() {
5447
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
5726
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
5448
5727
  }
5449
5728
  function getKeyPath() {
5450
- return path6.join(getKeyDir(), "master.key");
5729
+ return path7.join(getKeyDir(), "master.key");
5451
5730
  }
5452
5731
  function nativeKeychainAllowed() {
5453
5732
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -5479,7 +5758,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
5479
5758
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
5480
5759
  if (uid === 0) return true;
5481
5760
  const exeOsDir = process.env.EXE_OS_DIR;
5482
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
5761
+ return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
5483
5762
  } catch {
5484
5763
  return false;
5485
5764
  }
@@ -5677,7 +5956,7 @@ async function getMasterKey() {
5677
5956
  }
5678
5957
  }
5679
5958
  const keyPath = getKeyPath();
5680
- if (!existsSync7(keyPath)) {
5959
+ if (!existsSync8(keyPath)) {
5681
5960
  process.stderr.write(
5682
5961
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5683
5962
  `
@@ -6161,14 +6440,14 @@ function vectorToBlob(vector) {
6161
6440
 
6162
6441
  // src/lib/behaviors-export.ts
6163
6442
  import os6 from "os";
6164
- import path8 from "path";
6443
+ import path9 from "path";
6165
6444
  import {
6166
- existsSync as existsSync9,
6445
+ existsSync as existsSync10,
6167
6446
  mkdirSync as mkdirSync4,
6168
6447
  readdirSync as readdirSync2,
6169
6448
  statSync as statSync5,
6170
6449
  unlinkSync as unlinkSync4,
6171
- writeFileSync as writeFileSync3
6450
+ writeFileSync as writeFileSync4
6172
6451
  } from "fs";
6173
6452
 
6174
6453
  // src/lib/behaviors.ts
@@ -6214,7 +6493,7 @@ async function listBehaviors(agentId, projectName, limit = 30) {
6214
6493
  }
6215
6494
 
6216
6495
  // src/lib/behaviors-export.ts
6217
- var BEHAVIORS_EXPORT_DIR = path8.join(
6496
+ var BEHAVIORS_EXPORT_DIR = path9.join(
6218
6497
  os6.homedir(),
6219
6498
  ".exe-os",
6220
6499
  "behaviors-export"
@@ -6231,7 +6510,7 @@ function getBehaviorLimit() {
6231
6510
  }
6232
6511
  }
6233
6512
  function sweepStaleBehaviorExports(now = Date.now()) {
6234
- if (!existsSync9(BEHAVIORS_EXPORT_DIR)) return;
6513
+ if (!existsSync10(BEHAVIORS_EXPORT_DIR)) return;
6235
6514
  let entries;
6236
6515
  try {
6237
6516
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -6239,7 +6518,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
6239
6518
  return;
6240
6519
  }
6241
6520
  for (const entry of entries) {
6242
- const filePath = path8.join(BEHAVIORS_EXPORT_DIR, entry);
6521
+ const filePath = path9.join(BEHAVIORS_EXPORT_DIR, entry);
6243
6522
  try {
6244
6523
  const stat = statSync5(filePath);
6245
6524
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -6272,10 +6551,10 @@ function renderBehaviorExport(behaviors) {
6272
6551
  }
6273
6552
  function exportFilePath(agentId, projectName, sessionKey) {
6274
6553
  if (!sessionKey) {
6275
- return path8.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
6554
+ return path9.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
6276
6555
  }
6277
6556
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
6278
- return path8.join(
6557
+ return path9.join(
6279
6558
  BEHAVIORS_EXPORT_DIR,
6280
6559
  `${agentId}-${safeProject}-${sessionKey}.md`
6281
6560
  );
@@ -6287,7 +6566,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
6287
6566
  if (behaviors.length === 0) return null;
6288
6567
  const body = renderBehaviorExport(behaviors);
6289
6568
  const target = exportFilePath(agentId, projectName, sessionKey);
6290
- writeFileSync3(target, body, "utf-8");
6569
+ writeFileSync4(target, body, "utf-8");
6291
6570
  return target;
6292
6571
  }
6293
6572