@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.
- package/README.md +9 -7
- package/dist/bin/agentic-ontology-backfill.js +54 -11
- package/dist/bin/agentic-reflection-backfill.js +29 -1
- package/dist/bin/agentic-semantic-label.js +29 -1
- package/dist/bin/backfill-conversations.js +53 -10
- package/dist/bin/backfill-responses.js +54 -11
- package/dist/bin/backfill-vectors.js +29 -1
- package/dist/bin/bulk-sync-postgres.js +55 -12
- package/dist/bin/cleanup-stale-review-tasks.js +75 -15
- package/dist/bin/cli.js +293 -76
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +28 -2
- package/dist/bin/exe-assign.js +54 -11
- package/dist/bin/exe-boot.js +481 -147
- package/dist/bin/exe-call.js +45 -4
- package/dist/bin/exe-cloud.js +93 -15
- package/dist/bin/exe-dispatch.js +369 -24
- package/dist/bin/exe-doctor.js +53 -10
- package/dist/bin/exe-export-behaviors.js +54 -11
- package/dist/bin/exe-forget.js +54 -11
- package/dist/bin/exe-gateway.js +128 -23
- package/dist/bin/exe-heartbeat.js +75 -15
- package/dist/bin/exe-kill.js +54 -11
- package/dist/bin/exe-launch-agent.js +70 -12
- package/dist/bin/exe-new-employee.js +175 -7
- package/dist/bin/exe-pending-messages.js +75 -15
- package/dist/bin/exe-pending-notifications.js +75 -15
- package/dist/bin/exe-pending-reviews.js +75 -15
- package/dist/bin/exe-rename.js +54 -11
- package/dist/bin/exe-review.js +54 -11
- package/dist/bin/exe-search.js +54 -11
- package/dist/bin/exe-session-cleanup.js +491 -146
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +524 -245
- package/dist/bin/exe-start-opencode.js +534 -165
- package/dist/bin/exe-status.js +75 -15
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +54 -11
- package/dist/bin/git-sweep.js +369 -24
- package/dist/bin/graph-backfill.js +54 -11
- package/dist/bin/graph-export.js +54 -11
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +491 -146
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +369 -24
- package/dist/bin/setup.js +91 -13
- package/dist/bin/shard-migrate.js +54 -11
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +128 -23
- package/dist/hooks/bug-report-worker.js +128 -23
- package/dist/hooks/codex-stop-task-finalizer.js +512 -140
- package/dist/hooks/commit-complete.js +369 -24
- package/dist/hooks/error-recall.js +54 -11
- package/dist/hooks/ingest.js +4575 -252
- package/dist/hooks/instructions-loaded.js +54 -11
- package/dist/hooks/notification.js +54 -11
- package/dist/hooks/post-compact.js +75 -15
- package/dist/hooks/post-tool-combined.js +75 -15
- package/dist/hooks/pre-compact.js +449 -104
- package/dist/hooks/pre-tool-use.js +90 -15
- package/dist/hooks/prompt-submit.js +129 -24
- package/dist/hooks/session-end.js +451 -109
- package/dist/hooks/session-start.js +104 -16
- package/dist/hooks/stop.js +74 -14
- package/dist/hooks/subagent-stop.js +75 -15
- package/dist/hooks/summary-worker.js +73 -7
- package/dist/index.js +128 -23
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +38 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +16 -0
- package/dist/lib/db.js +16 -0
- package/dist/lib/device-registry.js +16 -0
- package/dist/lib/employee-templates.js +29 -3
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +268 -42
- package/dist/lib/hybrid-search.js +54 -11
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +29 -1
- package/dist/lib/skill-learning.js +458 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +54 -11
- package/dist/lib/tasks.js +393 -91
- package/dist/lib/tmux-routing.js +316 -14
- package/dist/mcp/server.js +169 -30
- package/dist/mcp/tools/create-task.js +75 -13
- package/dist/mcp/tools/deactivate-behavior.js +33 -24
- package/dist/mcp/tools/list-tasks.js +21 -4
- package/dist/mcp/tools/send-message.js +21 -4
- package/dist/mcp/tools/update-task.js +390 -91
- package/dist/runtime/index.js +446 -101
- package/dist/tui/App.js +208 -54
- package/package.json +1 -1
package/dist/lib/tmux-routing.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
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;
|