@askexenow/exe-os 0.8.41 → 0.8.43
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/backfill-conversations.js +805 -642
- package/dist/bin/backfill-responses.js +804 -641
- package/dist/bin/backfill-vectors.js +791 -634
- package/dist/bin/cleanup-stale-review-tasks.js +788 -631
- package/dist/bin/cli.js +1345 -660
- package/dist/bin/exe-agent.js +20 -1
- package/dist/bin/exe-assign.js +1503 -1343
- package/dist/bin/exe-boot.js +2518 -1798
- package/dist/bin/exe-call.js +39 -1
- package/dist/bin/exe-cloud.js +15 -1
- package/dist/bin/exe-dispatch.js +39 -2
- package/dist/bin/exe-doctor.js +790 -633
- package/dist/bin/exe-export-behaviors.js +792 -637
- package/dist/bin/exe-forget.js +145 -0
- package/dist/bin/exe-gateway.js +2500 -1877
- package/dist/bin/exe-heartbeat.js +147 -1
- package/dist/bin/exe-kill.js +795 -640
- package/dist/bin/exe-launch-agent.js +2168 -2008
- package/dist/bin/exe-link.js +28 -2
- package/dist/bin/exe-new-employee.js +25 -3
- package/dist/bin/exe-pending-messages.js +146 -1
- package/dist/bin/exe-pending-notifications.js +788 -631
- package/dist/bin/exe-pending-reviews.js +147 -1
- package/dist/bin/exe-rename.js +23 -0
- package/dist/bin/exe-review.js +490 -327
- package/dist/bin/exe-search.js +154 -3
- package/dist/bin/exe-session-cleanup.js +2466 -413
- package/dist/bin/exe-status.js +474 -317
- package/dist/bin/exe-team.js +474 -317
- package/dist/bin/git-sweep.js +2690 -150
- package/dist/bin/graph-backfill.js +794 -637
- package/dist/bin/graph-export.js +798 -641
- package/dist/bin/scan-tasks.js +2951 -44
- package/dist/bin/setup.js +62 -26
- package/dist/bin/shard-migrate.js +792 -637
- package/dist/bin/wiki-sync.js +794 -637
- package/dist/gateway/index.js +2504 -1895
- package/dist/hooks/bug-report-worker.js +2118 -576
- package/dist/hooks/commit-complete.js +2689 -149
- package/dist/hooks/error-recall.js +154 -3
- package/dist/hooks/ingest-worker.js +1439 -815
- package/dist/hooks/instructions-loaded.js +151 -0
- package/dist/hooks/notification.js +153 -2
- package/dist/hooks/post-compact.js +164 -0
- package/dist/hooks/pre-compact.js +3073 -101
- package/dist/hooks/pre-tool-use.js +151 -0
- package/dist/hooks/prompt-ingest-worker.js +1714 -1537
- package/dist/hooks/prompt-submit.js +2658 -1113
- package/dist/hooks/response-ingest-worker.js +170 -6
- package/dist/hooks/session-end.js +153 -2
- package/dist/hooks/session-start.js +154 -3
- package/dist/hooks/stop.js +151 -0
- package/dist/hooks/subagent-stop.js +151 -0
- package/dist/hooks/summary-worker.js +179 -7
- package/dist/index.js +278 -100
- package/dist/lib/cloud-sync.js +28 -2
- package/dist/lib/consolidation.js +69 -2
- package/dist/lib/database.js +19 -0
- package/dist/lib/device-registry.js +19 -0
- package/dist/lib/employee-templates.js +20 -1
- package/dist/lib/exe-daemon.js +236 -16
- package/dist/lib/hybrid-search.js +154 -3
- package/dist/lib/license.js +15 -1
- package/dist/lib/messaging.js +39 -2
- package/dist/lib/schedules.js +792 -637
- package/dist/lib/store.js +796 -636
- package/dist/lib/tasks.js +1614 -1091
- package/dist/lib/tmux-routing.js +149 -9
- package/dist/mcp/server.js +1825 -1138
- package/dist/mcp/tools/create-task.js +2280 -828
- package/dist/mcp/tools/list-tasks.js +2788 -159
- package/dist/mcp/tools/send-message.js +39 -2
- package/dist/mcp/tools/update-task.js +64 -0
- package/dist/runtime/index.js +235 -67
- package/dist/tui/App.js +1452 -644
- package/package.json +3 -2
|
@@ -631,8 +631,28 @@ function getMySession() {
|
|
|
631
631
|
return getTransport().getMySession();
|
|
632
632
|
}
|
|
633
633
|
function employeeSessionName(employee, exeSession, instance) {
|
|
634
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
635
|
+
const root = extractRootExe(exeSession);
|
|
636
|
+
if (root) {
|
|
637
|
+
process.stderr.write(
|
|
638
|
+
`[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
|
|
639
|
+
`
|
|
640
|
+
);
|
|
641
|
+
exeSession = root;
|
|
642
|
+
} else {
|
|
643
|
+
throw new Error(
|
|
644
|
+
`Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
634
648
|
const suffix = instance != null && instance > 0 ? String(instance) : "";
|
|
635
|
-
|
|
649
|
+
const name = `${employee}${suffix}-${exeSession}`;
|
|
650
|
+
if (!VALID_SESSION_NAME.test(name)) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
`Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return name;
|
|
636
656
|
}
|
|
637
657
|
function extractRootExe(name) {
|
|
638
658
|
const match = name.match(/(exe\d+)$/);
|
|
@@ -791,6 +811,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
791
811
|
error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
|
|
792
812
|
};
|
|
793
813
|
}
|
|
814
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
815
|
+
const root = extractRootExe(exeSession);
|
|
816
|
+
if (root) {
|
|
817
|
+
process.stderr.write(
|
|
818
|
+
`[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
|
|
819
|
+
`
|
|
820
|
+
);
|
|
821
|
+
exeSession = root;
|
|
822
|
+
} else {
|
|
823
|
+
return {
|
|
824
|
+
status: "failed",
|
|
825
|
+
sessionName: "",
|
|
826
|
+
error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
}
|
|
794
830
|
let effectiveInstance = opts?.instance;
|
|
795
831
|
if (effectiveInstance === void 0 && opts?.autoInstance) {
|
|
796
832
|
const free = findFreeInstance(
|
|
@@ -1037,7 +1073,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1037
1073
|
releaseSpawnLock(sessionName);
|
|
1038
1074
|
return { sessionName };
|
|
1039
1075
|
}
|
|
1040
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1076
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1041
1077
|
var init_tmux_routing = __esm({
|
|
1042
1078
|
"src/lib/tmux-routing.ts"() {
|
|
1043
1079
|
"use strict";
|
|
@@ -1052,6 +1088,7 @@ var init_tmux_routing = __esm({
|
|
|
1052
1088
|
SPAWN_LOCK_DIR = path7.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
1053
1089
|
SESSION_CACHE = path7.join(os4.homedir(), ".exe-os", "session-cache");
|
|
1054
1090
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
1091
|
+
VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
|
|
1055
1092
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
1056
1093
|
INTERCOM_LOG2 = path7.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
1057
1094
|
DEBOUNCE_FILE = path7.join(SESSION_CACHE, "intercom-debounce.json");
|
|
@@ -284,6 +284,61 @@ var init_notifications = __esm({
|
|
|
284
284
|
}
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
+
// src/lib/state-bus.ts
|
|
288
|
+
var StateBus, orgBus;
|
|
289
|
+
var init_state_bus = __esm({
|
|
290
|
+
"src/lib/state-bus.ts"() {
|
|
291
|
+
"use strict";
|
|
292
|
+
StateBus = class {
|
|
293
|
+
handlers = /* @__PURE__ */ new Map();
|
|
294
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
295
|
+
/** Emit an event to all subscribers */
|
|
296
|
+
emit(event) {
|
|
297
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
298
|
+
if (typeHandlers) {
|
|
299
|
+
for (const handler of typeHandlers) {
|
|
300
|
+
try {
|
|
301
|
+
handler(event);
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
for (const handler of this.globalHandlers) {
|
|
307
|
+
try {
|
|
308
|
+
handler(event);
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/** Subscribe to a specific event type */
|
|
314
|
+
on(type, handler) {
|
|
315
|
+
if (!this.handlers.has(type)) {
|
|
316
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
317
|
+
}
|
|
318
|
+
this.handlers.get(type).add(handler);
|
|
319
|
+
}
|
|
320
|
+
/** Subscribe to ALL events */
|
|
321
|
+
onAny(handler) {
|
|
322
|
+
this.globalHandlers.add(handler);
|
|
323
|
+
}
|
|
324
|
+
/** Unsubscribe from a specific event type */
|
|
325
|
+
off(type, handler) {
|
|
326
|
+
this.handlers.get(type)?.delete(handler);
|
|
327
|
+
}
|
|
328
|
+
/** Unsubscribe from ALL events */
|
|
329
|
+
offAny(handler) {
|
|
330
|
+
this.globalHandlers.delete(handler);
|
|
331
|
+
}
|
|
332
|
+
/** Remove all listeners */
|
|
333
|
+
clear() {
|
|
334
|
+
this.handlers.clear();
|
|
335
|
+
this.globalHandlers.clear();
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
orgBus = new StateBus();
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
287
342
|
// src/lib/tasks-crud.ts
|
|
288
343
|
import crypto2 from "crypto";
|
|
289
344
|
import path3 from "path";
|
|
@@ -1007,6 +1062,7 @@ var init_tasks_review = __esm({
|
|
|
1007
1062
|
init_tasks_crud();
|
|
1008
1063
|
init_tmux_routing();
|
|
1009
1064
|
init_session_key();
|
|
1065
|
+
init_state_bus();
|
|
1010
1066
|
}
|
|
1011
1067
|
});
|
|
1012
1068
|
|
|
@@ -1489,6 +1545,13 @@ async function updateTask(input) {
|
|
|
1489
1545
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
1490
1546
|
} catch {
|
|
1491
1547
|
}
|
|
1548
|
+
orgBus.emit({
|
|
1549
|
+
type: "task_completed",
|
|
1550
|
+
taskId,
|
|
1551
|
+
employee: String(row.assigned_to),
|
|
1552
|
+
result: input.result ?? "",
|
|
1553
|
+
timestamp: now
|
|
1554
|
+
});
|
|
1492
1555
|
if (row.parent_task_id) {
|
|
1493
1556
|
try {
|
|
1494
1557
|
await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
|
|
@@ -1543,6 +1606,7 @@ var init_tasks = __esm({
|
|
|
1543
1606
|
init_database();
|
|
1544
1607
|
init_config();
|
|
1545
1608
|
init_notifications();
|
|
1609
|
+
init_state_bus();
|
|
1546
1610
|
init_tasks_crud();
|
|
1547
1611
|
init_tasks_review();
|
|
1548
1612
|
init_tasks_crud();
|
package/dist/runtime/index.js
CHANGED
|
@@ -666,6 +666,13 @@ async function ensureSchema() {
|
|
|
666
666
|
});
|
|
667
667
|
} catch {
|
|
668
668
|
}
|
|
669
|
+
try {
|
|
670
|
+
await client.execute({
|
|
671
|
+
sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
|
|
672
|
+
args: []
|
|
673
|
+
});
|
|
674
|
+
} catch {
|
|
675
|
+
}
|
|
669
676
|
try {
|
|
670
677
|
await client.execute({
|
|
671
678
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -1112,6 +1119,18 @@ async function ensureSchema() {
|
|
|
1112
1119
|
CREATE INDEX IF NOT EXISTS idx_session_kills_agent
|
|
1113
1120
|
ON session_kills(agent_id);
|
|
1114
1121
|
`);
|
|
1122
|
+
await client.execute(`
|
|
1123
|
+
CREATE TABLE IF NOT EXISTS global_procedures (
|
|
1124
|
+
id TEXT PRIMARY KEY,
|
|
1125
|
+
title TEXT NOT NULL,
|
|
1126
|
+
content TEXT NOT NULL,
|
|
1127
|
+
priority TEXT NOT NULL DEFAULT 'p0',
|
|
1128
|
+
domain TEXT,
|
|
1129
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
1130
|
+
created_at TEXT NOT NULL,
|
|
1131
|
+
updated_at TEXT NOT NULL
|
|
1132
|
+
)
|
|
1133
|
+
`);
|
|
1115
1134
|
await client.executeMultiple(`
|
|
1116
1135
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
1117
1136
|
id TEXT PRIMARY KEY,
|
|
@@ -1677,6 +1696,61 @@ var init_session_kill_telemetry = __esm({
|
|
|
1677
1696
|
}
|
|
1678
1697
|
});
|
|
1679
1698
|
|
|
1699
|
+
// src/lib/state-bus.ts
|
|
1700
|
+
var StateBus, orgBus;
|
|
1701
|
+
var init_state_bus = __esm({
|
|
1702
|
+
"src/lib/state-bus.ts"() {
|
|
1703
|
+
"use strict";
|
|
1704
|
+
StateBus = class {
|
|
1705
|
+
handlers = /* @__PURE__ */ new Map();
|
|
1706
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
1707
|
+
/** Emit an event to all subscribers */
|
|
1708
|
+
emit(event) {
|
|
1709
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
1710
|
+
if (typeHandlers) {
|
|
1711
|
+
for (const handler of typeHandlers) {
|
|
1712
|
+
try {
|
|
1713
|
+
handler(event);
|
|
1714
|
+
} catch {
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
for (const handler of this.globalHandlers) {
|
|
1719
|
+
try {
|
|
1720
|
+
handler(event);
|
|
1721
|
+
} catch {
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
/** Subscribe to a specific event type */
|
|
1726
|
+
on(type, handler) {
|
|
1727
|
+
if (!this.handlers.has(type)) {
|
|
1728
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
1729
|
+
}
|
|
1730
|
+
this.handlers.get(type).add(handler);
|
|
1731
|
+
}
|
|
1732
|
+
/** Subscribe to ALL events */
|
|
1733
|
+
onAny(handler) {
|
|
1734
|
+
this.globalHandlers.add(handler);
|
|
1735
|
+
}
|
|
1736
|
+
/** Unsubscribe from a specific event type */
|
|
1737
|
+
off(type, handler) {
|
|
1738
|
+
this.handlers.get(type)?.delete(handler);
|
|
1739
|
+
}
|
|
1740
|
+
/** Unsubscribe from ALL events */
|
|
1741
|
+
offAny(handler) {
|
|
1742
|
+
this.globalHandlers.delete(handler);
|
|
1743
|
+
}
|
|
1744
|
+
/** Remove all listeners */
|
|
1745
|
+
clear() {
|
|
1746
|
+
this.handlers.clear();
|
|
1747
|
+
this.globalHandlers.clear();
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
orgBus = new StateBus();
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1680
1754
|
// src/lib/tasks-crud.ts
|
|
1681
1755
|
import crypto3 from "crypto";
|
|
1682
1756
|
import path9 from "path";
|
|
@@ -1820,9 +1894,15 @@ async function createTaskCore(input) {
|
|
|
1820
1894
|
}
|
|
1821
1895
|
}
|
|
1822
1896
|
const complexity = input.complexity ?? "standard";
|
|
1897
|
+
let sessionScope = null;
|
|
1898
|
+
try {
|
|
1899
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
1900
|
+
sessionScope = resolveExeSession2();
|
|
1901
|
+
} catch {
|
|
1902
|
+
}
|
|
1823
1903
|
await client.execute({
|
|
1824
|
-
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
|
|
1825
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1904
|
+
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
|
|
1905
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1826
1906
|
args: [
|
|
1827
1907
|
id,
|
|
1828
1908
|
input.title,
|
|
@@ -1841,6 +1921,7 @@ async function createTaskCore(input) {
|
|
|
1841
1921
|
input.budgetFallbackModel ?? null,
|
|
1842
1922
|
0,
|
|
1843
1923
|
null,
|
|
1924
|
+
sessionScope,
|
|
1844
1925
|
now,
|
|
1845
1926
|
now
|
|
1846
1927
|
]
|
|
@@ -1885,6 +1966,15 @@ async function listTasks(input) {
|
|
|
1885
1966
|
conditions.push("priority = ?");
|
|
1886
1967
|
args.push(input.priority);
|
|
1887
1968
|
}
|
|
1969
|
+
try {
|
|
1970
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
1971
|
+
const session = resolveExeSession2();
|
|
1972
|
+
if (session) {
|
|
1973
|
+
conditions.push("(session_scope IS NULL OR session_scope = ?)");
|
|
1974
|
+
args.push(session);
|
|
1975
|
+
}
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1888
1978
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1889
1979
|
const result = await client.execute({
|
|
1890
1980
|
sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
|
|
@@ -2249,6 +2339,7 @@ var init_tasks_review = __esm({
|
|
|
2249
2339
|
init_tasks_crud();
|
|
2250
2340
|
init_tmux_routing();
|
|
2251
2341
|
init_session_key();
|
|
2342
|
+
init_state_bus();
|
|
2252
2343
|
}
|
|
2253
2344
|
});
|
|
2254
2345
|
|
|
@@ -2413,13 +2504,12 @@ function assertSessionScope(actionType, targetProject) {
|
|
|
2413
2504
|
};
|
|
2414
2505
|
}
|
|
2415
2506
|
process.stderr.write(
|
|
2416
|
-
`[session-scope]
|
|
2507
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
2417
2508
|
`
|
|
2418
2509
|
);
|
|
2419
2510
|
return {
|
|
2420
|
-
allowed:
|
|
2421
|
-
|
|
2422
|
-
reason: "cross_session_granted",
|
|
2511
|
+
allowed: false,
|
|
2512
|
+
reason: "cross_session_denied",
|
|
2423
2513
|
currentProject,
|
|
2424
2514
|
targetProject,
|
|
2425
2515
|
targetSession: findSessionForProject(targetProject)?.windowName
|
|
@@ -2445,8 +2535,9 @@ async function dispatchTaskToEmployee(input) {
|
|
|
2445
2535
|
try {
|
|
2446
2536
|
const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
|
|
2447
2537
|
const check = assertSessionScope2("dispatch_task", input.projectName);
|
|
2448
|
-
if (check.reason === "
|
|
2538
|
+
if (check.reason === "cross_session_denied") {
|
|
2449
2539
|
crossProject = true;
|
|
2540
|
+
return { dispatched: "skipped", crossProject: true };
|
|
2450
2541
|
}
|
|
2451
2542
|
} catch {
|
|
2452
2543
|
}
|
|
@@ -2971,6 +3062,13 @@ async function updateTask(input) {
|
|
|
2971
3062
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
2972
3063
|
} catch {
|
|
2973
3064
|
}
|
|
3065
|
+
orgBus.emit({
|
|
3066
|
+
type: "task_completed",
|
|
3067
|
+
taskId,
|
|
3068
|
+
employee: String(row.assigned_to),
|
|
3069
|
+
result: input.result ?? "",
|
|
3070
|
+
timestamp: now
|
|
3071
|
+
});
|
|
2974
3072
|
if (row.parent_task_id) {
|
|
2975
3073
|
try {
|
|
2976
3074
|
await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
|
|
@@ -3038,6 +3136,7 @@ var init_tasks = __esm({
|
|
|
3038
3136
|
init_database();
|
|
3039
3137
|
init_config();
|
|
3040
3138
|
init_notifications();
|
|
3139
|
+
init_state_bus();
|
|
3041
3140
|
init_tasks_crud();
|
|
3042
3141
|
init_tasks_review();
|
|
3043
3142
|
init_tasks_crud();
|
|
@@ -3428,8 +3527,28 @@ function getMySession() {
|
|
|
3428
3527
|
return getTransport().getMySession();
|
|
3429
3528
|
}
|
|
3430
3529
|
function employeeSessionName(employee, exeSession, instance) {
|
|
3530
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
3531
|
+
const root = extractRootExe(exeSession);
|
|
3532
|
+
if (root) {
|
|
3533
|
+
process.stderr.write(
|
|
3534
|
+
`[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
|
|
3535
|
+
`
|
|
3536
|
+
);
|
|
3537
|
+
exeSession = root;
|
|
3538
|
+
} else {
|
|
3539
|
+
throw new Error(
|
|
3540
|
+
`Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3431
3544
|
const suffix = instance != null && instance > 0 ? String(instance) : "";
|
|
3432
|
-
|
|
3545
|
+
const name = `${employee}${suffix}-${exeSession}`;
|
|
3546
|
+
if (!VALID_SESSION_NAME.test(name)) {
|
|
3547
|
+
throw new Error(
|
|
3548
|
+
`Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
|
|
3549
|
+
);
|
|
3550
|
+
}
|
|
3551
|
+
return name;
|
|
3433
3552
|
}
|
|
3434
3553
|
function parseParentExe(sessionName, agentId) {
|
|
3435
3554
|
const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -3669,6 +3788,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3669
3788
|
error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
|
|
3670
3789
|
};
|
|
3671
3790
|
}
|
|
3791
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
3792
|
+
const root = extractRootExe(exeSession);
|
|
3793
|
+
if (root) {
|
|
3794
|
+
process.stderr.write(
|
|
3795
|
+
`[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
|
|
3796
|
+
`
|
|
3797
|
+
);
|
|
3798
|
+
exeSession = root;
|
|
3799
|
+
} else {
|
|
3800
|
+
return {
|
|
3801
|
+
status: "failed",
|
|
3802
|
+
sessionName: "",
|
|
3803
|
+
error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3672
3807
|
let effectiveInstance = opts?.instance;
|
|
3673
3808
|
if (effectiveInstance === void 0 && opts?.autoInstance) {
|
|
3674
3809
|
const free = findFreeInstance(
|
|
@@ -3915,7 +4050,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3915
4050
|
releaseSpawnLock(sessionName);
|
|
3916
4051
|
return { sessionName };
|
|
3917
4052
|
}
|
|
3918
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
4053
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
3919
4054
|
var init_tmux_routing = __esm({
|
|
3920
4055
|
"src/lib/tmux-routing.ts"() {
|
|
3921
4056
|
"use strict";
|
|
@@ -3930,6 +4065,7 @@ var init_tmux_routing = __esm({
|
|
|
3930
4065
|
SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
3931
4066
|
SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
|
|
3932
4067
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4068
|
+
VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
|
|
3933
4069
|
VERIFY_PANE_LINES = 200;
|
|
3934
4070
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
3935
4071
|
INTERCOM_LOG2 = path14.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
@@ -4239,6 +4375,71 @@ var init_shard_manager = __esm({
|
|
|
4239
4375
|
}
|
|
4240
4376
|
});
|
|
4241
4377
|
|
|
4378
|
+
// src/lib/global-procedures.ts
|
|
4379
|
+
var global_procedures_exports = {};
|
|
4380
|
+
__export(global_procedures_exports, {
|
|
4381
|
+
deactivateGlobalProcedure: () => deactivateGlobalProcedure,
|
|
4382
|
+
getGlobalProceduresBlock: () => getGlobalProceduresBlock,
|
|
4383
|
+
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
4384
|
+
storeGlobalProcedure: () => storeGlobalProcedure
|
|
4385
|
+
});
|
|
4386
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4387
|
+
async function loadGlobalProcedures() {
|
|
4388
|
+
const client = getClient();
|
|
4389
|
+
const result = await client.execute({
|
|
4390
|
+
sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
4391
|
+
args: []
|
|
4392
|
+
});
|
|
4393
|
+
const procedures = result.rows;
|
|
4394
|
+
if (procedures.length > 0) {
|
|
4395
|
+
_cache = procedures.map((p) => `### ${p.title}
|
|
4396
|
+
${p.content}`).join("\n\n");
|
|
4397
|
+
} else {
|
|
4398
|
+
_cache = "";
|
|
4399
|
+
}
|
|
4400
|
+
_cacheLoaded = true;
|
|
4401
|
+
return procedures;
|
|
4402
|
+
}
|
|
4403
|
+
function getGlobalProceduresBlock() {
|
|
4404
|
+
if (!_cacheLoaded) return "";
|
|
4405
|
+
if (!_cache) return "";
|
|
4406
|
+
return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
|
|
4407
|
+
|
|
4408
|
+
${_cache}
|
|
4409
|
+
`;
|
|
4410
|
+
}
|
|
4411
|
+
async function storeGlobalProcedure(input) {
|
|
4412
|
+
const id = randomUUID3();
|
|
4413
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4414
|
+
const client = getClient();
|
|
4415
|
+
await client.execute({
|
|
4416
|
+
sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
4417
|
+
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
4418
|
+
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
|
|
4419
|
+
});
|
|
4420
|
+
await loadGlobalProcedures();
|
|
4421
|
+
return id;
|
|
4422
|
+
}
|
|
4423
|
+
async function deactivateGlobalProcedure(id) {
|
|
4424
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4425
|
+
const client = getClient();
|
|
4426
|
+
const result = await client.execute({
|
|
4427
|
+
sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
4428
|
+
args: [now, id]
|
|
4429
|
+
});
|
|
4430
|
+
await loadGlobalProcedures();
|
|
4431
|
+
return result.rowsAffected > 0;
|
|
4432
|
+
}
|
|
4433
|
+
var _cache, _cacheLoaded;
|
|
4434
|
+
var init_global_procedures = __esm({
|
|
4435
|
+
"src/lib/global-procedures.ts"() {
|
|
4436
|
+
"use strict";
|
|
4437
|
+
init_database();
|
|
4438
|
+
_cache = "";
|
|
4439
|
+
_cacheLoaded = false;
|
|
4440
|
+
}
|
|
4441
|
+
});
|
|
4442
|
+
|
|
4242
4443
|
// src/lib/store.ts
|
|
4243
4444
|
var store_exports = {};
|
|
4244
4445
|
__export(store_exports, {
|
|
@@ -4318,6 +4519,11 @@ async function initStore(options) {
|
|
|
4318
4519
|
"version-query"
|
|
4319
4520
|
);
|
|
4320
4521
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
4522
|
+
try {
|
|
4523
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
4524
|
+
await loadGlobalProcedures2();
|
|
4525
|
+
} catch {
|
|
4526
|
+
}
|
|
4321
4527
|
}
|
|
4322
4528
|
function classifyTier(record) {
|
|
4323
4529
|
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
@@ -4359,6 +4565,12 @@ async function writeMemory(record) {
|
|
|
4359
4565
|
supersedes_id: record.supersedes_id ?? null
|
|
4360
4566
|
};
|
|
4361
4567
|
_pendingRecords.push(dbRow);
|
|
4568
|
+
orgBus.emit({
|
|
4569
|
+
type: "memory_stored",
|
|
4570
|
+
agentId: record.agent_id,
|
|
4571
|
+
project: record.project_name,
|
|
4572
|
+
timestamp: record.timestamp
|
|
4573
|
+
});
|
|
4362
4574
|
const MAX_PENDING = 1e3;
|
|
4363
4575
|
if (_pendingRecords.length > MAX_PENDING) {
|
|
4364
4576
|
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
@@ -4704,6 +4916,7 @@ var init_store = __esm({
|
|
|
4704
4916
|
init_database();
|
|
4705
4917
|
init_keychain();
|
|
4706
4918
|
init_config();
|
|
4919
|
+
init_state_bus();
|
|
4707
4920
|
INIT_MAX_RETRIES = 3;
|
|
4708
4921
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4709
4922
|
_pendingRecords = [];
|
|
@@ -6669,60 +6882,15 @@ function createQuietRenderer() {
|
|
|
6669
6882
|
};
|
|
6670
6883
|
}
|
|
6671
6884
|
|
|
6672
|
-
// src/runtime/
|
|
6673
|
-
|
|
6674
|
-
handlers = /* @__PURE__ */ new Map();
|
|
6675
|
-
globalHandlers = /* @__PURE__ */ new Set();
|
|
6676
|
-
/** Emit an event to all subscribers */
|
|
6677
|
-
emit(event) {
|
|
6678
|
-
const typeHandlers = this.handlers.get(event.type);
|
|
6679
|
-
if (typeHandlers) {
|
|
6680
|
-
for (const handler of typeHandlers) {
|
|
6681
|
-
try {
|
|
6682
|
-
handler(event);
|
|
6683
|
-
} catch {
|
|
6684
|
-
}
|
|
6685
|
-
}
|
|
6686
|
-
}
|
|
6687
|
-
for (const handler of this.globalHandlers) {
|
|
6688
|
-
try {
|
|
6689
|
-
handler(event);
|
|
6690
|
-
} catch {
|
|
6691
|
-
}
|
|
6692
|
-
}
|
|
6693
|
-
}
|
|
6694
|
-
/** Subscribe to a specific event type */
|
|
6695
|
-
on(type, handler) {
|
|
6696
|
-
if (!this.handlers.has(type)) {
|
|
6697
|
-
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
6698
|
-
}
|
|
6699
|
-
this.handlers.get(type).add(handler);
|
|
6700
|
-
}
|
|
6701
|
-
/** Subscribe to ALL events */
|
|
6702
|
-
onAny(handler) {
|
|
6703
|
-
this.globalHandlers.add(handler);
|
|
6704
|
-
}
|
|
6705
|
-
/** Unsubscribe from a specific event type */
|
|
6706
|
-
off(type, handler) {
|
|
6707
|
-
this.handlers.get(type)?.delete(handler);
|
|
6708
|
-
}
|
|
6709
|
-
/** Unsubscribe from ALL events */
|
|
6710
|
-
offAny(handler) {
|
|
6711
|
-
this.globalHandlers.delete(handler);
|
|
6712
|
-
}
|
|
6713
|
-
/** Remove all listeners */
|
|
6714
|
-
clear() {
|
|
6715
|
-
this.handlers.clear();
|
|
6716
|
-
this.globalHandlers.clear();
|
|
6717
|
-
}
|
|
6718
|
-
};
|
|
6719
|
-
var orgBus = new StateBus();
|
|
6885
|
+
// src/runtime/index.ts
|
|
6886
|
+
init_state_bus();
|
|
6720
6887
|
|
|
6721
6888
|
// src/runtime/session-manager.ts
|
|
6722
|
-
import { randomUUID as
|
|
6889
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6890
|
+
init_state_bus();
|
|
6723
6891
|
|
|
6724
6892
|
// src/runtime/exe-hooks.ts
|
|
6725
|
-
import { randomUUID as
|
|
6893
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6726
6894
|
function createExeOSHooks(config) {
|
|
6727
6895
|
let sessionRegistered = false;
|
|
6728
6896
|
return {
|
|
@@ -6781,7 +6949,7 @@ function createExeOSHooks(config) {
|
|
|
6781
6949
|
const toolResponse = result.isError ? { error: result.content } : { output: result.content };
|
|
6782
6950
|
const rawText = extractSemanticText2(toolName, toolInput, toolResponse);
|
|
6783
6951
|
await writeMemory2({
|
|
6784
|
-
id:
|
|
6952
|
+
id: randomUUID4(),
|
|
6785
6953
|
agent_id: config.agentId,
|
|
6786
6954
|
agent_role: "employee",
|
|
6787
6955
|
session_id: `api-${config.agentId}`,
|
|
@@ -6804,7 +6972,7 @@ function createExeOSHooks(config) {
|
|
|
6804
6972
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
6805
6973
|
if (summary) {
|
|
6806
6974
|
await writeMemory2({
|
|
6807
|
-
id:
|
|
6975
|
+
id: randomUUID4(),
|
|
6808
6976
|
agent_id: config.agentId,
|
|
6809
6977
|
agent_role: "employee",
|
|
6810
6978
|
session_id: `api-${config.agentId}`,
|
|
@@ -6875,7 +7043,7 @@ function createExeOSHooks(config) {
|
|
|
6875
7043
|
await client.execute({
|
|
6876
7044
|
sql: `INSERT OR IGNORE INTO notifications (id, type, source_agent, message, task_file, created_at, read)
|
|
6877
7045
|
VALUES (?, 'system', ?, ?, NULL, ?, 0)`,
|
|
6878
|
-
args: [
|
|
7046
|
+
args: [randomUUID4(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
|
|
6879
7047
|
});
|
|
6880
7048
|
} catch {
|
|
6881
7049
|
}
|
|
@@ -6891,7 +7059,7 @@ function createExeOSHooks(config) {
|
|
|
6891
7059
|
try {
|
|
6892
7060
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
6893
7061
|
await writeMemory2({
|
|
6894
|
-
id:
|
|
7062
|
+
id: randomUUID4(),
|
|
6895
7063
|
agent_id: config.agentId,
|
|
6896
7064
|
agent_role: "employee",
|
|
6897
7065
|
session_id: `api-${config.agentId}`,
|
|
@@ -6913,7 +7081,7 @@ function createExeOSHooks(config) {
|
|
|
6913
7081
|
try {
|
|
6914
7082
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
6915
7083
|
await writeMemory2({
|
|
6916
|
-
id:
|
|
7084
|
+
id: randomUUID4(),
|
|
6917
7085
|
agent_id: config.agentId,
|
|
6918
7086
|
agent_role: "employee",
|
|
6919
7087
|
session_id: `api-${config.agentId}`,
|
|
@@ -6950,7 +7118,7 @@ function createExeOSHooks(config) {
|
|
|
6950
7118
|
if (tasks.rows.length > 0) {
|
|
6951
7119
|
const taskList = tasks.rows.map((r) => `- [${String(r.status)}] ${String(r.title)} (${String(r.task_file)})`).join("\n");
|
|
6952
7120
|
await writeMemory2({
|
|
6953
|
-
id:
|
|
7121
|
+
id: randomUUID4(),
|
|
6954
7122
|
agent_id: config.agentId,
|
|
6955
7123
|
agent_role: "employee",
|
|
6956
7124
|
session_id: `api-${config.agentId}`,
|
|
@@ -6976,7 +7144,7 @@ ${taskList}`,
|
|
|
6976
7144
|
try {
|
|
6977
7145
|
const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
6978
7146
|
await writeMemory2({
|
|
6979
|
-
id:
|
|
7147
|
+
id: randomUUID4(),
|
|
6980
7148
|
agent_id: config.agentId,
|
|
6981
7149
|
agent_role: "employee",
|
|
6982
7150
|
session_id: `api-${config.agentId}`,
|
|
@@ -7003,7 +7171,7 @@ var SessionManager = class {
|
|
|
7003
7171
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
7004
7172
|
/** Start a new agent session for an employee */
|
|
7005
7173
|
startSession(employeeId, config) {
|
|
7006
|
-
const sessionId =
|
|
7174
|
+
const sessionId = randomUUID5();
|
|
7007
7175
|
const abortController = new AbortController();
|
|
7008
7176
|
const session = {
|
|
7009
7177
|
info: {
|