@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
package/dist/runtime/index.js
CHANGED
|
@@ -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 path2 from "path";
|
|
32
70
|
import os2 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 = path2.join(os2.homedir(), ".exe-os");
|
|
37
75
|
const legacyDir = path2.join(os2.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 = path2.join(dir, "config.json");
|
|
103
|
-
if (!
|
|
141
|
+
if (!existsSync2(configPath)) {
|
|
104
142
|
return { ...DEFAULT_CONFIG, dbPath: path2.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 = path2.join(EXE_AI_DIR, "memories.db");
|
|
137
177
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -210,7 +250,7 @@ var init_config = __esm({
|
|
|
210
250
|
|
|
211
251
|
// src/lib/employees.ts
|
|
212
252
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
213
|
-
import { existsSync as
|
|
253
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
214
254
|
import { execSync } from "child_process";
|
|
215
255
|
import path3 from "path";
|
|
216
256
|
import os3 from "os";
|
|
@@ -231,7 +271,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
231
271
|
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
232
272
|
}
|
|
233
273
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
234
|
-
if (!
|
|
274
|
+
if (!existsSync3(employeesPath)) return [];
|
|
235
275
|
try {
|
|
236
276
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
237
277
|
} catch {
|
|
@@ -275,14 +315,14 @@ __export(session_registry_exports, {
|
|
|
275
315
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
276
316
|
registerSession: () => registerSession
|
|
277
317
|
});
|
|
278
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, existsSync as
|
|
318
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
279
319
|
import { execSync as execSync2 } from "child_process";
|
|
280
320
|
import path4 from "path";
|
|
281
321
|
import os4 from "os";
|
|
282
322
|
function registerSession(entry) {
|
|
283
323
|
const dir = path4.dirname(REGISTRY_PATH);
|
|
284
|
-
if (!
|
|
285
|
-
|
|
324
|
+
if (!existsSync4(dir)) {
|
|
325
|
+
mkdirSync2(dir, { recursive: true });
|
|
286
326
|
}
|
|
287
327
|
const sessions = listSessions();
|
|
288
328
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -614,10 +654,10 @@ var init_runtime_table = __esm({
|
|
|
614
654
|
});
|
|
615
655
|
|
|
616
656
|
// src/lib/agent-config.ts
|
|
617
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as
|
|
657
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
618
658
|
import path5 from "path";
|
|
619
659
|
function loadAgentConfig() {
|
|
620
|
-
if (!
|
|
660
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
621
661
|
try {
|
|
622
662
|
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
623
663
|
} catch {
|
|
@@ -638,6 +678,7 @@ var init_agent_config = __esm({
|
|
|
638
678
|
"use strict";
|
|
639
679
|
init_config();
|
|
640
680
|
init_runtime_table();
|
|
681
|
+
init_secure_files();
|
|
641
682
|
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
642
683
|
DEFAULT_MODELS = {
|
|
643
684
|
claude: "claude-opus-4",
|
|
@@ -656,16 +697,16 @@ __export(intercom_queue_exports, {
|
|
|
656
697
|
queueIntercom: () => queueIntercom,
|
|
657
698
|
readQueue: () => readQueue
|
|
658
699
|
});
|
|
659
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as
|
|
700
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
660
701
|
import path6 from "path";
|
|
661
702
|
import os5 from "os";
|
|
662
703
|
function ensureDir() {
|
|
663
704
|
const dir = path6.dirname(QUEUE_PATH);
|
|
664
|
-
if (!
|
|
705
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
665
706
|
}
|
|
666
707
|
function readQueue() {
|
|
667
708
|
try {
|
|
668
|
-
if (!
|
|
709
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
669
710
|
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
670
711
|
} catch {
|
|
671
712
|
return [];
|
|
@@ -1412,13 +1453,50 @@ var init_database_adapter = __esm({
|
|
|
1412
1453
|
}
|
|
1413
1454
|
});
|
|
1414
1455
|
|
|
1456
|
+
// src/lib/daemon-auth.ts
|
|
1457
|
+
import crypto from "crypto";
|
|
1458
|
+
import path8 from "path";
|
|
1459
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1460
|
+
function normalizeToken(token) {
|
|
1461
|
+
if (!token) return null;
|
|
1462
|
+
const trimmed = token.trim();
|
|
1463
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1464
|
+
}
|
|
1465
|
+
function readDaemonToken() {
|
|
1466
|
+
try {
|
|
1467
|
+
if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
|
|
1468
|
+
return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
|
|
1469
|
+
} catch {
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function ensureDaemonToken(seed) {
|
|
1474
|
+
const existing = readDaemonToken();
|
|
1475
|
+
if (existing) return existing;
|
|
1476
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1477
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1478
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
1479
|
+
`, "utf8");
|
|
1480
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1481
|
+
return token;
|
|
1482
|
+
}
|
|
1483
|
+
var DAEMON_TOKEN_PATH;
|
|
1484
|
+
var init_daemon_auth = __esm({
|
|
1485
|
+
"src/lib/daemon-auth.ts"() {
|
|
1486
|
+
"use strict";
|
|
1487
|
+
init_config();
|
|
1488
|
+
init_secure_files();
|
|
1489
|
+
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1415
1493
|
// src/lib/exe-daemon-client.ts
|
|
1416
1494
|
import net from "net";
|
|
1417
1495
|
import os7 from "os";
|
|
1418
1496
|
import { spawn } from "child_process";
|
|
1419
1497
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1420
|
-
import { existsSync as
|
|
1421
|
-
import
|
|
1498
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
1499
|
+
import path9 from "path";
|
|
1422
1500
|
import { fileURLToPath } from "url";
|
|
1423
1501
|
function handleData(chunk) {
|
|
1424
1502
|
_buffer += chunk.toString();
|
|
@@ -1446,9 +1524,9 @@ function handleData(chunk) {
|
|
|
1446
1524
|
}
|
|
1447
1525
|
}
|
|
1448
1526
|
function cleanupStaleFiles() {
|
|
1449
|
-
if (
|
|
1527
|
+
if (existsSync8(PID_PATH)) {
|
|
1450
1528
|
try {
|
|
1451
|
-
const pid = parseInt(
|
|
1529
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
1452
1530
|
if (pid > 0) {
|
|
1453
1531
|
try {
|
|
1454
1532
|
process.kill(pid, 0);
|
|
@@ -1469,11 +1547,11 @@ function cleanupStaleFiles() {
|
|
|
1469
1547
|
}
|
|
1470
1548
|
}
|
|
1471
1549
|
function findPackageRoot() {
|
|
1472
|
-
let dir =
|
|
1473
|
-
const { root } =
|
|
1550
|
+
let dir = path9.dirname(fileURLToPath(import.meta.url));
|
|
1551
|
+
const { root } = path9.parse(dir);
|
|
1474
1552
|
while (dir !== root) {
|
|
1475
|
-
if (
|
|
1476
|
-
dir =
|
|
1553
|
+
if (existsSync8(path9.join(dir, "package.json"))) return dir;
|
|
1554
|
+
dir = path9.dirname(dir);
|
|
1477
1555
|
}
|
|
1478
1556
|
return null;
|
|
1479
1557
|
}
|
|
@@ -1499,16 +1577,17 @@ function spawnDaemon() {
|
|
|
1499
1577
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1500
1578
|
return;
|
|
1501
1579
|
}
|
|
1502
|
-
const daemonPath =
|
|
1503
|
-
if (!
|
|
1580
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1581
|
+
if (!existsSync8(daemonPath)) {
|
|
1504
1582
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1505
1583
|
`);
|
|
1506
1584
|
return;
|
|
1507
1585
|
}
|
|
1508
1586
|
const resolvedPath = daemonPath;
|
|
1587
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1509
1588
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1510
1589
|
`);
|
|
1511
|
-
const logPath =
|
|
1590
|
+
const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
|
|
1512
1591
|
let stderrFd = "ignore";
|
|
1513
1592
|
try {
|
|
1514
1593
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1526,7 +1605,8 @@ function spawnDaemon() {
|
|
|
1526
1605
|
TMUX_PANE: void 0,
|
|
1527
1606
|
// Prevents resolveExeSession() from scoping to one session
|
|
1528
1607
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1529
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1608
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1609
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1530
1610
|
}
|
|
1531
1611
|
});
|
|
1532
1612
|
child.unref();
|
|
@@ -1633,13 +1713,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1633
1713
|
return;
|
|
1634
1714
|
}
|
|
1635
1715
|
const id = randomUUID2();
|
|
1716
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1636
1717
|
const timer = setTimeout(() => {
|
|
1637
1718
|
_pending.delete(id);
|
|
1638
1719
|
resolve({ error: "Request timeout" });
|
|
1639
1720
|
}, timeoutMs);
|
|
1640
1721
|
_pending.set(id, { resolve, timer });
|
|
1641
1722
|
try {
|
|
1642
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1723
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1643
1724
|
} catch {
|
|
1644
1725
|
clearTimeout(timer);
|
|
1645
1726
|
_pending.delete(id);
|
|
@@ -1650,17 +1731,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1650
1731
|
function isClientConnected() {
|
|
1651
1732
|
return _connected;
|
|
1652
1733
|
}
|
|
1653
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
1734
|
+
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;
|
|
1654
1735
|
var init_exe_daemon_client = __esm({
|
|
1655
1736
|
"src/lib/exe-daemon-client.ts"() {
|
|
1656
1737
|
"use strict";
|
|
1657
1738
|
init_config();
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1739
|
+
init_daemon_auth();
|
|
1740
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
|
|
1741
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
|
|
1742
|
+
SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1661
1743
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1662
1744
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1663
1745
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1746
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1664
1747
|
_socket = null;
|
|
1665
1748
|
_connected = false;
|
|
1666
1749
|
_buffer = "";
|
|
@@ -2239,6 +2322,7 @@ async function ensureSchema() {
|
|
|
2239
2322
|
project TEXT NOT NULL,
|
|
2240
2323
|
summary TEXT NOT NULL,
|
|
2241
2324
|
task_file TEXT,
|
|
2325
|
+
session_scope TEXT,
|
|
2242
2326
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2243
2327
|
created_at TEXT NOT NULL
|
|
2244
2328
|
);
|
|
@@ -2247,7 +2331,7 @@ async function ensureSchema() {
|
|
|
2247
2331
|
ON notifications(read);
|
|
2248
2332
|
|
|
2249
2333
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2250
|
-
ON notifications(agent_id);
|
|
2334
|
+
ON notifications(agent_id, session_scope);
|
|
2251
2335
|
|
|
2252
2336
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2253
2337
|
ON notifications(task_file);
|
|
@@ -2285,6 +2369,7 @@ async function ensureSchema() {
|
|
|
2285
2369
|
target_agent TEXT NOT NULL,
|
|
2286
2370
|
target_project TEXT,
|
|
2287
2371
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2372
|
+
session_scope TEXT,
|
|
2288
2373
|
content TEXT NOT NULL,
|
|
2289
2374
|
priority TEXT DEFAULT 'normal',
|
|
2290
2375
|
status TEXT DEFAULT 'pending',
|
|
@@ -2298,10 +2383,31 @@ async function ensureSchema() {
|
|
|
2298
2383
|
);
|
|
2299
2384
|
|
|
2300
2385
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2301
|
-
ON messages(target_agent, status);
|
|
2386
|
+
ON messages(target_agent, session_scope, status);
|
|
2302
2387
|
|
|
2303
2388
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2304
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2389
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2390
|
+
`);
|
|
2391
|
+
try {
|
|
2392
|
+
await client.execute({
|
|
2393
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2394
|
+
args: []
|
|
2395
|
+
});
|
|
2396
|
+
} catch {
|
|
2397
|
+
}
|
|
2398
|
+
try {
|
|
2399
|
+
await client.execute({
|
|
2400
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2401
|
+
args: []
|
|
2402
|
+
});
|
|
2403
|
+
} catch {
|
|
2404
|
+
}
|
|
2405
|
+
await client.executeMultiple(`
|
|
2406
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2407
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2408
|
+
|
|
2409
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2410
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2305
2411
|
`);
|
|
2306
2412
|
try {
|
|
2307
2413
|
await client.execute({
|
|
@@ -2885,6 +2991,13 @@ async function ensureSchema() {
|
|
|
2885
2991
|
} catch {
|
|
2886
2992
|
}
|
|
2887
2993
|
}
|
|
2994
|
+
try {
|
|
2995
|
+
await client.execute({
|
|
2996
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2997
|
+
args: []
|
|
2998
|
+
});
|
|
2999
|
+
} catch {
|
|
3000
|
+
}
|
|
2888
3001
|
}
|
|
2889
3002
|
async function disposeDatabase() {
|
|
2890
3003
|
if (_walCheckpointTimer) {
|
|
@@ -2923,18 +3036,21 @@ var init_database = __esm({
|
|
|
2923
3036
|
});
|
|
2924
3037
|
|
|
2925
3038
|
// src/lib/license.ts
|
|
2926
|
-
import { readFileSync as
|
|
3039
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
2927
3040
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2928
|
-
import
|
|
3041
|
+
import { createRequire as createRequire2 } from "module";
|
|
3042
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3043
|
+
import os8 from "os";
|
|
3044
|
+
import path10 from "path";
|
|
2929
3045
|
import { jwtVerify, importSPKI } from "jose";
|
|
2930
3046
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2931
3047
|
var init_license = __esm({
|
|
2932
3048
|
"src/lib/license.ts"() {
|
|
2933
3049
|
"use strict";
|
|
2934
3050
|
init_config();
|
|
2935
|
-
LICENSE_PATH =
|
|
2936
|
-
CACHE_PATH =
|
|
2937
|
-
DEVICE_ID_PATH =
|
|
3051
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3052
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3053
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
2938
3054
|
PLAN_LIMITS = {
|
|
2939
3055
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2940
3056
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2946,12 +3062,12 @@ var init_license = __esm({
|
|
|
2946
3062
|
});
|
|
2947
3063
|
|
|
2948
3064
|
// src/lib/plan-limits.ts
|
|
2949
|
-
import { readFileSync as
|
|
2950
|
-
import
|
|
3065
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
3066
|
+
import path11 from "path";
|
|
2951
3067
|
function getLicenseSync() {
|
|
2952
3068
|
try {
|
|
2953
|
-
if (!
|
|
2954
|
-
const raw = JSON.parse(
|
|
3069
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3070
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
2955
3071
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2956
3072
|
const parts = raw.token.split(".");
|
|
2957
3073
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2989,8 +3105,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2989
3105
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2990
3106
|
let count = 0;
|
|
2991
3107
|
try {
|
|
2992
|
-
if (
|
|
2993
|
-
const raw =
|
|
3108
|
+
if (existsSync10(filePath)) {
|
|
3109
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
2994
3110
|
const employees = JSON.parse(raw);
|
|
2995
3111
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2996
3112
|
}
|
|
@@ -3019,29 +3135,69 @@ var init_plan_limits = __esm({
|
|
|
3019
3135
|
this.name = "PlanLimitError";
|
|
3020
3136
|
}
|
|
3021
3137
|
};
|
|
3022
|
-
CACHE_PATH2 =
|
|
3138
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
3139
|
+
}
|
|
3140
|
+
});
|
|
3141
|
+
|
|
3142
|
+
// src/lib/task-scope.ts
|
|
3143
|
+
var task_scope_exports = {};
|
|
3144
|
+
__export(task_scope_exports, {
|
|
3145
|
+
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
3146
|
+
sessionScopeFilter: () => sessionScopeFilter,
|
|
3147
|
+
strictSessionScopeFilter: () => strictSessionScopeFilter
|
|
3148
|
+
});
|
|
3149
|
+
function getCurrentSessionScope() {
|
|
3150
|
+
try {
|
|
3151
|
+
return resolveExeSession();
|
|
3152
|
+
} catch {
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
3157
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3158
|
+
if (!scope) return { sql: "", args: [] };
|
|
3159
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3160
|
+
return {
|
|
3161
|
+
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
3162
|
+
args: [scope]
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
3166
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3167
|
+
if (!scope) return { sql: "", args: [] };
|
|
3168
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3169
|
+
return {
|
|
3170
|
+
sql: ` AND ${col} = ?`,
|
|
3171
|
+
args: [scope]
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
var init_task_scope = __esm({
|
|
3175
|
+
"src/lib/task-scope.ts"() {
|
|
3176
|
+
"use strict";
|
|
3177
|
+
init_tmux_routing();
|
|
3023
3178
|
}
|
|
3024
3179
|
});
|
|
3025
3180
|
|
|
3026
3181
|
// src/lib/notifications.ts
|
|
3027
|
-
import
|
|
3028
|
-
import
|
|
3029
|
-
import
|
|
3182
|
+
import crypto2 from "crypto";
|
|
3183
|
+
import path12 from "path";
|
|
3184
|
+
import os9 from "os";
|
|
3030
3185
|
import {
|
|
3031
|
-
readFileSync as
|
|
3186
|
+
readFileSync as readFileSync10,
|
|
3032
3187
|
readdirSync,
|
|
3033
3188
|
unlinkSync as unlinkSync3,
|
|
3034
|
-
existsSync as
|
|
3189
|
+
existsSync as existsSync11,
|
|
3035
3190
|
rmdirSync
|
|
3036
3191
|
} from "fs";
|
|
3037
3192
|
async function writeNotification(notification) {
|
|
3038
3193
|
try {
|
|
3039
3194
|
const client = getClient();
|
|
3040
|
-
const id =
|
|
3195
|
+
const id = crypto2.randomUUID();
|
|
3041
3196
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3197
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
3042
3198
|
await client.execute({
|
|
3043
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3044
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3199
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
3200
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3045
3201
|
args: [
|
|
3046
3202
|
id,
|
|
3047
3203
|
notification.agentId,
|
|
@@ -3050,6 +3206,7 @@ async function writeNotification(notification) {
|
|
|
3050
3206
|
notification.project,
|
|
3051
3207
|
notification.summary,
|
|
3052
3208
|
notification.taskFile ?? null,
|
|
3209
|
+
sessionScope,
|
|
3053
3210
|
now
|
|
3054
3211
|
]
|
|
3055
3212
|
});
|
|
@@ -3058,12 +3215,14 @@ async function writeNotification(notification) {
|
|
|
3058
3215
|
`);
|
|
3059
3216
|
}
|
|
3060
3217
|
}
|
|
3061
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
3218
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
3062
3219
|
try {
|
|
3063
3220
|
const client = getClient();
|
|
3221
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3064
3222
|
await client.execute({
|
|
3065
|
-
sql:
|
|
3066
|
-
|
|
3223
|
+
sql: `UPDATE notifications SET read = 1
|
|
3224
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
3225
|
+
args: [taskFile, ...scope.args]
|
|
3067
3226
|
});
|
|
3068
3227
|
} catch {
|
|
3069
3228
|
}
|
|
@@ -3072,11 +3231,12 @@ var init_notifications = __esm({
|
|
|
3072
3231
|
"src/lib/notifications.ts"() {
|
|
3073
3232
|
"use strict";
|
|
3074
3233
|
init_database();
|
|
3234
|
+
init_task_scope();
|
|
3075
3235
|
}
|
|
3076
3236
|
});
|
|
3077
3237
|
|
|
3078
3238
|
// src/lib/session-kill-telemetry.ts
|
|
3079
|
-
import
|
|
3239
|
+
import crypto3 from "crypto";
|
|
3080
3240
|
async function recordSessionKill(input) {
|
|
3081
3241
|
try {
|
|
3082
3242
|
const client = getClient();
|
|
@@ -3086,7 +3246,7 @@ async function recordSessionKill(input) {
|
|
|
3086
3246
|
ticks_idle, estimated_tokens_saved)
|
|
3087
3247
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3088
3248
|
args: [
|
|
3089
|
-
|
|
3249
|
+
crypto3.randomUUID(),
|
|
3090
3250
|
input.sessionName,
|
|
3091
3251
|
input.agentId,
|
|
3092
3252
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3109,35 +3269,6 @@ var init_session_kill_telemetry = __esm({
|
|
|
3109
3269
|
}
|
|
3110
3270
|
});
|
|
3111
3271
|
|
|
3112
|
-
// src/lib/task-scope.ts
|
|
3113
|
-
var task_scope_exports = {};
|
|
3114
|
-
__export(task_scope_exports, {
|
|
3115
|
-
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
3116
|
-
sessionScopeFilter: () => sessionScopeFilter
|
|
3117
|
-
});
|
|
3118
|
-
function getCurrentSessionScope() {
|
|
3119
|
-
try {
|
|
3120
|
-
return resolveExeSession();
|
|
3121
|
-
} catch {
|
|
3122
|
-
return null;
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
3126
|
-
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3127
|
-
if (!scope) return { sql: "", args: [] };
|
|
3128
|
-
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3129
|
-
return {
|
|
3130
|
-
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
3131
|
-
args: [scope]
|
|
3132
|
-
};
|
|
3133
|
-
}
|
|
3134
|
-
var init_task_scope = __esm({
|
|
3135
|
-
"src/lib/task-scope.ts"() {
|
|
3136
|
-
"use strict";
|
|
3137
|
-
init_tmux_routing();
|
|
3138
|
-
}
|
|
3139
|
-
});
|
|
3140
|
-
|
|
3141
3272
|
// src/lib/state-bus.ts
|
|
3142
3273
|
var StateBus, orgBus;
|
|
3143
3274
|
var init_state_bus = __esm({
|
|
@@ -3194,12 +3325,12 @@ var init_state_bus = __esm({
|
|
|
3194
3325
|
});
|
|
3195
3326
|
|
|
3196
3327
|
// src/lib/tasks-crud.ts
|
|
3197
|
-
import
|
|
3198
|
-
import
|
|
3199
|
-
import
|
|
3328
|
+
import crypto4 from "crypto";
|
|
3329
|
+
import path13 from "path";
|
|
3330
|
+
import os10 from "os";
|
|
3200
3331
|
import { execSync as execSync5 } from "child_process";
|
|
3201
3332
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3202
|
-
import { existsSync as
|
|
3333
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
3203
3334
|
async function writeCheckpoint(input) {
|
|
3204
3335
|
const client = getClient();
|
|
3205
3336
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3315,7 +3446,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3315
3446
|
}
|
|
3316
3447
|
async function createTaskCore(input) {
|
|
3317
3448
|
const client = getClient();
|
|
3318
|
-
const id =
|
|
3449
|
+
const id = crypto4.randomUUID();
|
|
3319
3450
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3320
3451
|
const slug = slugify(input.title);
|
|
3321
3452
|
let earlySessionScope = null;
|
|
@@ -3374,8 +3505,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3374
3505
|
}
|
|
3375
3506
|
if (input.baseDir) {
|
|
3376
3507
|
try {
|
|
3377
|
-
await mkdir3(
|
|
3378
|
-
await mkdir3(
|
|
3508
|
+
await mkdir3(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3509
|
+
await mkdir3(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3379
3510
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3380
3511
|
await ensureGitignoreExe(input.baseDir);
|
|
3381
3512
|
} catch {
|
|
@@ -3411,13 +3542,19 @@ ${laneWarning}` : laneWarning;
|
|
|
3411
3542
|
});
|
|
3412
3543
|
if (input.baseDir) {
|
|
3413
3544
|
try {
|
|
3414
|
-
const EXE_OS_DIR =
|
|
3415
|
-
const mdPath =
|
|
3416
|
-
const mdDir =
|
|
3417
|
-
if (!
|
|
3545
|
+
const EXE_OS_DIR = path13.join(os10.homedir(), ".exe-os");
|
|
3546
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
3547
|
+
const mdDir = path13.dirname(mdPath);
|
|
3548
|
+
if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3418
3549
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3419
3550
|
const mdContent = `# ${input.title}
|
|
3420
3551
|
|
|
3552
|
+
## MANDATORY: When done
|
|
3553
|
+
|
|
3554
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
3555
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3556
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3557
|
+
|
|
3421
3558
|
**ID:** ${id}
|
|
3422
3559
|
**Status:** ${initialStatus}
|
|
3423
3560
|
**Priority:** ${input.priority}
|
|
@@ -3431,12 +3568,6 @@ ${laneWarning}` : laneWarning;
|
|
|
3431
3568
|
## Context
|
|
3432
3569
|
|
|
3433
3570
|
${input.context}
|
|
3434
|
-
|
|
3435
|
-
## MANDATORY: When done
|
|
3436
|
-
|
|
3437
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
3438
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3439
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3440
3571
|
`;
|
|
3441
3572
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3442
3573
|
} catch (err) {
|
|
@@ -3685,7 +3816,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
3685
3816
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
3686
3817
|
} catch {
|
|
3687
3818
|
}
|
|
3688
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
3819
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
3689
3820
|
try {
|
|
3690
3821
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
3691
3822
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -3714,9 +3845,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3714
3845
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3715
3846
|
}
|
|
3716
3847
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3717
|
-
const archPath =
|
|
3848
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3718
3849
|
try {
|
|
3719
|
-
if (
|
|
3850
|
+
if (existsSync12(archPath)) return;
|
|
3720
3851
|
const template = [
|
|
3721
3852
|
`# ${projectName} \u2014 System Architecture`,
|
|
3722
3853
|
"",
|
|
@@ -3749,10 +3880,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3749
3880
|
}
|
|
3750
3881
|
}
|
|
3751
3882
|
async function ensureGitignoreExe(baseDir) {
|
|
3752
|
-
const gitignorePath =
|
|
3883
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
3753
3884
|
try {
|
|
3754
|
-
if (
|
|
3755
|
-
const content =
|
|
3885
|
+
if (existsSync12(gitignorePath)) {
|
|
3886
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
3756
3887
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3757
3888
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3758
3889
|
} else {
|
|
@@ -3783,58 +3914,42 @@ var init_tasks_crud = __esm({
|
|
|
3783
3914
|
});
|
|
3784
3915
|
|
|
3785
3916
|
// src/lib/tasks-review.ts
|
|
3786
|
-
import
|
|
3787
|
-
import { existsSync as
|
|
3917
|
+
import path14 from "path";
|
|
3918
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
3788
3919
|
async function countPendingReviews(sessionScope) {
|
|
3789
3920
|
const client = getClient();
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
args: [sessionScope]
|
|
3794
|
-
});
|
|
3795
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
3796
|
-
}
|
|
3921
|
+
const scope = strictSessionScopeFilter(
|
|
3922
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
3923
|
+
);
|
|
3797
3924
|
const result = await client.execute({
|
|
3798
|
-
sql:
|
|
3799
|
-
|
|
3925
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
3926
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
3927
|
+
args: [...scope.args]
|
|
3800
3928
|
});
|
|
3801
3929
|
return Number(result.rows[0]?.cnt) || 0;
|
|
3802
3930
|
}
|
|
3803
3931
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
3804
3932
|
const client = getClient();
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
3809
|
-
AND session_scope = ?`,
|
|
3810
|
-
args: [sinceIso, sessionScope]
|
|
3811
|
-
});
|
|
3812
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
3813
|
-
}
|
|
3933
|
+
const scope = strictSessionScopeFilter(
|
|
3934
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
3935
|
+
);
|
|
3814
3936
|
const result = await client.execute({
|
|
3815
3937
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
3816
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
3817
|
-
args: [sinceIso]
|
|
3938
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
3939
|
+
args: [sinceIso, ...scope.args]
|
|
3818
3940
|
});
|
|
3819
3941
|
return Number(result.rows[0]?.cnt) || 0;
|
|
3820
3942
|
}
|
|
3821
3943
|
async function listPendingReviews(limit, sessionScope) {
|
|
3822
3944
|
const client = getClient();
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
WHERE status = 'needs_review'
|
|
3827
|
-
AND session_scope = ?
|
|
3828
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
3829
|
-
args: [sessionScope, limit]
|
|
3830
|
-
});
|
|
3831
|
-
return result2.rows;
|
|
3832
|
-
}
|
|
3945
|
+
const scope = strictSessionScopeFilter(
|
|
3946
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
3947
|
+
);
|
|
3833
3948
|
const result = await client.execute({
|
|
3834
3949
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
3835
|
-
WHERE status = 'needs_review'
|
|
3950
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
3836
3951
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
3837
|
-
args: [limit]
|
|
3952
|
+
args: [...scope.args, limit]
|
|
3838
3953
|
});
|
|
3839
3954
|
return result.rows;
|
|
3840
3955
|
}
|
|
@@ -3846,7 +3961,7 @@ async function cleanupOrphanedReviews() {
|
|
|
3846
3961
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
3847
3962
|
AND assigned_by = 'system'
|
|
3848
3963
|
AND title LIKE 'Review:%'
|
|
3849
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
3964
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
3850
3965
|
args: [now]
|
|
3851
3966
|
});
|
|
3852
3967
|
const r1b = await client.execute({
|
|
@@ -3965,11 +4080,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3965
4080
|
);
|
|
3966
4081
|
}
|
|
3967
4082
|
try {
|
|
3968
|
-
const cacheDir =
|
|
3969
|
-
if (
|
|
4083
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4084
|
+
if (existsSync13(cacheDir)) {
|
|
3970
4085
|
for (const f of readdirSync2(cacheDir)) {
|
|
3971
4086
|
if (f.startsWith("review-notified-")) {
|
|
3972
|
-
unlinkSync4(
|
|
4087
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
3973
4088
|
}
|
|
3974
4089
|
}
|
|
3975
4090
|
}
|
|
@@ -3986,11 +4101,12 @@ var init_tasks_review = __esm({
|
|
|
3986
4101
|
init_tmux_routing();
|
|
3987
4102
|
init_session_key();
|
|
3988
4103
|
init_state_bus();
|
|
4104
|
+
init_task_scope();
|
|
3989
4105
|
}
|
|
3990
4106
|
});
|
|
3991
4107
|
|
|
3992
4108
|
// src/lib/tasks-chain.ts
|
|
3993
|
-
import
|
|
4109
|
+
import path15 from "path";
|
|
3994
4110
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3995
4111
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3996
4112
|
const client = getClient();
|
|
@@ -4007,7 +4123,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4007
4123
|
});
|
|
4008
4124
|
for (const ur of unblockedRows.rows) {
|
|
4009
4125
|
try {
|
|
4010
|
-
const ubFile =
|
|
4126
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4011
4127
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
4012
4128
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4013
4129
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4042,7 +4158,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
4042
4158
|
const scScope = sessionScopeFilter();
|
|
4043
4159
|
const remaining = await client.execute({
|
|
4044
4160
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4045
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
4161
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
4046
4162
|
args: [parentTaskId, ...scScope.args]
|
|
4047
4163
|
});
|
|
4048
4164
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -4076,7 +4192,7 @@ var init_tasks_chain = __esm({
|
|
|
4076
4192
|
|
|
4077
4193
|
// src/lib/project-name.ts
|
|
4078
4194
|
import { execSync as execSync6 } from "child_process";
|
|
4079
|
-
import
|
|
4195
|
+
import path16 from "path";
|
|
4080
4196
|
function getProjectName(cwd) {
|
|
4081
4197
|
const dir = cwd ?? process.cwd();
|
|
4082
4198
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4089,7 +4205,7 @@ function getProjectName(cwd) {
|
|
|
4089
4205
|
timeout: 2e3,
|
|
4090
4206
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4091
4207
|
}).trim();
|
|
4092
|
-
repoRoot =
|
|
4208
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4093
4209
|
} catch {
|
|
4094
4210
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4095
4211
|
cwd: dir,
|
|
@@ -4098,11 +4214,11 @@ function getProjectName(cwd) {
|
|
|
4098
4214
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4099
4215
|
}).trim();
|
|
4100
4216
|
}
|
|
4101
|
-
_cached2 =
|
|
4217
|
+
_cached2 = path16.basename(repoRoot);
|
|
4102
4218
|
_cachedCwd = dir;
|
|
4103
4219
|
return _cached2;
|
|
4104
4220
|
} catch {
|
|
4105
|
-
_cached2 =
|
|
4221
|
+
_cached2 = path16.basename(dir);
|
|
4106
4222
|
_cachedCwd = dir;
|
|
4107
4223
|
return _cached2;
|
|
4108
4224
|
}
|
|
@@ -4252,10 +4368,10 @@ __export(behaviors_exports, {
|
|
|
4252
4368
|
listBehaviorsByDomain: () => listBehaviorsByDomain,
|
|
4253
4369
|
storeBehavior: () => storeBehavior
|
|
4254
4370
|
});
|
|
4255
|
-
import
|
|
4371
|
+
import crypto5 from "crypto";
|
|
4256
4372
|
async function storeBehavior(opts) {
|
|
4257
4373
|
const client = getClient();
|
|
4258
|
-
const id =
|
|
4374
|
+
const id = crypto5.randomUUID();
|
|
4259
4375
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4260
4376
|
await client.execute({
|
|
4261
4377
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -4339,7 +4455,7 @@ __export(skill_learning_exports, {
|
|
|
4339
4455
|
storeTrajectory: () => storeTrajectory,
|
|
4340
4456
|
sweepTrajectories: () => sweepTrajectories
|
|
4341
4457
|
});
|
|
4342
|
-
import
|
|
4458
|
+
import crypto6 from "crypto";
|
|
4343
4459
|
async function extractTrajectory(taskId, agentId) {
|
|
4344
4460
|
const client = getClient();
|
|
4345
4461
|
const result = await client.execute({
|
|
@@ -4368,11 +4484,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
4368
4484
|
return signature;
|
|
4369
4485
|
}
|
|
4370
4486
|
function hashSignature(signature) {
|
|
4371
|
-
return
|
|
4487
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
4372
4488
|
}
|
|
4373
4489
|
async function storeTrajectory(opts) {
|
|
4374
4490
|
const client = getClient();
|
|
4375
|
-
const id =
|
|
4491
|
+
const id = crypto6.randomUUID();
|
|
4376
4492
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4377
4493
|
const signatureHash = hashSignature(opts.signature);
|
|
4378
4494
|
await client.execute({
|
|
@@ -4637,8 +4753,8 @@ __export(tasks_exports, {
|
|
|
4637
4753
|
updateTaskStatus: () => updateTaskStatus,
|
|
4638
4754
|
writeCheckpoint: () => writeCheckpoint
|
|
4639
4755
|
});
|
|
4640
|
-
import
|
|
4641
|
-
import { writeFileSync as
|
|
4756
|
+
import path17 from "path";
|
|
4757
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
4642
4758
|
async function createTask(input) {
|
|
4643
4759
|
const result = await createTaskCore(input);
|
|
4644
4760
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -4657,12 +4773,12 @@ async function updateTask(input) {
|
|
|
4657
4773
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
4658
4774
|
try {
|
|
4659
4775
|
const agent = String(row.assigned_to);
|
|
4660
|
-
const cacheDir =
|
|
4661
|
-
const cachePath =
|
|
4776
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
4777
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
4662
4778
|
if (input.status === "in_progress") {
|
|
4663
4779
|
mkdirSync5(cacheDir, { recursive: true });
|
|
4664
|
-
|
|
4665
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
4780
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
4781
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
4666
4782
|
try {
|
|
4667
4783
|
unlinkSync5(cachePath);
|
|
4668
4784
|
} catch {
|
|
@@ -4670,10 +4786,10 @@ async function updateTask(input) {
|
|
|
4670
4786
|
}
|
|
4671
4787
|
} catch {
|
|
4672
4788
|
}
|
|
4673
|
-
if (input.status === "done") {
|
|
4789
|
+
if (input.status === "done" || input.status === "closed") {
|
|
4674
4790
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
4675
4791
|
}
|
|
4676
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
4792
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4677
4793
|
try {
|
|
4678
4794
|
const client = getClient();
|
|
4679
4795
|
const taskTitle = String(row.title);
|
|
@@ -4689,7 +4805,7 @@ async function updateTask(input) {
|
|
|
4689
4805
|
if (!isCoordinatorName(assignedAgent)) {
|
|
4690
4806
|
try {
|
|
4691
4807
|
const draftClient = getClient();
|
|
4692
|
-
if (input.status === "done") {
|
|
4808
|
+
if (input.status === "done" || input.status === "closed") {
|
|
4693
4809
|
await draftClient.execute({
|
|
4694
4810
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
4695
4811
|
args: [assignedAgent]
|
|
@@ -4706,7 +4822,7 @@ async function updateTask(input) {
|
|
|
4706
4822
|
try {
|
|
4707
4823
|
const client = getClient();
|
|
4708
4824
|
const cascaded = await client.execute({
|
|
4709
|
-
sql: `UPDATE tasks SET status = '
|
|
4825
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
4710
4826
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
4711
4827
|
args: [now, taskId]
|
|
4712
4828
|
});
|
|
@@ -4719,14 +4835,14 @@ async function updateTask(input) {
|
|
|
4719
4835
|
} catch {
|
|
4720
4836
|
}
|
|
4721
4837
|
}
|
|
4722
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
4838
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
4723
4839
|
if (isTerminal) {
|
|
4724
4840
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
4725
4841
|
if (!isCoordinator) {
|
|
4726
4842
|
notifyTaskDone();
|
|
4727
4843
|
}
|
|
4728
4844
|
await markTaskNotificationsRead(taskFile);
|
|
4729
|
-
if (input.status === "done") {
|
|
4845
|
+
if (input.status === "done" || input.status === "closed") {
|
|
4730
4846
|
try {
|
|
4731
4847
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
4732
4848
|
} catch {
|
|
@@ -4746,7 +4862,7 @@ async function updateTask(input) {
|
|
|
4746
4862
|
}
|
|
4747
4863
|
}
|
|
4748
4864
|
}
|
|
4749
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
4865
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
4750
4866
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
4751
4867
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
4752
4868
|
taskId,
|
|
@@ -5118,6 +5234,7 @@ __export(tmux_routing_exports, {
|
|
|
5118
5234
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
5119
5235
|
isExeSession: () => isExeSession,
|
|
5120
5236
|
isSessionBusy: () => isSessionBusy,
|
|
5237
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
5121
5238
|
notifyParentExe: () => notifyParentExe,
|
|
5122
5239
|
parseParentExe: () => parseParentExe,
|
|
5123
5240
|
registerParentExe: () => registerParentExe,
|
|
@@ -5128,13 +5245,13 @@ __export(tmux_routing_exports, {
|
|
|
5128
5245
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5129
5246
|
});
|
|
5130
5247
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5131
|
-
import { readFileSync as
|
|
5132
|
-
import
|
|
5133
|
-
import
|
|
5248
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5249
|
+
import path18 from "path";
|
|
5250
|
+
import os11 from "os";
|
|
5134
5251
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5135
5252
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5136
5253
|
function spawnLockPath(sessionName) {
|
|
5137
|
-
return
|
|
5254
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5138
5255
|
}
|
|
5139
5256
|
function isProcessAlive(pid) {
|
|
5140
5257
|
try {
|
|
@@ -5145,13 +5262,13 @@ function isProcessAlive(pid) {
|
|
|
5145
5262
|
}
|
|
5146
5263
|
}
|
|
5147
5264
|
function acquireSpawnLock2(sessionName) {
|
|
5148
|
-
if (!
|
|
5265
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5149
5266
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
5150
5267
|
}
|
|
5151
5268
|
const lockFile = spawnLockPath(sessionName);
|
|
5152
|
-
if (
|
|
5269
|
+
if (existsSync14(lockFile)) {
|
|
5153
5270
|
try {
|
|
5154
|
-
const lock = JSON.parse(
|
|
5271
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
5155
5272
|
const age = Date.now() - lock.timestamp;
|
|
5156
5273
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5157
5274
|
return false;
|
|
@@ -5159,7 +5276,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5159
5276
|
} catch {
|
|
5160
5277
|
}
|
|
5161
5278
|
}
|
|
5162
|
-
|
|
5279
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5163
5280
|
return true;
|
|
5164
5281
|
}
|
|
5165
5282
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5171,13 +5288,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5171
5288
|
function resolveBehaviorsExporterScript() {
|
|
5172
5289
|
try {
|
|
5173
5290
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5174
|
-
const scriptPath =
|
|
5175
|
-
|
|
5291
|
+
const scriptPath = path18.join(
|
|
5292
|
+
path18.dirname(thisFile),
|
|
5176
5293
|
"..",
|
|
5177
5294
|
"bin",
|
|
5178
5295
|
"exe-export-behaviors.js"
|
|
5179
5296
|
);
|
|
5180
|
-
return
|
|
5297
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
5181
5298
|
} catch {
|
|
5182
5299
|
return null;
|
|
5183
5300
|
}
|
|
@@ -5243,12 +5360,12 @@ function extractRootExe(name) {
|
|
|
5243
5360
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5244
5361
|
}
|
|
5245
5362
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5246
|
-
if (!
|
|
5363
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5247
5364
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5248
5365
|
}
|
|
5249
5366
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5250
|
-
const filePath =
|
|
5251
|
-
|
|
5367
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5368
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5252
5369
|
parentExe: rootExe,
|
|
5253
5370
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5254
5371
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5256,7 +5373,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5256
5373
|
}
|
|
5257
5374
|
function getParentExe(sessionKey) {
|
|
5258
5375
|
try {
|
|
5259
|
-
const data = JSON.parse(
|
|
5376
|
+
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5260
5377
|
return data.parentExe || null;
|
|
5261
5378
|
} catch {
|
|
5262
5379
|
return null;
|
|
@@ -5264,8 +5381,8 @@ function getParentExe(sessionKey) {
|
|
|
5264
5381
|
}
|
|
5265
5382
|
function getDispatchedBy(sessionKey) {
|
|
5266
5383
|
try {
|
|
5267
|
-
const data = JSON.parse(
|
|
5268
|
-
|
|
5384
|
+
const data = JSON.parse(readFileSync12(
|
|
5385
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5269
5386
|
"utf8"
|
|
5270
5387
|
));
|
|
5271
5388
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5335,8 +5452,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5335
5452
|
}
|
|
5336
5453
|
function readDebounceState() {
|
|
5337
5454
|
try {
|
|
5338
|
-
if (!
|
|
5339
|
-
const raw = JSON.parse(
|
|
5455
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5456
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
5340
5457
|
const state = {};
|
|
5341
5458
|
for (const [key, val] of Object.entries(raw)) {
|
|
5342
5459
|
if (typeof val === "number") {
|
|
@@ -5352,8 +5469,8 @@ function readDebounceState() {
|
|
|
5352
5469
|
}
|
|
5353
5470
|
function writeDebounceState(state) {
|
|
5354
5471
|
try {
|
|
5355
|
-
if (!
|
|
5356
|
-
|
|
5472
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5473
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5357
5474
|
} catch {
|
|
5358
5475
|
}
|
|
5359
5476
|
}
|
|
@@ -5451,8 +5568,8 @@ function sendIntercom(targetSession) {
|
|
|
5451
5568
|
try {
|
|
5452
5569
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5453
5570
|
const agent = baseAgentName(rawAgent);
|
|
5454
|
-
const markerPath =
|
|
5455
|
-
if (
|
|
5571
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5572
|
+
if (existsSync14(markerPath)) {
|
|
5456
5573
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
5457
5574
|
return "debounced";
|
|
5458
5575
|
}
|
|
@@ -5461,8 +5578,8 @@ function sendIntercom(targetSession) {
|
|
|
5461
5578
|
try {
|
|
5462
5579
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5463
5580
|
const agent = baseAgentName(rawAgent);
|
|
5464
|
-
const taskDir =
|
|
5465
|
-
if (
|
|
5581
|
+
const taskDir = path18.join(process.cwd(), "exe", agent);
|
|
5582
|
+
if (existsSync14(taskDir)) {
|
|
5466
5583
|
const files = readdirSync3(taskDir).filter(
|
|
5467
5584
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
5468
5585
|
);
|
|
@@ -5522,6 +5639,21 @@ function notifyParentExe(sessionKey) {
|
|
|
5522
5639
|
}
|
|
5523
5640
|
return true;
|
|
5524
5641
|
}
|
|
5642
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
5643
|
+
const transport = getTransport();
|
|
5644
|
+
try {
|
|
5645
|
+
const sessions = transport.listSessions();
|
|
5646
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
5647
|
+
execSync7(
|
|
5648
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
5649
|
+
{ timeout: 3e3 }
|
|
5650
|
+
);
|
|
5651
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
5652
|
+
return true;
|
|
5653
|
+
} catch {
|
|
5654
|
+
return false;
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5525
5657
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
5526
5658
|
if (isCoordinatorName(employeeName)) {
|
|
5527
5659
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -5595,26 +5727,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5595
5727
|
const transport = getTransport();
|
|
5596
5728
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5597
5729
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5598
|
-
const logDir =
|
|
5599
|
-
const logFile =
|
|
5600
|
-
if (!
|
|
5730
|
+
const logDir = path18.join(os11.homedir(), ".exe-os", "session-logs");
|
|
5731
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5732
|
+
if (!existsSync14(logDir)) {
|
|
5601
5733
|
mkdirSync6(logDir, { recursive: true });
|
|
5602
5734
|
}
|
|
5603
5735
|
transport.kill(sessionName);
|
|
5604
5736
|
let cleanupSuffix = "";
|
|
5605
5737
|
try {
|
|
5606
5738
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5607
|
-
const cleanupScript =
|
|
5608
|
-
if (
|
|
5739
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5740
|
+
if (existsSync14(cleanupScript)) {
|
|
5609
5741
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5610
5742
|
}
|
|
5611
5743
|
} catch {
|
|
5612
5744
|
}
|
|
5613
5745
|
try {
|
|
5614
|
-
const claudeJsonPath =
|
|
5746
|
+
const claudeJsonPath = path18.join(os11.homedir(), ".claude.json");
|
|
5615
5747
|
let claudeJson = {};
|
|
5616
5748
|
try {
|
|
5617
|
-
claudeJson = JSON.parse(
|
|
5749
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
5618
5750
|
} catch {
|
|
5619
5751
|
}
|
|
5620
5752
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5622,17 +5754,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5622
5754
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5623
5755
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5624
5756
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5625
|
-
|
|
5757
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5626
5758
|
} catch {
|
|
5627
5759
|
}
|
|
5628
5760
|
try {
|
|
5629
|
-
const settingsDir =
|
|
5761
|
+
const settingsDir = path18.join(os11.homedir(), ".claude", "projects");
|
|
5630
5762
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5631
|
-
const projSettingsDir =
|
|
5632
|
-
const settingsPath =
|
|
5763
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
5764
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
5633
5765
|
let settings = {};
|
|
5634
5766
|
try {
|
|
5635
|
-
settings = JSON.parse(
|
|
5767
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
5636
5768
|
} catch {
|
|
5637
5769
|
}
|
|
5638
5770
|
const perms = settings.permissions ?? {};
|
|
@@ -5661,7 +5793,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5661
5793
|
perms.allow = allow;
|
|
5662
5794
|
settings.permissions = perms;
|
|
5663
5795
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
5664
|
-
|
|
5796
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5665
5797
|
}
|
|
5666
5798
|
} catch {
|
|
5667
5799
|
}
|
|
@@ -5676,8 +5808,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5676
5808
|
let behaviorsFlag = "";
|
|
5677
5809
|
let legacyFallbackWarned = false;
|
|
5678
5810
|
if (!useExeAgent && !useBinSymlink) {
|
|
5679
|
-
const identityPath =
|
|
5680
|
-
|
|
5811
|
+
const identityPath = path18.join(
|
|
5812
|
+
os11.homedir(),
|
|
5681
5813
|
".exe-os",
|
|
5682
5814
|
"identity",
|
|
5683
5815
|
`${employeeName}.md`
|
|
@@ -5686,13 +5818,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5686
5818
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5687
5819
|
if (hasAgentFlag) {
|
|
5688
5820
|
identityFlag = ` --agent ${employeeName}`;
|
|
5689
|
-
} else if (
|
|
5821
|
+
} else if (existsSync14(identityPath)) {
|
|
5690
5822
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5691
5823
|
legacyFallbackWarned = true;
|
|
5692
5824
|
}
|
|
5693
5825
|
const behaviorsFile = exportBehaviorsSync(
|
|
5694
5826
|
employeeName,
|
|
5695
|
-
|
|
5827
|
+
path18.basename(spawnCwd),
|
|
5696
5828
|
sessionName
|
|
5697
5829
|
);
|
|
5698
5830
|
if (behaviorsFile) {
|
|
@@ -5707,16 +5839,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5707
5839
|
}
|
|
5708
5840
|
let sessionContextFlag = "";
|
|
5709
5841
|
try {
|
|
5710
|
-
const ctxDir =
|
|
5842
|
+
const ctxDir = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
5711
5843
|
mkdirSync6(ctxDir, { recursive: true });
|
|
5712
|
-
const ctxFile =
|
|
5844
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
5713
5845
|
const ctxContent = [
|
|
5714
5846
|
`## Session Context`,
|
|
5715
5847
|
`You are running in tmux session: ${sessionName}.`,
|
|
5716
5848
|
`Your parent coordinator session is ${exeSession}.`,
|
|
5717
5849
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
5718
5850
|
].join("\n");
|
|
5719
|
-
|
|
5851
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
5720
5852
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
5721
5853
|
} catch {
|
|
5722
5854
|
}
|
|
@@ -5793,8 +5925,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5793
5925
|
transport.pipeLog(sessionName, logFile);
|
|
5794
5926
|
try {
|
|
5795
5927
|
const mySession = getMySession();
|
|
5796
|
-
const dispatchInfo =
|
|
5797
|
-
|
|
5928
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5929
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
5798
5930
|
dispatchedBy: mySession,
|
|
5799
5931
|
rootExe: exeSession,
|
|
5800
5932
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -5868,15 +6000,15 @@ var init_tmux_routing = __esm({
|
|
|
5868
6000
|
init_intercom_queue();
|
|
5869
6001
|
init_plan_limits();
|
|
5870
6002
|
init_employees();
|
|
5871
|
-
SPAWN_LOCK_DIR =
|
|
5872
|
-
SESSION_CACHE =
|
|
6003
|
+
SPAWN_LOCK_DIR = path18.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
6004
|
+
SESSION_CACHE = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
5873
6005
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
5874
6006
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
5875
6007
|
VERIFY_PANE_LINES = 200;
|
|
5876
6008
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
5877
6009
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
5878
|
-
INTERCOM_LOG2 =
|
|
5879
|
-
DEBOUNCE_FILE =
|
|
6010
|
+
INTERCOM_LOG2 = path18.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
6011
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
5880
6012
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
5881
6013
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
5882
6014
|
}
|
|
@@ -5893,14 +6025,14 @@ var init_memory = __esm({
|
|
|
5893
6025
|
|
|
5894
6026
|
// src/lib/keychain.ts
|
|
5895
6027
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5896
|
-
import { existsSync as
|
|
5897
|
-
import
|
|
5898
|
-
import
|
|
6028
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6029
|
+
import path19 from "path";
|
|
6030
|
+
import os12 from "os";
|
|
5899
6031
|
function getKeyDir() {
|
|
5900
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
6032
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os12.homedir(), ".exe-os");
|
|
5901
6033
|
}
|
|
5902
6034
|
function getKeyPath() {
|
|
5903
|
-
return
|
|
6035
|
+
return path19.join(getKeyDir(), "master.key");
|
|
5904
6036
|
}
|
|
5905
6037
|
async function tryKeytar() {
|
|
5906
6038
|
try {
|
|
@@ -5921,9 +6053,9 @@ async function getMasterKey() {
|
|
|
5921
6053
|
}
|
|
5922
6054
|
}
|
|
5923
6055
|
const keyPath = getKeyPath();
|
|
5924
|
-
if (!
|
|
6056
|
+
if (!existsSync15(keyPath)) {
|
|
5925
6057
|
process.stderr.write(
|
|
5926
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
6058
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os12.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5927
6059
|
`
|
|
5928
6060
|
);
|
|
5929
6061
|
return null;
|
|
@@ -5953,6 +6085,7 @@ var shard_manager_exports = {};
|
|
|
5953
6085
|
__export(shard_manager_exports, {
|
|
5954
6086
|
disposeShards: () => disposeShards,
|
|
5955
6087
|
ensureShardSchema: () => ensureShardSchema,
|
|
6088
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
5956
6089
|
getReadyShardClient: () => getReadyShardClient,
|
|
5957
6090
|
getShardClient: () => getShardClient,
|
|
5958
6091
|
getShardsDir: () => getShardsDir,
|
|
@@ -5961,15 +6094,18 @@ __export(shard_manager_exports, {
|
|
|
5961
6094
|
listShards: () => listShards,
|
|
5962
6095
|
shardExists: () => shardExists
|
|
5963
6096
|
});
|
|
5964
|
-
import
|
|
5965
|
-
import { existsSync as
|
|
6097
|
+
import path20 from "path";
|
|
6098
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
5966
6099
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5967
6100
|
function initShardManager(encryptionKey) {
|
|
5968
6101
|
_encryptionKey = encryptionKey;
|
|
5969
|
-
if (!
|
|
6102
|
+
if (!existsSync16(SHARDS_DIR)) {
|
|
5970
6103
|
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
5971
6104
|
}
|
|
5972
6105
|
_shardingEnabled = true;
|
|
6106
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
6107
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
6108
|
+
_evictionTimer.unref();
|
|
5973
6109
|
}
|
|
5974
6110
|
function isShardingEnabled() {
|
|
5975
6111
|
return _shardingEnabled;
|
|
@@ -5986,21 +6122,28 @@ function getShardClient(projectName) {
|
|
|
5986
6122
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
5987
6123
|
}
|
|
5988
6124
|
const cached = _shards.get(safeName);
|
|
5989
|
-
if (cached)
|
|
5990
|
-
|
|
6125
|
+
if (cached) {
|
|
6126
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6127
|
+
return cached;
|
|
6128
|
+
}
|
|
6129
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
6130
|
+
evictLRU();
|
|
6131
|
+
}
|
|
6132
|
+
const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
|
|
5991
6133
|
const client = createClient2({
|
|
5992
6134
|
url: `file:${dbPath}`,
|
|
5993
6135
|
encryptionKey: _encryptionKey
|
|
5994
6136
|
});
|
|
5995
6137
|
_shards.set(safeName, client);
|
|
6138
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
5996
6139
|
return client;
|
|
5997
6140
|
}
|
|
5998
6141
|
function shardExists(projectName) {
|
|
5999
6142
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6000
|
-
return
|
|
6143
|
+
return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
|
|
6001
6144
|
}
|
|
6002
6145
|
function listShards() {
|
|
6003
|
-
if (!
|
|
6146
|
+
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6004
6147
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6005
6148
|
}
|
|
6006
6149
|
async function ensureShardSchema(client) {
|
|
@@ -6052,6 +6195,8 @@ async function ensureShardSchema(client) {
|
|
|
6052
6195
|
for (const col of [
|
|
6053
6196
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
6054
6197
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
6198
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
6199
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
6055
6200
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
6056
6201
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
6057
6202
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -6189,21 +6334,69 @@ async function getReadyShardClient(projectName) {
|
|
|
6189
6334
|
await ensureShardSchema(client);
|
|
6190
6335
|
return client;
|
|
6191
6336
|
}
|
|
6337
|
+
function evictLRU() {
|
|
6338
|
+
let oldest = null;
|
|
6339
|
+
let oldestTime = Infinity;
|
|
6340
|
+
for (const [name, time] of _shardLastAccess) {
|
|
6341
|
+
if (time < oldestTime) {
|
|
6342
|
+
oldestTime = time;
|
|
6343
|
+
oldest = name;
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
if (oldest) {
|
|
6347
|
+
const client = _shards.get(oldest);
|
|
6348
|
+
if (client) {
|
|
6349
|
+
client.close();
|
|
6350
|
+
}
|
|
6351
|
+
_shards.delete(oldest);
|
|
6352
|
+
_shardLastAccess.delete(oldest);
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
function evictIdleShards() {
|
|
6356
|
+
const now = Date.now();
|
|
6357
|
+
const toEvict = [];
|
|
6358
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
6359
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
6360
|
+
toEvict.push(name);
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
for (const name of toEvict) {
|
|
6364
|
+
const client = _shards.get(name);
|
|
6365
|
+
if (client) {
|
|
6366
|
+
client.close();
|
|
6367
|
+
}
|
|
6368
|
+
_shards.delete(name);
|
|
6369
|
+
_shardLastAccess.delete(name);
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
function getOpenShardCount() {
|
|
6373
|
+
return _shards.size;
|
|
6374
|
+
}
|
|
6192
6375
|
function disposeShards() {
|
|
6376
|
+
if (_evictionTimer) {
|
|
6377
|
+
clearInterval(_evictionTimer);
|
|
6378
|
+
_evictionTimer = null;
|
|
6379
|
+
}
|
|
6193
6380
|
for (const [, client] of _shards) {
|
|
6194
6381
|
client.close();
|
|
6195
6382
|
}
|
|
6196
6383
|
_shards.clear();
|
|
6384
|
+
_shardLastAccess.clear();
|
|
6197
6385
|
_shardingEnabled = false;
|
|
6198
6386
|
_encryptionKey = null;
|
|
6199
6387
|
}
|
|
6200
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
6388
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
6201
6389
|
var init_shard_manager = __esm({
|
|
6202
6390
|
"src/lib/shard-manager.ts"() {
|
|
6203
6391
|
"use strict";
|
|
6204
6392
|
init_config();
|
|
6205
|
-
SHARDS_DIR =
|
|
6393
|
+
SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
|
|
6394
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
6395
|
+
MAX_OPEN_SHARDS = 10;
|
|
6396
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
6206
6397
|
_shards = /* @__PURE__ */ new Map();
|
|
6398
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
6399
|
+
_evictionTimer = null;
|
|
6207
6400
|
_encryptionKey = null;
|
|
6208
6401
|
_shardingEnabled = false;
|
|
6209
6402
|
}
|
|
@@ -6999,8 +7192,8 @@ function findContainingChunk(filePath, snippet) {
|
|
|
6999
7192
|
try {
|
|
7000
7193
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
7001
7194
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
7002
|
-
const { readFileSync:
|
|
7003
|
-
const source =
|
|
7195
|
+
const { readFileSync: readFileSync13 } = __require("fs");
|
|
7196
|
+
const source = readFileSync13(filePath, "utf8");
|
|
7004
7197
|
const lines = source.split("\n");
|
|
7005
7198
|
const lowerSnippet = snippet.toLowerCase().slice(0, 80);
|
|
7006
7199
|
let matchLine = -1;
|
|
@@ -7066,9 +7259,9 @@ function extractBash(input, response) {
|
|
|
7066
7259
|
}
|
|
7067
7260
|
function extractGrep(input, response) {
|
|
7068
7261
|
const pattern = String(input.pattern ?? "");
|
|
7069
|
-
const
|
|
7262
|
+
const path21 = input.path ? String(input.path) : "";
|
|
7070
7263
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
7071
|
-
return `Searched for "${pattern}"${
|
|
7264
|
+
return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
|
|
7072
7265
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
7073
7266
|
}
|
|
7074
7267
|
function extractGlob(input, response) {
|
|
@@ -7171,7 +7364,7 @@ __export(error_detector_exports, {
|
|
|
7171
7364
|
errorFingerprint: () => errorFingerprint,
|
|
7172
7365
|
isExeOsError: () => isExeOsError
|
|
7173
7366
|
});
|
|
7174
|
-
import
|
|
7367
|
+
import crypto7 from "crypto";
|
|
7175
7368
|
function isRealStderr(stderr) {
|
|
7176
7369
|
const lines = stderr.trim().split("\n");
|
|
7177
7370
|
const meaningful = lines.filter(
|
|
@@ -7242,7 +7435,7 @@ function classifyError(errorText) {
|
|
|
7242
7435
|
}
|
|
7243
7436
|
function errorFingerprint(toolName, errorText) {
|
|
7244
7437
|
const normalized = errorText.replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z/g, "TIMESTAMP").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "UUID").replace(/\/Users\/[^\s]+/g, "PATH").replace(/:\d+:\d+/g, ":LINE:COL").slice(0, 200);
|
|
7245
|
-
return
|
|
7438
|
+
return crypto7.createHash("sha256").update(`${toolName}:${normalized}`).digest("hex").slice(0, 16);
|
|
7246
7439
|
}
|
|
7247
7440
|
var ERROR_PATTERNS, FILE_CONTENT_TOOLS, STDERR_IGNORE_PATTERNS, USER_ERROR_PATTERNS, SYSTEM_BUG_PATTERNS;
|
|
7248
7441
|
var init_error_detector = __esm({
|