@askexenow/exe-os 0.8.40 → 0.8.42
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 +1376 -659
- package/dist/bin/exe-agent.js +20 -1
- package/dist/bin/exe-assign.js +1503 -1343
- package/dist/bin/exe-boot.js +2549 -1784
- package/dist/bin/exe-call.js +39 -1
- package/dist/bin/exe-cloud.js +12 -2
- package/dist/bin/exe-dispatch.js +39 -2
- package/dist/bin/exe-doctor.js +791 -634
- package/dist/bin/exe-export-behaviors.js +792 -637
- package/dist/bin/exe-forget.js +145 -0
- package/dist/bin/exe-gateway.js +2501 -1846
- 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 +44 -12
- package/dist/bin/exe-new-employee.js +6 -2
- 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 +176 -1
- package/dist/bin/exe-rename.js +23 -0
- package/dist/bin/exe-review.js +490 -327
- package/dist/bin/exe-search.js +157 -4
- package/dist/bin/exe-session-cleanup.js +2487 -403
- package/dist/bin/exe-settings.js +2 -1
- package/dist/bin/exe-status.js +474 -317
- package/dist/bin/exe-team.js +474 -317
- package/dist/bin/git-sweep.js +2691 -151
- 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 +50 -26
- package/dist/bin/shard-migrate.js +792 -637
- package/dist/bin/wiki-sync.js +794 -637
- package/dist/gateway/index.js +2542 -1887
- package/dist/hooks/bug-report-worker.js +2118 -576
- package/dist/hooks/commit-complete.js +2690 -150
- package/dist/hooks/error-recall.js +157 -4
- package/dist/hooks/ingest-worker.js +1455 -803
- 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 +1670 -1509
- package/dist/hooks/prompt-submit.js +2650 -1074
- package/dist/hooks/response-ingest-worker.js +154 -6
- package/dist/hooks/session-end.js +153 -2
- package/dist/hooks/session-start.js +157 -4
- package/dist/hooks/stop.js +151 -0
- package/dist/hooks/subagent-stop.js +155 -2
- package/dist/hooks/summary-worker.js +190 -21
- package/dist/index.js +326 -102
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/config.js +2 -0
- 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/embedder.js +3 -1
- package/dist/lib/employee-templates.js +20 -1
- package/dist/lib/exe-daemon.js +285 -18
- package/dist/lib/hybrid-search.js +157 -4
- 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 +1485 -918
- package/dist/lib/tmux-routing.js +194 -10
- package/dist/mcp/server.js +1643 -924
- package/dist/mcp/tools/create-task.js +2283 -829
- 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 +79 -0
- package/dist/runtime/index.js +280 -68
- package/dist/tui/App.js +1485 -645
- package/package.json +3 -2
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -30,6 +30,7 @@ var config_exports = {};
|
|
|
30
30
|
__export(config_exports, {
|
|
31
31
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
32
32
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
33
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
33
34
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
34
35
|
DB_PATH: () => DB_PATH,
|
|
35
36
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -185,7 +186,7 @@ async function loadConfigFrom(configPath) {
|
|
|
185
186
|
return { ...DEFAULT_CONFIG };
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
189
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
189
190
|
var init_config = __esm({
|
|
190
191
|
"src/lib/config.ts"() {
|
|
191
192
|
"use strict";
|
|
@@ -193,6 +194,7 @@ var init_config = __esm({
|
|
|
193
194
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
194
195
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
195
196
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
197
|
+
COO_AGENT_NAME = "exe";
|
|
196
198
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
197
199
|
CURRENT_CONFIG_VERSION = 1;
|
|
198
200
|
DEFAULT_CONFIG = {
|
|
@@ -587,6 +589,13 @@ async function ensureSchema() {
|
|
|
587
589
|
});
|
|
588
590
|
} catch {
|
|
589
591
|
}
|
|
592
|
+
try {
|
|
593
|
+
await client.execute({
|
|
594
|
+
sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
|
|
595
|
+
args: []
|
|
596
|
+
});
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
590
599
|
try {
|
|
591
600
|
await client.execute({
|
|
592
601
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -1033,6 +1042,18 @@ async function ensureSchema() {
|
|
|
1033
1042
|
CREATE INDEX IF NOT EXISTS idx_session_kills_agent
|
|
1034
1043
|
ON session_kills(agent_id);
|
|
1035
1044
|
`);
|
|
1045
|
+
await client.execute(`
|
|
1046
|
+
CREATE TABLE IF NOT EXISTS global_procedures (
|
|
1047
|
+
id TEXT PRIMARY KEY,
|
|
1048
|
+
title TEXT NOT NULL,
|
|
1049
|
+
content TEXT NOT NULL,
|
|
1050
|
+
priority TEXT NOT NULL DEFAULT 'p0',
|
|
1051
|
+
domain TEXT,
|
|
1052
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
1053
|
+
created_at TEXT NOT NULL,
|
|
1054
|
+
updated_at TEXT NOT NULL
|
|
1055
|
+
)
|
|
1056
|
+
`);
|
|
1036
1057
|
await client.executeMultiple(`
|
|
1037
1058
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
1038
1059
|
id TEXT PRIMARY KEY,
|
|
@@ -1337,6 +1358,61 @@ var init_keychain = __esm({
|
|
|
1337
1358
|
}
|
|
1338
1359
|
});
|
|
1339
1360
|
|
|
1361
|
+
// src/lib/state-bus.ts
|
|
1362
|
+
var StateBus, orgBus;
|
|
1363
|
+
var init_state_bus = __esm({
|
|
1364
|
+
"src/lib/state-bus.ts"() {
|
|
1365
|
+
"use strict";
|
|
1366
|
+
StateBus = class {
|
|
1367
|
+
handlers = /* @__PURE__ */ new Map();
|
|
1368
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
1369
|
+
/** Emit an event to all subscribers */
|
|
1370
|
+
emit(event) {
|
|
1371
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
1372
|
+
if (typeHandlers) {
|
|
1373
|
+
for (const handler of typeHandlers) {
|
|
1374
|
+
try {
|
|
1375
|
+
handler(event);
|
|
1376
|
+
} catch {
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
for (const handler of this.globalHandlers) {
|
|
1381
|
+
try {
|
|
1382
|
+
handler(event);
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/** Subscribe to a specific event type */
|
|
1388
|
+
on(type, handler) {
|
|
1389
|
+
if (!this.handlers.has(type)) {
|
|
1390
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
1391
|
+
}
|
|
1392
|
+
this.handlers.get(type).add(handler);
|
|
1393
|
+
}
|
|
1394
|
+
/** Subscribe to ALL events */
|
|
1395
|
+
onAny(handler) {
|
|
1396
|
+
this.globalHandlers.add(handler);
|
|
1397
|
+
}
|
|
1398
|
+
/** Unsubscribe from a specific event type */
|
|
1399
|
+
off(type, handler) {
|
|
1400
|
+
this.handlers.get(type)?.delete(handler);
|
|
1401
|
+
}
|
|
1402
|
+
/** Unsubscribe from ALL events */
|
|
1403
|
+
offAny(handler) {
|
|
1404
|
+
this.globalHandlers.delete(handler);
|
|
1405
|
+
}
|
|
1406
|
+
/** Remove all listeners */
|
|
1407
|
+
clear() {
|
|
1408
|
+
this.handlers.clear();
|
|
1409
|
+
this.globalHandlers.clear();
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
orgBus = new StateBus();
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1340
1416
|
// src/lib/shard-manager.ts
|
|
1341
1417
|
var shard_manager_exports = {};
|
|
1342
1418
|
__export(shard_manager_exports, {
|
|
@@ -1578,6 +1654,71 @@ var init_shard_manager = __esm({
|
|
|
1578
1654
|
}
|
|
1579
1655
|
});
|
|
1580
1656
|
|
|
1657
|
+
// src/lib/global-procedures.ts
|
|
1658
|
+
var global_procedures_exports = {};
|
|
1659
|
+
__export(global_procedures_exports, {
|
|
1660
|
+
deactivateGlobalProcedure: () => deactivateGlobalProcedure,
|
|
1661
|
+
getGlobalProceduresBlock: () => getGlobalProceduresBlock,
|
|
1662
|
+
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
1663
|
+
storeGlobalProcedure: () => storeGlobalProcedure
|
|
1664
|
+
});
|
|
1665
|
+
import { randomUUID } from "crypto";
|
|
1666
|
+
async function loadGlobalProcedures() {
|
|
1667
|
+
const client = getClient();
|
|
1668
|
+
const result = await client.execute({
|
|
1669
|
+
sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
1670
|
+
args: []
|
|
1671
|
+
});
|
|
1672
|
+
const procedures = result.rows;
|
|
1673
|
+
if (procedures.length > 0) {
|
|
1674
|
+
_cache = procedures.map((p) => `### ${p.title}
|
|
1675
|
+
${p.content}`).join("\n\n");
|
|
1676
|
+
} else {
|
|
1677
|
+
_cache = "";
|
|
1678
|
+
}
|
|
1679
|
+
_cacheLoaded = true;
|
|
1680
|
+
return procedures;
|
|
1681
|
+
}
|
|
1682
|
+
function getGlobalProceduresBlock() {
|
|
1683
|
+
if (!_cacheLoaded) return "";
|
|
1684
|
+
if (!_cache) return "";
|
|
1685
|
+
return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
|
|
1686
|
+
|
|
1687
|
+
${_cache}
|
|
1688
|
+
`;
|
|
1689
|
+
}
|
|
1690
|
+
async function storeGlobalProcedure(input) {
|
|
1691
|
+
const id = randomUUID();
|
|
1692
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1693
|
+
const client = getClient();
|
|
1694
|
+
await client.execute({
|
|
1695
|
+
sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
1696
|
+
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
1697
|
+
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
|
|
1698
|
+
});
|
|
1699
|
+
await loadGlobalProcedures();
|
|
1700
|
+
return id;
|
|
1701
|
+
}
|
|
1702
|
+
async function deactivateGlobalProcedure(id) {
|
|
1703
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1704
|
+
const client = getClient();
|
|
1705
|
+
const result = await client.execute({
|
|
1706
|
+
sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
1707
|
+
args: [now, id]
|
|
1708
|
+
});
|
|
1709
|
+
await loadGlobalProcedures();
|
|
1710
|
+
return result.rowsAffected > 0;
|
|
1711
|
+
}
|
|
1712
|
+
var _cache, _cacheLoaded;
|
|
1713
|
+
var init_global_procedures = __esm({
|
|
1714
|
+
"src/lib/global-procedures.ts"() {
|
|
1715
|
+
"use strict";
|
|
1716
|
+
init_database();
|
|
1717
|
+
_cache = "";
|
|
1718
|
+
_cacheLoaded = false;
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1581
1722
|
// src/lib/store.ts
|
|
1582
1723
|
var store_exports = {};
|
|
1583
1724
|
__export(store_exports, {
|
|
@@ -1657,6 +1798,11 @@ async function initStore(options) {
|
|
|
1657
1798
|
"version-query"
|
|
1658
1799
|
);
|
|
1659
1800
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1801
|
+
try {
|
|
1802
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
1803
|
+
await loadGlobalProcedures2();
|
|
1804
|
+
} catch {
|
|
1805
|
+
}
|
|
1660
1806
|
}
|
|
1661
1807
|
function classifyTier(record) {
|
|
1662
1808
|
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
@@ -1698,6 +1844,12 @@ async function writeMemory(record) {
|
|
|
1698
1844
|
supersedes_id: record.supersedes_id ?? null
|
|
1699
1845
|
};
|
|
1700
1846
|
_pendingRecords.push(dbRow);
|
|
1847
|
+
orgBus.emit({
|
|
1848
|
+
type: "memory_stored",
|
|
1849
|
+
agentId: record.agent_id,
|
|
1850
|
+
project: record.project_name,
|
|
1851
|
+
timestamp: record.timestamp
|
|
1852
|
+
});
|
|
1701
1853
|
const MAX_PENDING = 1e3;
|
|
1702
1854
|
if (_pendingRecords.length > MAX_PENDING) {
|
|
1703
1855
|
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
@@ -2043,6 +2195,7 @@ var init_store = __esm({
|
|
|
2043
2195
|
init_database();
|
|
2044
2196
|
init_keychain();
|
|
2045
2197
|
init_config();
|
|
2198
|
+
init_state_bus();
|
|
2046
2199
|
INIT_MAX_RETRIES = 3;
|
|
2047
2200
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
2048
2201
|
_pendingRecords = [];
|
|
@@ -2498,7 +2651,7 @@ var init_employees = __esm({
|
|
|
2498
2651
|
|
|
2499
2652
|
// src/lib/license.ts
|
|
2500
2653
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2501
|
-
import { randomUUID } from "crypto";
|
|
2654
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2502
2655
|
import path7 from "path";
|
|
2503
2656
|
import { jwtVerify, importSPKI } from "jose";
|
|
2504
2657
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
@@ -2896,9 +3049,15 @@ async function createTaskCore(input) {
|
|
|
2896
3049
|
}
|
|
2897
3050
|
}
|
|
2898
3051
|
const complexity = input.complexity ?? "standard";
|
|
3052
|
+
let sessionScope = null;
|
|
3053
|
+
try {
|
|
3054
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3055
|
+
sessionScope = resolveExeSession2();
|
|
3056
|
+
} catch {
|
|
3057
|
+
}
|
|
2899
3058
|
await client.execute({
|
|
2900
|
-
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)
|
|
2901
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3059
|
+
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)
|
|
3060
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2902
3061
|
args: [
|
|
2903
3062
|
id,
|
|
2904
3063
|
input.title,
|
|
@@ -2917,6 +3076,7 @@ async function createTaskCore(input) {
|
|
|
2917
3076
|
input.budgetFallbackModel ?? null,
|
|
2918
3077
|
0,
|
|
2919
3078
|
null,
|
|
3079
|
+
sessionScope,
|
|
2920
3080
|
now,
|
|
2921
3081
|
now
|
|
2922
3082
|
]
|
|
@@ -2961,9 +3121,18 @@ async function listTasks(input) {
|
|
|
2961
3121
|
conditions.push("priority = ?");
|
|
2962
3122
|
args.push(input.priority);
|
|
2963
3123
|
}
|
|
3124
|
+
try {
|
|
3125
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
3126
|
+
const session = resolveExeSession2();
|
|
3127
|
+
if (session) {
|
|
3128
|
+
conditions.push("(session_scope IS NULL OR session_scope = ?)");
|
|
3129
|
+
args.push(session);
|
|
3130
|
+
}
|
|
3131
|
+
} catch {
|
|
3132
|
+
}
|
|
2964
3133
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2965
3134
|
const result = await client.execute({
|
|
2966
|
-
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`,
|
|
3135
|
+
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`,
|
|
2967
3136
|
args
|
|
2968
3137
|
});
|
|
2969
3138
|
return result.rows.map((r) => ({
|
|
@@ -3157,6 +3326,7 @@ var init_tasks_crud = __esm({
|
|
|
3157
3326
|
// src/lib/tasks-review.ts
|
|
3158
3327
|
var tasks_review_exports = {};
|
|
3159
3328
|
__export(tasks_review_exports, {
|
|
3329
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
3160
3330
|
cleanupReviewFile: () => cleanupReviewFile,
|
|
3161
3331
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
3162
3332
|
countPendingReviews: () => countPendingReviews,
|
|
@@ -3193,6 +3363,34 @@ async function listPendingReviews(limit) {
|
|
|
3193
3363
|
});
|
|
3194
3364
|
return result.rows;
|
|
3195
3365
|
}
|
|
3366
|
+
async function cleanupOrphanedReviews() {
|
|
3367
|
+
const client = getClient();
|
|
3368
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3369
|
+
const r1 = await client.execute({
|
|
3370
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
3371
|
+
WHERE status = 'needs_review'
|
|
3372
|
+
AND assigned_by = 'system'
|
|
3373
|
+
AND title LIKE 'Review:%'
|
|
3374
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
3375
|
+
args: [now]
|
|
3376
|
+
});
|
|
3377
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
3378
|
+
const r2 = await client.execute({
|
|
3379
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
3380
|
+
WHERE status = 'needs_review'
|
|
3381
|
+
AND result IS NOT NULL
|
|
3382
|
+
AND updated_at < ?`,
|
|
3383
|
+
args: [now, staleThreshold]
|
|
3384
|
+
});
|
|
3385
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
3386
|
+
if (total > 0) {
|
|
3387
|
+
process.stderr.write(
|
|
3388
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
3389
|
+
`
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
3392
|
+
return total;
|
|
3393
|
+
}
|
|
3196
3394
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
3197
3395
|
const roleLower = role.toLowerCase();
|
|
3198
3396
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
|
@@ -3300,6 +3498,13 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
3300
3498
|
skipDispatch: true
|
|
3301
3499
|
});
|
|
3302
3500
|
const reviewId = reviewTask.id;
|
|
3501
|
+
orgBus.emit({
|
|
3502
|
+
type: "review_created",
|
|
3503
|
+
reviewId,
|
|
3504
|
+
employee: agent,
|
|
3505
|
+
reviewer,
|
|
3506
|
+
timestamp: now
|
|
3507
|
+
});
|
|
3303
3508
|
await writeNotification({
|
|
3304
3509
|
agentId: agent,
|
|
3305
3510
|
agentRole: String(row.assigned_to),
|
|
@@ -3396,6 +3601,7 @@ var init_tasks_review = __esm({
|
|
|
3396
3601
|
init_tasks_crud();
|
|
3397
3602
|
init_tmux_routing();
|
|
3398
3603
|
init_session_key();
|
|
3604
|
+
init_state_bus();
|
|
3399
3605
|
}
|
|
3400
3606
|
});
|
|
3401
3607
|
|
|
@@ -3560,13 +3766,12 @@ function assertSessionScope(actionType, targetProject) {
|
|
|
3560
3766
|
};
|
|
3561
3767
|
}
|
|
3562
3768
|
process.stderr.write(
|
|
3563
|
-
`[session-scope]
|
|
3769
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
3564
3770
|
`
|
|
3565
3771
|
);
|
|
3566
3772
|
return {
|
|
3567
|
-
allowed:
|
|
3568
|
-
|
|
3569
|
-
reason: "cross_session_granted",
|
|
3773
|
+
allowed: false,
|
|
3774
|
+
reason: "cross_session_denied",
|
|
3570
3775
|
currentProject,
|
|
3571
3776
|
targetProject,
|
|
3572
3777
|
targetSession: findSessionForProject(targetProject)?.windowName
|
|
@@ -3592,8 +3797,9 @@ async function dispatchTaskToEmployee(input) {
|
|
|
3592
3797
|
try {
|
|
3593
3798
|
const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
|
|
3594
3799
|
const check = assertSessionScope2("dispatch_task", input.projectName);
|
|
3595
|
-
if (check.reason === "
|
|
3800
|
+
if (check.reason === "cross_session_denied") {
|
|
3596
3801
|
crossProject = true;
|
|
3802
|
+
return { dispatched: "skipped", crossProject: true };
|
|
3597
3803
|
}
|
|
3598
3804
|
} catch {
|
|
3599
3805
|
}
|
|
@@ -3962,6 +4168,7 @@ var init_skill_learning = __esm({
|
|
|
3962
4168
|
// src/lib/tasks.ts
|
|
3963
4169
|
var tasks_exports = {};
|
|
3964
4170
|
__export(tasks_exports, {
|
|
4171
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
3965
4172
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
3966
4173
|
countPendingReviews: () => countPendingReviews,
|
|
3967
4174
|
createTask: () => createTask,
|
|
@@ -4027,6 +4234,21 @@ async function updateTask(input) {
|
|
|
4027
4234
|
});
|
|
4028
4235
|
} catch {
|
|
4029
4236
|
}
|
|
4237
|
+
try {
|
|
4238
|
+
const client = getClient();
|
|
4239
|
+
const cascaded = await client.execute({
|
|
4240
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
4241
|
+
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
4242
|
+
args: [now, taskId]
|
|
4243
|
+
});
|
|
4244
|
+
if (cascaded.rowsAffected > 0) {
|
|
4245
|
+
process.stderr.write(
|
|
4246
|
+
`[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
|
|
4247
|
+
`
|
|
4248
|
+
);
|
|
4249
|
+
}
|
|
4250
|
+
} catch {
|
|
4251
|
+
}
|
|
4030
4252
|
}
|
|
4031
4253
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
4032
4254
|
if (isTerminal) {
|
|
@@ -4040,6 +4262,13 @@ async function updateTask(input) {
|
|
|
4040
4262
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
4041
4263
|
} catch {
|
|
4042
4264
|
}
|
|
4265
|
+
orgBus.emit({
|
|
4266
|
+
type: "task_completed",
|
|
4267
|
+
taskId,
|
|
4268
|
+
employee: String(row.assigned_to),
|
|
4269
|
+
result: input.result ?? "",
|
|
4270
|
+
timestamp: now
|
|
4271
|
+
});
|
|
4043
4272
|
if (row.parent_task_id) {
|
|
4044
4273
|
try {
|
|
4045
4274
|
await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
|
|
@@ -4107,6 +4336,7 @@ var init_tasks = __esm({
|
|
|
4107
4336
|
init_database();
|
|
4108
4337
|
init_config();
|
|
4109
4338
|
init_notifications();
|
|
4339
|
+
init_state_bus();
|
|
4110
4340
|
init_tasks_crud();
|
|
4111
4341
|
init_tasks_review();
|
|
4112
4342
|
init_tasks_crud();
|
|
@@ -4497,8 +4727,28 @@ function getMySession() {
|
|
|
4497
4727
|
return getTransport().getMySession();
|
|
4498
4728
|
}
|
|
4499
4729
|
function employeeSessionName(employee, exeSession, instance) {
|
|
4730
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
4731
|
+
const root = extractRootExe(exeSession);
|
|
4732
|
+
if (root) {
|
|
4733
|
+
process.stderr.write(
|
|
4734
|
+
`[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
|
|
4735
|
+
`
|
|
4736
|
+
);
|
|
4737
|
+
exeSession = root;
|
|
4738
|
+
} else {
|
|
4739
|
+
throw new Error(
|
|
4740
|
+
`Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
|
|
4741
|
+
);
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4500
4744
|
const suffix = instance != null && instance > 0 ? String(instance) : "";
|
|
4501
|
-
|
|
4745
|
+
const name = `${employee}${suffix}-${exeSession}`;
|
|
4746
|
+
if (!VALID_SESSION_NAME.test(name)) {
|
|
4747
|
+
throw new Error(
|
|
4748
|
+
`Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
|
|
4749
|
+
);
|
|
4750
|
+
}
|
|
4751
|
+
return name;
|
|
4502
4752
|
}
|
|
4503
4753
|
function parseParentExe(sessionName, agentId) {
|
|
4504
4754
|
const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4738,6 +4988,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4738
4988
|
error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
|
|
4739
4989
|
};
|
|
4740
4990
|
}
|
|
4991
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
4992
|
+
const root = extractRootExe(exeSession);
|
|
4993
|
+
if (root) {
|
|
4994
|
+
process.stderr.write(
|
|
4995
|
+
`[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
|
|
4996
|
+
`
|
|
4997
|
+
);
|
|
4998
|
+
exeSession = root;
|
|
4999
|
+
} else {
|
|
5000
|
+
return {
|
|
5001
|
+
status: "failed",
|
|
5002
|
+
sessionName: "",
|
|
5003
|
+
error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
|
|
5004
|
+
};
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
4741
5007
|
let effectiveInstance = opts?.instance;
|
|
4742
5008
|
if (effectiveInstance === void 0 && opts?.autoInstance) {
|
|
4743
5009
|
const free = findFreeInstance(
|
|
@@ -4984,7 +5250,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4984
5250
|
releaseSpawnLock(sessionName);
|
|
4985
5251
|
return { sessionName };
|
|
4986
5252
|
}
|
|
4987
|
-
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;
|
|
5253
|
+
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;
|
|
4988
5254
|
var init_tmux_routing = __esm({
|
|
4989
5255
|
"src/lib/tmux-routing.ts"() {
|
|
4990
5256
|
"use strict";
|
|
@@ -4999,6 +5265,7 @@ var init_tmux_routing = __esm({
|
|
|
4999
5265
|
SPAWN_LOCK_DIR = path15.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
5000
5266
|
SESSION_CACHE = path15.join(os6.homedir(), ".exe-os", "session-cache");
|
|
5001
5267
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5268
|
+
VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
|
|
5002
5269
|
VERIFY_PANE_LINES = 200;
|
|
5003
5270
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5004
5271
|
INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
@@ -5856,7 +6123,7 @@ __export(consolidation_exports, {
|
|
|
5856
6123
|
selectUnconsolidated: () => selectUnconsolidated,
|
|
5857
6124
|
storeConsolidation: () => storeConsolidation
|
|
5858
6125
|
});
|
|
5859
|
-
import { randomUUID as
|
|
6126
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5860
6127
|
async function selectUnconsolidated(client, limit = 200) {
|
|
5861
6128
|
const result = await client.execute({
|
|
5862
6129
|
sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
|
|
@@ -5952,7 +6219,7 @@ async function consolidateCluster(cluster, model) {
|
|
|
5952
6219
|
return textBlock?.text ?? "";
|
|
5953
6220
|
}
|
|
5954
6221
|
async function storeConsolidation(client, cluster, synthesisText, embedFn) {
|
|
5955
|
-
const consolidatedId =
|
|
6222
|
+
const consolidatedId = randomUUID3();
|
|
5956
6223
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5957
6224
|
const rawText = `CONSOLIDATION [${cluster.dateRange}, ${cluster.projectName}]:
|
|
5958
6225
|
|
|
@@ -5977,7 +6244,7 @@ ${synthesisText}`;
|
|
|
5977
6244
|
const linkStmts = sourceIds.map((sourceId) => ({
|
|
5978
6245
|
sql: `INSERT INTO consolidations (id, consolidated_memory_id, source_memory_id, created_at)
|
|
5979
6246
|
VALUES (?, ?, ?, ?)`,
|
|
5980
|
-
args: [
|
|
6247
|
+
args: [randomUUID3(), consolidatedId, sourceId, now]
|
|
5981
6248
|
}));
|
|
5982
6249
|
const placeholders = sourceIds.map(() => "?").join(",");
|
|
5983
6250
|
const markStmt = {
|
|
@@ -6216,7 +6483,7 @@ var init_consolidation = __esm({
|
|
|
6216
6483
|
// src/lib/exe-daemon-client.ts
|
|
6217
6484
|
import net from "net";
|
|
6218
6485
|
import { spawn } from "child_process";
|
|
6219
|
-
import { randomUUID as
|
|
6486
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6220
6487
|
import { existsSync as existsSync14, unlinkSync as unlinkSync5, readFileSync as readFileSync11, openSync, closeSync, statSync } from "fs";
|
|
6221
6488
|
import path17 from "path";
|
|
6222
6489
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -6408,7 +6675,7 @@ function sendRequest(texts, priority) {
|
|
|
6408
6675
|
resolve({ error: "Not connected" });
|
|
6409
6676
|
return;
|
|
6410
6677
|
}
|
|
6411
|
-
const id =
|
|
6678
|
+
const id = randomUUID4();
|
|
6412
6679
|
const timer = setTimeout(() => {
|
|
6413
6680
|
_pending.delete(id);
|
|
6414
6681
|
resolve({ error: "Request timeout" });
|
|
@@ -6426,7 +6693,7 @@ function sendRequest(texts, priority) {
|
|
|
6426
6693
|
async function pingDaemon() {
|
|
6427
6694
|
if (!_socket || !_connected) return null;
|
|
6428
6695
|
return new Promise((resolve) => {
|
|
6429
|
-
const id =
|
|
6696
|
+
const id = randomUUID4();
|
|
6430
6697
|
const timer = setTimeout(() => {
|
|
6431
6698
|
_pending.delete(id);
|
|
6432
6699
|
resolve(null);
|