@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
@@ -650,6 +650,19 @@ var init_runtime_table = __esm({
650
650
  });
651
651
 
652
652
  // src/lib/agent-config.ts
653
+ var agent_config_exports = {};
654
+ __export(agent_config_exports, {
655
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
656
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
657
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
658
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
659
+ clearAgentRuntime: () => clearAgentRuntime,
660
+ getAgentRuntime: () => getAgentRuntime,
661
+ loadAgentConfig: () => loadAgentConfig,
662
+ saveAgentConfig: () => saveAgentConfig,
663
+ setAgentMcps: () => setAgentMcps,
664
+ setAgentRuntime: () => setAgentRuntime
665
+ });
653
666
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4 } from "fs";
654
667
  import path3 from "path";
655
668
  function loadAgentConfig() {
@@ -660,6 +673,12 @@ function loadAgentConfig() {
660
673
  return {};
661
674
  }
662
675
  }
676
+ function saveAgentConfig(config) {
677
+ const dir = path3.dirname(AGENT_CONFIG_PATH);
678
+ ensurePrivateDirSync(dir);
679
+ writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
680
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
681
+ }
663
682
  function getAgentRuntime(agentId) {
664
683
  const config = loadAgentConfig();
665
684
  const entry = config[agentId];
@@ -668,7 +687,47 @@ function getAgentRuntime(agentId) {
668
687
  if (orgDefault) return orgDefault;
669
688
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
670
689
  }
671
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
690
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
691
+ const knownModels = KNOWN_RUNTIMES[runtime];
692
+ if (!knownModels) {
693
+ return {
694
+ ok: false,
695
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
696
+ };
697
+ }
698
+ if (!knownModels.includes(model)) {
699
+ return {
700
+ ok: false,
701
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
702
+ };
703
+ }
704
+ const config = loadAgentConfig();
705
+ const existing = config[agentId];
706
+ const entry = { runtime, model };
707
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
708
+ if (mcps !== void 0) {
709
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
710
+ } else if (existing?.mcps) {
711
+ entry.mcps = existing.mcps;
712
+ }
713
+ config[agentId] = entry;
714
+ saveAgentConfig(config);
715
+ return { ok: true };
716
+ }
717
+ function setAgentMcps(agentId, mcps) {
718
+ const config = loadAgentConfig();
719
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
720
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
721
+ config[agentId] = existing;
722
+ saveAgentConfig(config);
723
+ return { ok: true };
724
+ }
725
+ function clearAgentRuntime(agentId) {
726
+ const config = loadAgentConfig();
727
+ delete config[agentId];
728
+ saveAgentConfig(config);
729
+ }
730
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
672
731
  var init_agent_config = __esm({
673
732
  "src/lib/agent-config.ts"() {
674
733
  "use strict";
@@ -676,6 +735,16 @@ var init_agent_config = __esm({
676
735
  init_runtime_table();
677
736
  init_secure_files();
678
737
  AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
738
+ KNOWN_RUNTIMES = {
739
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
740
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
741
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
742
+ };
743
+ RUNTIME_LABELS = {
744
+ claude: "Claude Code (Anthropic)",
745
+ codex: "Codex (OpenAI)",
746
+ opencode: "OpenCode (open source)"
747
+ };
679
748
  DEFAULT_MODELS = {
680
749
  claude: "claude-opus-4.6",
681
750
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
@@ -818,6 +887,32 @@ var init_db_retry = __esm({
818
887
  });
819
888
 
820
889
  // src/lib/employees.ts
890
+ var employees_exports = {};
891
+ __export(employees_exports, {
892
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
893
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
894
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
895
+ addEmployee: () => addEmployee,
896
+ baseAgentName: () => baseAgentName,
897
+ canCoordinate: () => canCoordinate,
898
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
899
+ getCoordinatorName: () => getCoordinatorName,
900
+ getEmployee: () => getEmployee,
901
+ getEmployeeByRole: () => getEmployeeByRole,
902
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
903
+ hasRole: () => hasRole,
904
+ hireEmployee: () => hireEmployee,
905
+ isCoordinatorName: () => isCoordinatorName,
906
+ isCoordinatorRole: () => isCoordinatorRole,
907
+ isMultiInstance: () => isMultiInstance,
908
+ loadEmployees: () => loadEmployees,
909
+ loadEmployeesSync: () => loadEmployeesSync,
910
+ normalizeRole: () => normalizeRole,
911
+ normalizeRosterCase: () => normalizeRosterCase,
912
+ registerBinSymlinks: () => registerBinSymlinks,
913
+ saveEmployees: () => saveEmployees,
914
+ validateEmployeeName: () => validateEmployeeName
915
+ });
821
916
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
822
917
  import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
823
918
  import { execSync as execSync3 } from "child_process";
@@ -839,6 +934,24 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
839
934
  if (!agentName) return false;
840
935
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
841
936
  }
937
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
938
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
939
+ }
940
+ function validateEmployeeName(name) {
941
+ if (!name) {
942
+ return { valid: false, error: "Name is required" };
943
+ }
944
+ if (name.length > 32) {
945
+ return { valid: false, error: "Name must be 32 characters or fewer" };
946
+ }
947
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
948
+ return {
949
+ valid: false,
950
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
951
+ };
952
+ }
953
+ return { valid: true };
954
+ }
842
955
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
843
956
  if (!existsSync6(employeesPath)) {
844
957
  return [];
@@ -850,6 +963,10 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
850
963
  return [];
851
964
  }
852
965
  }
966
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
967
+ await mkdir2(path5.dirname(employeesPath), { recursive: true });
968
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
969
+ }
853
970
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
854
971
  if (!existsSync6(employeesPath)) return [];
855
972
  try {
@@ -861,6 +978,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
861
978
  function getEmployee(employees, name) {
862
979
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
863
980
  }
981
+ function getEmployeeByRole(employees, role) {
982
+ const lower = role.toLowerCase();
983
+ return employees.find((e) => e.role.toLowerCase() === lower);
984
+ }
985
+ function getEmployeeNamesByRole(employees, role) {
986
+ const lower = role.toLowerCase();
987
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
988
+ }
989
+ function hasRole(agentName, role) {
990
+ const employees = loadEmployeesSync();
991
+ const emp = getEmployee(employees, agentName);
992
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
993
+ }
864
994
  function baseAgentName(name, employees) {
865
995
  const match = name.match(/^([a-zA-Z]+)\d+$/);
866
996
  if (!match) return name;
@@ -875,7 +1005,131 @@ function isMultiInstance(agentName, employees) {
875
1005
  if (!emp) return false;
876
1006
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
877
1007
  }
878
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
1008
+ function addEmployee(employees, employee) {
1009
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
1010
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
1011
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
1012
+ throw new Error(`Employee '${normalized.name}' already exists`);
1013
+ }
1014
+ return [...employees, normalized];
1015
+ }
1016
+ function appendToCoordinatorTeam(employee) {
1017
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
1018
+ if (!coordinator) return;
1019
+ const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
1020
+ if (!existsSync6(idPath)) return;
1021
+ const content = readFileSync5(idPath, "utf-8");
1022
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
1023
+ const teamMatch = content.match(TEAM_SECTION_RE);
1024
+ if (!teamMatch || teamMatch.index === void 0) return;
1025
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
1026
+ const nextHeading = afterTeam.match(/\n## /);
1027
+ const entry = `
1028
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
1029
+ `;
1030
+ let updated;
1031
+ if (nextHeading && nextHeading.index !== void 0) {
1032
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
1033
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
1034
+ } else {
1035
+ updated = content.trimEnd() + "\n" + entry;
1036
+ }
1037
+ writeFileSync4(idPath, updated, "utf-8");
1038
+ }
1039
+ function capitalize(s) {
1040
+ return s.charAt(0).toUpperCase() + s.slice(1);
1041
+ }
1042
+ async function hireEmployee(employee) {
1043
+ const employees = await loadEmployees();
1044
+ const updated = addEmployee(employees, employee);
1045
+ await saveEmployees(updated);
1046
+ try {
1047
+ appendToCoordinatorTeam(employee);
1048
+ } catch {
1049
+ }
1050
+ try {
1051
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
1052
+ const config = loadAgentConfig2();
1053
+ const name = employee.name.toLowerCase();
1054
+ if (!config[name] && config["default"]) {
1055
+ config[name] = { ...config["default"] };
1056
+ saveAgentConfig2(config);
1057
+ }
1058
+ } catch {
1059
+ }
1060
+ return updated;
1061
+ }
1062
+ async function normalizeRosterCase(rosterPath) {
1063
+ const employees = await loadEmployees(rosterPath);
1064
+ let changed = false;
1065
+ for (const emp of employees) {
1066
+ if (emp.name !== emp.name.toLowerCase()) {
1067
+ const oldName = emp.name;
1068
+ emp.name = emp.name.toLowerCase();
1069
+ changed = true;
1070
+ try {
1071
+ const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
1072
+ const oldPath = path5.join(identityDir, `${oldName}.md`);
1073
+ const newPath = path5.join(identityDir, `${emp.name}.md`);
1074
+ if (existsSync6(oldPath) && !existsSync6(newPath)) {
1075
+ renameSync3(oldPath, newPath);
1076
+ } else if (existsSync6(oldPath) && oldPath !== newPath) {
1077
+ const content = readFileSync5(oldPath, "utf-8");
1078
+ writeFileSync4(newPath, content, "utf-8");
1079
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
1080
+ unlinkSync(oldPath);
1081
+ }
1082
+ }
1083
+ } catch {
1084
+ }
1085
+ }
1086
+ }
1087
+ if (changed) {
1088
+ await saveEmployees(employees, rosterPath);
1089
+ }
1090
+ return changed;
1091
+ }
1092
+ function findExeBin() {
1093
+ try {
1094
+ return execSync3(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1095
+ } catch {
1096
+ return null;
1097
+ }
1098
+ }
1099
+ function registerBinSymlinks(name) {
1100
+ const created = [];
1101
+ const skipped = [];
1102
+ const errors = [];
1103
+ const exeBinPath = findExeBin();
1104
+ if (!exeBinPath) {
1105
+ errors.push("Could not find 'exe-os' in PATH");
1106
+ return { created, skipped, errors };
1107
+ }
1108
+ const binDir = path5.dirname(exeBinPath);
1109
+ let target;
1110
+ try {
1111
+ target = readlinkSync(exeBinPath);
1112
+ } catch {
1113
+ errors.push("Could not read 'exe' symlink");
1114
+ return { created, skipped, errors };
1115
+ }
1116
+ for (const suffix of ["", "-opencode"]) {
1117
+ const linkName = `${name}${suffix}`;
1118
+ const linkPath = path5.join(binDir, linkName);
1119
+ if (existsSync6(linkPath)) {
1120
+ skipped.push(linkName);
1121
+ continue;
1122
+ }
1123
+ try {
1124
+ symlinkSync(target, linkPath);
1125
+ created.push(linkName);
1126
+ } catch (err) {
1127
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
1128
+ }
1129
+ }
1130
+ return { created, skipped, errors };
1131
+ }
1132
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
879
1133
  var init_employees = __esm({
880
1134
  "src/lib/employees.ts"() {
881
1135
  "use strict";
@@ -885,6 +1139,7 @@ var init_employees = __esm({
885
1139
  COORDINATOR_ROLE = "COO";
886
1140
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
887
1141
  IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
1142
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
888
1143
  }
889
1144
  });
890
1145
 
@@ -1339,7 +1594,7 @@ async function assertVpsLicense(opts) {
1339
1594
  }
1340
1595
  if (!transientFailure) {
1341
1596
  throw new Error(
1342
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
1597
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
1343
1598
  );
1344
1599
  }
1345
1600
  const fresh = await getCachedLicense();
@@ -1376,7 +1631,7 @@ async function assertVpsLicense(opts) {
1376
1631
  } catch {
1377
1632
  }
1378
1633
  throw new Error(
1379
- `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
1634
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
1380
1635
  );
1381
1636
  }
1382
1637
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -1408,7 +1663,7 @@ var init_license = __esm({
1408
1663
  LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
1409
1664
  CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
1410
1665
  DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1411
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
1666
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
1412
1667
  RETRY_DELAY_MS = 500;
1413
1668
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1414
1669
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -1946,6 +2201,19 @@ async function resolveTask(client, identifier, scopeSession) {
1946
2201
  args: [identifier, ...scope.args]
1947
2202
  });
1948
2203
  if (result.rows.length === 1) return result.rows[0];
2204
+ if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
2205
+ result = await client.execute({
2206
+ sql: `SELECT * FROM tasks WHERE id LIKE ?`,
2207
+ args: [`${identifier}%`]
2208
+ });
2209
+ if (result.rows.length === 1) return result.rows[0];
2210
+ if (result.rows.length > 1) {
2211
+ const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
2212
+ throw new Error(
2213
+ `Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
2214
+ );
2215
+ }
2216
+ }
1949
2217
  result = await client.execute({
1950
2218
  sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
1951
2219
  args: [`%${identifier}%`, ...scope.args]
@@ -2800,12 +3068,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
2800
3068
  WHERE blocked_by = ? AND status = 'blocked'`,
2801
3069
  args: [now, taskId]
2802
3070
  });
2803
- if (baseDir && unblocked.rowsAffected > 0) {
2804
- const ubScope = sessionScopeFilter();
2805
- const unblockedRows = await client.execute({
2806
- sql: `SELECT task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
2807
- args: [now, ...ubScope.args]
2808
- });
3071
+ if (unblocked.rowsAffected === 0) return;
3072
+ const ubScope = sessionScopeFilter();
3073
+ const unblockedRows = await client.execute({
3074
+ sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
3075
+ args: [now, ...ubScope.args]
3076
+ });
3077
+ if (baseDir) {
2809
3078
  for (const ur of unblockedRows.rows) {
2810
3079
  try {
2811
3080
  const ubFile = path14.join(baseDir, String(ur.task_file));
@@ -2817,6 +3086,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
2817
3086
  }
2818
3087
  }
2819
3088
  }
3089
+ if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
3090
+ try {
3091
+ const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
3092
+ const dispatched = /* @__PURE__ */ new Set();
3093
+ for (const ur of unblockedRows.rows) {
3094
+ const assignee = String(ur.assigned_to);
3095
+ if (dispatched.has(assignee)) continue;
3096
+ dispatched.add(assignee);
3097
+ queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
3098
+ }
3099
+ } catch {
3100
+ }
3101
+ }
2820
3102
  }
2821
3103
  async function findNextTask(assignedTo) {
2822
3104
  const client = getClient();
@@ -3475,6 +3757,15 @@ var init_embedder = __esm({
3475
3757
  // src/lib/behaviors.ts
3476
3758
  import crypto5 from "crypto";
3477
3759
  async function storeBehavior(opts) {
3760
+ try {
3761
+ const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
3762
+ const roster = loadEmployeesSync2();
3763
+ if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
3764
+ throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
3765
+ }
3766
+ } catch (e) {
3767
+ if (e instanceof Error && e.message.includes("not found in roster")) throw e;
3768
+ }
3478
3769
  const client = getClient();
3479
3770
  const id = crypto5.randomUUID();
3480
3771
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -3928,6 +4219,12 @@ async function updateTask(input) {
3928
4219
  }
3929
4220
  }
3930
4221
  }
4222
+ if (input.status === "cancelled") {
4223
+ try {
4224
+ await cascadeUnblock(taskId, input.baseDir, now);
4225
+ } catch {
4226
+ }
4227
+ }
3931
4228
  if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
3932
4229
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
3933
4230
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
@@ -4459,11 +4756,12 @@ function getDispatchedBy(sessionKey) {
4459
4756
  }
4460
4757
  }
4461
4758
  function resolveExeSession() {
4759
+ if (process.env.EXE_SESSION_NAME) {
4760
+ const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
4761
+ if (fromEnv) return fromEnv;
4762
+ }
4462
4763
  const mySession = getMySession();
4463
4764
  if (!mySession) {
4464
- if (process.env.EXE_SESSION_NAME) {
4465
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
4466
- }
4467
4765
  return null;
4468
4766
  }
4469
4767
  const fromSessionName = extractRootExe(mySession);
@@ -4478,6 +4776,10 @@ function resolveExeSession() {
4478
4776
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
4479
4777
  `
4480
4778
  );
4779
+ try {
4780
+ registerParentExe(key, fromSessionName);
4781
+ } catch {
4782
+ }
4481
4783
  candidate = fromSessionName;
4482
4784
  } else {
4483
4785
  candidate = fromCache;