@askexenow/exe-os 0.9.8 → 0.9.10
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 +1411 -953
- 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 +913 -543
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +418 -262
- 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 +793 -485
- 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 +566 -357
- 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 +530 -319
- 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 +547 -336
- 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 +649 -417
- package/dist/hooks/bug-report-worker.js +486 -316
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +528 -317
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3442 -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 +534 -323
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +614 -382
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +569 -347
- 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 +664 -431
- 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 +1049 -680
- 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 +422 -357
- package/dist/lib/tmux-routing.js +314 -248
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1408 -672
- 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 +448 -371
- 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 +1983 -315
- package/dist/runtime/index.js +567 -355
- package/dist/tui/App.js +887 -531
- package/package.json +4 -4
|
@@ -90,6 +90,44 @@ var init_db_retry = __esm({
|
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
// src/lib/secure-files.ts
|
|
94
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
95
|
+
import { chmod, mkdir } from "fs/promises";
|
|
96
|
+
async function ensurePrivateDir(dirPath) {
|
|
97
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
98
|
+
try {
|
|
99
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function ensurePrivateDirSync(dirPath) {
|
|
104
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
105
|
+
try {
|
|
106
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function enforcePrivateFile(filePath) {
|
|
111
|
+
try {
|
|
112
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function enforcePrivateFileSync(filePath) {
|
|
117
|
+
try {
|
|
118
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
123
|
+
var init_secure_files = __esm({
|
|
124
|
+
"src/lib/secure-files.ts"() {
|
|
125
|
+
"use strict";
|
|
126
|
+
PRIVATE_DIR_MODE = 448;
|
|
127
|
+
PRIVATE_FILE_MODE = 384;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
93
131
|
// src/lib/config.ts
|
|
94
132
|
var config_exports = {};
|
|
95
133
|
__export(config_exports, {
|
|
@@ -106,8 +144,8 @@ __export(config_exports, {
|
|
|
106
144
|
migrateConfig: () => migrateConfig,
|
|
107
145
|
saveConfig: () => saveConfig
|
|
108
146
|
});
|
|
109
|
-
import { readFile, writeFile
|
|
110
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
147
|
+
import { readFile, writeFile } from "fs/promises";
|
|
148
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
111
149
|
import path from "path";
|
|
112
150
|
import os from "os";
|
|
113
151
|
function resolveDataDir() {
|
|
@@ -115,7 +153,7 @@ function resolveDataDir() {
|
|
|
115
153
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
116
154
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
117
155
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
118
|
-
if (!
|
|
156
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
119
157
|
try {
|
|
120
158
|
renameSync(legacyDir, newDir);
|
|
121
159
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -178,9 +216,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
178
216
|
}
|
|
179
217
|
async function loadConfig() {
|
|
180
218
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
181
|
-
await
|
|
219
|
+
await ensurePrivateDir(dir);
|
|
182
220
|
const configPath = path.join(dir, "config.json");
|
|
183
|
-
if (!
|
|
221
|
+
if (!existsSync2(configPath)) {
|
|
184
222
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
185
223
|
}
|
|
186
224
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -193,6 +231,7 @@ async function loadConfig() {
|
|
|
193
231
|
`);
|
|
194
232
|
try {
|
|
195
233
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
234
|
+
await enforcePrivateFile(configPath);
|
|
196
235
|
} catch {
|
|
197
236
|
}
|
|
198
237
|
}
|
|
@@ -211,7 +250,7 @@ async function loadConfig() {
|
|
|
211
250
|
function loadConfigSync() {
|
|
212
251
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
213
252
|
const configPath = path.join(dir, "config.json");
|
|
214
|
-
if (!
|
|
253
|
+
if (!existsSync2(configPath)) {
|
|
215
254
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
216
255
|
}
|
|
217
256
|
try {
|
|
@@ -229,12 +268,10 @@ function loadConfigSync() {
|
|
|
229
268
|
}
|
|
230
269
|
async function saveConfig(config) {
|
|
231
270
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
232
|
-
await
|
|
271
|
+
await ensurePrivateDir(dir);
|
|
233
272
|
const configPath = path.join(dir, "config.json");
|
|
234
273
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
235
|
-
|
|
236
|
-
await chmod(configPath, 384);
|
|
237
|
-
}
|
|
274
|
+
await enforcePrivateFile(configPath);
|
|
238
275
|
}
|
|
239
276
|
async function loadConfigFrom(configPath) {
|
|
240
277
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -254,6 +291,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
254
291
|
var init_config = __esm({
|
|
255
292
|
"src/lib/config.ts"() {
|
|
256
293
|
"use strict";
|
|
294
|
+
init_secure_files();
|
|
257
295
|
EXE_AI_DIR = resolveDataDir();
|
|
258
296
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
259
297
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -332,7 +370,7 @@ var init_config = __esm({
|
|
|
332
370
|
|
|
333
371
|
// src/lib/employees.ts
|
|
334
372
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
335
|
-
import { existsSync as
|
|
373
|
+
import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
336
374
|
import { execSync } from "child_process";
|
|
337
375
|
import path2 from "path";
|
|
338
376
|
import os2 from "os";
|
|
@@ -353,7 +391,7 @@ function isCoordinatorName(agentName2, employees = loadEmployeesSync()) {
|
|
|
353
391
|
return agentName2.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
354
392
|
}
|
|
355
393
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
356
|
-
if (!
|
|
394
|
+
if (!existsSync3(employeesPath)) {
|
|
357
395
|
return [];
|
|
358
396
|
}
|
|
359
397
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -364,7 +402,7 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
364
402
|
}
|
|
365
403
|
}
|
|
366
404
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
367
|
-
if (!
|
|
405
|
+
if (!existsSync3(employeesPath)) return [];
|
|
368
406
|
try {
|
|
369
407
|
return JSON.parse(readFileSync2(employeesPath, "utf-8"));
|
|
370
408
|
} catch {
|
|
@@ -985,13 +1023,50 @@ var init_database_adapter = __esm({
|
|
|
985
1023
|
}
|
|
986
1024
|
});
|
|
987
1025
|
|
|
1026
|
+
// src/lib/daemon-auth.ts
|
|
1027
|
+
import crypto from "crypto";
|
|
1028
|
+
import path4 from "path";
|
|
1029
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1030
|
+
function normalizeToken(token) {
|
|
1031
|
+
if (!token) return null;
|
|
1032
|
+
const trimmed = token.trim();
|
|
1033
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1034
|
+
}
|
|
1035
|
+
function readDaemonToken() {
|
|
1036
|
+
try {
|
|
1037
|
+
if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
|
|
1038
|
+
return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
|
|
1039
|
+
} catch {
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
function ensureDaemonToken(seed) {
|
|
1044
|
+
const existing = readDaemonToken();
|
|
1045
|
+
if (existing) return existing;
|
|
1046
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1047
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1048
|
+
writeFileSync2(DAEMON_TOKEN_PATH, `${token}
|
|
1049
|
+
`, "utf8");
|
|
1050
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1051
|
+
return token;
|
|
1052
|
+
}
|
|
1053
|
+
var DAEMON_TOKEN_PATH;
|
|
1054
|
+
var init_daemon_auth = __esm({
|
|
1055
|
+
"src/lib/daemon-auth.ts"() {
|
|
1056
|
+
"use strict";
|
|
1057
|
+
init_config();
|
|
1058
|
+
init_secure_files();
|
|
1059
|
+
DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
|
|
988
1063
|
// src/lib/exe-daemon-client.ts
|
|
989
1064
|
import net from "net";
|
|
990
1065
|
import os4 from "os";
|
|
991
1066
|
import { spawn } from "child_process";
|
|
992
1067
|
import { randomUUID } from "crypto";
|
|
993
|
-
import { existsSync as
|
|
994
|
-
import
|
|
1068
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
|
|
1069
|
+
import path5 from "path";
|
|
995
1070
|
import { fileURLToPath } from "url";
|
|
996
1071
|
function handleData(chunk) {
|
|
997
1072
|
_buffer += chunk.toString();
|
|
@@ -1019,9 +1094,9 @@ function handleData(chunk) {
|
|
|
1019
1094
|
}
|
|
1020
1095
|
}
|
|
1021
1096
|
function cleanupStaleFiles() {
|
|
1022
|
-
if (
|
|
1097
|
+
if (existsSync5(PID_PATH)) {
|
|
1023
1098
|
try {
|
|
1024
|
-
const pid = parseInt(
|
|
1099
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1025
1100
|
if (pid > 0) {
|
|
1026
1101
|
try {
|
|
1027
1102
|
process.kill(pid, 0);
|
|
@@ -1042,11 +1117,11 @@ function cleanupStaleFiles() {
|
|
|
1042
1117
|
}
|
|
1043
1118
|
}
|
|
1044
1119
|
function findPackageRoot() {
|
|
1045
|
-
let dir =
|
|
1046
|
-
const { root } =
|
|
1120
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1121
|
+
const { root } = path5.parse(dir);
|
|
1047
1122
|
while (dir !== root) {
|
|
1048
|
-
if (
|
|
1049
|
-
dir =
|
|
1123
|
+
if (existsSync5(path5.join(dir, "package.json"))) return dir;
|
|
1124
|
+
dir = path5.dirname(dir);
|
|
1050
1125
|
}
|
|
1051
1126
|
return null;
|
|
1052
1127
|
}
|
|
@@ -1072,16 +1147,17 @@ function spawnDaemon() {
|
|
|
1072
1147
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1073
1148
|
return;
|
|
1074
1149
|
}
|
|
1075
|
-
const daemonPath =
|
|
1076
|
-
if (!
|
|
1150
|
+
const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1151
|
+
if (!existsSync5(daemonPath)) {
|
|
1077
1152
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1078
1153
|
`);
|
|
1079
1154
|
return;
|
|
1080
1155
|
}
|
|
1081
1156
|
const resolvedPath = daemonPath;
|
|
1157
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1082
1158
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1083
1159
|
`);
|
|
1084
|
-
const logPath =
|
|
1160
|
+
const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
|
|
1085
1161
|
let stderrFd = "ignore";
|
|
1086
1162
|
try {
|
|
1087
1163
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1099,7 +1175,8 @@ function spawnDaemon() {
|
|
|
1099
1175
|
TMUX_PANE: void 0,
|
|
1100
1176
|
// Prevents resolveExeSession() from scoping to one session
|
|
1101
1177
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1102
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1178
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1179
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1103
1180
|
}
|
|
1104
1181
|
});
|
|
1105
1182
|
child.unref();
|
|
@@ -1209,13 +1286,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1209
1286
|
return;
|
|
1210
1287
|
}
|
|
1211
1288
|
const id = randomUUID();
|
|
1289
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1212
1290
|
const timer = setTimeout(() => {
|
|
1213
1291
|
_pending.delete(id);
|
|
1214
1292
|
resolve({ error: "Request timeout" });
|
|
1215
1293
|
}, timeoutMs);
|
|
1216
1294
|
_pending.set(id, { resolve, timer });
|
|
1217
1295
|
try {
|
|
1218
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
1296
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1219
1297
|
} catch {
|
|
1220
1298
|
clearTimeout(timer);
|
|
1221
1299
|
_pending.delete(id);
|
|
@@ -1244,9 +1322,9 @@ function killAndRespawnDaemon() {
|
|
|
1244
1322
|
}
|
|
1245
1323
|
try {
|
|
1246
1324
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1247
|
-
if (
|
|
1325
|
+
if (existsSync5(PID_PATH)) {
|
|
1248
1326
|
try {
|
|
1249
|
-
const pid = parseInt(
|
|
1327
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1250
1328
|
if (pid > 0) {
|
|
1251
1329
|
try {
|
|
1252
1330
|
process.kill(pid, "SIGKILL");
|
|
@@ -1366,17 +1444,19 @@ function disconnectClient() {
|
|
|
1366
1444
|
function isClientConnected() {
|
|
1367
1445
|
return _connected;
|
|
1368
1446
|
}
|
|
1369
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
1447
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
1370
1448
|
var init_exe_daemon_client = __esm({
|
|
1371
1449
|
"src/lib/exe-daemon-client.ts"() {
|
|
1372
1450
|
"use strict";
|
|
1373
1451
|
init_config();
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1452
|
+
init_daemon_auth();
|
|
1453
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
|
|
1454
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
|
|
1455
|
+
SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1377
1456
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1378
1457
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1379
1458
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
1459
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1380
1460
|
_socket = null;
|
|
1381
1461
|
_connected = false;
|
|
1382
1462
|
_buffer = "";
|
|
@@ -1961,6 +2041,7 @@ async function ensureSchema() {
|
|
|
1961
2041
|
project TEXT NOT NULL,
|
|
1962
2042
|
summary TEXT NOT NULL,
|
|
1963
2043
|
task_file TEXT,
|
|
2044
|
+
session_scope TEXT,
|
|
1964
2045
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1965
2046
|
created_at TEXT NOT NULL
|
|
1966
2047
|
);
|
|
@@ -1969,7 +2050,7 @@ async function ensureSchema() {
|
|
|
1969
2050
|
ON notifications(read);
|
|
1970
2051
|
|
|
1971
2052
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1972
|
-
ON notifications(agent_id);
|
|
2053
|
+
ON notifications(agent_id, session_scope);
|
|
1973
2054
|
|
|
1974
2055
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1975
2056
|
ON notifications(task_file);
|
|
@@ -2007,6 +2088,7 @@ async function ensureSchema() {
|
|
|
2007
2088
|
target_agent TEXT NOT NULL,
|
|
2008
2089
|
target_project TEXT,
|
|
2009
2090
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2091
|
+
session_scope TEXT,
|
|
2010
2092
|
content TEXT NOT NULL,
|
|
2011
2093
|
priority TEXT DEFAULT 'normal',
|
|
2012
2094
|
status TEXT DEFAULT 'pending',
|
|
@@ -2020,10 +2102,31 @@ async function ensureSchema() {
|
|
|
2020
2102
|
);
|
|
2021
2103
|
|
|
2022
2104
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2023
|
-
ON messages(target_agent, status);
|
|
2105
|
+
ON messages(target_agent, session_scope, status);
|
|
2024
2106
|
|
|
2025
2107
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2026
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2108
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2109
|
+
`);
|
|
2110
|
+
try {
|
|
2111
|
+
await client.execute({
|
|
2112
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2113
|
+
args: []
|
|
2114
|
+
});
|
|
2115
|
+
} catch {
|
|
2116
|
+
}
|
|
2117
|
+
try {
|
|
2118
|
+
await client.execute({
|
|
2119
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2120
|
+
args: []
|
|
2121
|
+
});
|
|
2122
|
+
} catch {
|
|
2123
|
+
}
|
|
2124
|
+
await client.executeMultiple(`
|
|
2125
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2126
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2127
|
+
|
|
2128
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2129
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2027
2130
|
`);
|
|
2028
2131
|
try {
|
|
2029
2132
|
await client.execute({
|
|
@@ -2607,6 +2710,13 @@ async function ensureSchema() {
|
|
|
2607
2710
|
} catch {
|
|
2608
2711
|
}
|
|
2609
2712
|
}
|
|
2713
|
+
try {
|
|
2714
|
+
await client.execute({
|
|
2715
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2716
|
+
args: []
|
|
2717
|
+
});
|
|
2718
|
+
} catch {
|
|
2719
|
+
}
|
|
2610
2720
|
}
|
|
2611
2721
|
async function disposeDatabase() {
|
|
2612
2722
|
if (_walCheckpointTimer) {
|
|
@@ -2646,14 +2756,14 @@ var init_database = __esm({
|
|
|
2646
2756
|
|
|
2647
2757
|
// src/lib/keychain.ts
|
|
2648
2758
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2649
|
-
import { existsSync as
|
|
2650
|
-
import
|
|
2759
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2760
|
+
import path6 from "path";
|
|
2651
2761
|
import os5 from "os";
|
|
2652
2762
|
function getKeyDir() {
|
|
2653
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
2763
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2654
2764
|
}
|
|
2655
2765
|
function getKeyPath() {
|
|
2656
|
-
return
|
|
2766
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2657
2767
|
}
|
|
2658
2768
|
async function tryKeytar() {
|
|
2659
2769
|
try {
|
|
@@ -2674,7 +2784,7 @@ async function getMasterKey() {
|
|
|
2674
2784
|
}
|
|
2675
2785
|
}
|
|
2676
2786
|
const keyPath = getKeyPath();
|
|
2677
|
-
if (!
|
|
2787
|
+
if (!existsSync6(keyPath)) {
|
|
2678
2788
|
process.stderr.write(
|
|
2679
2789
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2680
2790
|
`
|
|
@@ -2761,6 +2871,7 @@ var shard_manager_exports = {};
|
|
|
2761
2871
|
__export(shard_manager_exports, {
|
|
2762
2872
|
disposeShards: () => disposeShards,
|
|
2763
2873
|
ensureShardSchema: () => ensureShardSchema,
|
|
2874
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2764
2875
|
getReadyShardClient: () => getReadyShardClient,
|
|
2765
2876
|
getShardClient: () => getShardClient,
|
|
2766
2877
|
getShardsDir: () => getShardsDir,
|
|
@@ -2769,15 +2880,18 @@ __export(shard_manager_exports, {
|
|
|
2769
2880
|
listShards: () => listShards,
|
|
2770
2881
|
shardExists: () => shardExists
|
|
2771
2882
|
});
|
|
2772
|
-
import
|
|
2773
|
-
import { existsSync as
|
|
2883
|
+
import path7 from "path";
|
|
2884
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2774
2885
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2775
2886
|
function initShardManager(encryptionKey) {
|
|
2776
2887
|
_encryptionKey = encryptionKey;
|
|
2777
|
-
if (!
|
|
2778
|
-
|
|
2888
|
+
if (!existsSync7(SHARDS_DIR)) {
|
|
2889
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2779
2890
|
}
|
|
2780
2891
|
_shardingEnabled = true;
|
|
2892
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2893
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2894
|
+
_evictionTimer.unref();
|
|
2781
2895
|
}
|
|
2782
2896
|
function isShardingEnabled() {
|
|
2783
2897
|
return _shardingEnabled;
|
|
@@ -2794,21 +2908,28 @@ function getShardClient(projectName) {
|
|
|
2794
2908
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2795
2909
|
}
|
|
2796
2910
|
const cached = _shards.get(safeName);
|
|
2797
|
-
if (cached)
|
|
2798
|
-
|
|
2911
|
+
if (cached) {
|
|
2912
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2913
|
+
return cached;
|
|
2914
|
+
}
|
|
2915
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2916
|
+
evictLRU();
|
|
2917
|
+
}
|
|
2918
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2799
2919
|
const client = createClient2({
|
|
2800
2920
|
url: `file:${dbPath}`,
|
|
2801
2921
|
encryptionKey: _encryptionKey
|
|
2802
2922
|
});
|
|
2803
2923
|
_shards.set(safeName, client);
|
|
2924
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2804
2925
|
return client;
|
|
2805
2926
|
}
|
|
2806
2927
|
function shardExists(projectName) {
|
|
2807
2928
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2808
|
-
return
|
|
2929
|
+
return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
2809
2930
|
}
|
|
2810
2931
|
function listShards() {
|
|
2811
|
-
if (!
|
|
2932
|
+
if (!existsSync7(SHARDS_DIR)) return [];
|
|
2812
2933
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2813
2934
|
}
|
|
2814
2935
|
async function ensureShardSchema(client) {
|
|
@@ -2860,6 +2981,8 @@ async function ensureShardSchema(client) {
|
|
|
2860
2981
|
for (const col of [
|
|
2861
2982
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2862
2983
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2984
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2985
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2863
2986
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2864
2987
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2865
2988
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2997,21 +3120,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2997
3120
|
await ensureShardSchema(client);
|
|
2998
3121
|
return client;
|
|
2999
3122
|
}
|
|
3123
|
+
function evictLRU() {
|
|
3124
|
+
let oldest = null;
|
|
3125
|
+
let oldestTime = Infinity;
|
|
3126
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3127
|
+
if (time < oldestTime) {
|
|
3128
|
+
oldestTime = time;
|
|
3129
|
+
oldest = name;
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
if (oldest) {
|
|
3133
|
+
const client = _shards.get(oldest);
|
|
3134
|
+
if (client) {
|
|
3135
|
+
client.close();
|
|
3136
|
+
}
|
|
3137
|
+
_shards.delete(oldest);
|
|
3138
|
+
_shardLastAccess.delete(oldest);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
function evictIdleShards() {
|
|
3142
|
+
const now = Date.now();
|
|
3143
|
+
const toEvict = [];
|
|
3144
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3145
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3146
|
+
toEvict.push(name);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
for (const name of toEvict) {
|
|
3150
|
+
const client = _shards.get(name);
|
|
3151
|
+
if (client) {
|
|
3152
|
+
client.close();
|
|
3153
|
+
}
|
|
3154
|
+
_shards.delete(name);
|
|
3155
|
+
_shardLastAccess.delete(name);
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
function getOpenShardCount() {
|
|
3159
|
+
return _shards.size;
|
|
3160
|
+
}
|
|
3000
3161
|
function disposeShards() {
|
|
3162
|
+
if (_evictionTimer) {
|
|
3163
|
+
clearInterval(_evictionTimer);
|
|
3164
|
+
_evictionTimer = null;
|
|
3165
|
+
}
|
|
3001
3166
|
for (const [, client] of _shards) {
|
|
3002
3167
|
client.close();
|
|
3003
3168
|
}
|
|
3004
3169
|
_shards.clear();
|
|
3170
|
+
_shardLastAccess.clear();
|
|
3005
3171
|
_shardingEnabled = false;
|
|
3006
3172
|
_encryptionKey = null;
|
|
3007
3173
|
}
|
|
3008
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3174
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3009
3175
|
var init_shard_manager = __esm({
|
|
3010
3176
|
"src/lib/shard-manager.ts"() {
|
|
3011
3177
|
"use strict";
|
|
3012
3178
|
init_config();
|
|
3013
|
-
SHARDS_DIR =
|
|
3179
|
+
SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
|
|
3180
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3181
|
+
MAX_OPEN_SHARDS = 10;
|
|
3182
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3014
3183
|
_shards = /* @__PURE__ */ new Map();
|
|
3184
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3185
|
+
_evictionTimer = null;
|
|
3015
3186
|
_encryptionKey = null;
|
|
3016
3187
|
_shardingEnabled = false;
|
|
3017
3188
|
}
|
|
@@ -3775,13 +3946,13 @@ var init_store = __esm({
|
|
|
3775
3946
|
});
|
|
3776
3947
|
|
|
3777
3948
|
// src/lib/session-registry.ts
|
|
3778
|
-
import { readFileSync as
|
|
3779
|
-
import
|
|
3949
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
3950
|
+
import path8 from "path";
|
|
3780
3951
|
import os6 from "os";
|
|
3781
3952
|
function registerSession(entry) {
|
|
3782
|
-
const dir =
|
|
3783
|
-
if (!
|
|
3784
|
-
|
|
3953
|
+
const dir = path8.dirname(REGISTRY_PATH);
|
|
3954
|
+
if (!existsSync8(dir)) {
|
|
3955
|
+
mkdirSync3(dir, { recursive: true });
|
|
3785
3956
|
}
|
|
3786
3957
|
const sessions = listSessions();
|
|
3787
3958
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -3790,11 +3961,11 @@ function registerSession(entry) {
|
|
|
3790
3961
|
} else {
|
|
3791
3962
|
sessions.push(entry);
|
|
3792
3963
|
}
|
|
3793
|
-
|
|
3964
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
3794
3965
|
}
|
|
3795
3966
|
function listSessions() {
|
|
3796
3967
|
try {
|
|
3797
|
-
const raw =
|
|
3968
|
+
const raw = readFileSync5(REGISTRY_PATH, "utf8");
|
|
3798
3969
|
return JSON.parse(raw);
|
|
3799
3970
|
} catch {
|
|
3800
3971
|
return [];
|
|
@@ -3804,7 +3975,7 @@ var REGISTRY_PATH;
|
|
|
3804
3975
|
var init_session_registry = __esm({
|
|
3805
3976
|
"src/lib/session-registry.ts"() {
|
|
3806
3977
|
"use strict";
|
|
3807
|
-
REGISTRY_PATH =
|
|
3978
|
+
REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
3808
3979
|
}
|
|
3809
3980
|
});
|
|
3810
3981
|
|
|
@@ -4084,12 +4255,12 @@ var init_runtime_table = __esm({
|
|
|
4084
4255
|
});
|
|
4085
4256
|
|
|
4086
4257
|
// src/lib/agent-config.ts
|
|
4087
|
-
import { readFileSync as
|
|
4088
|
-
import
|
|
4258
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync9 } from "fs";
|
|
4259
|
+
import path9 from "path";
|
|
4089
4260
|
function loadAgentConfig() {
|
|
4090
|
-
if (!
|
|
4261
|
+
if (!existsSync9(AGENT_CONFIG_PATH)) return {};
|
|
4091
4262
|
try {
|
|
4092
|
-
return JSON.parse(
|
|
4263
|
+
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
4093
4264
|
} catch {
|
|
4094
4265
|
return {};
|
|
4095
4266
|
}
|
|
@@ -4108,7 +4279,8 @@ var init_agent_config = __esm({
|
|
|
4108
4279
|
"use strict";
|
|
4109
4280
|
init_config();
|
|
4110
4281
|
init_runtime_table();
|
|
4111
|
-
|
|
4282
|
+
init_secure_files();
|
|
4283
|
+
AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
|
|
4112
4284
|
DEFAULT_MODELS = {
|
|
4113
4285
|
claude: "claude-opus-4",
|
|
4114
4286
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
@@ -4126,17 +4298,17 @@ __export(intercom_queue_exports, {
|
|
|
4126
4298
|
queueIntercom: () => queueIntercom,
|
|
4127
4299
|
readQueue: () => readQueue
|
|
4128
4300
|
});
|
|
4129
|
-
import { readFileSync as
|
|
4130
|
-
import
|
|
4301
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
4302
|
+
import path10 from "path";
|
|
4131
4303
|
import os7 from "os";
|
|
4132
4304
|
function ensureDir() {
|
|
4133
|
-
const dir =
|
|
4134
|
-
if (!
|
|
4305
|
+
const dir = path10.dirname(QUEUE_PATH);
|
|
4306
|
+
if (!existsSync10(dir)) mkdirSync4(dir, { recursive: true });
|
|
4135
4307
|
}
|
|
4136
4308
|
function readQueue() {
|
|
4137
4309
|
try {
|
|
4138
|
-
if (!
|
|
4139
|
-
return JSON.parse(
|
|
4310
|
+
if (!existsSync10(QUEUE_PATH)) return [];
|
|
4311
|
+
return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
|
|
4140
4312
|
} catch {
|
|
4141
4313
|
return [];
|
|
4142
4314
|
}
|
|
@@ -4144,7 +4316,7 @@ function readQueue() {
|
|
|
4144
4316
|
function writeQueue(queue) {
|
|
4145
4317
|
ensureDir();
|
|
4146
4318
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
4147
|
-
|
|
4319
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
4148
4320
|
renameSync3(tmp, QUEUE_PATH);
|
|
4149
4321
|
}
|
|
4150
4322
|
function queueIntercom(targetSession, reason) {
|
|
@@ -4236,26 +4408,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
4236
4408
|
var init_intercom_queue = __esm({
|
|
4237
4409
|
"src/lib/intercom-queue.ts"() {
|
|
4238
4410
|
"use strict";
|
|
4239
|
-
QUEUE_PATH =
|
|
4411
|
+
QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
4240
4412
|
MAX_RETRIES2 = 5;
|
|
4241
4413
|
TTL_MS = 60 * 60 * 1e3;
|
|
4242
|
-
INTERCOM_LOG =
|
|
4414
|
+
INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4243
4415
|
}
|
|
4244
4416
|
});
|
|
4245
4417
|
|
|
4246
4418
|
// src/lib/license.ts
|
|
4247
|
-
import { readFileSync as
|
|
4419
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
|
|
4248
4420
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4249
|
-
import
|
|
4421
|
+
import { createRequire as createRequire2 } from "module";
|
|
4422
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4423
|
+
import os8 from "os";
|
|
4424
|
+
import path11 from "path";
|
|
4250
4425
|
import { jwtVerify, importSPKI } from "jose";
|
|
4251
4426
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
4252
4427
|
var init_license = __esm({
|
|
4253
4428
|
"src/lib/license.ts"() {
|
|
4254
4429
|
"use strict";
|
|
4255
4430
|
init_config();
|
|
4256
|
-
LICENSE_PATH =
|
|
4257
|
-
CACHE_PATH =
|
|
4258
|
-
DEVICE_ID_PATH =
|
|
4431
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
4432
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4433
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
4259
4434
|
PLAN_LIMITS = {
|
|
4260
4435
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
4261
4436
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -4267,12 +4442,12 @@ var init_license = __esm({
|
|
|
4267
4442
|
});
|
|
4268
4443
|
|
|
4269
4444
|
// src/lib/plan-limits.ts
|
|
4270
|
-
import { readFileSync as
|
|
4271
|
-
import
|
|
4445
|
+
import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
|
|
4446
|
+
import path12 from "path";
|
|
4272
4447
|
function getLicenseSync() {
|
|
4273
4448
|
try {
|
|
4274
|
-
if (!
|
|
4275
|
-
const raw = JSON.parse(
|
|
4449
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
4450
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
4276
4451
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4277
4452
|
const parts = raw.token.split(".");
|
|
4278
4453
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4310,8 +4485,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4310
4485
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
4311
4486
|
let count = 0;
|
|
4312
4487
|
try {
|
|
4313
|
-
if (
|
|
4314
|
-
const raw =
|
|
4488
|
+
if (existsSync12(filePath)) {
|
|
4489
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
4315
4490
|
const employees = JSON.parse(raw);
|
|
4316
4491
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4317
4492
|
}
|
|
@@ -4340,29 +4515,30 @@ var init_plan_limits = __esm({
|
|
|
4340
4515
|
this.name = "PlanLimitError";
|
|
4341
4516
|
}
|
|
4342
4517
|
};
|
|
4343
|
-
CACHE_PATH2 =
|
|
4518
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
4344
4519
|
}
|
|
4345
4520
|
});
|
|
4346
4521
|
|
|
4347
4522
|
// src/lib/notifications.ts
|
|
4348
|
-
import
|
|
4349
|
-
import
|
|
4350
|
-
import
|
|
4523
|
+
import crypto2 from "crypto";
|
|
4524
|
+
import path13 from "path";
|
|
4525
|
+
import os9 from "os";
|
|
4351
4526
|
import {
|
|
4352
|
-
readFileSync as
|
|
4527
|
+
readFileSync as readFileSync10,
|
|
4353
4528
|
readdirSync as readdirSync2,
|
|
4354
4529
|
unlinkSync as unlinkSync3,
|
|
4355
|
-
existsSync as
|
|
4530
|
+
existsSync as existsSync13,
|
|
4356
4531
|
rmdirSync
|
|
4357
4532
|
} from "fs";
|
|
4358
4533
|
async function writeNotification(notification) {
|
|
4359
4534
|
try {
|
|
4360
4535
|
const client = getClient();
|
|
4361
|
-
const id =
|
|
4536
|
+
const id = crypto2.randomUUID();
|
|
4362
4537
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4538
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
4363
4539
|
await client.execute({
|
|
4364
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
4365
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4540
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
4541
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4366
4542
|
args: [
|
|
4367
4543
|
id,
|
|
4368
4544
|
notification.agentId,
|
|
@@ -4371,6 +4547,7 @@ async function writeNotification(notification) {
|
|
|
4371
4547
|
notification.project,
|
|
4372
4548
|
notification.summary,
|
|
4373
4549
|
notification.taskFile ?? null,
|
|
4550
|
+
sessionScope,
|
|
4374
4551
|
now
|
|
4375
4552
|
]
|
|
4376
4553
|
});
|
|
@@ -4379,12 +4556,14 @@ async function writeNotification(notification) {
|
|
|
4379
4556
|
`);
|
|
4380
4557
|
}
|
|
4381
4558
|
}
|
|
4382
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
4559
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
4383
4560
|
try {
|
|
4384
4561
|
const client = getClient();
|
|
4562
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
4385
4563
|
await client.execute({
|
|
4386
|
-
sql:
|
|
4387
|
-
|
|
4564
|
+
sql: `UPDATE notifications SET read = 1
|
|
4565
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
4566
|
+
args: [taskFile, ...scope.args]
|
|
4388
4567
|
});
|
|
4389
4568
|
} catch {
|
|
4390
4569
|
}
|
|
@@ -4393,11 +4572,12 @@ var init_notifications = __esm({
|
|
|
4393
4572
|
"src/lib/notifications.ts"() {
|
|
4394
4573
|
"use strict";
|
|
4395
4574
|
init_database();
|
|
4575
|
+
init_task_scope();
|
|
4396
4576
|
}
|
|
4397
4577
|
});
|
|
4398
4578
|
|
|
4399
4579
|
// src/lib/session-kill-telemetry.ts
|
|
4400
|
-
import
|
|
4580
|
+
import crypto3 from "crypto";
|
|
4401
4581
|
async function recordSessionKill(input) {
|
|
4402
4582
|
try {
|
|
4403
4583
|
const client = getClient();
|
|
@@ -4407,7 +4587,7 @@ async function recordSessionKill(input) {
|
|
|
4407
4587
|
ticks_idle, estimated_tokens_saved)
|
|
4408
4588
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
4409
4589
|
args: [
|
|
4410
|
-
|
|
4590
|
+
crypto3.randomUUID(),
|
|
4411
4591
|
input.sessionName,
|
|
4412
4592
|
input.agentId,
|
|
4413
4593
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4430,6 +4610,110 @@ var init_session_kill_telemetry = __esm({
|
|
|
4430
4610
|
}
|
|
4431
4611
|
});
|
|
4432
4612
|
|
|
4613
|
+
// src/lib/project-name.ts
|
|
4614
|
+
import { execSync as execSync4 } from "child_process";
|
|
4615
|
+
import path14 from "path";
|
|
4616
|
+
function getProjectName(cwd) {
|
|
4617
|
+
const dir = cwd ?? process.cwd();
|
|
4618
|
+
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
4619
|
+
try {
|
|
4620
|
+
let repoRoot;
|
|
4621
|
+
try {
|
|
4622
|
+
const gitCommonDir = execSync4("git rev-parse --path-format=absolute --git-common-dir", {
|
|
4623
|
+
cwd: dir,
|
|
4624
|
+
encoding: "utf8",
|
|
4625
|
+
timeout: 2e3,
|
|
4626
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4627
|
+
}).trim();
|
|
4628
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
4629
|
+
} catch {
|
|
4630
|
+
repoRoot = execSync4("git rev-parse --show-toplevel", {
|
|
4631
|
+
cwd: dir,
|
|
4632
|
+
encoding: "utf8",
|
|
4633
|
+
timeout: 2e3,
|
|
4634
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4635
|
+
}).trim();
|
|
4636
|
+
}
|
|
4637
|
+
_cached2 = path14.basename(repoRoot);
|
|
4638
|
+
_cachedCwd = dir;
|
|
4639
|
+
return _cached2;
|
|
4640
|
+
} catch {
|
|
4641
|
+
_cached2 = path14.basename(dir);
|
|
4642
|
+
_cachedCwd = dir;
|
|
4643
|
+
return _cached2;
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
var _cached2, _cachedCwd;
|
|
4647
|
+
var init_project_name = __esm({
|
|
4648
|
+
"src/lib/project-name.ts"() {
|
|
4649
|
+
"use strict";
|
|
4650
|
+
_cached2 = null;
|
|
4651
|
+
_cachedCwd = null;
|
|
4652
|
+
}
|
|
4653
|
+
});
|
|
4654
|
+
|
|
4655
|
+
// src/lib/session-scope.ts
|
|
4656
|
+
var session_scope_exports = {};
|
|
4657
|
+
__export(session_scope_exports, {
|
|
4658
|
+
assertSessionScope: () => assertSessionScope,
|
|
4659
|
+
findSessionForProject: () => findSessionForProject,
|
|
4660
|
+
getSessionProject: () => getSessionProject
|
|
4661
|
+
});
|
|
4662
|
+
function getSessionProject(sessionName) {
|
|
4663
|
+
const sessions = listSessions();
|
|
4664
|
+
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
4665
|
+
if (!entry) return null;
|
|
4666
|
+
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
4667
|
+
return parts[parts.length - 1] ?? null;
|
|
4668
|
+
}
|
|
4669
|
+
function findSessionForProject(projectName) {
|
|
4670
|
+
const sessions = listSessions();
|
|
4671
|
+
for (const s of sessions) {
|
|
4672
|
+
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
4673
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
4674
|
+
}
|
|
4675
|
+
return null;
|
|
4676
|
+
}
|
|
4677
|
+
function assertSessionScope(actionType, targetProject) {
|
|
4678
|
+
try {
|
|
4679
|
+
const currentProject = getProjectName();
|
|
4680
|
+
const exeSession2 = resolveExeSession();
|
|
4681
|
+
if (!exeSession2) {
|
|
4682
|
+
return { allowed: true, reason: "no_session" };
|
|
4683
|
+
}
|
|
4684
|
+
if (currentProject === targetProject) {
|
|
4685
|
+
return {
|
|
4686
|
+
allowed: true,
|
|
4687
|
+
reason: "same_session",
|
|
4688
|
+
currentProject,
|
|
4689
|
+
targetProject
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
process.stderr.write(
|
|
4693
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
4694
|
+
`
|
|
4695
|
+
);
|
|
4696
|
+
return {
|
|
4697
|
+
allowed: false,
|
|
4698
|
+
reason: "cross_session_denied",
|
|
4699
|
+
currentProject,
|
|
4700
|
+
targetProject,
|
|
4701
|
+
targetSession: findSessionForProject(targetProject)?.windowName
|
|
4702
|
+
};
|
|
4703
|
+
} catch {
|
|
4704
|
+
return { allowed: true, reason: "no_session" };
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
var init_session_scope = __esm({
|
|
4708
|
+
"src/lib/session-scope.ts"() {
|
|
4709
|
+
"use strict";
|
|
4710
|
+
init_session_registry();
|
|
4711
|
+
init_project_name();
|
|
4712
|
+
init_tmux_routing();
|
|
4713
|
+
init_employees();
|
|
4714
|
+
}
|
|
4715
|
+
});
|
|
4716
|
+
|
|
4433
4717
|
// src/lib/tasks-crud.ts
|
|
4434
4718
|
var tasks_crud_exports = {};
|
|
4435
4719
|
__export(tasks_crud_exports, {
|
|
@@ -4447,12 +4731,12 @@ __export(tasks_crud_exports, {
|
|
|
4447
4731
|
updateTaskStatus: () => updateTaskStatus,
|
|
4448
4732
|
writeCheckpoint: () => writeCheckpoint
|
|
4449
4733
|
});
|
|
4450
|
-
import
|
|
4451
|
-
import
|
|
4452
|
-
import
|
|
4453
|
-
import { execSync as
|
|
4734
|
+
import crypto4 from "crypto";
|
|
4735
|
+
import path15 from "path";
|
|
4736
|
+
import os10 from "os";
|
|
4737
|
+
import { execSync as execSync5 } from "child_process";
|
|
4454
4738
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4455
|
-
import { existsSync as
|
|
4739
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
4456
4740
|
async function writeCheckpoint(input) {
|
|
4457
4741
|
const client = getClient();
|
|
4458
4742
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -4568,13 +4852,28 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
4568
4852
|
}
|
|
4569
4853
|
async function createTaskCore(input) {
|
|
4570
4854
|
const client = getClient();
|
|
4571
|
-
const id =
|
|
4855
|
+
const id = crypto4.randomUUID();
|
|
4572
4856
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4573
4857
|
const slug = slugify(input.title);
|
|
4574
4858
|
let earlySessionScope = null;
|
|
4859
|
+
let scopeMismatchWarning;
|
|
4575
4860
|
try {
|
|
4576
4861
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
4577
|
-
|
|
4862
|
+
const resolved = resolveExeSession2();
|
|
4863
|
+
if (resolved && input.projectName) {
|
|
4864
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
4865
|
+
const sessionProject = getSessionProject2(resolved);
|
|
4866
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
4867
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
4868
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
4869
|
+
`);
|
|
4870
|
+
earlySessionScope = null;
|
|
4871
|
+
} else {
|
|
4872
|
+
earlySessionScope = resolved;
|
|
4873
|
+
}
|
|
4874
|
+
} else {
|
|
4875
|
+
earlySessionScope = resolved;
|
|
4876
|
+
}
|
|
4578
4877
|
} catch {
|
|
4579
4878
|
}
|
|
4580
4879
|
const scope = earlySessionScope ?? "default";
|
|
@@ -4625,10 +4924,14 @@ async function createTaskCore(input) {
|
|
|
4625
4924
|
${laneWarning}` : laneWarning;
|
|
4626
4925
|
}
|
|
4627
4926
|
}
|
|
4927
|
+
if (scopeMismatchWarning) {
|
|
4928
|
+
warning = warning ? `${warning}
|
|
4929
|
+
${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
4930
|
+
}
|
|
4628
4931
|
if (input.baseDir) {
|
|
4629
4932
|
try {
|
|
4630
|
-
await mkdir4(
|
|
4631
|
-
await mkdir4(
|
|
4933
|
+
await mkdir4(path15.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4934
|
+
await mkdir4(path15.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4632
4935
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4633
4936
|
await ensureGitignoreExe(input.baseDir);
|
|
4634
4937
|
} catch {
|
|
@@ -4664,13 +4967,19 @@ ${laneWarning}` : laneWarning;
|
|
|
4664
4967
|
});
|
|
4665
4968
|
if (input.baseDir) {
|
|
4666
4969
|
try {
|
|
4667
|
-
const EXE_OS_DIR =
|
|
4668
|
-
const mdPath =
|
|
4669
|
-
const mdDir =
|
|
4670
|
-
if (!
|
|
4970
|
+
const EXE_OS_DIR = path15.join(os10.homedir(), ".exe-os");
|
|
4971
|
+
const mdPath = path15.join(EXE_OS_DIR, taskFile);
|
|
4972
|
+
const mdDir = path15.dirname(mdPath);
|
|
4973
|
+
if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4671
4974
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4672
4975
|
const mdContent = `# ${input.title}
|
|
4673
4976
|
|
|
4977
|
+
## MANDATORY: When done
|
|
4978
|
+
|
|
4979
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4980
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4981
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4982
|
+
|
|
4674
4983
|
**ID:** ${id}
|
|
4675
4984
|
**Status:** ${initialStatus}
|
|
4676
4985
|
**Priority:** ${input.priority}
|
|
@@ -4684,12 +4993,6 @@ ${laneWarning}` : laneWarning;
|
|
|
4684
4993
|
## Context
|
|
4685
4994
|
|
|
4686
4995
|
${input.context}
|
|
4687
|
-
|
|
4688
|
-
## MANDATORY: When done
|
|
4689
|
-
|
|
4690
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
4691
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4692
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4693
4996
|
`;
|
|
4694
4997
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
4695
4998
|
} catch (err) {
|
|
@@ -4771,14 +5074,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
4771
5074
|
if (!identifier || identifier === "unknown") return true;
|
|
4772
5075
|
try {
|
|
4773
5076
|
if (identifier.startsWith("%")) {
|
|
4774
|
-
const output =
|
|
5077
|
+
const output = execSync5("tmux list-panes -a -F '#{pane_id}'", {
|
|
4775
5078
|
timeout: 2e3,
|
|
4776
5079
|
encoding: "utf8",
|
|
4777
5080
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4778
5081
|
});
|
|
4779
5082
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
4780
5083
|
} else {
|
|
4781
|
-
|
|
5084
|
+
execSync5(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
4782
5085
|
timeout: 2e3,
|
|
4783
5086
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4784
5087
|
});
|
|
@@ -4787,7 +5090,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
4787
5090
|
} catch {
|
|
4788
5091
|
if (identifier.startsWith("%")) return true;
|
|
4789
5092
|
try {
|
|
4790
|
-
|
|
5093
|
+
execSync5("tmux list-sessions", {
|
|
4791
5094
|
timeout: 2e3,
|
|
4792
5095
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4793
5096
|
});
|
|
@@ -4802,12 +5105,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
4802
5105
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
4803
5106
|
try {
|
|
4804
5107
|
const since = new Date(taskCreatedAt).toISOString();
|
|
4805
|
-
const branch =
|
|
5108
|
+
const branch = execSync5(
|
|
4806
5109
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
4807
5110
|
{ encoding: "utf8", timeout: 3e3 }
|
|
4808
5111
|
).trim();
|
|
4809
5112
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
4810
|
-
const commitCount =
|
|
5113
|
+
const commitCount = execSync5(
|
|
4811
5114
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
4812
5115
|
{ encoding: "utf8", timeout: 5e3 }
|
|
4813
5116
|
).trim();
|
|
@@ -4938,7 +5241,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4938
5241
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4939
5242
|
} catch {
|
|
4940
5243
|
}
|
|
4941
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5244
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4942
5245
|
try {
|
|
4943
5246
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4944
5247
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -4967,9 +5270,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4967
5270
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4968
5271
|
}
|
|
4969
5272
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4970
|
-
const archPath =
|
|
5273
|
+
const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4971
5274
|
try {
|
|
4972
|
-
if (
|
|
5275
|
+
if (existsSync14(archPath)) return;
|
|
4973
5276
|
const template = [
|
|
4974
5277
|
`# ${projectName} \u2014 System Architecture`,
|
|
4975
5278
|
"",
|
|
@@ -5002,10 +5305,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5002
5305
|
}
|
|
5003
5306
|
}
|
|
5004
5307
|
async function ensureGitignoreExe(baseDir) {
|
|
5005
|
-
const gitignorePath =
|
|
5308
|
+
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
5006
5309
|
try {
|
|
5007
|
-
if (
|
|
5008
|
-
const content =
|
|
5310
|
+
if (existsSync14(gitignorePath)) {
|
|
5311
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5009
5312
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5010
5313
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5011
5314
|
} else {
|
|
@@ -5048,8 +5351,8 @@ __export(tasks_review_exports, {
|
|
|
5048
5351
|
isStale: () => isStale,
|
|
5049
5352
|
listPendingReviews: () => listPendingReviews
|
|
5050
5353
|
});
|
|
5051
|
-
import
|
|
5052
|
-
import { existsSync as
|
|
5354
|
+
import path16 from "path";
|
|
5355
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5053
5356
|
function formatAge(isoTimestamp) {
|
|
5054
5357
|
if (!isoTimestamp) return "";
|
|
5055
5358
|
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
@@ -5067,54 +5370,38 @@ function isStale(isoTimestamp) {
|
|
|
5067
5370
|
}
|
|
5068
5371
|
async function countPendingReviews(sessionScope) {
|
|
5069
5372
|
const client = getClient();
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
args: [sessionScope]
|
|
5074
|
-
});
|
|
5075
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5076
|
-
}
|
|
5373
|
+
const scope = strictSessionScopeFilter(
|
|
5374
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5375
|
+
);
|
|
5077
5376
|
const result = await client.execute({
|
|
5078
|
-
sql:
|
|
5079
|
-
|
|
5377
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5378
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
5379
|
+
args: [...scope.args]
|
|
5080
5380
|
});
|
|
5081
5381
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5082
5382
|
}
|
|
5083
5383
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
5084
5384
|
const client = getClient();
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
5089
|
-
AND session_scope = ?`,
|
|
5090
|
-
args: [sinceIso, sessionScope]
|
|
5091
|
-
});
|
|
5092
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5093
|
-
}
|
|
5385
|
+
const scope = strictSessionScopeFilter(
|
|
5386
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5387
|
+
);
|
|
5094
5388
|
const result = await client.execute({
|
|
5095
5389
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5096
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
5097
|
-
args: [sinceIso]
|
|
5390
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
5391
|
+
args: [sinceIso, ...scope.args]
|
|
5098
5392
|
});
|
|
5099
5393
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5100
5394
|
}
|
|
5101
5395
|
async function listPendingReviews(limit, sessionScope) {
|
|
5102
5396
|
const client = getClient();
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
WHERE status = 'needs_review'
|
|
5107
|
-
AND session_scope = ?
|
|
5108
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
5109
|
-
args: [sessionScope, limit]
|
|
5110
|
-
});
|
|
5111
|
-
return result2.rows;
|
|
5112
|
-
}
|
|
5397
|
+
const scope = strictSessionScopeFilter(
|
|
5398
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5399
|
+
);
|
|
5113
5400
|
const result = await client.execute({
|
|
5114
5401
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
5115
|
-
WHERE status = 'needs_review'
|
|
5402
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
5116
5403
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
5117
|
-
args: [limit]
|
|
5404
|
+
args: [...scope.args, limit]
|
|
5118
5405
|
});
|
|
5119
5406
|
return result.rows;
|
|
5120
5407
|
}
|
|
@@ -5126,7 +5413,7 @@ async function cleanupOrphanedReviews() {
|
|
|
5126
5413
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
5127
5414
|
AND assigned_by = 'system'
|
|
5128
5415
|
AND title LIKE 'Review:%'
|
|
5129
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
5416
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
5130
5417
|
args: [now]
|
|
5131
5418
|
});
|
|
5132
5419
|
const r1b = await client.execute({
|
|
@@ -5334,11 +5621,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5334
5621
|
);
|
|
5335
5622
|
}
|
|
5336
5623
|
try {
|
|
5337
|
-
const cacheDir =
|
|
5338
|
-
if (
|
|
5624
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5625
|
+
if (existsSync15(cacheDir)) {
|
|
5339
5626
|
for (const f of readdirSync3(cacheDir)) {
|
|
5340
5627
|
if (f.startsWith("review-notified-")) {
|
|
5341
|
-
unlinkSync4(
|
|
5628
|
+
unlinkSync4(path16.join(cacheDir, f));
|
|
5342
5629
|
}
|
|
5343
5630
|
}
|
|
5344
5631
|
}
|
|
@@ -5355,11 +5642,12 @@ var init_tasks_review = __esm({
|
|
|
5355
5642
|
init_tmux_routing();
|
|
5356
5643
|
init_session_key();
|
|
5357
5644
|
init_state_bus();
|
|
5645
|
+
init_task_scope();
|
|
5358
5646
|
}
|
|
5359
5647
|
});
|
|
5360
5648
|
|
|
5361
5649
|
// src/lib/tasks-chain.ts
|
|
5362
|
-
import
|
|
5650
|
+
import path17 from "path";
|
|
5363
5651
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5364
5652
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5365
5653
|
const client = getClient();
|
|
@@ -5376,7 +5664,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5376
5664
|
});
|
|
5377
5665
|
for (const ur of unblockedRows.rows) {
|
|
5378
5666
|
try {
|
|
5379
|
-
const ubFile =
|
|
5667
|
+
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
5380
5668
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5381
5669
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5382
5670
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5411,7 +5699,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
5411
5699
|
const scScope = sessionScopeFilter();
|
|
5412
5700
|
const remaining = await client.execute({
|
|
5413
5701
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5414
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
5702
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
5415
5703
|
args: [parentTaskId, ...scScope.args]
|
|
5416
5704
|
});
|
|
5417
5705
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -5443,110 +5731,6 @@ var init_tasks_chain = __esm({
|
|
|
5443
5731
|
}
|
|
5444
5732
|
});
|
|
5445
5733
|
|
|
5446
|
-
// src/lib/project-name.ts
|
|
5447
|
-
import { execSync as execSync5 } from "child_process";
|
|
5448
|
-
import path16 from "path";
|
|
5449
|
-
function getProjectName(cwd) {
|
|
5450
|
-
const dir = cwd ?? process.cwd();
|
|
5451
|
-
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
5452
|
-
try {
|
|
5453
|
-
let repoRoot;
|
|
5454
|
-
try {
|
|
5455
|
-
const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
|
|
5456
|
-
cwd: dir,
|
|
5457
|
-
encoding: "utf8",
|
|
5458
|
-
timeout: 2e3,
|
|
5459
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
5460
|
-
}).trim();
|
|
5461
|
-
repoRoot = path16.dirname(gitCommonDir);
|
|
5462
|
-
} catch {
|
|
5463
|
-
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
5464
|
-
cwd: dir,
|
|
5465
|
-
encoding: "utf8",
|
|
5466
|
-
timeout: 2e3,
|
|
5467
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
5468
|
-
}).trim();
|
|
5469
|
-
}
|
|
5470
|
-
_cached2 = path16.basename(repoRoot);
|
|
5471
|
-
_cachedCwd = dir;
|
|
5472
|
-
return _cached2;
|
|
5473
|
-
} catch {
|
|
5474
|
-
_cached2 = path16.basename(dir);
|
|
5475
|
-
_cachedCwd = dir;
|
|
5476
|
-
return _cached2;
|
|
5477
|
-
}
|
|
5478
|
-
}
|
|
5479
|
-
var _cached2, _cachedCwd;
|
|
5480
|
-
var init_project_name = __esm({
|
|
5481
|
-
"src/lib/project-name.ts"() {
|
|
5482
|
-
"use strict";
|
|
5483
|
-
_cached2 = null;
|
|
5484
|
-
_cachedCwd = null;
|
|
5485
|
-
}
|
|
5486
|
-
});
|
|
5487
|
-
|
|
5488
|
-
// src/lib/session-scope.ts
|
|
5489
|
-
var session_scope_exports = {};
|
|
5490
|
-
__export(session_scope_exports, {
|
|
5491
|
-
assertSessionScope: () => assertSessionScope,
|
|
5492
|
-
findSessionForProject: () => findSessionForProject,
|
|
5493
|
-
getSessionProject: () => getSessionProject
|
|
5494
|
-
});
|
|
5495
|
-
function getSessionProject(sessionName) {
|
|
5496
|
-
const sessions = listSessions();
|
|
5497
|
-
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
5498
|
-
if (!entry) return null;
|
|
5499
|
-
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
5500
|
-
return parts[parts.length - 1] ?? null;
|
|
5501
|
-
}
|
|
5502
|
-
function findSessionForProject(projectName) {
|
|
5503
|
-
const sessions = listSessions();
|
|
5504
|
-
for (const s of sessions) {
|
|
5505
|
-
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
5506
|
-
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
5507
|
-
}
|
|
5508
|
-
return null;
|
|
5509
|
-
}
|
|
5510
|
-
function assertSessionScope(actionType, targetProject) {
|
|
5511
|
-
try {
|
|
5512
|
-
const currentProject = getProjectName();
|
|
5513
|
-
const exeSession2 = resolveExeSession();
|
|
5514
|
-
if (!exeSession2) {
|
|
5515
|
-
return { allowed: true, reason: "no_session" };
|
|
5516
|
-
}
|
|
5517
|
-
if (currentProject === targetProject) {
|
|
5518
|
-
return {
|
|
5519
|
-
allowed: true,
|
|
5520
|
-
reason: "same_session",
|
|
5521
|
-
currentProject,
|
|
5522
|
-
targetProject
|
|
5523
|
-
};
|
|
5524
|
-
}
|
|
5525
|
-
process.stderr.write(
|
|
5526
|
-
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
5527
|
-
`
|
|
5528
|
-
);
|
|
5529
|
-
return {
|
|
5530
|
-
allowed: false,
|
|
5531
|
-
reason: "cross_session_denied",
|
|
5532
|
-
currentProject,
|
|
5533
|
-
targetProject,
|
|
5534
|
-
targetSession: findSessionForProject(targetProject)?.windowName
|
|
5535
|
-
};
|
|
5536
|
-
} catch {
|
|
5537
|
-
return { allowed: true, reason: "no_session" };
|
|
5538
|
-
}
|
|
5539
|
-
}
|
|
5540
|
-
var init_session_scope = __esm({
|
|
5541
|
-
"src/lib/session-scope.ts"() {
|
|
5542
|
-
"use strict";
|
|
5543
|
-
init_session_registry();
|
|
5544
|
-
init_project_name();
|
|
5545
|
-
init_tmux_routing();
|
|
5546
|
-
init_employees();
|
|
5547
|
-
}
|
|
5548
|
-
});
|
|
5549
|
-
|
|
5550
5734
|
// src/lib/tasks-notify.ts
|
|
5551
5735
|
async function dispatchTaskToEmployee(input) {
|
|
5552
5736
|
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|
|
@@ -5614,10 +5798,10 @@ var init_tasks_notify = __esm({
|
|
|
5614
5798
|
});
|
|
5615
5799
|
|
|
5616
5800
|
// src/lib/behaviors.ts
|
|
5617
|
-
import
|
|
5801
|
+
import crypto5 from "crypto";
|
|
5618
5802
|
async function storeBehavior(opts) {
|
|
5619
5803
|
const client = getClient();
|
|
5620
|
-
const id =
|
|
5804
|
+
const id = crypto5.randomUUID();
|
|
5621
5805
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5622
5806
|
await client.execute({
|
|
5623
5807
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -5646,7 +5830,7 @@ __export(skill_learning_exports, {
|
|
|
5646
5830
|
storeTrajectory: () => storeTrajectory,
|
|
5647
5831
|
sweepTrajectories: () => sweepTrajectories
|
|
5648
5832
|
});
|
|
5649
|
-
import
|
|
5833
|
+
import crypto6 from "crypto";
|
|
5650
5834
|
async function extractTrajectory(taskId, agentId) {
|
|
5651
5835
|
const client = getClient();
|
|
5652
5836
|
const result = await client.execute({
|
|
@@ -5675,11 +5859,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
5675
5859
|
return signature;
|
|
5676
5860
|
}
|
|
5677
5861
|
function hashSignature(signature) {
|
|
5678
|
-
return
|
|
5862
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
5679
5863
|
}
|
|
5680
5864
|
async function storeTrajectory(opts) {
|
|
5681
5865
|
const client = getClient();
|
|
5682
|
-
const id =
|
|
5866
|
+
const id = crypto6.randomUUID();
|
|
5683
5867
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5684
5868
|
const signatureHash = hashSignature(opts.signature);
|
|
5685
5869
|
await client.execute({
|
|
@@ -5944,8 +6128,8 @@ __export(tasks_exports, {
|
|
|
5944
6128
|
updateTaskStatus: () => updateTaskStatus,
|
|
5945
6129
|
writeCheckpoint: () => writeCheckpoint
|
|
5946
6130
|
});
|
|
5947
|
-
import
|
|
5948
|
-
import { writeFileSync as
|
|
6131
|
+
import path18 from "path";
|
|
6132
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5949
6133
|
async function createTask(input) {
|
|
5950
6134
|
const result = await createTaskCore(input);
|
|
5951
6135
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5964,12 +6148,12 @@ async function updateTask(input) {
|
|
|
5964
6148
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5965
6149
|
try {
|
|
5966
6150
|
const agent = String(row.assigned_to);
|
|
5967
|
-
const cacheDir =
|
|
5968
|
-
const cachePath =
|
|
6151
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
6152
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
5969
6153
|
if (input.status === "in_progress") {
|
|
5970
6154
|
mkdirSync6(cacheDir, { recursive: true });
|
|
5971
|
-
|
|
5972
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
6155
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6156
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5973
6157
|
try {
|
|
5974
6158
|
unlinkSync5(cachePath);
|
|
5975
6159
|
} catch {
|
|
@@ -5977,10 +6161,10 @@ async function updateTask(input) {
|
|
|
5977
6161
|
}
|
|
5978
6162
|
} catch {
|
|
5979
6163
|
}
|
|
5980
|
-
if (input.status === "done") {
|
|
6164
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5981
6165
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5982
6166
|
}
|
|
5983
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
6167
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5984
6168
|
try {
|
|
5985
6169
|
const client = getClient();
|
|
5986
6170
|
const taskTitle = String(row.title);
|
|
@@ -5996,7 +6180,7 @@ async function updateTask(input) {
|
|
|
5996
6180
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5997
6181
|
try {
|
|
5998
6182
|
const draftClient = getClient();
|
|
5999
|
-
if (input.status === "done") {
|
|
6183
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6000
6184
|
await draftClient.execute({
|
|
6001
6185
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
6002
6186
|
args: [assignedAgent]
|
|
@@ -6013,7 +6197,7 @@ async function updateTask(input) {
|
|
|
6013
6197
|
try {
|
|
6014
6198
|
const client = getClient();
|
|
6015
6199
|
const cascaded = await client.execute({
|
|
6016
|
-
sql: `UPDATE tasks SET status = '
|
|
6200
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
6017
6201
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
6018
6202
|
args: [now, taskId]
|
|
6019
6203
|
});
|
|
@@ -6026,14 +6210,14 @@ async function updateTask(input) {
|
|
|
6026
6210
|
} catch {
|
|
6027
6211
|
}
|
|
6028
6212
|
}
|
|
6029
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
6213
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
6030
6214
|
if (isTerminal) {
|
|
6031
6215
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
6032
6216
|
if (!isCoordinator) {
|
|
6033
6217
|
notifyTaskDone();
|
|
6034
6218
|
}
|
|
6035
6219
|
await markTaskNotificationsRead(taskFile);
|
|
6036
|
-
if (input.status === "done") {
|
|
6220
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6037
6221
|
try {
|
|
6038
6222
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
6039
6223
|
} catch {
|
|
@@ -6053,7 +6237,7 @@ async function updateTask(input) {
|
|
|
6053
6237
|
}
|
|
6054
6238
|
}
|
|
6055
6239
|
}
|
|
6056
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6240
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6057
6241
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6058
6242
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
6059
6243
|
taskId,
|
|
@@ -6425,6 +6609,7 @@ __export(tmux_routing_exports, {
|
|
|
6425
6609
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
6426
6610
|
isExeSession: () => isExeSession,
|
|
6427
6611
|
isSessionBusy: () => isSessionBusy,
|
|
6612
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
6428
6613
|
notifyParentExe: () => notifyParentExe,
|
|
6429
6614
|
parseParentExe: () => parseParentExe,
|
|
6430
6615
|
registerParentExe: () => registerParentExe,
|
|
@@ -6435,13 +6620,13 @@ __export(tmux_routing_exports, {
|
|
|
6435
6620
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6436
6621
|
});
|
|
6437
6622
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6438
|
-
import { readFileSync as
|
|
6439
|
-
import
|
|
6440
|
-
import
|
|
6623
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
6624
|
+
import path19 from "path";
|
|
6625
|
+
import os11 from "os";
|
|
6441
6626
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6442
6627
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6443
6628
|
function spawnLockPath(sessionName) {
|
|
6444
|
-
return
|
|
6629
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6445
6630
|
}
|
|
6446
6631
|
function isProcessAlive(pid) {
|
|
6447
6632
|
try {
|
|
@@ -6452,13 +6637,13 @@ function isProcessAlive(pid) {
|
|
|
6452
6637
|
}
|
|
6453
6638
|
}
|
|
6454
6639
|
function acquireSpawnLock2(sessionName) {
|
|
6455
|
-
if (!
|
|
6640
|
+
if (!existsSync16(SPAWN_LOCK_DIR)) {
|
|
6456
6641
|
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
6457
6642
|
}
|
|
6458
6643
|
const lockFile = spawnLockPath(sessionName);
|
|
6459
|
-
if (
|
|
6644
|
+
if (existsSync16(lockFile)) {
|
|
6460
6645
|
try {
|
|
6461
|
-
const lock = JSON.parse(
|
|
6646
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
6462
6647
|
const age = Date.now() - lock.timestamp;
|
|
6463
6648
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
6464
6649
|
return false;
|
|
@@ -6466,7 +6651,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
6466
6651
|
} catch {
|
|
6467
6652
|
}
|
|
6468
6653
|
}
|
|
6469
|
-
|
|
6654
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
6470
6655
|
return true;
|
|
6471
6656
|
}
|
|
6472
6657
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -6478,13 +6663,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6478
6663
|
function resolveBehaviorsExporterScript() {
|
|
6479
6664
|
try {
|
|
6480
6665
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6481
|
-
const scriptPath =
|
|
6482
|
-
|
|
6666
|
+
const scriptPath = path19.join(
|
|
6667
|
+
path19.dirname(thisFile),
|
|
6483
6668
|
"..",
|
|
6484
6669
|
"bin",
|
|
6485
6670
|
"exe-export-behaviors.js"
|
|
6486
6671
|
);
|
|
6487
|
-
return
|
|
6672
|
+
return existsSync16(scriptPath) ? scriptPath : null;
|
|
6488
6673
|
} catch {
|
|
6489
6674
|
return null;
|
|
6490
6675
|
}
|
|
@@ -6550,12 +6735,12 @@ function extractRootExe(name) {
|
|
|
6550
6735
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6551
6736
|
}
|
|
6552
6737
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
6553
|
-
if (!
|
|
6738
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
6554
6739
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6555
6740
|
}
|
|
6556
6741
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6557
|
-
const filePath =
|
|
6558
|
-
|
|
6742
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6743
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
6559
6744
|
parentExe: rootExe,
|
|
6560
6745
|
dispatchedBy: dispatchedBy || rootExe,
|
|
6561
6746
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6563,7 +6748,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6563
6748
|
}
|
|
6564
6749
|
function getParentExe(sessionKey) {
|
|
6565
6750
|
try {
|
|
6566
|
-
const data = JSON.parse(
|
|
6751
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6567
6752
|
return data.parentExe || null;
|
|
6568
6753
|
} catch {
|
|
6569
6754
|
return null;
|
|
@@ -6571,8 +6756,8 @@ function getParentExe(sessionKey) {
|
|
|
6571
6756
|
}
|
|
6572
6757
|
function getDispatchedBy(sessionKey) {
|
|
6573
6758
|
try {
|
|
6574
|
-
const data = JSON.parse(
|
|
6575
|
-
|
|
6759
|
+
const data = JSON.parse(readFileSync12(
|
|
6760
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6576
6761
|
"utf8"
|
|
6577
6762
|
));
|
|
6578
6763
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6642,8 +6827,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
6642
6827
|
}
|
|
6643
6828
|
function readDebounceState() {
|
|
6644
6829
|
try {
|
|
6645
|
-
if (!
|
|
6646
|
-
const raw = JSON.parse(
|
|
6830
|
+
if (!existsSync16(DEBOUNCE_FILE)) return {};
|
|
6831
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
6647
6832
|
const state = {};
|
|
6648
6833
|
for (const [key, val] of Object.entries(raw)) {
|
|
6649
6834
|
if (typeof val === "number") {
|
|
@@ -6659,8 +6844,8 @@ function readDebounceState() {
|
|
|
6659
6844
|
}
|
|
6660
6845
|
function writeDebounceState(state) {
|
|
6661
6846
|
try {
|
|
6662
|
-
if (!
|
|
6663
|
-
|
|
6847
|
+
if (!existsSync16(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6848
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
6664
6849
|
} catch {
|
|
6665
6850
|
}
|
|
6666
6851
|
}
|
|
@@ -6758,8 +6943,8 @@ function sendIntercom(targetSession) {
|
|
|
6758
6943
|
try {
|
|
6759
6944
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6760
6945
|
const agent = baseAgentName(rawAgent);
|
|
6761
|
-
const markerPath =
|
|
6762
|
-
if (
|
|
6946
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6947
|
+
if (existsSync16(markerPath)) {
|
|
6763
6948
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6764
6949
|
return "debounced";
|
|
6765
6950
|
}
|
|
@@ -6768,8 +6953,8 @@ function sendIntercom(targetSession) {
|
|
|
6768
6953
|
try {
|
|
6769
6954
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6770
6955
|
const agent = baseAgentName(rawAgent);
|
|
6771
|
-
const taskDir =
|
|
6772
|
-
if (
|
|
6956
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
6957
|
+
if (existsSync16(taskDir)) {
|
|
6773
6958
|
const files = readdirSync4(taskDir).filter(
|
|
6774
6959
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
6775
6960
|
);
|
|
@@ -6829,6 +7014,21 @@ function notifyParentExe(sessionKey) {
|
|
|
6829
7014
|
}
|
|
6830
7015
|
return true;
|
|
6831
7016
|
}
|
|
7017
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName2, taskTitle) {
|
|
7018
|
+
const transport = getTransport();
|
|
7019
|
+
try {
|
|
7020
|
+
const sessions = transport.listSessions();
|
|
7021
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
7022
|
+
execSync6(
|
|
7023
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
7024
|
+
{ timeout: 3e3 }
|
|
7025
|
+
);
|
|
7026
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}")`);
|
|
7027
|
+
return true;
|
|
7028
|
+
} catch {
|
|
7029
|
+
return false;
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
6832
7032
|
function ensureEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
6833
7033
|
if (isCoordinatorName(employeeName)) {
|
|
6834
7034
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6902,26 +7102,26 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6902
7102
|
const transport = getTransport();
|
|
6903
7103
|
const sessionName = employeeSessionName(employeeName, exeSession2, opts?.instance);
|
|
6904
7104
|
const instanceLabel2 = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6905
|
-
const logDir =
|
|
6906
|
-
const logFile =
|
|
6907
|
-
if (!
|
|
7105
|
+
const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
|
|
7106
|
+
const logFile = path19.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
|
|
7107
|
+
if (!existsSync16(logDir)) {
|
|
6908
7108
|
mkdirSync7(logDir, { recursive: true });
|
|
6909
7109
|
}
|
|
6910
7110
|
transport.kill(sessionName);
|
|
6911
7111
|
let cleanupSuffix = "";
|
|
6912
7112
|
try {
|
|
6913
7113
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6914
|
-
const cleanupScript =
|
|
6915
|
-
if (
|
|
7114
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7115
|
+
if (existsSync16(cleanupScript)) {
|
|
6916
7116
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession2}"`;
|
|
6917
7117
|
}
|
|
6918
7118
|
} catch {
|
|
6919
7119
|
}
|
|
6920
7120
|
try {
|
|
6921
|
-
const claudeJsonPath =
|
|
7121
|
+
const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
|
|
6922
7122
|
let claudeJson = {};
|
|
6923
7123
|
try {
|
|
6924
|
-
claudeJson = JSON.parse(
|
|
7124
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6925
7125
|
} catch {
|
|
6926
7126
|
}
|
|
6927
7127
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6929,17 +7129,17 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6929
7129
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6930
7130
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6931
7131
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6932
|
-
|
|
7132
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6933
7133
|
} catch {
|
|
6934
7134
|
}
|
|
6935
7135
|
try {
|
|
6936
|
-
const settingsDir =
|
|
7136
|
+
const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
|
|
6937
7137
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6938
|
-
const projSettingsDir =
|
|
6939
|
-
const settingsPath =
|
|
7138
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
7139
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
6940
7140
|
let settings = {};
|
|
6941
7141
|
try {
|
|
6942
|
-
settings = JSON.parse(
|
|
7142
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
6943
7143
|
} catch {
|
|
6944
7144
|
}
|
|
6945
7145
|
const perms = settings.permissions ?? {};
|
|
@@ -6968,7 +7168,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6968
7168
|
perms.allow = allow;
|
|
6969
7169
|
settings.permissions = perms;
|
|
6970
7170
|
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6971
|
-
|
|
7171
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6972
7172
|
}
|
|
6973
7173
|
} catch {
|
|
6974
7174
|
}
|
|
@@ -6983,8 +7183,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6983
7183
|
let behaviorsFlag = "";
|
|
6984
7184
|
let legacyFallbackWarned = false;
|
|
6985
7185
|
if (!useExeAgent && !useBinSymlink) {
|
|
6986
|
-
const identityPath =
|
|
6987
|
-
|
|
7186
|
+
const identityPath = path19.join(
|
|
7187
|
+
os11.homedir(),
|
|
6988
7188
|
".exe-os",
|
|
6989
7189
|
"identity",
|
|
6990
7190
|
`${employeeName}.md`
|
|
@@ -6993,13 +7193,13 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6993
7193
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6994
7194
|
if (hasAgentFlag) {
|
|
6995
7195
|
identityFlag = ` --agent ${employeeName}`;
|
|
6996
|
-
} else if (
|
|
7196
|
+
} else if (existsSync16(identityPath)) {
|
|
6997
7197
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6998
7198
|
legacyFallbackWarned = true;
|
|
6999
7199
|
}
|
|
7000
7200
|
const behaviorsFile = exportBehaviorsSync(
|
|
7001
7201
|
employeeName,
|
|
7002
|
-
|
|
7202
|
+
path19.basename(spawnCwd),
|
|
7003
7203
|
sessionName
|
|
7004
7204
|
);
|
|
7005
7205
|
if (behaviorsFile) {
|
|
@@ -7014,16 +7214,16 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
7014
7214
|
}
|
|
7015
7215
|
let sessionContextFlag = "";
|
|
7016
7216
|
try {
|
|
7017
|
-
const ctxDir =
|
|
7217
|
+
const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7018
7218
|
mkdirSync7(ctxDir, { recursive: true });
|
|
7019
|
-
const ctxFile =
|
|
7219
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7020
7220
|
const ctxContent = [
|
|
7021
7221
|
`## Session Context`,
|
|
7022
7222
|
`You are running in tmux session: ${sessionName}.`,
|
|
7023
7223
|
`Your parent coordinator session is ${exeSession2}.`,
|
|
7024
7224
|
`Your employees (if any) use the -${exeSession2} suffix.`
|
|
7025
7225
|
].join("\n");
|
|
7026
|
-
|
|
7226
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
7027
7227
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
7028
7228
|
} catch {
|
|
7029
7229
|
}
|
|
@@ -7100,8 +7300,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
7100
7300
|
transport.pipeLog(sessionName, logFile);
|
|
7101
7301
|
try {
|
|
7102
7302
|
const mySession = getMySession();
|
|
7103
|
-
const dispatchInfo =
|
|
7104
|
-
|
|
7303
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7304
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
7105
7305
|
dispatchedBy: mySession,
|
|
7106
7306
|
rootExe: exeSession2,
|
|
7107
7307
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -7175,15 +7375,15 @@ var init_tmux_routing = __esm({
|
|
|
7175
7375
|
init_intercom_queue();
|
|
7176
7376
|
init_plan_limits();
|
|
7177
7377
|
init_employees();
|
|
7178
|
-
SPAWN_LOCK_DIR =
|
|
7179
|
-
SESSION_CACHE =
|
|
7378
|
+
SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
7379
|
+
SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7180
7380
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7181
7381
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7182
7382
|
VERIFY_PANE_LINES = 200;
|
|
7183
7383
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7184
7384
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7185
|
-
INTERCOM_LOG2 =
|
|
7186
|
-
DEBOUNCE_FILE =
|
|
7385
|
+
INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
7386
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7187
7387
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7188
7388
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7189
7389
|
}
|
|
@@ -7206,6 +7406,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
7206
7406
|
args: [scope]
|
|
7207
7407
|
};
|
|
7208
7408
|
}
|
|
7409
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
7410
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
7411
|
+
if (!scope) return { sql: "", args: [] };
|
|
7412
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
7413
|
+
return {
|
|
7414
|
+
sql: ` AND ${col} = ?`,
|
|
7415
|
+
args: [scope]
|
|
7416
|
+
};
|
|
7417
|
+
}
|
|
7209
7418
|
var init_task_scope = __esm({
|
|
7210
7419
|
"src/lib/task-scope.ts"() {
|
|
7211
7420
|
"use strict";
|
|
@@ -7250,10 +7459,10 @@ async function disposeEmbedder() {
|
|
|
7250
7459
|
async function embedDirect(text) {
|
|
7251
7460
|
const llamaCpp = await import("node-llama-cpp");
|
|
7252
7461
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
7253
|
-
const { existsSync:
|
|
7254
|
-
const
|
|
7255
|
-
const modelPath =
|
|
7256
|
-
if (!
|
|
7462
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
7463
|
+
const path21 = await import("path");
|
|
7464
|
+
const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
7465
|
+
if (!existsSync18(modelPath)) {
|
|
7257
7466
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
7258
7467
|
}
|
|
7259
7468
|
const llama = await llamaCpp.getLlama();
|
|
@@ -7511,8 +7720,8 @@ __export(worktree_exports, {
|
|
|
7511
7720
|
worktreePath: () => worktreePath
|
|
7512
7721
|
});
|
|
7513
7722
|
import { execSync as execSync8 } from "child_process";
|
|
7514
|
-
import { existsSync as
|
|
7515
|
-
import
|
|
7723
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, realpathSync } from "fs";
|
|
7724
|
+
import path20 from "path";
|
|
7516
7725
|
function getGitRoot(dir) {
|
|
7517
7726
|
try {
|
|
7518
7727
|
const root = execSync8("git rev-parse --show-toplevel", {
|
|
@@ -7532,14 +7741,14 @@ function getMainRepoRoot(dir) {
|
|
|
7532
7741
|
"git rev-parse --path-format=absolute --git-common-dir",
|
|
7533
7742
|
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
7534
7743
|
).trim();
|
|
7535
|
-
return realpath(
|
|
7744
|
+
return realpath(path20.dirname(commonDir));
|
|
7536
7745
|
} catch {
|
|
7537
7746
|
return null;
|
|
7538
7747
|
}
|
|
7539
7748
|
}
|
|
7540
7749
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
7541
7750
|
const label = instanceLabel(employeeName, instance);
|
|
7542
|
-
return
|
|
7751
|
+
return path20.join(repoRoot, ".worktrees", label);
|
|
7543
7752
|
}
|
|
7544
7753
|
function worktreeBranch(employeeName, instance) {
|
|
7545
7754
|
return `${instanceLabel(employeeName, instance)}-work`;
|
|
@@ -7552,10 +7761,10 @@ function ensureWorktree(projectDir, employeeName, instance) {
|
|
|
7552
7761
|
if (!repoRoot) return null;
|
|
7553
7762
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
7554
7763
|
const branch = worktreeBranch(employeeName, instance);
|
|
7555
|
-
if (
|
|
7764
|
+
if (existsSync17(path20.join(wtPath, ".git"))) {
|
|
7556
7765
|
return wtPath;
|
|
7557
7766
|
}
|
|
7558
|
-
const worktreesDir =
|
|
7767
|
+
const worktreesDir = path20.join(repoRoot, ".worktrees");
|
|
7559
7768
|
mkdirSync8(worktreesDir, { recursive: true });
|
|
7560
7769
|
ensureGitignoreEntry(repoRoot, "/.worktrees/");
|
|
7561
7770
|
try {
|
|
@@ -7611,7 +7820,7 @@ function cleanupWorktree(projectDir, employeeName, instance) {
|
|
|
7611
7820
|
if (!repoRoot) return { cleaned: false, reason: "not a git repo" };
|
|
7612
7821
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
7613
7822
|
const branch = worktreeBranch(employeeName, instance);
|
|
7614
|
-
if (!
|
|
7823
|
+
if (!existsSync17(wtPath)) {
|
|
7615
7824
|
return { cleaned: false, reason: "worktree does not exist" };
|
|
7616
7825
|
}
|
|
7617
7826
|
if (isWorktreeDirty(wtPath)) {
|
|
@@ -7689,9 +7898,9 @@ function realpath(p) {
|
|
|
7689
7898
|
}
|
|
7690
7899
|
function ensureGitignoreEntry(repoRoot, entry) {
|
|
7691
7900
|
try {
|
|
7692
|
-
const gitignorePath =
|
|
7693
|
-
if (
|
|
7694
|
-
const content =
|
|
7901
|
+
const gitignorePath = path20.join(repoRoot, ".gitignore");
|
|
7902
|
+
if (existsSync17(gitignorePath)) {
|
|
7903
|
+
const content = readFileSync13(gitignorePath, "utf-8");
|
|
7695
7904
|
if (content.includes(entry)) return;
|
|
7696
7905
|
appendFileSync2(gitignorePath, `
|
|
7697
7906
|
# Agent worktrees (exe-os)
|
|
@@ -7715,7 +7924,7 @@ init_store();
|
|
|
7715
7924
|
init_database();
|
|
7716
7925
|
init_task_scope();
|
|
7717
7926
|
init_project_name();
|
|
7718
|
-
import
|
|
7927
|
+
import crypto7 from "crypto";
|
|
7719
7928
|
import { execSync as execSync9 } from "child_process";
|
|
7720
7929
|
var agentName = process.argv[2];
|
|
7721
7930
|
var exeSession = process.argv[3];
|
|
@@ -7769,7 +7978,7 @@ try {
|
|
|
7769
7978
|
} catch {
|
|
7770
7979
|
}
|
|
7771
7980
|
await writeMemory({
|
|
7772
|
-
id:
|
|
7981
|
+
id: crypto7.randomUUID(),
|
|
7773
7982
|
agent_id: agentName,
|
|
7774
7983
|
agent_role: "employee",
|
|
7775
7984
|
session_id: `cleanup-${Date.now()}`,
|