@askexenow/exe-os 0.9.111 → 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 +62 -12
  3. package/dist/bin/agentic-reflection-backfill.js +37 -2
  4. package/dist/bin/agentic-semantic-label.js +37 -2
  5. package/dist/bin/backfill-conversations.js +61 -11
  6. package/dist/bin/backfill-responses.js +62 -12
  7. package/dist/bin/backfill-vectors.js +37 -2
  8. package/dist/bin/bulk-sync-postgres.js +63 -13
  9. package/dist/bin/cleanup-stale-review-tasks.js +83 -16
  10. package/dist/bin/cli.js +312 -80
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +29 -3
  13. package/dist/bin/exe-assign.js +62 -12
  14. package/dist/bin/exe-boot.js +500 -151
  15. package/dist/bin/exe-call.js +46 -5
  16. package/dist/bin/exe-cloud.js +101 -16
  17. package/dist/bin/exe-dispatch.js +827 -27
  18. package/dist/bin/exe-doctor.js +61 -11
  19. package/dist/bin/exe-export-behaviors.js +67 -14
  20. package/dist/bin/exe-forget.js +62 -12
  21. package/dist/bin/exe-gateway.js +147 -27
  22. package/dist/bin/exe-heartbeat.js +83 -16
  23. package/dist/bin/exe-kill.js +62 -12
  24. package/dist/bin/exe-launch-agent.js +83 -15
  25. package/dist/bin/exe-new-employee.js +176 -8
  26. package/dist/bin/exe-pending-messages.js +83 -16
  27. package/dist/bin/exe-pending-notifications.js +83 -16
  28. package/dist/bin/exe-pending-reviews.js +83 -16
  29. package/dist/bin/exe-rename.js +62 -12
  30. package/dist/bin/exe-review.js +62 -12
  31. package/dist/bin/exe-search.js +62 -12
  32. package/dist/bin/exe-session-cleanup.js +949 -149
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +537 -248
  35. package/dist/bin/exe-start-opencode.js +547 -168
  36. package/dist/bin/exe-status.js +83 -16
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +62 -12
  39. package/dist/bin/git-sweep.js +827 -27
  40. package/dist/bin/graph-backfill.js +62 -12
  41. package/dist/bin/graph-export.js +62 -12
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +949 -149
  44. package/dist/bin/pre-publish.js +14 -2
  45. package/dist/bin/scan-tasks.js +827 -27
  46. package/dist/bin/setup.js +99 -14
  47. package/dist/bin/shard-migrate.js +62 -12
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +586 -26
  51. package/dist/hooks/bug-report-worker.js +586 -26
  52. package/dist/hooks/codex-stop-task-finalizer.js +977 -143
  53. package/dist/hooks/commit-complete.js +827 -27
  54. package/dist/hooks/error-recall.js +62 -12
  55. package/dist/hooks/ingest.js +4579 -249
  56. package/dist/hooks/instructions-loaded.js +62 -12
  57. package/dist/hooks/notification.js +62 -12
  58. package/dist/hooks/post-compact.js +83 -16
  59. package/dist/hooks/post-tool-combined.js +83 -16
  60. package/dist/hooks/pre-compact.js +907 -107
  61. package/dist/hooks/pre-tool-use.js +98 -16
  62. package/dist/hooks/prompt-submit.js +596 -30
  63. package/dist/hooks/session-end.js +909 -112
  64. package/dist/hooks/session-start.js +112 -17
  65. package/dist/hooks/stop.js +82 -15
  66. package/dist/hooks/subagent-stop.js +83 -16
  67. package/dist/hooks/summary-worker.js +81 -8
  68. package/dist/index.js +595 -29
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +45 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +23 -0
  73. package/dist/lib/db.js +23 -0
  74. package/dist/lib/device-registry.js +23 -0
  75. package/dist/lib/employee-templates.js +30 -4
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +482 -52
  78. package/dist/lib/hybrid-search.js +62 -12
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +37 -2
  82. package/dist/lib/skill-learning.js +910 -41
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +62 -12
  85. package/dist/lib/tasks.js +843 -93
  86. package/dist/lib/tmux-routing.js +766 -16
  87. package/dist/mcp/server.js +238 -41
  88. package/dist/mcp/tools/create-task.js +525 -15
  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 +840 -93
  93. package/dist/runtime/index.js +913 -107
  94. package/dist/tui/App.js +227 -58
  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);
@@ -1898,6 +2237,13 @@ async function ensureSchema() {
1898
2237
  } catch (e) {
1899
2238
  logCatchDebug("migration", e);
1900
2239
  }
2240
+ for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
2241
+ try {
2242
+ await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
2243
+ } catch (e) {
2244
+ logCatchDebug("migration", e);
2245
+ }
2246
+ }
1901
2247
  try {
1902
2248
  await client.execute({
1903
2249
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -3114,6 +3460,22 @@ async function ensureSchema() {
3114
3460
  } catch (e) {
3115
3461
  logCatchDebug("migration", e);
3116
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
+ }
3117
3479
  }
3118
3480
  async function disposeDatabase() {
3119
3481
  if (_walCheckpointTimer) {
@@ -3179,14 +3541,14 @@ __export(shard_manager_exports, {
3179
3541
  listShards: () => listShards,
3180
3542
  shardExists: () => shardExists
3181
3543
  });
3182
- import path7 from "path";
3183
- 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";
3184
3546
  import { createClient as createClient2 } from "@libsql/client";
3185
3547
  function initShardManager(encryptionKey) {
3186
3548
  _encryptionKey = encryptionKey;
3187
3549
  _keyValidated = false;
3188
3550
  _keyValidationPromise = null;
3189
- if (!existsSync8(SHARDS_DIR)) {
3551
+ if (!existsSync9(SHARDS_DIR)) {
3190
3552
  mkdirSync3(SHARDS_DIR, { recursive: true });
3191
3553
  }
3192
3554
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3207,7 +3569,7 @@ async function validateEncryptionKey() {
3207
3569
  return true;
3208
3570
  }
3209
3571
  for (const shardFile of existingShards.slice(0, 3)) {
3210
- const dbPath = path7.join(SHARDS_DIR, shardFile);
3572
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3211
3573
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3212
3574
  try {
3213
3575
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -3250,7 +3612,7 @@ function getShardClient(projectName) {
3250
3612
  while (_shards.size >= MAX_OPEN_SHARDS) {
3251
3613
  evictLRU();
3252
3614
  }
3253
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3615
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3254
3616
  const client = createClient2({
3255
3617
  url: `file:${dbPath}`,
3256
3618
  encryptionKey: _encryptionKey
@@ -3261,13 +3623,13 @@ function getShardClient(projectName) {
3261
3623
  }
3262
3624
  function shardExists(projectName) {
3263
3625
  const safeName = safeShardName(projectName);
3264
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
3626
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
3265
3627
  }
3266
3628
  function safeShardName(projectName) {
3267
3629
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3268
3630
  }
3269
3631
  function listShards() {
3270
- if (!existsSync8(SHARDS_DIR)) return [];
3632
+ if (!existsSync9(SHARDS_DIR)) return [];
3271
3633
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3272
3634
  }
3273
3635
  async function auditShardHealth(options = {}) {
@@ -3279,7 +3641,7 @@ async function auditShardHealth(options = {}) {
3279
3641
  const names = listShards();
3280
3642
  const shards = [];
3281
3643
  for (const name of names) {
3282
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3644
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
3283
3645
  const stat = statSync4(dbPath);
3284
3646
  const item = {
3285
3647
  name,
@@ -3314,7 +3676,7 @@ async function auditShardHealth(options = {}) {
3314
3676
  _shards.delete(name);
3315
3677
  _shardLastAccess.delete(name);
3316
3678
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3317
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3679
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3318
3680
  renameSync3(dbPath, archivedPath);
3319
3681
  item.archivedPath = archivedPath;
3320
3682
  }
@@ -3542,11 +3904,11 @@ async function getReadyShardClient(projectName) {
3542
3904
  client.close();
3543
3905
  _shards.delete(safeName);
3544
3906
  _shardLastAccess.delete(safeName);
3545
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3546
- if (existsSync8(dbPath)) {
3907
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3908
+ if (existsSync9(dbPath)) {
3547
3909
  const stat = statSync4(dbPath);
3548
3910
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3549
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3911
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3550
3912
  renameSync3(dbPath, archivedPath);
3551
3913
  process.stderr.write(
3552
3914
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -3614,7 +3976,7 @@ var init_shard_manager = __esm({
3614
3976
  "src/lib/shard-manager.ts"() {
3615
3977
  "use strict";
3616
3978
  init_config();
3617
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
3979
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
3618
3980
  SHARD_IDLE_MS = 5 * 60 * 1e3;
3619
3981
  MAX_OPEN_SHARDS = 10;
3620
3982
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -3680,11 +4042,17 @@ var init_platform_procedures = __esm({
3680
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."
3681
4043
  },
3682
4044
  {
3683
- title: "Customer orchestration maturity \u2014 recommend, never trap",
4045
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
3684
4046
  domain: "workflow",
3685
4047
  priority: "p1",
3686
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."
3687
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
+ },
3688
4056
  {
3689
4057
  title: "Single dispatch path \u2014 create_task only",
3690
4058
  domain: "workflow",
@@ -3718,6 +4086,12 @@ var init_platform_procedures = __esm({
3718
4086
  priority: "p0",
3719
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."
3720
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
+ },
3721
4095
  {
3722
4096
  title: "Customer patch triage \u2014 upstream bug vs customization",
3723
4097
  domain: "support",
@@ -3869,7 +4243,7 @@ var init_platform_procedures = __esm({
3869
4243
  title: "MCP tool dispatch \u2014 all tools use action parameter",
3870
4244
  domain: "tool-use",
3871
4245
  priority: "p0",
3872
- content: 'exe-os MCP tools come in two surfaces depending on EXE_MCP_TOOL_SURFACE config. Consolidated (19 tools): action-based dispatch \u2014 memory(action="recall"), task(action="create"), etc. Legacy (108 tools): one tool per operation \u2014 recall_my_memory, create_task, etc. Both surfaces have identical functionality. Use whichever tool names are available in your session. If you see domain tools (memory, task, config, etc.), use the action parameter. If you see specific tools (recall_my_memory, create_task, etc.), call them directly.'
4246
+ content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
3873
4247
  },
3874
4248
  {
3875
4249
  title: "MCP tools \u2014 memory, decision, and search",
@@ -4003,10 +4377,24 @@ function stableId(memoryId, type, content) {
4003
4377
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4004
4378
  }
4005
4379
  function cleanText(text) {
4006
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4007
- }
4008
- function splitSentences(text) {
4009
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
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;
4386
+ }
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;
4010
4398
  }
4011
4399
  function inferCardType(sentence, toolName) {
4012
4400
  const lower = sentence.toLowerCase();
@@ -4038,12 +4426,12 @@ function predicateFor(type) {
4038
4426
  }
4039
4427
  }
4040
4428
  function extractMemoryCards(row) {
4041
- const sentences = splitSentences(row.raw_text);
4429
+ const segments = splitSegments(row.raw_text);
4042
4430
  const cards = [];
4043
- for (const sentence of sentences) {
4431
+ for (const sentence of segments) {
4044
4432
  const type = inferCardType(sentence, row.tool_name);
4045
4433
  const subject = extractSubject(sentence, row.agent_id);
4046
- 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;
4047
4435
  cards.push({
4048
4436
  id: stableId(row.id, type, content),
4049
4437
  memory_id: row.id,
@@ -4139,13 +4527,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4139
4527
  last_accessed: String(row.timestamp)
4140
4528
  }));
4141
4529
  }
4142
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4530
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
4143
4531
  var init_memory_cards = __esm({
4144
4532
  "src/lib/memory-cards.ts"() {
4145
4533
  "use strict";
4146
4534
  init_database();
4147
- MAX_CARDS_PER_MEMORY = 6;
4148
- MAX_SENTENCE_CHARS = 360;
4535
+ MAX_CARDS_PER_MEMORY = 8;
4536
+ MAX_SEGMENT_CHARS = 500;
4537
+ MIN_SEGMENT_CHARS = 20;
4149
4538
  }
4150
4539
  });
4151
4540
 
@@ -4417,32 +4806,6 @@ var init_agentic_ontology = __esm({
4417
4806
  }
4418
4807
  });
4419
4808
 
4420
- // src/lib/runtime-table.ts
4421
- var RUNTIME_TABLE;
4422
- var init_runtime_table = __esm({
4423
- "src/lib/runtime-table.ts"() {
4424
- "use strict";
4425
- RUNTIME_TABLE = {
4426
- codex: {
4427
- binary: "codex",
4428
- launchMode: "interactive",
4429
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
4430
- inlineFlag: "--no-alt-screen",
4431
- apiKeyEnv: "OPENAI_API_KEY",
4432
- defaultModel: "gpt-5.5"
4433
- },
4434
- opencode: {
4435
- binary: "opencode",
4436
- launchMode: "exec",
4437
- autoApproveFlag: "--dangerously-skip-permissions",
4438
- inlineFlag: "",
4439
- apiKeyEnv: "ANTHROPIC_API_KEY",
4440
- defaultModel: "anthropic/claude-sonnet-4-6"
4441
- }
4442
- };
4443
- }
4444
- });
4445
-
4446
4809
  // src/lib/session-key.ts
4447
4810
  import { execSync as execSync4 } from "child_process";
4448
4811
  function normalizeCommand(command) {
@@ -4534,9 +4897,9 @@ __export(active_agent_exports, {
4534
4897
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
4535
4898
  writeActiveAgent: () => writeActiveAgent
4536
4899
  });
4537
- 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";
4538
4901
  import { execSync as execSync5 } from "child_process";
4539
- import path9 from "path";
4902
+ import path10 from "path";
4540
4903
  function isNameWithOptionalInstance(candidate, baseName) {
4541
4904
  if (candidate === baseName) return true;
4542
4905
  if (!candidate.startsWith(baseName)) return false;
@@ -4580,12 +4943,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
4580
4943
  return null;
4581
4944
  }
4582
4945
  function getMarkerPath() {
4583
- return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
4946
+ return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
4584
4947
  }
4585
4948
  function writeActiveAgent(agentId, agentRole) {
4586
4949
  try {
4587
4950
  mkdirSync5(CACHE_DIR, { recursive: true });
4588
- writeFileSync4(
4951
+ writeFileSync5(
4589
4952
  getMarkerPath(),
4590
4953
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
4591
4954
  );
@@ -4603,7 +4966,7 @@ function getActiveAgent() {
4603
4966
  if (httpCtx) return httpCtx;
4604
4967
  try {
4605
4968
  const markerPath = getMarkerPath();
4606
- const raw = readFileSync5(markerPath, "utf8");
4969
+ const raw = readFileSync6(markerPath, "utf8");
4607
4970
  const data = JSON.parse(raw);
4608
4971
  if (data.agentId) {
4609
4972
  if (data.startedAt) {
@@ -4651,14 +5014,14 @@ function getAllActiveAgents() {
4651
5014
  const key = file.slice("active-agent-".length, -".json".length);
4652
5015
  if (key === "undefined") continue;
4653
5016
  try {
4654
- const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
5017
+ const raw = readFileSync6(path10.join(CACHE_DIR, file), "utf8");
4655
5018
  const data = JSON.parse(raw);
4656
5019
  if (!data.agentId) continue;
4657
5020
  if (data.startedAt) {
4658
5021
  const age = Date.now() - new Date(data.startedAt).getTime();
4659
5022
  if (age > STALE_MS) {
4660
5023
  try {
4661
- unlinkSync5(path9.join(CACHE_DIR, file));
5024
+ unlinkSync5(path10.join(CACHE_DIR, file));
4662
5025
  } catch {
4663
5026
  }
4664
5027
  continue;
@@ -4681,11 +5044,11 @@ function getAllActiveAgents() {
4681
5044
  function cleanupSessionMarkers() {
4682
5045
  const key = getSessionKey();
4683
5046
  try {
4684
- unlinkSync5(path9.join(CACHE_DIR, `active-agent-${key}.json`));
5047
+ unlinkSync5(path10.join(CACHE_DIR, `active-agent-${key}.json`));
4685
5048
  } catch {
4686
5049
  }
4687
5050
  try {
4688
- unlinkSync5(path9.join(CACHE_DIR, "active-agent-undefined.json"));
5051
+ unlinkSync5(path10.join(CACHE_DIR, "active-agent-undefined.json"));
4689
5052
  } catch {
4690
5053
  }
4691
5054
  }
@@ -4697,16 +5060,16 @@ var init_active_agent = __esm({
4697
5060
  init_session_key();
4698
5061
  init_agent_context();
4699
5062
  init_employees();
4700
- CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
5063
+ CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
4701
5064
  STALE_MS = 24 * 60 * 60 * 1e3;
4702
5065
  }
4703
5066
  });
4704
5067
 
4705
5068
  // src/lib/agent-symlinks.ts
4706
5069
  import os7 from "os";
4707
- import path10 from "path";
5070
+ import path11 from "path";
4708
5071
  import {
4709
- existsSync as existsSync10,
5072
+ existsSync as existsSync11,
4710
5073
  lstatSync,
4711
5074
  mkdirSync as mkdirSync6,
4712
5075
  readlinkSync as readlinkSync2,
@@ -4734,8 +5097,8 @@ var init_mcp_prefix = __esm({
4734
5097
  });
4735
5098
 
4736
5099
  // src/lib/preferences.ts
4737
- import { existsSync as existsSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4738
- import path11 from "path";
5100
+ import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
5101
+ import path12 from "path";
4739
5102
  import os8 from "os";
4740
5103
  var init_preferences = __esm({
4741
5104
  "src/lib/preferences.ts"() {
@@ -4745,10 +5108,20 @@ var init_preferences = __esm({
4745
5108
  });
4746
5109
 
4747
5110
  // src/adapters/mcp-http-config.ts
4748
- import { chmodSync as chmodSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
5111
+ import { chmodSync as chmodSync3, existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4749
5112
  import { randomBytes } from "crypto";
4750
- import path12 from "path";
5113
+ import path13 from "path";
4751
5114
  import os9 from "os";
5115
+ function resolveDefaultAgentId() {
5116
+ if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
5117
+ try {
5118
+ const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
5119
+ const coo = loadEmployeesSync2().find((e) => e.role === "COO");
5120
+ if (coo) return coo.name;
5121
+ } catch {
5122
+ }
5123
+ return "exe";
5124
+ }
4752
5125
  function mcpHttpPort() {
4753
5126
  return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
4754
5127
  }
@@ -4756,18 +5129,18 @@ function mcpHttpUrl() {
4756
5129
  return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
4757
5130
  }
4758
5131
  function readOrCreateDaemonToken(homeDir = os9.homedir()) {
4759
- const exeDir = path12.join(homeDir, ".exe-os");
4760
- const tokenPath = path12.join(exeDir, "exed.token");
4761
- if (existsSync12(tokenPath)) {
5132
+ const exeDir = path13.join(homeDir, ".exe-os");
5133
+ const tokenPath = path13.join(exeDir, "exed.token");
5134
+ if (existsSync13(tokenPath)) {
4762
5135
  try {
4763
- const token2 = readFileSync7(tokenPath, "utf-8").trim();
5136
+ const token2 = readFileSync8(tokenPath, "utf-8").trim();
4764
5137
  if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
4765
5138
  } catch {
4766
5139
  }
4767
5140
  }
4768
5141
  const token = randomBytes(32).toString("hex");
4769
5142
  mkdirSync7(exeDir, { recursive: true });
4770
- writeFileSync6(tokenPath, `${token}
5143
+ writeFileSync7(tokenPath, `${token}
4771
5144
  `, "utf-8");
4772
5145
  try {
4773
5146
  chmodSync3(tokenPath, 384);
@@ -4778,18 +5151,21 @@ function readOrCreateDaemonToken(homeDir = os9.homedir()) {
4778
5151
  function buildMcpHttpHeaders(homeDir = os9.homedir(), opts = {}) {
4779
5152
  const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
4780
5153
  const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
4781
- return {
5154
+ const sessionName = opts.useShellPlaceholders ? "$(tmux display-message -p '#{session_name}' 2>/dev/null || echo '')" : process.env.EXE_SESSION_NAME ?? "";
5155
+ const headers = {
4782
5156
  Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
4783
5157
  "X-Agent-Id": agentId,
4784
5158
  "X-Agent-Role": agentRole
4785
5159
  };
5160
+ if (sessionName) headers["X-Exe-Session"] = sessionName;
5161
+ return headers;
4786
5162
  }
4787
5163
  var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
4788
5164
  var init_mcp_http_config = __esm({
4789
5165
  "src/adapters/mcp-http-config.ts"() {
4790
5166
  "use strict";
4791
5167
  DEFAULT_MCP_HTTP_PORT = "48739";
4792
- DEFAULT_MCP_HTTP_AGENT_ID = "exe";
5168
+ DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
4793
5169
  DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
4794
5170
  }
4795
5171
  });
@@ -4937,28 +5313,28 @@ var init_runtime_hook_manifest = __esm({
4937
5313
 
4938
5314
  // src/adapters/claude/installer.ts
4939
5315
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir, rm } from "fs/promises";
4940
- import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync7, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
5316
+ import { existsSync as existsSync14, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
4941
5317
  import { createHash as createHash4 } from "crypto";
4942
- import path13 from "path";
5318
+ import path14 from "path";
4943
5319
  import os10 from "os";
4944
5320
  import { execSync as execSync6 } from "child_process";
4945
5321
  import { fileURLToPath as fileURLToPath2 } from "url";
4946
5322
  function resolvePackageRoot() {
4947
5323
  const thisFile = fileURLToPath2(import.meta.url);
4948
- let dir = path13.dirname(thisFile);
4949
- const root = path13.parse(dir).root;
5324
+ let dir = path14.dirname(thisFile);
5325
+ const root = path14.parse(dir).root;
4950
5326
  while (dir !== root) {
4951
- const pkgPath = path13.join(dir, "package.json");
4952
- if (existsSync13(pkgPath)) {
5327
+ const pkgPath = path14.join(dir, "package.json");
5328
+ if (existsSync14(pkgPath)) {
4953
5329
  try {
4954
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
5330
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
4955
5331
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
4956
5332
  } catch {
4957
5333
  }
4958
5334
  }
4959
- dir = path13.dirname(dir);
5335
+ dir = path14.dirname(dir);
4960
5336
  }
4961
- return path13.resolve(path13.dirname(thisFile), "..", "..", "..");
5337
+ return path14.resolve(path14.dirname(thisFile), "..", "..", "..");
4962
5338
  }
4963
5339
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
4964
5340
  var init_installer = __esm({
@@ -5178,16 +5554,16 @@ __export(installer_exports, {
5178
5554
  verifyOpenCodeHooks: () => verifyOpenCodeHooks
5179
5555
  });
5180
5556
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
5181
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
5182
- import path14 from "path";
5557
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
5558
+ import path15 from "path";
5183
5559
  import os11 from "os";
5184
5560
  async function registerOpenCodeMcp(packageRoot, homeDir = os11.homedir()) {
5185
5561
  void packageRoot;
5186
- const configDir = path14.join(homeDir, ".config", "opencode");
5187
- const configPath = path14.join(configDir, "opencode.json");
5562
+ const configDir = path15.join(homeDir, ".config", "opencode");
5563
+ const configPath = path15.join(configDir, "opencode.json");
5188
5564
  await mkdir5(configDir, { recursive: true });
5189
5565
  let config = {};
5190
- if (existsSync14(configPath)) {
5566
+ if (existsSync15(configPath)) {
5191
5567
  try {
5192
5568
  config = JSON.parse(await readFile5(configPath, "utf-8"));
5193
5569
  } catch {
@@ -5215,14 +5591,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os11.homedir()) {
5215
5591
  return true;
5216
5592
  }
5217
5593
  async function installOpenCodePlugin(packageRoot, homeDir = os11.homedir()) {
5218
- const pluginDir = path14.join(homeDir, ".config", "opencode", "plugins");
5219
- const pluginPath = path14.join(pluginDir, "exe-os.mjs");
5594
+ const pluginDir = path15.join(homeDir, ".config", "opencode", "plugins");
5595
+ const pluginPath = path15.join(pluginDir, "exe-os.mjs");
5220
5596
  await mkdir5(pluginDir, { recursive: true });
5221
5597
  const pluginContent = PLUGIN_TEMPLATE.replace(
5222
5598
  /__PACKAGE_ROOT__/g,
5223
5599
  packageRoot.replace(/\\/g, "\\\\")
5224
5600
  );
5225
- if (existsSync14(pluginPath)) {
5601
+ if (existsSync15(pluginPath)) {
5226
5602
  const existing = await readFile5(pluginPath, "utf-8");
5227
5603
  if (existing === pluginContent) {
5228
5604
  return false;
@@ -5232,18 +5608,18 @@ async function installOpenCodePlugin(packageRoot, homeDir = os11.homedir()) {
5232
5608
  return true;
5233
5609
  }
5234
5610
  function verifyOpenCodeHooks(homeDir = os11.homedir()) {
5235
- const configPath = path14.join(homeDir, ".config", "opencode", "opencode.json");
5236
- const pluginPath = path14.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
5237
- if (!existsSync14(configPath)) return false;
5611
+ const configPath = path15.join(homeDir, ".config", "opencode", "opencode.json");
5612
+ const pluginPath = path15.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
5613
+ if (!existsSync15(configPath)) return false;
5238
5614
  try {
5239
- const config = JSON.parse(readFileSync9(configPath, "utf-8"));
5615
+ const config = JSON.parse(readFileSync10(configPath, "utf-8"));
5240
5616
  if (!config.mcp?.["exe-os"]?.enabled) return false;
5241
5617
  } catch {
5242
5618
  return false;
5243
5619
  }
5244
- if (!existsSync14(pluginPath)) return false;
5620
+ if (!existsSync15(pluginPath)) return false;
5245
5621
  try {
5246
- const plugin = readFileSync9(pluginPath, "utf-8");
5622
+ const plugin = readFileSync10(pluginPath, "utf-8");
5247
5623
  if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
5248
5624
  if (textHasLegacySplitPostToolHook(plugin)) return false;
5249
5625
  } catch {
@@ -5276,11 +5652,11 @@ var init_installer2 = __esm({
5276
5652
 
5277
5653
  // src/bin/exe-start-opencode.ts
5278
5654
  import os12 from "os";
5279
- import path15 from "path";
5655
+ import path16 from "path";
5280
5656
  import {
5281
- existsSync as existsSync15,
5282
- readFileSync as readFileSync10,
5283
- writeFileSync as writeFileSync8,
5657
+ existsSync as existsSync16,
5658
+ readFileSync as readFileSync11,
5659
+ writeFileSync as writeFileSync9,
5284
5660
  mkdirSync as mkdirSync9,
5285
5661
  readdirSync as readdirSync4
5286
5662
  } from "fs";
@@ -5292,18 +5668,18 @@ init_database();
5292
5668
 
5293
5669
  // src/lib/keychain.ts
5294
5670
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5295
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
5671
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
5296
5672
  import { execSync as execSync3 } from "child_process";
5297
- import path6 from "path";
5673
+ import path7 from "path";
5298
5674
  import os5 from "os";
5299
5675
  var SERVICE = "exe-os";
5300
5676
  var LEGACY_SERVICE = "exe-mem";
5301
5677
  var ACCOUNT = "master-key";
5302
5678
  function getKeyDir() {
5303
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
5679
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
5304
5680
  }
5305
5681
  function getKeyPath() {
5306
- return path6.join(getKeyDir(), "master.key");
5682
+ return path7.join(getKeyDir(), "master.key");
5307
5683
  }
5308
5684
  function nativeKeychainAllowed() {
5309
5685
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -5335,7 +5711,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
5335
5711
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
5336
5712
  if (uid === 0) return true;
5337
5713
  const exeOsDir = process.env.EXE_OS_DIR;
5338
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
5714
+ return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
5339
5715
  } catch {
5340
5716
  return false;
5341
5717
  }
@@ -5448,8 +5824,8 @@ function deriveMachineKey() {
5448
5824
  }
5449
5825
  function readMachineId() {
5450
5826
  try {
5451
- const { readFileSync: readFileSync11 } = __require("fs");
5452
- return readFileSync11("/etc/machine-id", "utf-8").trim();
5827
+ const { readFileSync: readFileSync12 } = __require("fs");
5828
+ return readFileSync12("/etc/machine-id", "utf-8").trim();
5453
5829
  } catch {
5454
5830
  return "";
5455
5831
  }
@@ -5533,7 +5909,7 @@ async function getMasterKey() {
5533
5909
  }
5534
5910
  }
5535
5911
  const keyPath = getKeyPath();
5536
- if (!existsSync7(keyPath)) {
5912
+ if (!existsSync8(keyPath)) {
5537
5913
  process.stderr.write(
5538
5914
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5539
5915
  `
@@ -6017,14 +6393,14 @@ function vectorToBlob(vector) {
6017
6393
 
6018
6394
  // src/lib/behaviors-export.ts
6019
6395
  import os6 from "os";
6020
- import path8 from "path";
6396
+ import path9 from "path";
6021
6397
  import {
6022
- existsSync as existsSync9,
6398
+ existsSync as existsSync10,
6023
6399
  mkdirSync as mkdirSync4,
6024
6400
  readdirSync as readdirSync2,
6025
6401
  statSync as statSync5,
6026
6402
  unlinkSync as unlinkSync4,
6027
- writeFileSync as writeFileSync3
6403
+ writeFileSync as writeFileSync4
6028
6404
  } from "fs";
6029
6405
 
6030
6406
  // src/lib/behaviors.ts
@@ -6033,7 +6409,7 @@ import crypto2 from "crypto";
6033
6409
  async function listBehaviors(agentId, projectName, limit = 30) {
6034
6410
  const client = getClient();
6035
6411
  const result = await client.execute({
6036
- sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
6412
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id
6037
6413
  FROM behaviors
6038
6414
  WHERE agent_id = ? AND active = 1
6039
6415
  AND (project_name IS NULL OR project_name = ?)
@@ -6062,12 +6438,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
6062
6438
  active: Number(r.active),
6063
6439
  created_at: String(r.created_at),
6064
6440
  updated_at: String(r.updated_at),
6065
- vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
6441
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
6442
+ created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
6443
+ created_by_device: r.created_by_device ? String(r.created_by_device) : null,
6444
+ source_session_id: r.source_session_id ? String(r.source_session_id) : null
6066
6445
  }));
6067
6446
  }
6068
6447
 
6069
6448
  // src/lib/behaviors-export.ts
6070
- var BEHAVIORS_EXPORT_DIR = path8.join(
6449
+ var BEHAVIORS_EXPORT_DIR = path9.join(
6071
6450
  os6.homedir(),
6072
6451
  ".exe-os",
6073
6452
  "behaviors-export"
@@ -6084,7 +6463,7 @@ function getBehaviorLimit() {
6084
6463
  }
6085
6464
  }
6086
6465
  function sweepStaleBehaviorExports(now = Date.now()) {
6087
- if (!existsSync9(BEHAVIORS_EXPORT_DIR)) return;
6466
+ if (!existsSync10(BEHAVIORS_EXPORT_DIR)) return;
6088
6467
  let entries;
6089
6468
  try {
6090
6469
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -6092,7 +6471,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
6092
6471
  return;
6093
6472
  }
6094
6473
  for (const entry of entries) {
6095
- const filePath = path8.join(BEHAVIORS_EXPORT_DIR, entry);
6474
+ const filePath = path9.join(BEHAVIORS_EXPORT_DIR, entry);
6096
6475
  try {
6097
6476
  const stat = statSync5(filePath);
6098
6477
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -6125,10 +6504,10 @@ function renderBehaviorExport(behaviors) {
6125
6504
  }
6126
6505
  function exportFilePath(agentId, projectName, sessionKey) {
6127
6506
  if (!sessionKey) {
6128
- return path8.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
6507
+ return path9.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
6129
6508
  }
6130
6509
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
6131
- return path8.join(
6510
+ return path9.join(
6132
6511
  BEHAVIORS_EXPORT_DIR,
6133
6512
  `${agentId}-${safeProject}-${sessionKey}.md`
6134
6513
  );
@@ -6140,7 +6519,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
6140
6519
  if (behaviors.length === 0) return null;
6141
6520
  const body = renderBehaviorExport(behaviors);
6142
6521
  const target = exportFilePath(agentId, projectName, sessionKey);
6143
- writeFileSync3(target, body, "utf-8");
6522
+ writeFileSync4(target, body, "utf-8");
6144
6523
  return target;
6145
6524
  }
6146
6525
 
@@ -6157,7 +6536,7 @@ When done with a task: call update_task with status "done".
6157
6536
  Always call store_memory to persist important findings.
6158
6537
  `;
6159
6538
  function resolveAgent(argv) {
6160
- const invokedAs = path15.basename(argv[1] ?? "");
6539
+ const invokedAs = path16.basename(argv[1] ?? "");
6161
6540
  if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
6162
6541
  const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
6163
6542
  return { agent: agent2, passthrough: argv.slice(2) };
@@ -6174,24 +6553,24 @@ function resolveAgent(argv) {
6174
6553
  return { agent, passthrough };
6175
6554
  }
6176
6555
  function loadIdentity(agent) {
6177
- const dir = path15.join(os12.homedir(), ".exe-os", "identity");
6178
- const exact = path15.join(dir, `${agent}.md`);
6179
- if (existsSync15(exact)) {
6180
- const content = readFileSync10(exact, "utf-8").trim();
6556
+ const dir = path16.join(os12.homedir(), ".exe-os", "identity");
6557
+ const exact = path16.join(dir, `${agent}.md`);
6558
+ if (existsSync16(exact)) {
6559
+ const content = readFileSync11(exact, "utf-8").trim();
6181
6560
  if (content) return content;
6182
6561
  }
6183
6562
  try {
6184
6563
  const files = readdirSync4(dir);
6185
6564
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
6186
6565
  if (match) {
6187
- const content = readFileSync10(path15.join(dir, match), "utf-8").trim();
6566
+ const content = readFileSync11(path16.join(dir, match), "utf-8").trim();
6188
6567
  if (content) return content;
6189
6568
  }
6190
6569
  } catch {
6191
6570
  }
6192
6571
  try {
6193
- const rosterPath = path15.join(os12.homedir(), ".exe-os", "exe-employees.json");
6194
- const roster = JSON.parse(readFileSync10(rosterPath, "utf8"));
6572
+ const rosterPath = path16.join(os12.homedir(), ".exe-os", "exe-employees.json");
6573
+ const roster = JSON.parse(readFileSync11(rosterPath, "utf8"));
6195
6574
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
6196
6575
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
6197
6576
  return emp.systemPrompt;
@@ -6201,18 +6580,18 @@ function loadIdentity(agent) {
6201
6580
  return null;
6202
6581
  }
6203
6582
  function writeAgentFile(agent, identity, behaviorsPath) {
6204
- const agentDir = path15.join(os12.homedir(), ".config", "opencode", "agents");
6583
+ const agentDir = path16.join(os12.homedir(), ".config", "opencode", "agents");
6205
6584
  mkdirSync9(agentDir, { recursive: true });
6206
6585
  let content = identity;
6207
- if (behaviorsPath && existsSync15(behaviorsPath)) {
6208
- const behaviors = readFileSync10(behaviorsPath, "utf-8").trim();
6586
+ if (behaviorsPath && existsSync16(behaviorsPath)) {
6587
+ const behaviors = readFileSync11(behaviorsPath, "utf-8").trim();
6209
6588
  if (behaviors) {
6210
6589
  content += "\n\n" + behaviors;
6211
6590
  }
6212
6591
  }
6213
6592
  content += "\n" + BOOT_INSTRUCTIONS;
6214
- const outPath = path15.join(agentDir, `${agent}.md`);
6215
- writeFileSync8(outPath, content, "utf-8");
6593
+ const outPath = path16.join(agentDir, `${agent}.md`);
6594
+ writeFileSync9(outPath, content, "utf-8");
6216
6595
  return outPath;
6217
6596
  }
6218
6597
  async function main() {
@@ -6273,8 +6652,8 @@ async function main() {
6273
6652
  process.env.EXE_RUNTIME = "opencode";
6274
6653
  const empRole = (() => {
6275
6654
  try {
6276
- const emps = readFileSync10(
6277
- path15.join(os12.homedir(), ".exe-os", "exe-employees.json"),
6655
+ const emps = readFileSync11(
6656
+ path16.join(os12.homedir(), ".exe-os", "exe-employees.json"),
6278
6657
  "utf-8"
6279
6658
  );
6280
6659
  const found = JSON.parse(emps).find(