@askexenow/exe-os 0.9.8 → 0.9.9
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 +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
|
@@ -25,9 +25,47 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
25
|
};
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
|
|
28
|
+
// src/lib/secure-files.ts
|
|
29
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
30
|
+
import { chmod, mkdir } from "fs/promises";
|
|
31
|
+
async function ensurePrivateDir(dirPath) {
|
|
32
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
33
|
+
try {
|
|
34
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function ensurePrivateDirSync(dirPath) {
|
|
39
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
40
|
+
try {
|
|
41
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function enforcePrivateFile(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function enforcePrivateFileSync(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
58
|
+
var init_secure_files = __esm({
|
|
59
|
+
"src/lib/secure-files.ts"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
PRIVATE_DIR_MODE = 448;
|
|
62
|
+
PRIVATE_FILE_MODE = 384;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
28
66
|
// src/lib/config.ts
|
|
29
|
-
import { readFile, writeFile
|
|
30
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
67
|
+
import { readFile, writeFile } from "fs/promises";
|
|
68
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
31
69
|
import path from "path";
|
|
32
70
|
import os from "os";
|
|
33
71
|
function resolveDataDir() {
|
|
@@ -35,7 +73,7 @@ function resolveDataDir() {
|
|
|
35
73
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
36
74
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
37
75
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
38
|
-
if (!
|
|
76
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
39
77
|
try {
|
|
40
78
|
renameSync(legacyDir, newDir);
|
|
41
79
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -98,9 +136,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
98
136
|
}
|
|
99
137
|
async function loadConfig() {
|
|
100
138
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
101
|
-
await
|
|
139
|
+
await ensurePrivateDir(dir);
|
|
102
140
|
const configPath = path.join(dir, "config.json");
|
|
103
|
-
if (!
|
|
141
|
+
if (!existsSync2(configPath)) {
|
|
104
142
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
105
143
|
}
|
|
106
144
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -113,6 +151,7 @@ async function loadConfig() {
|
|
|
113
151
|
`);
|
|
114
152
|
try {
|
|
115
153
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
154
|
+
await enforcePrivateFile(configPath);
|
|
116
155
|
} catch {
|
|
117
156
|
}
|
|
118
157
|
}
|
|
@@ -132,6 +171,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
132
171
|
var init_config = __esm({
|
|
133
172
|
"src/lib/config.ts"() {
|
|
134
173
|
"use strict";
|
|
174
|
+
init_secure_files();
|
|
135
175
|
EXE_AI_DIR = resolveDataDir();
|
|
136
176
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
137
177
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -278,7 +318,7 @@ var init_session_key = __esm({
|
|
|
278
318
|
|
|
279
319
|
// src/lib/employees.ts
|
|
280
320
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
281
|
-
import { existsSync as
|
|
321
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
282
322
|
import { execSync as execSync2 } from "child_process";
|
|
283
323
|
import path2 from "path";
|
|
284
324
|
import os2 from "os";
|
|
@@ -302,7 +342,7 @@ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
|
302
342
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
303
343
|
}
|
|
304
344
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
305
|
-
if (!
|
|
345
|
+
if (!existsSync3(employeesPath)) return [];
|
|
306
346
|
try {
|
|
307
347
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
308
348
|
} catch {
|
|
@@ -340,13 +380,13 @@ var init_employees = __esm({
|
|
|
340
380
|
});
|
|
341
381
|
|
|
342
382
|
// src/lib/session-registry.ts
|
|
343
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as
|
|
383
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
|
|
344
384
|
import path4 from "path";
|
|
345
385
|
import os3 from "os";
|
|
346
386
|
function registerSession(entry) {
|
|
347
387
|
const dir = path4.dirname(REGISTRY_PATH);
|
|
348
|
-
if (!
|
|
349
|
-
|
|
388
|
+
if (!existsSync4(dir)) {
|
|
389
|
+
mkdirSync3(dir, { recursive: true });
|
|
350
390
|
}
|
|
351
391
|
const sessions = listSessions();
|
|
352
392
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -581,10 +621,10 @@ var init_runtime_table = __esm({
|
|
|
581
621
|
});
|
|
582
622
|
|
|
583
623
|
// src/lib/agent-config.ts
|
|
584
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as
|
|
624
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5 } from "fs";
|
|
585
625
|
import path5 from "path";
|
|
586
626
|
function loadAgentConfig() {
|
|
587
|
-
if (!
|
|
627
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
588
628
|
try {
|
|
589
629
|
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
590
630
|
} catch {
|
|
@@ -605,6 +645,7 @@ var init_agent_config = __esm({
|
|
|
605
645
|
"use strict";
|
|
606
646
|
init_config();
|
|
607
647
|
init_runtime_table();
|
|
648
|
+
init_secure_files();
|
|
608
649
|
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
609
650
|
DEFAULT_MODELS = {
|
|
610
651
|
claude: "claude-opus-4",
|
|
@@ -623,16 +664,16 @@ __export(intercom_queue_exports, {
|
|
|
623
664
|
queueIntercom: () => queueIntercom,
|
|
624
665
|
readQueue: () => readQueue
|
|
625
666
|
});
|
|
626
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as
|
|
667
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
627
668
|
import path6 from "path";
|
|
628
669
|
import os4 from "os";
|
|
629
670
|
function ensureDir() {
|
|
630
671
|
const dir = path6.dirname(QUEUE_PATH);
|
|
631
|
-
if (!
|
|
672
|
+
if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
|
|
632
673
|
}
|
|
633
674
|
function readQueue() {
|
|
634
675
|
try {
|
|
635
|
-
if (!
|
|
676
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
636
677
|
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
637
678
|
} catch {
|
|
638
679
|
return [];
|
|
@@ -1379,13 +1420,50 @@ var init_database_adapter = __esm({
|
|
|
1379
1420
|
}
|
|
1380
1421
|
});
|
|
1381
1422
|
|
|
1423
|
+
// src/lib/daemon-auth.ts
|
|
1424
|
+
import crypto from "crypto";
|
|
1425
|
+
import path8 from "path";
|
|
1426
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
1427
|
+
function normalizeToken(token) {
|
|
1428
|
+
if (!token) return null;
|
|
1429
|
+
const trimmed = token.trim();
|
|
1430
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1431
|
+
}
|
|
1432
|
+
function readDaemonToken() {
|
|
1433
|
+
try {
|
|
1434
|
+
if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
|
|
1435
|
+
return normalizeToken(readFileSync7(DAEMON_TOKEN_PATH, "utf8"));
|
|
1436
|
+
} catch {
|
|
1437
|
+
return null;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function ensureDaemonToken(seed) {
|
|
1441
|
+
const existing = readDaemonToken();
|
|
1442
|
+
if (existing) return existing;
|
|
1443
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1444
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1445
|
+
writeFileSync6(DAEMON_TOKEN_PATH, `${token}
|
|
1446
|
+
`, "utf8");
|
|
1447
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1448
|
+
return token;
|
|
1449
|
+
}
|
|
1450
|
+
var DAEMON_TOKEN_PATH;
|
|
1451
|
+
var init_daemon_auth = __esm({
|
|
1452
|
+
"src/lib/daemon-auth.ts"() {
|
|
1453
|
+
"use strict";
|
|
1454
|
+
init_config();
|
|
1455
|
+
init_secure_files();
|
|
1456
|
+
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1382
1460
|
// src/lib/exe-daemon-client.ts
|
|
1383
1461
|
import net from "net";
|
|
1384
1462
|
import os6 from "os";
|
|
1385
1463
|
import { spawn } from "child_process";
|
|
1386
1464
|
import { randomUUID } from "crypto";
|
|
1387
|
-
import { existsSync as
|
|
1388
|
-
import
|
|
1465
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync3, readFileSync as readFileSync8, openSync, closeSync, statSync } from "fs";
|
|
1466
|
+
import path9 from "path";
|
|
1389
1467
|
import { fileURLToPath } from "url";
|
|
1390
1468
|
function handleData(chunk) {
|
|
1391
1469
|
_buffer += chunk.toString();
|
|
@@ -1413,9 +1491,9 @@ function handleData(chunk) {
|
|
|
1413
1491
|
}
|
|
1414
1492
|
}
|
|
1415
1493
|
function cleanupStaleFiles() {
|
|
1416
|
-
if (
|
|
1494
|
+
if (existsSync8(PID_PATH)) {
|
|
1417
1495
|
try {
|
|
1418
|
-
const pid = parseInt(
|
|
1496
|
+
const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
|
|
1419
1497
|
if (pid > 0) {
|
|
1420
1498
|
try {
|
|
1421
1499
|
process.kill(pid, 0);
|
|
@@ -1436,11 +1514,11 @@ function cleanupStaleFiles() {
|
|
|
1436
1514
|
}
|
|
1437
1515
|
}
|
|
1438
1516
|
function findPackageRoot() {
|
|
1439
|
-
let dir =
|
|
1440
|
-
const { root } =
|
|
1517
|
+
let dir = path9.dirname(fileURLToPath(import.meta.url));
|
|
1518
|
+
const { root } = path9.parse(dir);
|
|
1441
1519
|
while (dir !== root) {
|
|
1442
|
-
if (
|
|
1443
|
-
dir =
|
|
1520
|
+
if (existsSync8(path9.join(dir, "package.json"))) return dir;
|
|
1521
|
+
dir = path9.dirname(dir);
|
|
1444
1522
|
}
|
|
1445
1523
|
return null;
|
|
1446
1524
|
}
|
|
@@ -1466,16 +1544,17 @@ function spawnDaemon() {
|
|
|
1466
1544
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1467
1545
|
return;
|
|
1468
1546
|
}
|
|
1469
|
-
const daemonPath =
|
|
1470
|
-
if (!
|
|
1547
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1548
|
+
if (!existsSync8(daemonPath)) {
|
|
1471
1549
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1472
1550
|
`);
|
|
1473
1551
|
return;
|
|
1474
1552
|
}
|
|
1475
1553
|
const resolvedPath = daemonPath;
|
|
1554
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1476
1555
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1477
1556
|
`);
|
|
1478
|
-
const logPath =
|
|
1557
|
+
const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
|
|
1479
1558
|
let stderrFd = "ignore";
|
|
1480
1559
|
try {
|
|
1481
1560
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1493,7 +1572,8 @@ function spawnDaemon() {
|
|
|
1493
1572
|
TMUX_PANE: void 0,
|
|
1494
1573
|
// Prevents resolveExeSession() from scoping to one session
|
|
1495
1574
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1496
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1575
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1576
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1497
1577
|
}
|
|
1498
1578
|
});
|
|
1499
1579
|
child.unref();
|
|
@@ -1600,13 +1680,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1600
1680
|
return;
|
|
1601
1681
|
}
|
|
1602
1682
|
const id = randomUUID();
|
|
1683
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1603
1684
|
const timer = setTimeout(() => {
|
|
1604
1685
|
_pending.delete(id);
|
|
1605
1686
|
resolve({ error: "Request timeout" });
|
|
1606
1687
|
}, timeoutMs);
|
|
1607
1688
|
_pending.set(id, { resolve, timer });
|
|
1608
1689
|
try {
|
|
1609
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1690
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1610
1691
|
} catch {
|
|
1611
1692
|
clearTimeout(timer);
|
|
1612
1693
|
_pending.delete(id);
|
|
@@ -1617,17 +1698,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1617
1698
|
function isClientConnected() {
|
|
1618
1699
|
return _connected;
|
|
1619
1700
|
}
|
|
1620
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1701
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1621
1702
|
var init_exe_daemon_client = __esm({
|
|
1622
1703
|
"src/lib/exe-daemon-client.ts"() {
|
|
1623
1704
|
"use strict";
|
|
1624
1705
|
init_config();
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1706
|
+
init_daemon_auth();
|
|
1707
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
|
|
1708
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
|
|
1709
|
+
SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1628
1710
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1629
1711
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1630
1712
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1713
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1631
1714
|
_socket = null;
|
|
1632
1715
|
_connected = false;
|
|
1633
1716
|
_buffer = "";
|
|
@@ -2206,6 +2289,7 @@ async function ensureSchema() {
|
|
|
2206
2289
|
project TEXT NOT NULL,
|
|
2207
2290
|
summary TEXT NOT NULL,
|
|
2208
2291
|
task_file TEXT,
|
|
2292
|
+
session_scope TEXT,
|
|
2209
2293
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2210
2294
|
created_at TEXT NOT NULL
|
|
2211
2295
|
);
|
|
@@ -2214,7 +2298,7 @@ async function ensureSchema() {
|
|
|
2214
2298
|
ON notifications(read);
|
|
2215
2299
|
|
|
2216
2300
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2217
|
-
ON notifications(agent_id);
|
|
2301
|
+
ON notifications(agent_id, session_scope);
|
|
2218
2302
|
|
|
2219
2303
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2220
2304
|
ON notifications(task_file);
|
|
@@ -2252,6 +2336,7 @@ async function ensureSchema() {
|
|
|
2252
2336
|
target_agent TEXT NOT NULL,
|
|
2253
2337
|
target_project TEXT,
|
|
2254
2338
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2339
|
+
session_scope TEXT,
|
|
2255
2340
|
content TEXT NOT NULL,
|
|
2256
2341
|
priority TEXT DEFAULT 'normal',
|
|
2257
2342
|
status TEXT DEFAULT 'pending',
|
|
@@ -2265,10 +2350,31 @@ async function ensureSchema() {
|
|
|
2265
2350
|
);
|
|
2266
2351
|
|
|
2267
2352
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2268
|
-
ON messages(target_agent, status);
|
|
2353
|
+
ON messages(target_agent, session_scope, status);
|
|
2269
2354
|
|
|
2270
2355
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2271
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2356
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2357
|
+
`);
|
|
2358
|
+
try {
|
|
2359
|
+
await client.execute({
|
|
2360
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2361
|
+
args: []
|
|
2362
|
+
});
|
|
2363
|
+
} catch {
|
|
2364
|
+
}
|
|
2365
|
+
try {
|
|
2366
|
+
await client.execute({
|
|
2367
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2368
|
+
args: []
|
|
2369
|
+
});
|
|
2370
|
+
} catch {
|
|
2371
|
+
}
|
|
2372
|
+
await client.executeMultiple(`
|
|
2373
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2374
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2375
|
+
|
|
2376
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2377
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2272
2378
|
`);
|
|
2273
2379
|
try {
|
|
2274
2380
|
await client.execute({
|
|
@@ -2852,6 +2958,13 @@ async function ensureSchema() {
|
|
|
2852
2958
|
} catch {
|
|
2853
2959
|
}
|
|
2854
2960
|
}
|
|
2961
|
+
try {
|
|
2962
|
+
await client.execute({
|
|
2963
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2964
|
+
args: []
|
|
2965
|
+
});
|
|
2966
|
+
} catch {
|
|
2967
|
+
}
|
|
2855
2968
|
}
|
|
2856
2969
|
async function disposeDatabase() {
|
|
2857
2970
|
if (_walCheckpointTimer) {
|
|
@@ -2890,18 +3003,21 @@ var init_database = __esm({
|
|
|
2890
3003
|
});
|
|
2891
3004
|
|
|
2892
3005
|
// src/lib/license.ts
|
|
2893
|
-
import { readFileSync as
|
|
3006
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
2894
3007
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2895
|
-
import
|
|
3008
|
+
import { createRequire as createRequire2 } from "module";
|
|
3009
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3010
|
+
import os7 from "os";
|
|
3011
|
+
import path10 from "path";
|
|
2896
3012
|
import { jwtVerify, importSPKI } from "jose";
|
|
2897
3013
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2898
3014
|
var init_license = __esm({
|
|
2899
3015
|
"src/lib/license.ts"() {
|
|
2900
3016
|
"use strict";
|
|
2901
3017
|
init_config();
|
|
2902
|
-
LICENSE_PATH =
|
|
2903
|
-
CACHE_PATH =
|
|
2904
|
-
DEVICE_ID_PATH =
|
|
3018
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3019
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3020
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
2905
3021
|
PLAN_LIMITS = {
|
|
2906
3022
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2907
3023
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2913,12 +3029,12 @@ var init_license = __esm({
|
|
|
2913
3029
|
});
|
|
2914
3030
|
|
|
2915
3031
|
// src/lib/plan-limits.ts
|
|
2916
|
-
import { readFileSync as
|
|
2917
|
-
import
|
|
3032
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
3033
|
+
import path11 from "path";
|
|
2918
3034
|
function getLicenseSync() {
|
|
2919
3035
|
try {
|
|
2920
|
-
if (!
|
|
2921
|
-
const raw = JSON.parse(
|
|
3036
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3037
|
+
const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
|
|
2922
3038
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2923
3039
|
const parts = raw.token.split(".");
|
|
2924
3040
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2956,8 +3072,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2956
3072
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2957
3073
|
let count = 0;
|
|
2958
3074
|
try {
|
|
2959
|
-
if (
|
|
2960
|
-
const raw =
|
|
3075
|
+
if (existsSync10(filePath)) {
|
|
3076
|
+
const raw = readFileSync10(filePath, "utf8");
|
|
2961
3077
|
const employees = JSON.parse(raw);
|
|
2962
3078
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2963
3079
|
}
|
|
@@ -2986,7 +3102,7 @@ var init_plan_limits = __esm({
|
|
|
2986
3102
|
this.name = "PlanLimitError";
|
|
2987
3103
|
}
|
|
2988
3104
|
};
|
|
2989
|
-
CACHE_PATH2 =
|
|
3105
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
2990
3106
|
}
|
|
2991
3107
|
});
|
|
2992
3108
|
|
|
@@ -3002,24 +3118,25 @@ __export(notifications_exports, {
|
|
|
3002
3118
|
readUnreadNotifications: () => readUnreadNotifications,
|
|
3003
3119
|
writeNotification: () => writeNotification
|
|
3004
3120
|
});
|
|
3005
|
-
import
|
|
3006
|
-
import
|
|
3007
|
-
import
|
|
3121
|
+
import crypto2 from "crypto";
|
|
3122
|
+
import path12 from "path";
|
|
3123
|
+
import os8 from "os";
|
|
3008
3124
|
import {
|
|
3009
|
-
readFileSync as
|
|
3125
|
+
readFileSync as readFileSync11,
|
|
3010
3126
|
readdirSync as readdirSync2,
|
|
3011
3127
|
unlinkSync as unlinkSync4,
|
|
3012
|
-
existsSync as
|
|
3128
|
+
existsSync as existsSync11,
|
|
3013
3129
|
rmdirSync
|
|
3014
3130
|
} from "fs";
|
|
3015
3131
|
async function writeNotification(notification) {
|
|
3016
3132
|
try {
|
|
3017
3133
|
const client = getClient();
|
|
3018
|
-
const id =
|
|
3134
|
+
const id = crypto2.randomUUID();
|
|
3019
3135
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3136
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
3020
3137
|
await client.execute({
|
|
3021
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3022
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3138
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
3139
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3023
3140
|
args: [
|
|
3024
3141
|
id,
|
|
3025
3142
|
notification.agentId,
|
|
@@ -3028,6 +3145,7 @@ async function writeNotification(notification) {
|
|
|
3028
3145
|
notification.project,
|
|
3029
3146
|
notification.summary,
|
|
3030
3147
|
notification.taskFile ?? null,
|
|
3148
|
+
sessionScope,
|
|
3031
3149
|
now
|
|
3032
3150
|
]
|
|
3033
3151
|
});
|
|
@@ -3036,21 +3154,22 @@ async function writeNotification(notification) {
|
|
|
3036
3154
|
`);
|
|
3037
3155
|
}
|
|
3038
3156
|
}
|
|
3039
|
-
async function readUnreadNotifications(agentFilter) {
|
|
3157
|
+
async function readUnreadNotifications(agentFilter, sessionScope) {
|
|
3040
3158
|
try {
|
|
3041
3159
|
const client = getClient();
|
|
3042
3160
|
const conditions = ["read = 0"];
|
|
3043
3161
|
const args = [];
|
|
3162
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3044
3163
|
if (agentFilter) {
|
|
3045
3164
|
conditions.push("agent_id = ?");
|
|
3046
3165
|
args.push(agentFilter);
|
|
3047
3166
|
}
|
|
3048
3167
|
const result = await client.execute({
|
|
3049
|
-
sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, created_at
|
|
3168
|
+
sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, session_scope, created_at
|
|
3050
3169
|
FROM notifications
|
|
3051
|
-
WHERE ${conditions.join(" AND ")}
|
|
3170
|
+
WHERE ${conditions.join(" AND ")}${scope.sql}
|
|
3052
3171
|
ORDER BY created_at ASC`,
|
|
3053
|
-
args
|
|
3172
|
+
args: [...args, ...scope.args]
|
|
3054
3173
|
});
|
|
3055
3174
|
return result.rows.map((r) => ({
|
|
3056
3175
|
id: String(r.id),
|
|
@@ -3060,6 +3179,7 @@ async function readUnreadNotifications(agentFilter) {
|
|
|
3060
3179
|
project: String(r.project),
|
|
3061
3180
|
summary: String(r.summary),
|
|
3062
3181
|
taskFile: r.task_file ? String(r.task_file) : void 0,
|
|
3182
|
+
sessionScope: r.session_scope == null ? null : String(r.session_scope),
|
|
3063
3183
|
timestamp: String(r.created_at),
|
|
3064
3184
|
read: false
|
|
3065
3185
|
}));
|
|
@@ -3067,54 +3187,60 @@ async function readUnreadNotifications(agentFilter) {
|
|
|
3067
3187
|
return [];
|
|
3068
3188
|
}
|
|
3069
3189
|
}
|
|
3070
|
-
async function markAsRead(ids) {
|
|
3190
|
+
async function markAsRead(ids, sessionScope) {
|
|
3071
3191
|
if (ids.length === 0) return;
|
|
3072
3192
|
try {
|
|
3073
3193
|
const client = getClient();
|
|
3074
3194
|
const placeholders = ids.map(() => "?").join(", ");
|
|
3195
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3075
3196
|
await client.execute({
|
|
3076
|
-
sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})`,
|
|
3077
|
-
args: ids
|
|
3197
|
+
sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})${scope.sql}`,
|
|
3198
|
+
args: [...ids, ...scope.args]
|
|
3078
3199
|
});
|
|
3079
3200
|
} catch {
|
|
3080
3201
|
}
|
|
3081
3202
|
}
|
|
3082
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
3203
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
3083
3204
|
try {
|
|
3084
3205
|
const client = getClient();
|
|
3206
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3085
3207
|
await client.execute({
|
|
3086
|
-
sql:
|
|
3087
|
-
|
|
3208
|
+
sql: `UPDATE notifications SET read = 1
|
|
3209
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
3210
|
+
args: [taskFile, ...scope.args]
|
|
3088
3211
|
});
|
|
3089
3212
|
} catch {
|
|
3090
3213
|
}
|
|
3091
3214
|
}
|
|
3092
|
-
async function cleanupOldNotifications(daysOld = CLEANUP_DAYS) {
|
|
3215
|
+
async function cleanupOldNotifications(daysOld = CLEANUP_DAYS, sessionScope) {
|
|
3093
3216
|
try {
|
|
3094
3217
|
const client = getClient();
|
|
3095
3218
|
const cutoff = new Date(
|
|
3096
3219
|
Date.now() - daysOld * 24 * 60 * 60 * 1e3
|
|
3097
3220
|
).toISOString();
|
|
3221
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3098
3222
|
const result = await client.execute({
|
|
3099
|
-
sql:
|
|
3100
|
-
args: [cutoff]
|
|
3223
|
+
sql: `DELETE FROM notifications WHERE created_at < ?${scope.sql}`,
|
|
3224
|
+
args: [cutoff, ...scope.args]
|
|
3101
3225
|
});
|
|
3102
3226
|
return result.rowsAffected;
|
|
3103
3227
|
} catch {
|
|
3104
3228
|
return 0;
|
|
3105
3229
|
}
|
|
3106
3230
|
}
|
|
3107
|
-
async function markDoneTaskNotificationsAsRead() {
|
|
3231
|
+
async function markDoneTaskNotificationsAsRead(sessionScope) {
|
|
3108
3232
|
try {
|
|
3109
3233
|
const client = getClient();
|
|
3234
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3110
3235
|
const result = await client.execute({
|
|
3111
3236
|
sql: `UPDATE notifications SET read = 1
|
|
3112
3237
|
WHERE read = 0
|
|
3113
3238
|
AND task_file IS NOT NULL
|
|
3239
|
+
${scope.sql}
|
|
3114
3240
|
AND task_file IN (
|
|
3115
|
-
SELECT task_file FROM tasks WHERE status = 'done'
|
|
3241
|
+
SELECT task_file FROM tasks WHERE status = 'done'${scope.sql}
|
|
3116
3242
|
)`,
|
|
3117
|
-
args: []
|
|
3243
|
+
args: [...scope.args, ...scope.args]
|
|
3118
3244
|
});
|
|
3119
3245
|
return result.rowsAffected;
|
|
3120
3246
|
} catch {
|
|
@@ -3145,9 +3271,9 @@ function formatNotifications(notifications) {
|
|
|
3145
3271
|
return lines.join("\n");
|
|
3146
3272
|
}
|
|
3147
3273
|
async function migrateJsonNotifications() {
|
|
3148
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
3149
|
-
const notifDir =
|
|
3150
|
-
if (!
|
|
3274
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path12.join(os8.homedir(), ".exe-os");
|
|
3275
|
+
const notifDir = path12.join(base, "notifications");
|
|
3276
|
+
if (!existsSync11(notifDir)) return 0;
|
|
3151
3277
|
let migrated = 0;
|
|
3152
3278
|
try {
|
|
3153
3279
|
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -3155,19 +3281,20 @@ async function migrateJsonNotifications() {
|
|
|
3155
3281
|
const client = getClient();
|
|
3156
3282
|
for (const file of files) {
|
|
3157
3283
|
try {
|
|
3158
|
-
const filePath =
|
|
3159
|
-
const data = JSON.parse(
|
|
3284
|
+
const filePath = path12.join(notifDir, file);
|
|
3285
|
+
const data = JSON.parse(readFileSync11(filePath, "utf8"));
|
|
3160
3286
|
await client.execute({
|
|
3161
|
-
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3162
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3287
|
+
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
3288
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3163
3289
|
args: [
|
|
3164
|
-
|
|
3290
|
+
crypto2.randomUUID(),
|
|
3165
3291
|
data.agentId ?? "unknown",
|
|
3166
3292
|
data.agentRole ?? "unknown",
|
|
3167
3293
|
data.event ?? "session_summary",
|
|
3168
3294
|
data.project ?? "unknown",
|
|
3169
3295
|
data.summary ?? "",
|
|
3170
3296
|
data.taskFile ?? null,
|
|
3297
|
+
null,
|
|
3171
3298
|
data.read ? 1 : 0,
|
|
3172
3299
|
data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3173
3300
|
]
|
|
@@ -3221,12 +3348,13 @@ var init_notifications = __esm({
|
|
|
3221
3348
|
"src/lib/notifications.ts"() {
|
|
3222
3349
|
"use strict";
|
|
3223
3350
|
init_database();
|
|
3351
|
+
init_task_scope();
|
|
3224
3352
|
CLEANUP_DAYS = 7;
|
|
3225
3353
|
}
|
|
3226
3354
|
});
|
|
3227
3355
|
|
|
3228
3356
|
// src/lib/session-kill-telemetry.ts
|
|
3229
|
-
import
|
|
3357
|
+
import crypto3 from "crypto";
|
|
3230
3358
|
async function recordSessionKill(input2) {
|
|
3231
3359
|
try {
|
|
3232
3360
|
const client = getClient();
|
|
@@ -3236,7 +3364,7 @@ async function recordSessionKill(input2) {
|
|
|
3236
3364
|
ticks_idle, estimated_tokens_saved)
|
|
3237
3365
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3238
3366
|
args: [
|
|
3239
|
-
|
|
3367
|
+
crypto3.randomUUID(),
|
|
3240
3368
|
input2.sessionName,
|
|
3241
3369
|
input2.agentId,
|
|
3242
3370
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3331,12 +3459,12 @@ __export(tasks_crud_exports, {
|
|
|
3331
3459
|
updateTaskStatus: () => updateTaskStatus,
|
|
3332
3460
|
writeCheckpoint: () => writeCheckpoint
|
|
3333
3461
|
});
|
|
3334
|
-
import
|
|
3335
|
-
import
|
|
3336
|
-
import
|
|
3462
|
+
import crypto4 from "crypto";
|
|
3463
|
+
import path13 from "path";
|
|
3464
|
+
import os9 from "os";
|
|
3337
3465
|
import { execSync as execSync5 } from "child_process";
|
|
3338
3466
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3339
|
-
import { existsSync as
|
|
3467
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
3340
3468
|
async function writeCheckpoint(input2) {
|
|
3341
3469
|
const client = getClient();
|
|
3342
3470
|
const row = await resolveTask(client, input2.taskId);
|
|
@@ -3452,7 +3580,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3452
3580
|
}
|
|
3453
3581
|
async function createTaskCore(input2) {
|
|
3454
3582
|
const client = getClient();
|
|
3455
|
-
const id =
|
|
3583
|
+
const id = crypto4.randomUUID();
|
|
3456
3584
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3457
3585
|
const slug = slugify(input2.title);
|
|
3458
3586
|
let earlySessionScope = null;
|
|
@@ -3511,8 +3639,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3511
3639
|
}
|
|
3512
3640
|
if (input2.baseDir) {
|
|
3513
3641
|
try {
|
|
3514
|
-
await mkdir3(
|
|
3515
|
-
await mkdir3(
|
|
3642
|
+
await mkdir3(path13.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
3643
|
+
await mkdir3(path13.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
3516
3644
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
3517
3645
|
await ensureGitignoreExe(input2.baseDir);
|
|
3518
3646
|
} catch {
|
|
@@ -3548,13 +3676,19 @@ ${laneWarning}` : laneWarning;
|
|
|
3548
3676
|
});
|
|
3549
3677
|
if (input2.baseDir) {
|
|
3550
3678
|
try {
|
|
3551
|
-
const EXE_OS_DIR =
|
|
3552
|
-
const mdPath =
|
|
3553
|
-
const mdDir =
|
|
3554
|
-
if (!
|
|
3679
|
+
const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
|
|
3680
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
3681
|
+
const mdDir = path13.dirname(mdPath);
|
|
3682
|
+
if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3555
3683
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
3556
3684
|
const mdContent = `# ${input2.title}
|
|
3557
3685
|
|
|
3686
|
+
## MANDATORY: When done
|
|
3687
|
+
|
|
3688
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
3689
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3690
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3691
|
+
|
|
3558
3692
|
**ID:** ${id}
|
|
3559
3693
|
**Status:** ${initialStatus}
|
|
3560
3694
|
**Priority:** ${input2.priority}
|
|
@@ -3568,12 +3702,6 @@ ${laneWarning}` : laneWarning;
|
|
|
3568
3702
|
## Context
|
|
3569
3703
|
|
|
3570
3704
|
${input2.context}
|
|
3571
|
-
|
|
3572
|
-
## MANDATORY: When done
|
|
3573
|
-
|
|
3574
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
3575
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3576
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3577
3705
|
`;
|
|
3578
3706
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3579
3707
|
} catch (err) {
|
|
@@ -3822,7 +3950,7 @@ ${input2.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
3822
3950
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
3823
3951
|
} catch {
|
|
3824
3952
|
}
|
|
3825
|
-
if (input2.status === "done" || input2.status === "cancelled") {
|
|
3953
|
+
if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
|
|
3826
3954
|
try {
|
|
3827
3955
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
3828
3956
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -3851,9 +3979,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3851
3979
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3852
3980
|
}
|
|
3853
3981
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3854
|
-
const archPath =
|
|
3982
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3855
3983
|
try {
|
|
3856
|
-
if (
|
|
3984
|
+
if (existsSync12(archPath)) return;
|
|
3857
3985
|
const template = [
|
|
3858
3986
|
`# ${projectName} \u2014 System Architecture`,
|
|
3859
3987
|
"",
|
|
@@ -3886,10 +4014,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3886
4014
|
}
|
|
3887
4015
|
}
|
|
3888
4016
|
async function ensureGitignoreExe(baseDir) {
|
|
3889
|
-
const gitignorePath =
|
|
4017
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
3890
4018
|
try {
|
|
3891
|
-
if (
|
|
3892
|
-
const content =
|
|
4019
|
+
if (existsSync12(gitignorePath)) {
|
|
4020
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
3893
4021
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3894
4022
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3895
4023
|
} else {
|
|
@@ -3920,58 +4048,42 @@ var init_tasks_crud = __esm({
|
|
|
3920
4048
|
});
|
|
3921
4049
|
|
|
3922
4050
|
// src/lib/tasks-review.ts
|
|
3923
|
-
import
|
|
3924
|
-
import { existsSync as
|
|
4051
|
+
import path14 from "path";
|
|
4052
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
3925
4053
|
async function countPendingReviews(sessionScope) {
|
|
3926
4054
|
const client = getClient();
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
args: [sessionScope]
|
|
3931
|
-
});
|
|
3932
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
3933
|
-
}
|
|
4055
|
+
const scope = strictSessionScopeFilter(
|
|
4056
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4057
|
+
);
|
|
3934
4058
|
const result = await client.execute({
|
|
3935
|
-
sql:
|
|
3936
|
-
|
|
4059
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4060
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
4061
|
+
args: [...scope.args]
|
|
3937
4062
|
});
|
|
3938
4063
|
return Number(result.rows[0]?.cnt) || 0;
|
|
3939
4064
|
}
|
|
3940
4065
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
3941
4066
|
const client = getClient();
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
3946
|
-
AND session_scope = ?`,
|
|
3947
|
-
args: [sinceIso, sessionScope]
|
|
3948
|
-
});
|
|
3949
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
3950
|
-
}
|
|
4067
|
+
const scope = strictSessionScopeFilter(
|
|
4068
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4069
|
+
);
|
|
3951
4070
|
const result = await client.execute({
|
|
3952
4071
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
3953
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
3954
|
-
args: [sinceIso]
|
|
4072
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
4073
|
+
args: [sinceIso, ...scope.args]
|
|
3955
4074
|
});
|
|
3956
4075
|
return Number(result.rows[0]?.cnt) || 0;
|
|
3957
4076
|
}
|
|
3958
4077
|
async function listPendingReviews(limit, sessionScope) {
|
|
3959
4078
|
const client = getClient();
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
WHERE status = 'needs_review'
|
|
3964
|
-
AND session_scope = ?
|
|
3965
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
3966
|
-
args: [sessionScope, limit]
|
|
3967
|
-
});
|
|
3968
|
-
return result2.rows;
|
|
3969
|
-
}
|
|
4079
|
+
const scope = strictSessionScopeFilter(
|
|
4080
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4081
|
+
);
|
|
3970
4082
|
const result = await client.execute({
|
|
3971
4083
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
3972
|
-
WHERE status = 'needs_review'
|
|
4084
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
3973
4085
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
3974
|
-
args: [limit]
|
|
4086
|
+
args: [...scope.args, limit]
|
|
3975
4087
|
});
|
|
3976
4088
|
return result.rows;
|
|
3977
4089
|
}
|
|
@@ -3983,7 +4095,7 @@ async function cleanupOrphanedReviews() {
|
|
|
3983
4095
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
3984
4096
|
AND assigned_by = 'system'
|
|
3985
4097
|
AND title LIKE 'Review:%'
|
|
3986
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
4098
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
3987
4099
|
args: [now]
|
|
3988
4100
|
});
|
|
3989
4101
|
const r1b = await client.execute({
|
|
@@ -4102,11 +4214,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4102
4214
|
);
|
|
4103
4215
|
}
|
|
4104
4216
|
try {
|
|
4105
|
-
const cacheDir =
|
|
4106
|
-
if (
|
|
4217
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4218
|
+
if (existsSync13(cacheDir)) {
|
|
4107
4219
|
for (const f of readdirSync3(cacheDir)) {
|
|
4108
4220
|
if (f.startsWith("review-notified-")) {
|
|
4109
|
-
unlinkSync5(
|
|
4221
|
+
unlinkSync5(path14.join(cacheDir, f));
|
|
4110
4222
|
}
|
|
4111
4223
|
}
|
|
4112
4224
|
}
|
|
@@ -4123,11 +4235,12 @@ var init_tasks_review = __esm({
|
|
|
4123
4235
|
init_tmux_routing();
|
|
4124
4236
|
init_session_key();
|
|
4125
4237
|
init_state_bus();
|
|
4238
|
+
init_task_scope();
|
|
4126
4239
|
}
|
|
4127
4240
|
});
|
|
4128
4241
|
|
|
4129
4242
|
// src/lib/tasks-chain.ts
|
|
4130
|
-
import
|
|
4243
|
+
import path15 from "path";
|
|
4131
4244
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
4132
4245
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4133
4246
|
const client = getClient();
|
|
@@ -4144,7 +4257,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4144
4257
|
});
|
|
4145
4258
|
for (const ur of unblockedRows.rows) {
|
|
4146
4259
|
try {
|
|
4147
|
-
const ubFile =
|
|
4260
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4148
4261
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
4149
4262
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4150
4263
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4179,7 +4292,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
4179
4292
|
const scScope = sessionScopeFilter();
|
|
4180
4293
|
const remaining = await client.execute({
|
|
4181
4294
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4182
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
4295
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
4183
4296
|
args: [parentTaskId, ...scScope.args]
|
|
4184
4297
|
});
|
|
4185
4298
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -4213,7 +4326,7 @@ var init_tasks_chain = __esm({
|
|
|
4213
4326
|
|
|
4214
4327
|
// src/lib/project-name.ts
|
|
4215
4328
|
import { execSync as execSync6 } from "child_process";
|
|
4216
|
-
import
|
|
4329
|
+
import path16 from "path";
|
|
4217
4330
|
function getProjectName(cwd) {
|
|
4218
4331
|
const dir = cwd ?? process.cwd();
|
|
4219
4332
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4226,7 +4339,7 @@ function getProjectName(cwd) {
|
|
|
4226
4339
|
timeout: 2e3,
|
|
4227
4340
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4228
4341
|
}).trim();
|
|
4229
|
-
repoRoot =
|
|
4342
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4230
4343
|
} catch {
|
|
4231
4344
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4232
4345
|
cwd: dir,
|
|
@@ -4235,11 +4348,11 @@ function getProjectName(cwd) {
|
|
|
4235
4348
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4236
4349
|
}).trim();
|
|
4237
4350
|
}
|
|
4238
|
-
_cached2 =
|
|
4351
|
+
_cached2 = path16.basename(repoRoot);
|
|
4239
4352
|
_cachedCwd = dir;
|
|
4240
4353
|
return _cached2;
|
|
4241
4354
|
} catch {
|
|
4242
|
-
_cached2 =
|
|
4355
|
+
_cached2 = path16.basename(dir);
|
|
4243
4356
|
_cachedCwd = dir;
|
|
4244
4357
|
return _cached2;
|
|
4245
4358
|
}
|
|
@@ -4382,10 +4495,10 @@ var init_tasks_notify = __esm({
|
|
|
4382
4495
|
});
|
|
4383
4496
|
|
|
4384
4497
|
// src/lib/behaviors.ts
|
|
4385
|
-
import
|
|
4498
|
+
import crypto5 from "crypto";
|
|
4386
4499
|
async function storeBehavior(opts) {
|
|
4387
4500
|
const client = getClient();
|
|
4388
|
-
const id =
|
|
4501
|
+
const id = crypto5.randomUUID();
|
|
4389
4502
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4390
4503
|
await client.execute({
|
|
4391
4504
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -4414,7 +4527,7 @@ __export(skill_learning_exports, {
|
|
|
4414
4527
|
storeTrajectory: () => storeTrajectory,
|
|
4415
4528
|
sweepTrajectories: () => sweepTrajectories
|
|
4416
4529
|
});
|
|
4417
|
-
import
|
|
4530
|
+
import crypto6 from "crypto";
|
|
4418
4531
|
async function extractTrajectory(taskId, agentId) {
|
|
4419
4532
|
const client = getClient();
|
|
4420
4533
|
const result = await client.execute({
|
|
@@ -4443,11 +4556,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
4443
4556
|
return signature;
|
|
4444
4557
|
}
|
|
4445
4558
|
function hashSignature(signature) {
|
|
4446
|
-
return
|
|
4559
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
4447
4560
|
}
|
|
4448
4561
|
async function storeTrajectory(opts) {
|
|
4449
4562
|
const client = getClient();
|
|
4450
|
-
const id =
|
|
4563
|
+
const id = crypto6.randomUUID();
|
|
4451
4564
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4452
4565
|
const signatureHash = hashSignature(opts.signature);
|
|
4453
4566
|
await client.execute({
|
|
@@ -4712,8 +4825,8 @@ __export(tasks_exports, {
|
|
|
4712
4825
|
updateTaskStatus: () => updateTaskStatus,
|
|
4713
4826
|
writeCheckpoint: () => writeCheckpoint
|
|
4714
4827
|
});
|
|
4715
|
-
import
|
|
4716
|
-
import { writeFileSync as
|
|
4828
|
+
import path17 from "path";
|
|
4829
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
4717
4830
|
async function createTask(input2) {
|
|
4718
4831
|
const result = await createTaskCore(input2);
|
|
4719
4832
|
if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4732,12 +4845,12 @@ async function updateTask(input2) {
|
|
|
4732
4845
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
4733
4846
|
try {
|
|
4734
4847
|
const agent = String(row.assigned_to);
|
|
4735
|
-
const cacheDir =
|
|
4736
|
-
const cachePath =
|
|
4848
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
4849
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
4737
4850
|
if (input2.status === "in_progress") {
|
|
4738
4851
|
mkdirSync6(cacheDir, { recursive: true });
|
|
4739
|
-
|
|
4740
|
-
} else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
|
|
4852
|
+
writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4853
|
+
} else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled" || input2.status === "closed") {
|
|
4741
4854
|
try {
|
|
4742
4855
|
unlinkSync6(cachePath);
|
|
4743
4856
|
} catch {
|
|
@@ -4745,10 +4858,10 @@ async function updateTask(input2) {
|
|
|
4745
4858
|
}
|
|
4746
4859
|
} catch {
|
|
4747
4860
|
}
|
|
4748
|
-
if (input2.status === "done") {
|
|
4861
|
+
if (input2.status === "done" || input2.status === "closed") {
|
|
4749
4862
|
await cleanupReviewFile(row, taskFile, input2.baseDir);
|
|
4750
4863
|
}
|
|
4751
|
-
if (input2.status === "done" || input2.status === "cancelled") {
|
|
4864
|
+
if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
|
|
4752
4865
|
try {
|
|
4753
4866
|
const client = getClient();
|
|
4754
4867
|
const taskTitle = String(row.title);
|
|
@@ -4764,7 +4877,7 @@ async function updateTask(input2) {
|
|
|
4764
4877
|
if (!isCoordinatorName(assignedAgent)) {
|
|
4765
4878
|
try {
|
|
4766
4879
|
const draftClient = getClient();
|
|
4767
|
-
if (input2.status === "done") {
|
|
4880
|
+
if (input2.status === "done" || input2.status === "closed") {
|
|
4768
4881
|
await draftClient.execute({
|
|
4769
4882
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
4770
4883
|
args: [assignedAgent]
|
|
@@ -4781,7 +4894,7 @@ async function updateTask(input2) {
|
|
|
4781
4894
|
try {
|
|
4782
4895
|
const client = getClient();
|
|
4783
4896
|
const cascaded = await client.execute({
|
|
4784
|
-
sql: `UPDATE tasks SET status = '
|
|
4897
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
4785
4898
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
4786
4899
|
args: [now, taskId]
|
|
4787
4900
|
});
|
|
@@ -4794,14 +4907,14 @@ async function updateTask(input2) {
|
|
|
4794
4907
|
} catch {
|
|
4795
4908
|
}
|
|
4796
4909
|
}
|
|
4797
|
-
const isTerminal = input2.status === "done" || input2.status === "needs_review";
|
|
4910
|
+
const isTerminal = input2.status === "done" || input2.status === "needs_review" || input2.status === "closed";
|
|
4798
4911
|
if (isTerminal) {
|
|
4799
4912
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
4800
4913
|
if (!isCoordinator) {
|
|
4801
4914
|
notifyTaskDone();
|
|
4802
4915
|
}
|
|
4803
4916
|
await markTaskNotificationsRead(taskFile);
|
|
4804
|
-
if (input2.status === "done") {
|
|
4917
|
+
if (input2.status === "done" || input2.status === "closed") {
|
|
4805
4918
|
try {
|
|
4806
4919
|
await cascadeUnblock(taskId, input2.baseDir, now);
|
|
4807
4920
|
} catch {
|
|
@@ -4821,7 +4934,7 @@ async function updateTask(input2) {
|
|
|
4821
4934
|
}
|
|
4822
4935
|
}
|
|
4823
4936
|
}
|
|
4824
|
-
if (input2.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
4937
|
+
if ((input2.status === "done" || input2.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
4825
4938
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
4826
4939
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
4827
4940
|
taskId,
|
|
@@ -5193,6 +5306,7 @@ __export(tmux_routing_exports, {
|
|
|
5193
5306
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
5194
5307
|
isExeSession: () => isExeSession,
|
|
5195
5308
|
isSessionBusy: () => isSessionBusy,
|
|
5309
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
5196
5310
|
notifyParentExe: () => notifyParentExe,
|
|
5197
5311
|
parseParentExe: () => parseParentExe,
|
|
5198
5312
|
registerParentExe: () => registerParentExe,
|
|
@@ -5203,13 +5317,13 @@ __export(tmux_routing_exports, {
|
|
|
5203
5317
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5204
5318
|
});
|
|
5205
5319
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5206
|
-
import { readFileSync as
|
|
5207
|
-
import
|
|
5208
|
-
import
|
|
5320
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
5321
|
+
import path18 from "path";
|
|
5322
|
+
import os10 from "os";
|
|
5209
5323
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5210
5324
|
import { unlinkSync as unlinkSync7 } from "fs";
|
|
5211
5325
|
function spawnLockPath(sessionName) {
|
|
5212
|
-
return
|
|
5326
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5213
5327
|
}
|
|
5214
5328
|
function isProcessAlive(pid) {
|
|
5215
5329
|
try {
|
|
@@ -5220,13 +5334,13 @@ function isProcessAlive(pid) {
|
|
|
5220
5334
|
}
|
|
5221
5335
|
}
|
|
5222
5336
|
function acquireSpawnLock2(sessionName) {
|
|
5223
|
-
if (!
|
|
5337
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5224
5338
|
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
5225
5339
|
}
|
|
5226
5340
|
const lockFile = spawnLockPath(sessionName);
|
|
5227
|
-
if (
|
|
5341
|
+
if (existsSync14(lockFile)) {
|
|
5228
5342
|
try {
|
|
5229
|
-
const lock = JSON.parse(
|
|
5343
|
+
const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
|
|
5230
5344
|
const age = Date.now() - lock.timestamp;
|
|
5231
5345
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5232
5346
|
return false;
|
|
@@ -5234,7 +5348,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5234
5348
|
} catch {
|
|
5235
5349
|
}
|
|
5236
5350
|
}
|
|
5237
|
-
|
|
5351
|
+
writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5238
5352
|
return true;
|
|
5239
5353
|
}
|
|
5240
5354
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5246,13 +5360,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5246
5360
|
function resolveBehaviorsExporterScript() {
|
|
5247
5361
|
try {
|
|
5248
5362
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5249
|
-
const scriptPath =
|
|
5250
|
-
|
|
5363
|
+
const scriptPath = path18.join(
|
|
5364
|
+
path18.dirname(thisFile),
|
|
5251
5365
|
"..",
|
|
5252
5366
|
"bin",
|
|
5253
5367
|
"exe-export-behaviors.js"
|
|
5254
5368
|
);
|
|
5255
|
-
return
|
|
5369
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
5256
5370
|
} catch {
|
|
5257
5371
|
return null;
|
|
5258
5372
|
}
|
|
@@ -5318,12 +5432,12 @@ function extractRootExe(name) {
|
|
|
5318
5432
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5319
5433
|
}
|
|
5320
5434
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5321
|
-
if (!
|
|
5435
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5322
5436
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5323
5437
|
}
|
|
5324
5438
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5325
|
-
const filePath =
|
|
5326
|
-
|
|
5439
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5440
|
+
writeFileSync9(filePath, JSON.stringify({
|
|
5327
5441
|
parentExe: rootExe,
|
|
5328
5442
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5329
5443
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5331,7 +5445,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5331
5445
|
}
|
|
5332
5446
|
function getParentExe(sessionKey) {
|
|
5333
5447
|
try {
|
|
5334
|
-
const data = JSON.parse(
|
|
5448
|
+
const data = JSON.parse(readFileSync13(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5335
5449
|
return data.parentExe || null;
|
|
5336
5450
|
} catch {
|
|
5337
5451
|
return null;
|
|
@@ -5339,8 +5453,8 @@ function getParentExe(sessionKey) {
|
|
|
5339
5453
|
}
|
|
5340
5454
|
function getDispatchedBy(sessionKey) {
|
|
5341
5455
|
try {
|
|
5342
|
-
const data = JSON.parse(
|
|
5343
|
-
|
|
5456
|
+
const data = JSON.parse(readFileSync13(
|
|
5457
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5344
5458
|
"utf8"
|
|
5345
5459
|
));
|
|
5346
5460
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5410,8 +5524,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5410
5524
|
}
|
|
5411
5525
|
function readDebounceState() {
|
|
5412
5526
|
try {
|
|
5413
|
-
if (!
|
|
5414
|
-
const raw = JSON.parse(
|
|
5527
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5528
|
+
const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
|
|
5415
5529
|
const state = {};
|
|
5416
5530
|
for (const [key, val] of Object.entries(raw)) {
|
|
5417
5531
|
if (typeof val === "number") {
|
|
@@ -5427,8 +5541,8 @@ function readDebounceState() {
|
|
|
5427
5541
|
}
|
|
5428
5542
|
function writeDebounceState(state) {
|
|
5429
5543
|
try {
|
|
5430
|
-
if (!
|
|
5431
|
-
|
|
5544
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5545
|
+
writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5432
5546
|
} catch {
|
|
5433
5547
|
}
|
|
5434
5548
|
}
|
|
@@ -5526,8 +5640,8 @@ function sendIntercom(targetSession) {
|
|
|
5526
5640
|
try {
|
|
5527
5641
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5528
5642
|
const agent = baseAgentName(rawAgent);
|
|
5529
|
-
const markerPath =
|
|
5530
|
-
if (
|
|
5643
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5644
|
+
if (existsSync14(markerPath)) {
|
|
5531
5645
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
5532
5646
|
return "debounced";
|
|
5533
5647
|
}
|
|
@@ -5536,8 +5650,8 @@ function sendIntercom(targetSession) {
|
|
|
5536
5650
|
try {
|
|
5537
5651
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5538
5652
|
const agent = baseAgentName(rawAgent);
|
|
5539
|
-
const taskDir =
|
|
5540
|
-
if (
|
|
5653
|
+
const taskDir = path18.join(process.cwd(), "exe", agent);
|
|
5654
|
+
if (existsSync14(taskDir)) {
|
|
5541
5655
|
const files = readdirSync4(taskDir).filter(
|
|
5542
5656
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
5543
5657
|
);
|
|
@@ -5597,6 +5711,21 @@ function notifyParentExe(sessionKey) {
|
|
|
5597
5711
|
}
|
|
5598
5712
|
return true;
|
|
5599
5713
|
}
|
|
5714
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
5715
|
+
const transport = getTransport();
|
|
5716
|
+
try {
|
|
5717
|
+
const sessions = transport.listSessions();
|
|
5718
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
5719
|
+
execSync7(
|
|
5720
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
5721
|
+
{ timeout: 3e3 }
|
|
5722
|
+
);
|
|
5723
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
5724
|
+
return true;
|
|
5725
|
+
} catch {
|
|
5726
|
+
return false;
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5600
5729
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
5601
5730
|
if (isCoordinatorName(employeeName)) {
|
|
5602
5731
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -5670,26 +5799,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5670
5799
|
const transport = getTransport();
|
|
5671
5800
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5672
5801
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5673
|
-
const logDir =
|
|
5674
|
-
const logFile =
|
|
5675
|
-
if (!
|
|
5802
|
+
const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
|
|
5803
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5804
|
+
if (!existsSync14(logDir)) {
|
|
5676
5805
|
mkdirSync7(logDir, { recursive: true });
|
|
5677
5806
|
}
|
|
5678
5807
|
transport.kill(sessionName);
|
|
5679
5808
|
let cleanupSuffix = "";
|
|
5680
5809
|
try {
|
|
5681
5810
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5682
|
-
const cleanupScript =
|
|
5683
|
-
if (
|
|
5811
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5812
|
+
if (existsSync14(cleanupScript)) {
|
|
5684
5813
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5685
5814
|
}
|
|
5686
5815
|
} catch {
|
|
5687
5816
|
}
|
|
5688
5817
|
try {
|
|
5689
|
-
const claudeJsonPath =
|
|
5818
|
+
const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
|
|
5690
5819
|
let claudeJson = {};
|
|
5691
5820
|
try {
|
|
5692
|
-
claudeJson = JSON.parse(
|
|
5821
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
5693
5822
|
} catch {
|
|
5694
5823
|
}
|
|
5695
5824
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5697,17 +5826,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5697
5826
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5698
5827
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5699
5828
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5700
|
-
|
|
5829
|
+
writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5701
5830
|
} catch {
|
|
5702
5831
|
}
|
|
5703
5832
|
try {
|
|
5704
|
-
const settingsDir =
|
|
5833
|
+
const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
|
|
5705
5834
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5706
|
-
const projSettingsDir =
|
|
5707
|
-
const settingsPath =
|
|
5835
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
5836
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
5708
5837
|
let settings = {};
|
|
5709
5838
|
try {
|
|
5710
|
-
settings = JSON.parse(
|
|
5839
|
+
settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
|
|
5711
5840
|
} catch {
|
|
5712
5841
|
}
|
|
5713
5842
|
const perms = settings.permissions ?? {};
|
|
@@ -5736,7 +5865,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5736
5865
|
perms.allow = allow;
|
|
5737
5866
|
settings.permissions = perms;
|
|
5738
5867
|
mkdirSync7(projSettingsDir, { recursive: true });
|
|
5739
|
-
|
|
5868
|
+
writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5740
5869
|
}
|
|
5741
5870
|
} catch {
|
|
5742
5871
|
}
|
|
@@ -5751,8 +5880,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5751
5880
|
let behaviorsFlag = "";
|
|
5752
5881
|
let legacyFallbackWarned = false;
|
|
5753
5882
|
if (!useExeAgent && !useBinSymlink) {
|
|
5754
|
-
const identityPath =
|
|
5755
|
-
|
|
5883
|
+
const identityPath = path18.join(
|
|
5884
|
+
os10.homedir(),
|
|
5756
5885
|
".exe-os",
|
|
5757
5886
|
"identity",
|
|
5758
5887
|
`${employeeName}.md`
|
|
@@ -5761,13 +5890,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5761
5890
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5762
5891
|
if (hasAgentFlag) {
|
|
5763
5892
|
identityFlag = ` --agent ${employeeName}`;
|
|
5764
|
-
} else if (
|
|
5893
|
+
} else if (existsSync14(identityPath)) {
|
|
5765
5894
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5766
5895
|
legacyFallbackWarned = true;
|
|
5767
5896
|
}
|
|
5768
5897
|
const behaviorsFile = exportBehaviorsSync(
|
|
5769
5898
|
employeeName,
|
|
5770
|
-
|
|
5899
|
+
path18.basename(spawnCwd),
|
|
5771
5900
|
sessionName
|
|
5772
5901
|
);
|
|
5773
5902
|
if (behaviorsFile) {
|
|
@@ -5782,16 +5911,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5782
5911
|
}
|
|
5783
5912
|
let sessionContextFlag = "";
|
|
5784
5913
|
try {
|
|
5785
|
-
const ctxDir =
|
|
5914
|
+
const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
5786
5915
|
mkdirSync7(ctxDir, { recursive: true });
|
|
5787
|
-
const ctxFile =
|
|
5916
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5788
5917
|
const ctxContent = [
|
|
5789
5918
|
`## Session Context`,
|
|
5790
5919
|
`You are running in tmux session: ${sessionName}.`,
|
|
5791
5920
|
`Your parent coordinator session is ${exeSession}.`,
|
|
5792
5921
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
5793
5922
|
].join("\n");
|
|
5794
|
-
|
|
5923
|
+
writeFileSync9(ctxFile, ctxContent);
|
|
5795
5924
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5796
5925
|
} catch {
|
|
5797
5926
|
}
|
|
@@ -5868,8 +5997,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5868
5997
|
transport.pipeLog(sessionName, logFile);
|
|
5869
5998
|
try {
|
|
5870
5999
|
const mySession = getMySession();
|
|
5871
|
-
const dispatchInfo =
|
|
5872
|
-
|
|
6000
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6001
|
+
writeFileSync9(dispatchInfo, JSON.stringify({
|
|
5873
6002
|
dispatchedBy: mySession,
|
|
5874
6003
|
rootExe: exeSession,
|
|
5875
6004
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -5943,15 +6072,15 @@ var init_tmux_routing = __esm({
|
|
|
5943
6072
|
init_intercom_queue();
|
|
5944
6073
|
init_plan_limits();
|
|
5945
6074
|
init_employees();
|
|
5946
|
-
SPAWN_LOCK_DIR =
|
|
5947
|
-
SESSION_CACHE =
|
|
6075
|
+
SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
6076
|
+
SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
|
|
5948
6077
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5949
6078
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5950
6079
|
VERIFY_PANE_LINES = 200;
|
|
5951
6080
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5952
6081
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5953
|
-
INTERCOM_LOG2 =
|
|
5954
|
-
DEBOUNCE_FILE =
|
|
6082
|
+
INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
6083
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5955
6084
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5956
6085
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5957
6086
|
}
|
|
@@ -5974,6 +6103,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
5974
6103
|
args: [scope]
|
|
5975
6104
|
};
|
|
5976
6105
|
}
|
|
6106
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
6107
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
6108
|
+
if (!scope) return { sql: "", args: [] };
|
|
6109
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
6110
|
+
return {
|
|
6111
|
+
sql: ` AND ${col} = ?`,
|
|
6112
|
+
args: [scope]
|
|
6113
|
+
};
|
|
6114
|
+
}
|
|
5977
6115
|
var init_task_scope = __esm({
|
|
5978
6116
|
"src/lib/task-scope.ts"() {
|
|
5979
6117
|
"use strict";
|
|
@@ -5992,14 +6130,14 @@ var init_memory = __esm({
|
|
|
5992
6130
|
|
|
5993
6131
|
// src/lib/keychain.ts
|
|
5994
6132
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5995
|
-
import { existsSync as
|
|
5996
|
-
import
|
|
5997
|
-
import
|
|
6133
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6134
|
+
import path19 from "path";
|
|
6135
|
+
import os11 from "os";
|
|
5998
6136
|
function getKeyDir() {
|
|
5999
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
6137
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os11.homedir(), ".exe-os");
|
|
6000
6138
|
}
|
|
6001
6139
|
function getKeyPath() {
|
|
6002
|
-
return
|
|
6140
|
+
return path19.join(getKeyDir(), "master.key");
|
|
6003
6141
|
}
|
|
6004
6142
|
async function tryKeytar() {
|
|
6005
6143
|
try {
|
|
@@ -6020,9 +6158,9 @@ async function getMasterKey() {
|
|
|
6020
6158
|
}
|
|
6021
6159
|
}
|
|
6022
6160
|
const keyPath = getKeyPath();
|
|
6023
|
-
if (!
|
|
6161
|
+
if (!existsSync15(keyPath)) {
|
|
6024
6162
|
process.stderr.write(
|
|
6025
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
6163
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6026
6164
|
`
|
|
6027
6165
|
);
|
|
6028
6166
|
return null;
|
|
@@ -6052,6 +6190,7 @@ var shard_manager_exports = {};
|
|
|
6052
6190
|
__export(shard_manager_exports, {
|
|
6053
6191
|
disposeShards: () => disposeShards,
|
|
6054
6192
|
ensureShardSchema: () => ensureShardSchema,
|
|
6193
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
6055
6194
|
getReadyShardClient: () => getReadyShardClient,
|
|
6056
6195
|
getShardClient: () => getShardClient,
|
|
6057
6196
|
getShardsDir: () => getShardsDir,
|
|
@@ -6060,15 +6199,18 @@ __export(shard_manager_exports, {
|
|
|
6060
6199
|
listShards: () => listShards,
|
|
6061
6200
|
shardExists: () => shardExists
|
|
6062
6201
|
});
|
|
6063
|
-
import
|
|
6064
|
-
import { existsSync as
|
|
6202
|
+
import path20 from "path";
|
|
6203
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
|
|
6065
6204
|
import { createClient as createClient2 } from "@libsql/client";
|
|
6066
6205
|
function initShardManager(encryptionKey) {
|
|
6067
6206
|
_encryptionKey = encryptionKey;
|
|
6068
|
-
if (!
|
|
6207
|
+
if (!existsSync16(SHARDS_DIR)) {
|
|
6069
6208
|
mkdirSync8(SHARDS_DIR, { recursive: true });
|
|
6070
6209
|
}
|
|
6071
6210
|
_shardingEnabled = true;
|
|
6211
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
6212
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
6213
|
+
_evictionTimer.unref();
|
|
6072
6214
|
}
|
|
6073
6215
|
function isShardingEnabled() {
|
|
6074
6216
|
return _shardingEnabled;
|
|
@@ -6085,21 +6227,28 @@ function getShardClient(projectName) {
|
|
|
6085
6227
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
6086
6228
|
}
|
|
6087
6229
|
const cached = _shards.get(safeName);
|
|
6088
|
-
if (cached)
|
|
6089
|
-
|
|
6230
|
+
if (cached) {
|
|
6231
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6232
|
+
return cached;
|
|
6233
|
+
}
|
|
6234
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
6235
|
+
evictLRU();
|
|
6236
|
+
}
|
|
6237
|
+
const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
|
|
6090
6238
|
const client = createClient2({
|
|
6091
6239
|
url: `file:${dbPath}`,
|
|
6092
6240
|
encryptionKey: _encryptionKey
|
|
6093
6241
|
});
|
|
6094
6242
|
_shards.set(safeName, client);
|
|
6243
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6095
6244
|
return client;
|
|
6096
6245
|
}
|
|
6097
6246
|
function shardExists(projectName) {
|
|
6098
6247
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6099
|
-
return
|
|
6248
|
+
return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
|
|
6100
6249
|
}
|
|
6101
6250
|
function listShards() {
|
|
6102
|
-
if (!
|
|
6251
|
+
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6103
6252
|
return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6104
6253
|
}
|
|
6105
6254
|
async function ensureShardSchema(client) {
|
|
@@ -6151,6 +6300,8 @@ async function ensureShardSchema(client) {
|
|
|
6151
6300
|
for (const col of [
|
|
6152
6301
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
6153
6302
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
6303
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
6304
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
6154
6305
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
6155
6306
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
6156
6307
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -6288,21 +6439,69 @@ async function getReadyShardClient(projectName) {
|
|
|
6288
6439
|
await ensureShardSchema(client);
|
|
6289
6440
|
return client;
|
|
6290
6441
|
}
|
|
6442
|
+
function evictLRU() {
|
|
6443
|
+
let oldest = null;
|
|
6444
|
+
let oldestTime = Infinity;
|
|
6445
|
+
for (const [name, time] of _shardLastAccess) {
|
|
6446
|
+
if (time < oldestTime) {
|
|
6447
|
+
oldestTime = time;
|
|
6448
|
+
oldest = name;
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
if (oldest) {
|
|
6452
|
+
const client = _shards.get(oldest);
|
|
6453
|
+
if (client) {
|
|
6454
|
+
client.close();
|
|
6455
|
+
}
|
|
6456
|
+
_shards.delete(oldest);
|
|
6457
|
+
_shardLastAccess.delete(oldest);
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
function evictIdleShards() {
|
|
6461
|
+
const now = Date.now();
|
|
6462
|
+
const toEvict = [];
|
|
6463
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
6464
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
6465
|
+
toEvict.push(name);
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
for (const name of toEvict) {
|
|
6469
|
+
const client = _shards.get(name);
|
|
6470
|
+
if (client) {
|
|
6471
|
+
client.close();
|
|
6472
|
+
}
|
|
6473
|
+
_shards.delete(name);
|
|
6474
|
+
_shardLastAccess.delete(name);
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
function getOpenShardCount() {
|
|
6478
|
+
return _shards.size;
|
|
6479
|
+
}
|
|
6291
6480
|
function disposeShards() {
|
|
6481
|
+
if (_evictionTimer) {
|
|
6482
|
+
clearInterval(_evictionTimer);
|
|
6483
|
+
_evictionTimer = null;
|
|
6484
|
+
}
|
|
6292
6485
|
for (const [, client] of _shards) {
|
|
6293
6486
|
client.close();
|
|
6294
6487
|
}
|
|
6295
6488
|
_shards.clear();
|
|
6489
|
+
_shardLastAccess.clear();
|
|
6296
6490
|
_shardingEnabled = false;
|
|
6297
6491
|
_encryptionKey = null;
|
|
6298
6492
|
}
|
|
6299
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
6493
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
6300
6494
|
var init_shard_manager = __esm({
|
|
6301
6495
|
"src/lib/shard-manager.ts"() {
|
|
6302
6496
|
"use strict";
|
|
6303
6497
|
init_config();
|
|
6304
|
-
SHARDS_DIR =
|
|
6498
|
+
SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
|
|
6499
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
6500
|
+
MAX_OPEN_SHARDS = 10;
|
|
6501
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
6305
6502
|
_shards = /* @__PURE__ */ new Map();
|
|
6503
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
6504
|
+
_evictionTimer = null;
|
|
6306
6505
|
_encryptionKey = null;
|
|
6307
6506
|
_shardingEnabled = false;
|
|
6308
6507
|
}
|
|
@@ -7287,7 +7486,7 @@ var init_git_task_sweep = __esm({
|
|
|
7287
7486
|
init_config();
|
|
7288
7487
|
init_session_key();
|
|
7289
7488
|
init_employees();
|
|
7290
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
7489
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
7291
7490
|
import { execSync as execSync3 } from "child_process";
|
|
7292
7491
|
import path3 from "path";
|
|
7293
7492
|
var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
|
|
@@ -7418,7 +7617,7 @@ process.stdin.on("end", async () => {
|
|
|
7418
7617
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
7419
7618
|
const { randomUUID: randomUUID4 } = await import("crypto");
|
|
7420
7619
|
const client = getClient2();
|
|
7421
|
-
const seScope =
|
|
7620
|
+
const seScope = strictSessionScopeFilter();
|
|
7422
7621
|
const orphanResult = await client.execute({
|
|
7423
7622
|
sql: `SELECT title, status FROM tasks WHERE assigned_to = ? AND status IN ('open', 'in_progress')${seScope.sql}`,
|
|
7424
7623
|
args: [agent.agentId, ...seScope.args]
|
|
@@ -7462,8 +7661,10 @@ Orphaned tasks at session end: ${orphanResult.rows.map((r) => `"${String(r.title
|
|
|
7462
7661
|
let context;
|
|
7463
7662
|
try {
|
|
7464
7663
|
const ctxResult = await client.execute({
|
|
7465
|
-
sql:
|
|
7466
|
-
|
|
7664
|
+
sql: `SELECT id, context FROM tasks
|
|
7665
|
+
WHERE title = ? AND assigned_to = ? AND status = 'in_progress'${seScope.sql}
|
|
7666
|
+
LIMIT 1`,
|
|
7667
|
+
args: [title, agent.agentId, ...seScope.args]
|
|
7467
7668
|
});
|
|
7468
7669
|
if (ctxResult.rows.length > 0) {
|
|
7469
7670
|
context = ctxResult.rows[0].context ? String(ctxResult.rows[0].context) : void 0;
|
|
@@ -7474,12 +7675,14 @@ Orphaned tasks at session end: ${orphanResult.rows.map((r) => `"${String(r.title
|
|
|
7474
7675
|
const match = findBestMatch2(taskForMatch, commits);
|
|
7475
7676
|
if (match) {
|
|
7476
7677
|
await client.execute({
|
|
7477
|
-
sql:
|
|
7678
|
+
sql: `UPDATE tasks SET status = 'done', result = ?, updated_at = ?
|
|
7679
|
+
WHERE title = ? AND assigned_to = ? AND status = 'in_progress'${seScope.sql}`,
|
|
7478
7680
|
args: [
|
|
7479
7681
|
`Auto-closed: session ended but matching commit ${match.commit.hash} found (score: ${match.score.toFixed(2)}). Message: "${match.commit.message}"`,
|
|
7480
7682
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
7481
7683
|
title,
|
|
7482
|
-
agent.agentId
|
|
7684
|
+
agent.agentId,
|
|
7685
|
+
...seScope.args
|
|
7483
7686
|
]
|
|
7484
7687
|
});
|
|
7485
7688
|
autoClosed.push(`"${title}" \u2192 commit ${match.commit.hash}`);
|