@askexenow/exe-os 0.9.113 → 0.9.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agentic-ontology-backfill.js +36 -12
- package/dist/bin/agentic-reflection-backfill.js +36 -12
- package/dist/bin/agentic-semantic-label.js +36 -12
- package/dist/bin/backfill-conversations.js +36 -12
- package/dist/bin/backfill-responses.js +36 -12
- package/dist/bin/backfill-vectors.js +36 -12
- package/dist/bin/bulk-sync-postgres.js +36 -12
- package/dist/bin/cleanup-stale-review-tasks.js +470 -113
- package/dist/bin/cli.js +413 -62
- package/dist/bin/exe-agent.js +27 -0
- package/dist/bin/exe-assign.js +36 -12
- package/dist/bin/exe-boot.js +246 -54
- package/dist/bin/exe-call.js +8 -0
- package/dist/bin/exe-cloud.js +47 -12
- package/dist/bin/exe-dispatch.js +348 -53
- package/dist/bin/exe-doctor.js +51 -13
- package/dist/bin/exe-export-behaviors.js +37 -12
- package/dist/bin/exe-forget.js +36 -12
- package/dist/bin/exe-gateway.js +348 -53
- package/dist/bin/exe-heartbeat.js +471 -113
- package/dist/bin/exe-kill.js +36 -12
- package/dist/bin/exe-launch-agent.js +117 -18
- package/dist/bin/exe-new-employee.js +9 -1
- package/dist/bin/exe-pending-messages.js +452 -95
- package/dist/bin/exe-pending-notifications.js +452 -95
- package/dist/bin/exe-pending-reviews.js +452 -95
- package/dist/bin/exe-rename.js +36 -12
- package/dist/bin/exe-review.js +36 -12
- package/dist/bin/exe-search.js +37 -12
- package/dist/bin/exe-session-cleanup.js +348 -53
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +46 -13
- package/dist/bin/exe-start-opencode.js +46 -13
- package/dist/bin/exe-status.js +460 -114
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +36 -12
- package/dist/bin/git-sweep.js +348 -53
- package/dist/bin/graph-backfill.js +36 -12
- package/dist/bin/graph-export.js +36 -12
- package/dist/bin/install.js +9 -1
- package/dist/bin/intercom-check.js +255 -53
- package/dist/bin/scan-tasks.js +348 -53
- package/dist/bin/setup.js +74 -12
- package/dist/bin/shard-migrate.js +36 -12
- package/dist/gateway/index.js +348 -53
- package/dist/hooks/bug-report-worker.js +348 -53
- package/dist/hooks/codex-stop-task-finalizer.js +308 -37
- package/dist/hooks/commit-complete.js +348 -53
- package/dist/hooks/error-recall.js +37 -12
- package/dist/hooks/ingest.js +363 -54
- package/dist/hooks/instructions-loaded.js +36 -12
- package/dist/hooks/notification.js +36 -12
- package/dist/hooks/post-compact.js +426 -72
- package/dist/hooks/post-tool-combined.js +501 -146
- package/dist/hooks/pre-compact.js +348 -53
- package/dist/hooks/pre-tool-use.js +92 -13
- package/dist/hooks/prompt-submit.js +348 -53
- package/dist/hooks/session-end.js +158 -53
- package/dist/hooks/session-start.js +66 -13
- package/dist/hooks/stop.js +420 -72
- package/dist/hooks/subagent-stop.js +419 -72
- package/dist/hooks/summary-worker.js +442 -121
- package/dist/index.js +375 -53
- package/dist/lib/agent-config.js +8 -0
- package/dist/lib/cloud-sync.js +35 -12
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +9 -1
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employees.js +8 -0
- package/dist/lib/exe-daemon.js +524 -60
- package/dist/lib/hybrid-search.js +37 -12
- package/dist/lib/keychain.js +25 -13
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +36 -12
- package/dist/lib/skill-learning.js +21 -0
- package/dist/lib/store.js +36 -12
- package/dist/lib/tasks.js +324 -41
- package/dist/lib/tmux-routing.js +324 -41
- package/dist/mcp/server.js +374 -54
- package/dist/mcp/tools/create-task.js +324 -41
- package/dist/mcp/tools/list-tasks.js +406 -57
- package/dist/mcp/tools/send-message.js +395 -74
- package/dist/mcp/tools/update-task.js +324 -41
- package/dist/runtime/index.js +375 -53
- package/dist/tui/App.js +377 -55
- package/package.json +1 -1
package/dist/lib/tmux-routing.js
CHANGED
|
@@ -446,6 +446,17 @@ function normalizeOrchestration(raw) {
|
|
|
446
446
|
const userOrg = raw.orchestration ?? {};
|
|
447
447
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
448
448
|
}
|
|
449
|
+
function normalizeCloudEndpoint(raw) {
|
|
450
|
+
const cloud = raw.cloud;
|
|
451
|
+
if (!cloud?.endpoint) return;
|
|
452
|
+
const ep = String(cloud.endpoint);
|
|
453
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
454
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
455
|
+
process.stderr.write(
|
|
456
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
449
460
|
async function loadConfig() {
|
|
450
461
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
451
462
|
await ensurePrivateDir(dir);
|
|
@@ -471,6 +482,7 @@ async function loadConfig() {
|
|
|
471
482
|
normalizeSessionLifecycle(migratedCfg);
|
|
472
483
|
normalizeAutoUpdate(migratedCfg);
|
|
473
484
|
normalizeOrchestration(migratedCfg);
|
|
485
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
474
486
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
475
487
|
if (config.dbPath.startsWith("~")) {
|
|
476
488
|
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
@@ -499,6 +511,7 @@ function loadConfigSync() {
|
|
|
499
511
|
normalizeSessionLifecycle(migratedCfg);
|
|
500
512
|
normalizeAutoUpdate(migratedCfg);
|
|
501
513
|
normalizeOrchestration(migratedCfg);
|
|
514
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
502
515
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
503
516
|
if (config.dbPath.startsWith("~")) {
|
|
504
517
|
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
@@ -659,6 +672,7 @@ __export(agent_config_exports, {
|
|
|
659
672
|
clearAgentRuntime: () => clearAgentRuntime,
|
|
660
673
|
getAgentRuntime: () => getAgentRuntime,
|
|
661
674
|
loadAgentConfig: () => loadAgentConfig,
|
|
675
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
662
676
|
saveAgentConfig: () => saveAgentConfig,
|
|
663
677
|
setAgentMcps: () => setAgentMcps,
|
|
664
678
|
setAgentRuntime: () => setAgentRuntime
|
|
@@ -687,6 +701,13 @@ function getAgentRuntime(agentId) {
|
|
|
687
701
|
if (orgDefault) return orgDefault;
|
|
688
702
|
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
689
703
|
}
|
|
704
|
+
function normalizeCcModelName(model) {
|
|
705
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
706
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
707
|
+
ccModel += "[1m]";
|
|
708
|
+
}
|
|
709
|
+
return ccModel;
|
|
710
|
+
}
|
|
690
711
|
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
691
712
|
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
692
713
|
if (!knownModels) {
|
|
@@ -756,6 +777,7 @@ var init_agent_config = __esm({
|
|
|
756
777
|
// src/lib/intercom-queue.ts
|
|
757
778
|
var intercom_queue_exports = {};
|
|
758
779
|
__export(intercom_queue_exports, {
|
|
780
|
+
_resetDrainGuard: () => _resetDrainGuard,
|
|
759
781
|
clearQueueForAgent: () => clearQueueForAgent,
|
|
760
782
|
drainForSession: () => drainForSession,
|
|
761
783
|
drainQueue: () => drainQueue,
|
|
@@ -801,38 +823,47 @@ function queueIntercom(targetSession, reason) {
|
|
|
801
823
|
writeQueue(queue);
|
|
802
824
|
}
|
|
803
825
|
function drainQueue(isSessionBusy2, sendKeys) {
|
|
826
|
+
if (_draining) {
|
|
827
|
+
logQueue("SKIP_DRAIN \u2014 previous drain still running (possible tmux hang)");
|
|
828
|
+
return { drained: 0, failed: 0 };
|
|
829
|
+
}
|
|
804
830
|
const queue = readQueue();
|
|
805
831
|
if (queue.length === 0) return { drained: 0, failed: 0 };
|
|
832
|
+
_draining = true;
|
|
806
833
|
const remaining = [];
|
|
807
834
|
let drained = 0;
|
|
808
835
|
let failed = 0;
|
|
809
|
-
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
836
|
+
try {
|
|
837
|
+
for (const item of queue) {
|
|
838
|
+
const age = Date.now() - new Date(item.queuedAt).getTime();
|
|
839
|
+
if (age > TTL_MS) {
|
|
840
|
+
logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
|
|
841
|
+
failed++;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
try {
|
|
845
|
+
if (!isSessionBusy2(item.targetSession)) {
|
|
846
|
+
const success = sendKeys(item.targetSession);
|
|
847
|
+
if (success) {
|
|
848
|
+
logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
|
|
849
|
+
drained++;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
823
852
|
}
|
|
853
|
+
} catch {
|
|
824
854
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
855
|
+
item.attempts++;
|
|
856
|
+
if (item.attempts >= MAX_RETRIES) {
|
|
857
|
+
logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES} retries exhausted, reason: ${item.reason})`);
|
|
858
|
+
failed++;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
remaining.push(item);
|
|
832
862
|
}
|
|
833
|
-
remaining
|
|
863
|
+
writeQueue(remaining);
|
|
864
|
+
} finally {
|
|
865
|
+
_draining = false;
|
|
834
866
|
}
|
|
835
|
-
writeQueue(remaining);
|
|
836
867
|
return { drained, failed };
|
|
837
868
|
}
|
|
838
869
|
function drainForSession(targetSession, sendKeys) {
|
|
@@ -857,6 +888,9 @@ function clearQueueForAgent(agentName) {
|
|
|
857
888
|
logQueue(`CLEARED ${before - filtered.length} stale item(s) for ${agentName}`);
|
|
858
889
|
}
|
|
859
890
|
}
|
|
891
|
+
function _resetDrainGuard() {
|
|
892
|
+
_draining = false;
|
|
893
|
+
}
|
|
860
894
|
function logQueue(msg) {
|
|
861
895
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
|
|
862
896
|
`;
|
|
@@ -868,13 +902,14 @@ function logQueue(msg) {
|
|
|
868
902
|
} catch {
|
|
869
903
|
}
|
|
870
904
|
}
|
|
871
|
-
var QUEUE_PATH, MAX_RETRIES, TTL_MS, INTERCOM_LOG;
|
|
905
|
+
var QUEUE_PATH, MAX_RETRIES, TTL_MS, _draining, INTERCOM_LOG;
|
|
872
906
|
var init_intercom_queue = __esm({
|
|
873
907
|
"src/lib/intercom-queue.ts"() {
|
|
874
908
|
"use strict";
|
|
875
909
|
QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
|
|
876
910
|
MAX_RETRIES = 5;
|
|
877
911
|
TTL_MS = 60 * 60 * 1e3;
|
|
912
|
+
_draining = false;
|
|
878
913
|
INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
|
|
879
914
|
}
|
|
880
915
|
});
|
|
@@ -1869,6 +1904,17 @@ var init_task_scope = __esm({
|
|
|
1869
1904
|
});
|
|
1870
1905
|
|
|
1871
1906
|
// src/lib/notifications.ts
|
|
1907
|
+
var notifications_exports = {};
|
|
1908
|
+
__export(notifications_exports, {
|
|
1909
|
+
cleanupOldNotifications: () => cleanupOldNotifications,
|
|
1910
|
+
formatNotifications: () => formatNotifications,
|
|
1911
|
+
markAsRead: () => markAsRead,
|
|
1912
|
+
markAsReadByTaskFile: () => markAsReadByTaskFile,
|
|
1913
|
+
markDoneTaskNotificationsAsRead: () => markDoneTaskNotificationsAsRead,
|
|
1914
|
+
migrateJsonNotifications: () => migrateJsonNotifications,
|
|
1915
|
+
readUnreadNotifications: () => readUnreadNotifications,
|
|
1916
|
+
writeNotification: () => writeNotification
|
|
1917
|
+
});
|
|
1872
1918
|
import crypto from "crypto";
|
|
1873
1919
|
import path10 from "path";
|
|
1874
1920
|
import os8 from "os";
|
|
@@ -1905,6 +1951,52 @@ async function writeNotification(notification) {
|
|
|
1905
1951
|
`);
|
|
1906
1952
|
}
|
|
1907
1953
|
}
|
|
1954
|
+
async function readUnreadNotifications(agentFilter, sessionScope) {
|
|
1955
|
+
try {
|
|
1956
|
+
const client = getClient();
|
|
1957
|
+
const conditions = ["read = 0"];
|
|
1958
|
+
const args = [];
|
|
1959
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
1960
|
+
if (agentFilter) {
|
|
1961
|
+
conditions.push("agent_id = ?");
|
|
1962
|
+
args.push(agentFilter);
|
|
1963
|
+
}
|
|
1964
|
+
const result = await client.execute({
|
|
1965
|
+
sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, session_scope, created_at
|
|
1966
|
+
FROM notifications
|
|
1967
|
+
WHERE ${conditions.join(" AND ")}${scope.sql}
|
|
1968
|
+
ORDER BY created_at ASC`,
|
|
1969
|
+
args: [...args, ...scope.args]
|
|
1970
|
+
});
|
|
1971
|
+
return result.rows.map((r) => ({
|
|
1972
|
+
id: String(r.id),
|
|
1973
|
+
agentId: String(r.agent_id),
|
|
1974
|
+
agentRole: String(r.agent_role),
|
|
1975
|
+
event: String(r.event),
|
|
1976
|
+
project: String(r.project),
|
|
1977
|
+
summary: String(r.summary),
|
|
1978
|
+
taskFile: r.task_file ? String(r.task_file) : void 0,
|
|
1979
|
+
sessionScope: r.session_scope == null ? null : String(r.session_scope),
|
|
1980
|
+
timestamp: String(r.created_at),
|
|
1981
|
+
read: false
|
|
1982
|
+
}));
|
|
1983
|
+
} catch {
|
|
1984
|
+
return [];
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
async function markAsRead(ids, sessionScope) {
|
|
1988
|
+
if (ids.length === 0) return;
|
|
1989
|
+
try {
|
|
1990
|
+
const client = getClient();
|
|
1991
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
1992
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
1993
|
+
await client.execute({
|
|
1994
|
+
sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})${scope.sql}`,
|
|
1995
|
+
args: [...ids, ...scope.args]
|
|
1996
|
+
});
|
|
1997
|
+
} catch {
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
1908
2000
|
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
1909
2001
|
try {
|
|
1910
2002
|
const client = getClient();
|
|
@@ -1917,11 +2009,144 @@ async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
|
1917
2009
|
} catch {
|
|
1918
2010
|
}
|
|
1919
2011
|
}
|
|
2012
|
+
async function cleanupOldNotifications(daysOld = CLEANUP_DAYS, sessionScope) {
|
|
2013
|
+
try {
|
|
2014
|
+
const client = getClient();
|
|
2015
|
+
const cutoff = new Date(
|
|
2016
|
+
Date.now() - daysOld * 24 * 60 * 60 * 1e3
|
|
2017
|
+
).toISOString();
|
|
2018
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
2019
|
+
const result = await client.execute({
|
|
2020
|
+
sql: `DELETE FROM notifications WHERE created_at < ?${scope.sql}`,
|
|
2021
|
+
args: [cutoff, ...scope.args]
|
|
2022
|
+
});
|
|
2023
|
+
return result.rowsAffected;
|
|
2024
|
+
} catch {
|
|
2025
|
+
return 0;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
async function markDoneTaskNotificationsAsRead(sessionScope) {
|
|
2029
|
+
try {
|
|
2030
|
+
const client = getClient();
|
|
2031
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
2032
|
+
const result = await client.execute({
|
|
2033
|
+
sql: `UPDATE notifications SET read = 1
|
|
2034
|
+
WHERE read = 0
|
|
2035
|
+
AND task_file IS NOT NULL
|
|
2036
|
+
${scope.sql}
|
|
2037
|
+
AND task_file IN (
|
|
2038
|
+
SELECT task_file FROM tasks WHERE status = 'done'${scope.sql}
|
|
2039
|
+
)`,
|
|
2040
|
+
args: [...scope.args, ...scope.args]
|
|
2041
|
+
});
|
|
2042
|
+
return result.rowsAffected;
|
|
2043
|
+
} catch {
|
|
2044
|
+
return 0;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
function formatNotifications(notifications) {
|
|
2048
|
+
if (notifications.length === 0) return "";
|
|
2049
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2050
|
+
for (const n of notifications) {
|
|
2051
|
+
const key = `${n.agentId}|${n.agentRole}`;
|
|
2052
|
+
if (!grouped.has(key)) grouped.set(key, []);
|
|
2053
|
+
grouped.get(key).push(n);
|
|
2054
|
+
}
|
|
2055
|
+
const lines = [];
|
|
2056
|
+
lines.push(`## Notifications (${notifications.length} unread)
|
|
2057
|
+
`);
|
|
2058
|
+
for (const [key, items] of grouped) {
|
|
2059
|
+
const [agentId, agentRole] = key.split("|");
|
|
2060
|
+
lines.push(`**${agentId}** (${agentRole}):`);
|
|
2061
|
+
for (const item of items) {
|
|
2062
|
+
const ago = formatTimeAgo(item.timestamp);
|
|
2063
|
+
const icon = eventIcon(item.event);
|
|
2064
|
+
lines.push(`- ${icon} ${item.summary} (${item.project}) \u2014 ${ago}`);
|
|
2065
|
+
}
|
|
2066
|
+
lines.push("");
|
|
2067
|
+
}
|
|
2068
|
+
return lines.join("\n");
|
|
2069
|
+
}
|
|
2070
|
+
async function migrateJsonNotifications() {
|
|
2071
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path10.join(os8.homedir(), ".exe-os");
|
|
2072
|
+
const notifDir = path10.join(base, "notifications");
|
|
2073
|
+
if (!existsSync11(notifDir)) return 0;
|
|
2074
|
+
let migrated = 0;
|
|
2075
|
+
try {
|
|
2076
|
+
const files = readdirSync(notifDir).filter((f) => f.endsWith(".json"));
|
|
2077
|
+
if (files.length === 0) return 0;
|
|
2078
|
+
const client = getClient();
|
|
2079
|
+
for (const file of files) {
|
|
2080
|
+
try {
|
|
2081
|
+
const filePath = path10.join(notifDir, file);
|
|
2082
|
+
const data = JSON.parse(readFileSync8(filePath, "utf8"));
|
|
2083
|
+
await client.execute({
|
|
2084
|
+
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
2085
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2086
|
+
args: [
|
|
2087
|
+
crypto.randomUUID(),
|
|
2088
|
+
data.agentId ?? "unknown",
|
|
2089
|
+
data.agentRole ?? "unknown",
|
|
2090
|
+
data.event ?? "session_summary",
|
|
2091
|
+
data.project ?? "unknown",
|
|
2092
|
+
data.summary ?? "",
|
|
2093
|
+
data.taskFile ?? null,
|
|
2094
|
+
null,
|
|
2095
|
+
data.read ? 1 : 0,
|
|
2096
|
+
data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
2097
|
+
]
|
|
2098
|
+
});
|
|
2099
|
+
unlinkSync3(filePath);
|
|
2100
|
+
migrated++;
|
|
2101
|
+
} catch {
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
try {
|
|
2105
|
+
const remaining = readdirSync(notifDir);
|
|
2106
|
+
if (remaining.length === 0) {
|
|
2107
|
+
rmdirSync(notifDir);
|
|
2108
|
+
}
|
|
2109
|
+
} catch {
|
|
2110
|
+
}
|
|
2111
|
+
} catch {
|
|
2112
|
+
}
|
|
2113
|
+
return migrated;
|
|
2114
|
+
}
|
|
2115
|
+
function eventIcon(event) {
|
|
2116
|
+
switch (event) {
|
|
2117
|
+
case "task_complete":
|
|
2118
|
+
return "Completed:";
|
|
2119
|
+
case "task_needs_fix":
|
|
2120
|
+
return "Needs fix:";
|
|
2121
|
+
case "session_summary":
|
|
2122
|
+
return "Session:";
|
|
2123
|
+
case "error_spike":
|
|
2124
|
+
return "Errors:";
|
|
2125
|
+
case "orphan_task":
|
|
2126
|
+
return "Orphan:";
|
|
2127
|
+
case "subtasks_complete":
|
|
2128
|
+
return "Subtasks done:";
|
|
2129
|
+
case "capacity_relaunch":
|
|
2130
|
+
return "Relaunched:";
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
function formatTimeAgo(timestamp) {
|
|
2134
|
+
const diffMs = Date.now() - new Date(timestamp).getTime();
|
|
2135
|
+
const mins = Math.floor(diffMs / 6e4);
|
|
2136
|
+
if (mins < 1) return "just now";
|
|
2137
|
+
if (mins < 60) return `${mins}m ago`;
|
|
2138
|
+
const hours = Math.floor(mins / 60);
|
|
2139
|
+
if (hours < 24) return `${hours}h ago`;
|
|
2140
|
+
const days = Math.floor(hours / 24);
|
|
2141
|
+
return `${days}d ago`;
|
|
2142
|
+
}
|
|
2143
|
+
var CLEANUP_DAYS;
|
|
1920
2144
|
var init_notifications = __esm({
|
|
1921
2145
|
"src/lib/notifications.ts"() {
|
|
1922
2146
|
"use strict";
|
|
1923
2147
|
init_database();
|
|
1924
2148
|
init_task_scope();
|
|
2149
|
+
CLEANUP_DAYS = 7;
|
|
1925
2150
|
}
|
|
1926
2151
|
});
|
|
1927
2152
|
|
|
@@ -2967,7 +3192,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
2967
3192
|
taskFile
|
|
2968
3193
|
});
|
|
2969
3194
|
const originalPriority = String(row.priority).toLowerCase();
|
|
2970
|
-
const
|
|
3195
|
+
const resultLower = result?.toLowerCase() ?? "";
|
|
3196
|
+
const hasTestEvidence = (
|
|
3197
|
+
// Vitest/Jest output patterns (hard to fake without actually running tests)
|
|
3198
|
+
/\d+\s+pass(ed|ing)/.test(resultLower) || /test files?\s+\d+\s+passed/.test(resultLower) || /tests?\s+\d+\s+passed/.test(resultLower)
|
|
3199
|
+
);
|
|
3200
|
+
const hasNoFailures = !/fail(ed|ure|ing)|error/i.test(resultLower);
|
|
3201
|
+
const autoApprove = originalPriority === "p2" && hasTestEvidence && hasNoFailures;
|
|
2971
3202
|
if (!autoApprove) {
|
|
2972
3203
|
try {
|
|
2973
3204
|
const key = getSessionKey();
|
|
@@ -2975,6 +3206,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
2975
3206
|
if (exeSession) {
|
|
2976
3207
|
sendIntercom(exeSession);
|
|
2977
3208
|
}
|
|
3209
|
+
if (reviewer && reviewer !== coordinatorName && reviewer !== exeSession) {
|
|
3210
|
+
const { employeeSessionName: employeeSessionName2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3211
|
+
if (exeSession) {
|
|
3212
|
+
const reviewerSession = employeeSessionName2(reviewer, exeSession);
|
|
3213
|
+
sendIntercom(reviewerSession);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
2978
3216
|
} catch {
|
|
2979
3217
|
}
|
|
2980
3218
|
}
|
|
@@ -4199,6 +4437,20 @@ async function updateTask(input) {
|
|
|
4199
4437
|
notifyTaskDone();
|
|
4200
4438
|
}
|
|
4201
4439
|
await markTaskNotificationsRead(taskFile);
|
|
4440
|
+
if (input.status === "needs_review" && !isCoordinator) {
|
|
4441
|
+
try {
|
|
4442
|
+
const { writeNotification: writeNotification2 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
|
|
4443
|
+
await writeNotification2({
|
|
4444
|
+
agentId: String(row.assigned_to),
|
|
4445
|
+
agentRole: String(row.assigned_to),
|
|
4446
|
+
event: "task_complete",
|
|
4447
|
+
project: String(row.project_name),
|
|
4448
|
+
summary: `"${String(row.title)}" is ready for review`,
|
|
4449
|
+
taskFile
|
|
4450
|
+
});
|
|
4451
|
+
} catch {
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4202
4454
|
if (input.status === "done" || input.status === "closed") {
|
|
4203
4455
|
try {
|
|
4204
4456
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
@@ -4631,18 +4883,31 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4631
4883
|
mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
|
|
4632
4884
|
}
|
|
4633
4885
|
const lockFile = spawnLockPath(sessionName);
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4886
|
+
const lockData = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
|
|
4887
|
+
const { openSync: openSync3, closeSync: closeSync3, writeSync } = __require("fs");
|
|
4888
|
+
const { constants } = __require("fs");
|
|
4889
|
+
try {
|
|
4890
|
+
const fd = openSync3(lockFile, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 420);
|
|
4891
|
+
writeSync(fd, lockData);
|
|
4892
|
+
closeSync3(fd);
|
|
4893
|
+
return true;
|
|
4894
|
+
} catch (err) {
|
|
4895
|
+
if (err?.code !== "EEXIST") {
|
|
4896
|
+
return true;
|
|
4642
4897
|
}
|
|
4643
4898
|
}
|
|
4644
|
-
|
|
4645
|
-
|
|
4899
|
+
try {
|
|
4900
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
4901
|
+
const age = Date.now() - lock.timestamp;
|
|
4902
|
+
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4903
|
+
return false;
|
|
4904
|
+
}
|
|
4905
|
+
writeFileSync8(lockFile, lockData);
|
|
4906
|
+
return true;
|
|
4907
|
+
} catch {
|
|
4908
|
+
writeFileSync8(lockFile, lockData);
|
|
4909
|
+
return true;
|
|
4910
|
+
}
|
|
4646
4911
|
}
|
|
4647
4912
|
function releaseSpawnLock2(sessionName) {
|
|
4648
4913
|
try {
|
|
@@ -4721,6 +4986,21 @@ function parseParentExe(sessionName, agentId) {
|
|
|
4721
4986
|
function extractRootExe(name) {
|
|
4722
4987
|
if (!name) return null;
|
|
4723
4988
|
if (!name.includes("-")) return name;
|
|
4989
|
+
try {
|
|
4990
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
4991
|
+
if (roster.length > 0) {
|
|
4992
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
4993
|
+
for (const agentName of sortedNames) {
|
|
4994
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4995
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
4996
|
+
const match = name.match(regex);
|
|
4997
|
+
if (match) {
|
|
4998
|
+
return extractRootExe(match[1]);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
} catch {
|
|
5003
|
+
}
|
|
4724
5004
|
const parts = name.split("-").filter(Boolean);
|
|
4725
5005
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4726
5006
|
}
|
|
@@ -4739,6 +5019,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4739
5019
|
function getParentExe(sessionKey) {
|
|
4740
5020
|
try {
|
|
4741
5021
|
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5022
|
+
if (data.registeredAt) {
|
|
5023
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
5024
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
5025
|
+
}
|
|
4742
5026
|
return data.parentExe || null;
|
|
4743
5027
|
} catch {
|
|
4744
5028
|
return null;
|
|
@@ -5287,7 +5571,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5287
5571
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5288
5572
|
} catch {
|
|
5289
5573
|
}
|
|
5290
|
-
let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName}`;
|
|
5574
|
+
let envPrefix = `EXE_SESSION=${exeSession} EXE_SESSION_NAME=${sessionName} EXE_SESSION_START_ISO=${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
5291
5575
|
if (ccProvider !== DEFAULT_PROVIDER) {
|
|
5292
5576
|
const cfg = PROVIDER_TABLE[ccProvider];
|
|
5293
5577
|
if (cfg?.apiKeyEnv) {
|
|
@@ -5322,10 +5606,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5322
5606
|
}
|
|
5323
5607
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
5324
5608
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
ccModel += "[1m]";
|
|
5328
|
-
}
|
|
5609
|
+
const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
|
|
5610
|
+
const ccModel = normalizeCcModelName2(agentRtConfig.model);
|
|
5329
5611
|
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
5330
5612
|
}
|
|
5331
5613
|
}
|
|
@@ -5426,7 +5708,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5426
5708
|
releaseSpawnLock2(sessionName);
|
|
5427
5709
|
return { sessionName };
|
|
5428
5710
|
}
|
|
5429
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
5711
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, PARENT_EXE_CACHE_TTL_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
5430
5712
|
var init_tmux_routing = __esm({
|
|
5431
5713
|
"src/lib/tmux-routing.ts"() {
|
|
5432
5714
|
init_session_registry();
|
|
@@ -5445,6 +5727,7 @@ var init_tmux_routing = __esm({
|
|
|
5445
5727
|
SESSION_CACHE = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
5446
5728
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5447
5729
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5730
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
5448
5731
|
VERIFY_PANE_LINES = 200;
|
|
5449
5732
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5450
5733
|
CODEX_DEBOUNCE_MS = 12e4;
|