@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
|
@@ -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(),
|
|
@@ -4447,12 +4627,12 @@ __export(tasks_crud_exports, {
|
|
|
4447
4627
|
updateTaskStatus: () => updateTaskStatus,
|
|
4448
4628
|
writeCheckpoint: () => writeCheckpoint
|
|
4449
4629
|
});
|
|
4450
|
-
import
|
|
4451
|
-
import
|
|
4452
|
-
import
|
|
4630
|
+
import crypto4 from "crypto";
|
|
4631
|
+
import path14 from "path";
|
|
4632
|
+
import os10 from "os";
|
|
4453
4633
|
import { execSync as execSync4 } from "child_process";
|
|
4454
4634
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4455
|
-
import { existsSync as
|
|
4635
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
4456
4636
|
async function writeCheckpoint(input) {
|
|
4457
4637
|
const client = getClient();
|
|
4458
4638
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -4568,7 +4748,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
4568
4748
|
}
|
|
4569
4749
|
async function createTaskCore(input) {
|
|
4570
4750
|
const client = getClient();
|
|
4571
|
-
const id =
|
|
4751
|
+
const id = crypto4.randomUUID();
|
|
4572
4752
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4573
4753
|
const slug = slugify(input.title);
|
|
4574
4754
|
let earlySessionScope = null;
|
|
@@ -4627,8 +4807,8 @@ ${laneWarning}` : laneWarning;
|
|
|
4627
4807
|
}
|
|
4628
4808
|
if (input.baseDir) {
|
|
4629
4809
|
try {
|
|
4630
|
-
await mkdir4(
|
|
4631
|
-
await mkdir4(
|
|
4810
|
+
await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
4811
|
+
await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
4632
4812
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
4633
4813
|
await ensureGitignoreExe(input.baseDir);
|
|
4634
4814
|
} catch {
|
|
@@ -4664,13 +4844,19 @@ ${laneWarning}` : laneWarning;
|
|
|
4664
4844
|
});
|
|
4665
4845
|
if (input.baseDir) {
|
|
4666
4846
|
try {
|
|
4667
|
-
const EXE_OS_DIR =
|
|
4668
|
-
const mdPath =
|
|
4669
|
-
const mdDir =
|
|
4670
|
-
if (!
|
|
4847
|
+
const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
|
|
4848
|
+
const mdPath = path14.join(EXE_OS_DIR, taskFile);
|
|
4849
|
+
const mdDir = path14.dirname(mdPath);
|
|
4850
|
+
if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
4671
4851
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
4672
4852
|
const mdContent = `# ${input.title}
|
|
4673
4853
|
|
|
4854
|
+
## MANDATORY: When done
|
|
4855
|
+
|
|
4856
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4857
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4858
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4859
|
+
|
|
4674
4860
|
**ID:** ${id}
|
|
4675
4861
|
**Status:** ${initialStatus}
|
|
4676
4862
|
**Priority:** ${input.priority}
|
|
@@ -4684,12 +4870,6 @@ ${laneWarning}` : laneWarning;
|
|
|
4684
4870
|
## Context
|
|
4685
4871
|
|
|
4686
4872
|
${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
4873
|
`;
|
|
4694
4874
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
4695
4875
|
} catch (err) {
|
|
@@ -4938,7 +5118,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4938
5118
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4939
5119
|
} catch {
|
|
4940
5120
|
}
|
|
4941
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5121
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4942
5122
|
try {
|
|
4943
5123
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4944
5124
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -4967,9 +5147,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4967
5147
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4968
5148
|
}
|
|
4969
5149
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4970
|
-
const archPath =
|
|
5150
|
+
const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4971
5151
|
try {
|
|
4972
|
-
if (
|
|
5152
|
+
if (existsSync14(archPath)) return;
|
|
4973
5153
|
const template = [
|
|
4974
5154
|
`# ${projectName} \u2014 System Architecture`,
|
|
4975
5155
|
"",
|
|
@@ -5002,10 +5182,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5002
5182
|
}
|
|
5003
5183
|
}
|
|
5004
5184
|
async function ensureGitignoreExe(baseDir) {
|
|
5005
|
-
const gitignorePath =
|
|
5185
|
+
const gitignorePath = path14.join(baseDir, ".gitignore");
|
|
5006
5186
|
try {
|
|
5007
|
-
if (
|
|
5008
|
-
const content =
|
|
5187
|
+
if (existsSync14(gitignorePath)) {
|
|
5188
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5009
5189
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5010
5190
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5011
5191
|
} else {
|
|
@@ -5048,8 +5228,8 @@ __export(tasks_review_exports, {
|
|
|
5048
5228
|
isStale: () => isStale,
|
|
5049
5229
|
listPendingReviews: () => listPendingReviews
|
|
5050
5230
|
});
|
|
5051
|
-
import
|
|
5052
|
-
import { existsSync as
|
|
5231
|
+
import path15 from "path";
|
|
5232
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5053
5233
|
function formatAge(isoTimestamp) {
|
|
5054
5234
|
if (!isoTimestamp) return "";
|
|
5055
5235
|
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
@@ -5067,54 +5247,38 @@ function isStale(isoTimestamp) {
|
|
|
5067
5247
|
}
|
|
5068
5248
|
async function countPendingReviews(sessionScope) {
|
|
5069
5249
|
const client = getClient();
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
args: [sessionScope]
|
|
5074
|
-
});
|
|
5075
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5076
|
-
}
|
|
5250
|
+
const scope = strictSessionScopeFilter(
|
|
5251
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5252
|
+
);
|
|
5077
5253
|
const result = await client.execute({
|
|
5078
|
-
sql:
|
|
5079
|
-
|
|
5254
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5255
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
5256
|
+
args: [...scope.args]
|
|
5080
5257
|
});
|
|
5081
5258
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5082
5259
|
}
|
|
5083
5260
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
5084
5261
|
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
|
-
}
|
|
5262
|
+
const scope = strictSessionScopeFilter(
|
|
5263
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5264
|
+
);
|
|
5094
5265
|
const result = await client.execute({
|
|
5095
5266
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5096
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
5097
|
-
args: [sinceIso]
|
|
5267
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
5268
|
+
args: [sinceIso, ...scope.args]
|
|
5098
5269
|
});
|
|
5099
5270
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5100
5271
|
}
|
|
5101
5272
|
async function listPendingReviews(limit, sessionScope) {
|
|
5102
5273
|
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
|
-
}
|
|
5274
|
+
const scope = strictSessionScopeFilter(
|
|
5275
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
5276
|
+
);
|
|
5113
5277
|
const result = await client.execute({
|
|
5114
5278
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
5115
|
-
WHERE status = 'needs_review'
|
|
5279
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
5116
5280
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
5117
|
-
args: [limit]
|
|
5281
|
+
args: [...scope.args, limit]
|
|
5118
5282
|
});
|
|
5119
5283
|
return result.rows;
|
|
5120
5284
|
}
|
|
@@ -5126,7 +5290,7 @@ async function cleanupOrphanedReviews() {
|
|
|
5126
5290
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
5127
5291
|
AND assigned_by = 'system'
|
|
5128
5292
|
AND title LIKE 'Review:%'
|
|
5129
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
5293
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
5130
5294
|
args: [now]
|
|
5131
5295
|
});
|
|
5132
5296
|
const r1b = await client.execute({
|
|
@@ -5334,11 +5498,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5334
5498
|
);
|
|
5335
5499
|
}
|
|
5336
5500
|
try {
|
|
5337
|
-
const cacheDir =
|
|
5338
|
-
if (
|
|
5501
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
5502
|
+
if (existsSync15(cacheDir)) {
|
|
5339
5503
|
for (const f of readdirSync3(cacheDir)) {
|
|
5340
5504
|
if (f.startsWith("review-notified-")) {
|
|
5341
|
-
unlinkSync4(
|
|
5505
|
+
unlinkSync4(path15.join(cacheDir, f));
|
|
5342
5506
|
}
|
|
5343
5507
|
}
|
|
5344
5508
|
}
|
|
@@ -5355,11 +5519,12 @@ var init_tasks_review = __esm({
|
|
|
5355
5519
|
init_tmux_routing();
|
|
5356
5520
|
init_session_key();
|
|
5357
5521
|
init_state_bus();
|
|
5522
|
+
init_task_scope();
|
|
5358
5523
|
}
|
|
5359
5524
|
});
|
|
5360
5525
|
|
|
5361
5526
|
// src/lib/tasks-chain.ts
|
|
5362
|
-
import
|
|
5527
|
+
import path16 from "path";
|
|
5363
5528
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5364
5529
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5365
5530
|
const client = getClient();
|
|
@@ -5376,7 +5541,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5376
5541
|
});
|
|
5377
5542
|
for (const ur of unblockedRows.rows) {
|
|
5378
5543
|
try {
|
|
5379
|
-
const ubFile =
|
|
5544
|
+
const ubFile = path16.join(baseDir, String(ur.task_file));
|
|
5380
5545
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5381
5546
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5382
5547
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5411,7 +5576,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
5411
5576
|
const scScope = sessionScopeFilter();
|
|
5412
5577
|
const remaining = await client.execute({
|
|
5413
5578
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5414
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
5579
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
5415
5580
|
args: [parentTaskId, ...scScope.args]
|
|
5416
5581
|
});
|
|
5417
5582
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -5445,7 +5610,7 @@ var init_tasks_chain = __esm({
|
|
|
5445
5610
|
|
|
5446
5611
|
// src/lib/project-name.ts
|
|
5447
5612
|
import { execSync as execSync5 } from "child_process";
|
|
5448
|
-
import
|
|
5613
|
+
import path17 from "path";
|
|
5449
5614
|
function getProjectName(cwd) {
|
|
5450
5615
|
const dir = cwd ?? process.cwd();
|
|
5451
5616
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -5458,7 +5623,7 @@ function getProjectName(cwd) {
|
|
|
5458
5623
|
timeout: 2e3,
|
|
5459
5624
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5460
5625
|
}).trim();
|
|
5461
|
-
repoRoot =
|
|
5626
|
+
repoRoot = path17.dirname(gitCommonDir);
|
|
5462
5627
|
} catch {
|
|
5463
5628
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
5464
5629
|
cwd: dir,
|
|
@@ -5467,11 +5632,11 @@ function getProjectName(cwd) {
|
|
|
5467
5632
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5468
5633
|
}).trim();
|
|
5469
5634
|
}
|
|
5470
|
-
_cached2 =
|
|
5635
|
+
_cached2 = path17.basename(repoRoot);
|
|
5471
5636
|
_cachedCwd = dir;
|
|
5472
5637
|
return _cached2;
|
|
5473
5638
|
} catch {
|
|
5474
|
-
_cached2 =
|
|
5639
|
+
_cached2 = path17.basename(dir);
|
|
5475
5640
|
_cachedCwd = dir;
|
|
5476
5641
|
return _cached2;
|
|
5477
5642
|
}
|
|
@@ -5614,10 +5779,10 @@ var init_tasks_notify = __esm({
|
|
|
5614
5779
|
});
|
|
5615
5780
|
|
|
5616
5781
|
// src/lib/behaviors.ts
|
|
5617
|
-
import
|
|
5782
|
+
import crypto5 from "crypto";
|
|
5618
5783
|
async function storeBehavior(opts) {
|
|
5619
5784
|
const client = getClient();
|
|
5620
|
-
const id =
|
|
5785
|
+
const id = crypto5.randomUUID();
|
|
5621
5786
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5622
5787
|
await client.execute({
|
|
5623
5788
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -5646,7 +5811,7 @@ __export(skill_learning_exports, {
|
|
|
5646
5811
|
storeTrajectory: () => storeTrajectory,
|
|
5647
5812
|
sweepTrajectories: () => sweepTrajectories
|
|
5648
5813
|
});
|
|
5649
|
-
import
|
|
5814
|
+
import crypto6 from "crypto";
|
|
5650
5815
|
async function extractTrajectory(taskId, agentId) {
|
|
5651
5816
|
const client = getClient();
|
|
5652
5817
|
const result = await client.execute({
|
|
@@ -5675,11 +5840,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
5675
5840
|
return signature;
|
|
5676
5841
|
}
|
|
5677
5842
|
function hashSignature(signature) {
|
|
5678
|
-
return
|
|
5843
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
5679
5844
|
}
|
|
5680
5845
|
async function storeTrajectory(opts) {
|
|
5681
5846
|
const client = getClient();
|
|
5682
|
-
const id =
|
|
5847
|
+
const id = crypto6.randomUUID();
|
|
5683
5848
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5684
5849
|
const signatureHash = hashSignature(opts.signature);
|
|
5685
5850
|
await client.execute({
|
|
@@ -5944,8 +6109,8 @@ __export(tasks_exports, {
|
|
|
5944
6109
|
updateTaskStatus: () => updateTaskStatus,
|
|
5945
6110
|
writeCheckpoint: () => writeCheckpoint
|
|
5946
6111
|
});
|
|
5947
|
-
import
|
|
5948
|
-
import { writeFileSync as
|
|
6112
|
+
import path18 from "path";
|
|
6113
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5949
6114
|
async function createTask(input) {
|
|
5950
6115
|
const result = await createTaskCore(input);
|
|
5951
6116
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5964,12 +6129,12 @@ async function updateTask(input) {
|
|
|
5964
6129
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5965
6130
|
try {
|
|
5966
6131
|
const agent = String(row.assigned_to);
|
|
5967
|
-
const cacheDir =
|
|
5968
|
-
const cachePath =
|
|
6132
|
+
const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
|
|
6133
|
+
const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
|
|
5969
6134
|
if (input.status === "in_progress") {
|
|
5970
6135
|
mkdirSync6(cacheDir, { recursive: true });
|
|
5971
|
-
|
|
5972
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
6136
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6137
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5973
6138
|
try {
|
|
5974
6139
|
unlinkSync5(cachePath);
|
|
5975
6140
|
} catch {
|
|
@@ -5977,10 +6142,10 @@ async function updateTask(input) {
|
|
|
5977
6142
|
}
|
|
5978
6143
|
} catch {
|
|
5979
6144
|
}
|
|
5980
|
-
if (input.status === "done") {
|
|
6145
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5981
6146
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5982
6147
|
}
|
|
5983
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
6148
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5984
6149
|
try {
|
|
5985
6150
|
const client = getClient();
|
|
5986
6151
|
const taskTitle = String(row.title);
|
|
@@ -5996,7 +6161,7 @@ async function updateTask(input) {
|
|
|
5996
6161
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5997
6162
|
try {
|
|
5998
6163
|
const draftClient = getClient();
|
|
5999
|
-
if (input.status === "done") {
|
|
6164
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6000
6165
|
await draftClient.execute({
|
|
6001
6166
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
6002
6167
|
args: [assignedAgent]
|
|
@@ -6013,7 +6178,7 @@ async function updateTask(input) {
|
|
|
6013
6178
|
try {
|
|
6014
6179
|
const client = getClient();
|
|
6015
6180
|
const cascaded = await client.execute({
|
|
6016
|
-
sql: `UPDATE tasks SET status = '
|
|
6181
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
6017
6182
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
6018
6183
|
args: [now, taskId]
|
|
6019
6184
|
});
|
|
@@ -6026,14 +6191,14 @@ async function updateTask(input) {
|
|
|
6026
6191
|
} catch {
|
|
6027
6192
|
}
|
|
6028
6193
|
}
|
|
6029
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
6194
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
6030
6195
|
if (isTerminal) {
|
|
6031
6196
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
6032
6197
|
if (!isCoordinator) {
|
|
6033
6198
|
notifyTaskDone();
|
|
6034
6199
|
}
|
|
6035
6200
|
await markTaskNotificationsRead(taskFile);
|
|
6036
|
-
if (input.status === "done") {
|
|
6201
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6037
6202
|
try {
|
|
6038
6203
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
6039
6204
|
} catch {
|
|
@@ -6053,7 +6218,7 @@ async function updateTask(input) {
|
|
|
6053
6218
|
}
|
|
6054
6219
|
}
|
|
6055
6220
|
}
|
|
6056
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6221
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6057
6222
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6058
6223
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
6059
6224
|
taskId,
|
|
@@ -6425,6 +6590,7 @@ __export(tmux_routing_exports, {
|
|
|
6425
6590
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
6426
6591
|
isExeSession: () => isExeSession,
|
|
6427
6592
|
isSessionBusy: () => isSessionBusy,
|
|
6593
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
6428
6594
|
notifyParentExe: () => notifyParentExe,
|
|
6429
6595
|
parseParentExe: () => parseParentExe,
|
|
6430
6596
|
registerParentExe: () => registerParentExe,
|
|
@@ -6435,13 +6601,13 @@ __export(tmux_routing_exports, {
|
|
|
6435
6601
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6436
6602
|
});
|
|
6437
6603
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6438
|
-
import { readFileSync as
|
|
6439
|
-
import
|
|
6440
|
-
import
|
|
6604
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
6605
|
+
import path19 from "path";
|
|
6606
|
+
import os11 from "os";
|
|
6441
6607
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6442
6608
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6443
6609
|
function spawnLockPath(sessionName) {
|
|
6444
|
-
return
|
|
6610
|
+
return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6445
6611
|
}
|
|
6446
6612
|
function isProcessAlive(pid) {
|
|
6447
6613
|
try {
|
|
@@ -6452,13 +6618,13 @@ function isProcessAlive(pid) {
|
|
|
6452
6618
|
}
|
|
6453
6619
|
}
|
|
6454
6620
|
function acquireSpawnLock2(sessionName) {
|
|
6455
|
-
if (!
|
|
6621
|
+
if (!existsSync16(SPAWN_LOCK_DIR)) {
|
|
6456
6622
|
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
6457
6623
|
}
|
|
6458
6624
|
const lockFile = spawnLockPath(sessionName);
|
|
6459
|
-
if (
|
|
6625
|
+
if (existsSync16(lockFile)) {
|
|
6460
6626
|
try {
|
|
6461
|
-
const lock = JSON.parse(
|
|
6627
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
6462
6628
|
const age = Date.now() - lock.timestamp;
|
|
6463
6629
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
6464
6630
|
return false;
|
|
@@ -6466,7 +6632,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
6466
6632
|
} catch {
|
|
6467
6633
|
}
|
|
6468
6634
|
}
|
|
6469
|
-
|
|
6635
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
6470
6636
|
return true;
|
|
6471
6637
|
}
|
|
6472
6638
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -6478,13 +6644,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6478
6644
|
function resolveBehaviorsExporterScript() {
|
|
6479
6645
|
try {
|
|
6480
6646
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6481
|
-
const scriptPath =
|
|
6482
|
-
|
|
6647
|
+
const scriptPath = path19.join(
|
|
6648
|
+
path19.dirname(thisFile),
|
|
6483
6649
|
"..",
|
|
6484
6650
|
"bin",
|
|
6485
6651
|
"exe-export-behaviors.js"
|
|
6486
6652
|
);
|
|
6487
|
-
return
|
|
6653
|
+
return existsSync16(scriptPath) ? scriptPath : null;
|
|
6488
6654
|
} catch {
|
|
6489
6655
|
return null;
|
|
6490
6656
|
}
|
|
@@ -6550,12 +6716,12 @@ function extractRootExe(name) {
|
|
|
6550
6716
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6551
6717
|
}
|
|
6552
6718
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
6553
|
-
if (!
|
|
6719
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
6554
6720
|
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6555
6721
|
}
|
|
6556
6722
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6557
|
-
const filePath =
|
|
6558
|
-
|
|
6723
|
+
const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6724
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
6559
6725
|
parentExe: rootExe,
|
|
6560
6726
|
dispatchedBy: dispatchedBy || rootExe,
|
|
6561
6727
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6563,7 +6729,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6563
6729
|
}
|
|
6564
6730
|
function getParentExe(sessionKey) {
|
|
6565
6731
|
try {
|
|
6566
|
-
const data = JSON.parse(
|
|
6732
|
+
const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6567
6733
|
return data.parentExe || null;
|
|
6568
6734
|
} catch {
|
|
6569
6735
|
return null;
|
|
@@ -6571,8 +6737,8 @@ function getParentExe(sessionKey) {
|
|
|
6571
6737
|
}
|
|
6572
6738
|
function getDispatchedBy(sessionKey) {
|
|
6573
6739
|
try {
|
|
6574
|
-
const data = JSON.parse(
|
|
6575
|
-
|
|
6740
|
+
const data = JSON.parse(readFileSync12(
|
|
6741
|
+
path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6576
6742
|
"utf8"
|
|
6577
6743
|
));
|
|
6578
6744
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6642,8 +6808,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
6642
6808
|
}
|
|
6643
6809
|
function readDebounceState() {
|
|
6644
6810
|
try {
|
|
6645
|
-
if (!
|
|
6646
|
-
const raw = JSON.parse(
|
|
6811
|
+
if (!existsSync16(DEBOUNCE_FILE)) return {};
|
|
6812
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
6647
6813
|
const state = {};
|
|
6648
6814
|
for (const [key, val] of Object.entries(raw)) {
|
|
6649
6815
|
if (typeof val === "number") {
|
|
@@ -6659,8 +6825,8 @@ function readDebounceState() {
|
|
|
6659
6825
|
}
|
|
6660
6826
|
function writeDebounceState(state) {
|
|
6661
6827
|
try {
|
|
6662
|
-
if (!
|
|
6663
|
-
|
|
6828
|
+
if (!existsSync16(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6829
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
6664
6830
|
} catch {
|
|
6665
6831
|
}
|
|
6666
6832
|
}
|
|
@@ -6758,8 +6924,8 @@ function sendIntercom(targetSession) {
|
|
|
6758
6924
|
try {
|
|
6759
6925
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6760
6926
|
const agent = baseAgentName(rawAgent);
|
|
6761
|
-
const markerPath =
|
|
6762
|
-
if (
|
|
6927
|
+
const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6928
|
+
if (existsSync16(markerPath)) {
|
|
6763
6929
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
6764
6930
|
return "debounced";
|
|
6765
6931
|
}
|
|
@@ -6768,8 +6934,8 @@ function sendIntercom(targetSession) {
|
|
|
6768
6934
|
try {
|
|
6769
6935
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
6770
6936
|
const agent = baseAgentName(rawAgent);
|
|
6771
|
-
const taskDir =
|
|
6772
|
-
if (
|
|
6937
|
+
const taskDir = path19.join(process.cwd(), "exe", agent);
|
|
6938
|
+
if (existsSync16(taskDir)) {
|
|
6773
6939
|
const files = readdirSync4(taskDir).filter(
|
|
6774
6940
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
6775
6941
|
);
|
|
@@ -6829,6 +6995,21 @@ function notifyParentExe(sessionKey) {
|
|
|
6829
6995
|
}
|
|
6830
6996
|
return true;
|
|
6831
6997
|
}
|
|
6998
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName2, taskTitle) {
|
|
6999
|
+
const transport = getTransport();
|
|
7000
|
+
try {
|
|
7001
|
+
const sessions = transport.listSessions();
|
|
7002
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
7003
|
+
execSync6(
|
|
7004
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
7005
|
+
{ timeout: 3e3 }
|
|
7006
|
+
);
|
|
7007
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}")`);
|
|
7008
|
+
return true;
|
|
7009
|
+
} catch {
|
|
7010
|
+
return false;
|
|
7011
|
+
}
|
|
7012
|
+
}
|
|
6832
7013
|
function ensureEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
6833
7014
|
if (isCoordinatorName(employeeName)) {
|
|
6834
7015
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6902,26 +7083,26 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6902
7083
|
const transport = getTransport();
|
|
6903
7084
|
const sessionName = employeeSessionName(employeeName, exeSession2, opts?.instance);
|
|
6904
7085
|
const instanceLabel2 = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6905
|
-
const logDir =
|
|
6906
|
-
const logFile =
|
|
6907
|
-
if (!
|
|
7086
|
+
const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
|
|
7087
|
+
const logFile = path19.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
|
|
7088
|
+
if (!existsSync16(logDir)) {
|
|
6908
7089
|
mkdirSync7(logDir, { recursive: true });
|
|
6909
7090
|
}
|
|
6910
7091
|
transport.kill(sessionName);
|
|
6911
7092
|
let cleanupSuffix = "";
|
|
6912
7093
|
try {
|
|
6913
7094
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6914
|
-
const cleanupScript =
|
|
6915
|
-
if (
|
|
7095
|
+
const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7096
|
+
if (existsSync16(cleanupScript)) {
|
|
6916
7097
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession2}"`;
|
|
6917
7098
|
}
|
|
6918
7099
|
} catch {
|
|
6919
7100
|
}
|
|
6920
7101
|
try {
|
|
6921
|
-
const claudeJsonPath =
|
|
7102
|
+
const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
|
|
6922
7103
|
let claudeJson = {};
|
|
6923
7104
|
try {
|
|
6924
|
-
claudeJson = JSON.parse(
|
|
7105
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6925
7106
|
} catch {
|
|
6926
7107
|
}
|
|
6927
7108
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6929,17 +7110,17 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6929
7110
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6930
7111
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6931
7112
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6932
|
-
|
|
7113
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6933
7114
|
} catch {
|
|
6934
7115
|
}
|
|
6935
7116
|
try {
|
|
6936
|
-
const settingsDir =
|
|
7117
|
+
const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
|
|
6937
7118
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6938
|
-
const projSettingsDir =
|
|
6939
|
-
const settingsPath =
|
|
7119
|
+
const projSettingsDir = path19.join(settingsDir, normalizedKey);
|
|
7120
|
+
const settingsPath = path19.join(projSettingsDir, "settings.json");
|
|
6940
7121
|
let settings = {};
|
|
6941
7122
|
try {
|
|
6942
|
-
settings = JSON.parse(
|
|
7123
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
6943
7124
|
} catch {
|
|
6944
7125
|
}
|
|
6945
7126
|
const perms = settings.permissions ?? {};
|
|
@@ -6968,7 +7149,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6968
7149
|
perms.allow = allow;
|
|
6969
7150
|
settings.permissions = perms;
|
|
6970
7151
|
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6971
|
-
|
|
7152
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6972
7153
|
}
|
|
6973
7154
|
} catch {
|
|
6974
7155
|
}
|
|
@@ -6983,8 +7164,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6983
7164
|
let behaviorsFlag = "";
|
|
6984
7165
|
let legacyFallbackWarned = false;
|
|
6985
7166
|
if (!useExeAgent && !useBinSymlink) {
|
|
6986
|
-
const identityPath =
|
|
6987
|
-
|
|
7167
|
+
const identityPath = path19.join(
|
|
7168
|
+
os11.homedir(),
|
|
6988
7169
|
".exe-os",
|
|
6989
7170
|
"identity",
|
|
6990
7171
|
`${employeeName}.md`
|
|
@@ -6993,13 +7174,13 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6993
7174
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6994
7175
|
if (hasAgentFlag) {
|
|
6995
7176
|
identityFlag = ` --agent ${employeeName}`;
|
|
6996
|
-
} else if (
|
|
7177
|
+
} else if (existsSync16(identityPath)) {
|
|
6997
7178
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6998
7179
|
legacyFallbackWarned = true;
|
|
6999
7180
|
}
|
|
7000
7181
|
const behaviorsFile = exportBehaviorsSync(
|
|
7001
7182
|
employeeName,
|
|
7002
|
-
|
|
7183
|
+
path19.basename(spawnCwd),
|
|
7003
7184
|
sessionName
|
|
7004
7185
|
);
|
|
7005
7186
|
if (behaviorsFile) {
|
|
@@ -7014,16 +7195,16 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
7014
7195
|
}
|
|
7015
7196
|
let sessionContextFlag = "";
|
|
7016
7197
|
try {
|
|
7017
|
-
const ctxDir =
|
|
7198
|
+
const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7018
7199
|
mkdirSync7(ctxDir, { recursive: true });
|
|
7019
|
-
const ctxFile =
|
|
7200
|
+
const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7020
7201
|
const ctxContent = [
|
|
7021
7202
|
`## Session Context`,
|
|
7022
7203
|
`You are running in tmux session: ${sessionName}.`,
|
|
7023
7204
|
`Your parent coordinator session is ${exeSession2}.`,
|
|
7024
7205
|
`Your employees (if any) use the -${exeSession2} suffix.`
|
|
7025
7206
|
].join("\n");
|
|
7026
|
-
|
|
7207
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
7027
7208
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
7028
7209
|
} catch {
|
|
7029
7210
|
}
|
|
@@ -7100,8 +7281,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
7100
7281
|
transport.pipeLog(sessionName, logFile);
|
|
7101
7282
|
try {
|
|
7102
7283
|
const mySession = getMySession();
|
|
7103
|
-
const dispatchInfo =
|
|
7104
|
-
|
|
7284
|
+
const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7285
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
7105
7286
|
dispatchedBy: mySession,
|
|
7106
7287
|
rootExe: exeSession2,
|
|
7107
7288
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -7175,15 +7356,15 @@ var init_tmux_routing = __esm({
|
|
|
7175
7356
|
init_intercom_queue();
|
|
7176
7357
|
init_plan_limits();
|
|
7177
7358
|
init_employees();
|
|
7178
|
-
SPAWN_LOCK_DIR =
|
|
7179
|
-
SESSION_CACHE =
|
|
7359
|
+
SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
7360
|
+
SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
|
|
7180
7361
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7181
7362
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7182
7363
|
VERIFY_PANE_LINES = 200;
|
|
7183
7364
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7184
7365
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7185
|
-
INTERCOM_LOG2 =
|
|
7186
|
-
DEBOUNCE_FILE =
|
|
7366
|
+
INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
7367
|
+
DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7187
7368
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7188
7369
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7189
7370
|
}
|
|
@@ -7206,6 +7387,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
|
|
|
7206
7387
|
args: [scope]
|
|
7207
7388
|
};
|
|
7208
7389
|
}
|
|
7390
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
7391
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
7392
|
+
if (!scope) return { sql: "", args: [] };
|
|
7393
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
7394
|
+
return {
|
|
7395
|
+
sql: ` AND ${col} = ?`,
|
|
7396
|
+
args: [scope]
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7209
7399
|
var init_task_scope = __esm({
|
|
7210
7400
|
"src/lib/task-scope.ts"() {
|
|
7211
7401
|
"use strict";
|
|
@@ -7250,10 +7440,10 @@ async function disposeEmbedder() {
|
|
|
7250
7440
|
async function embedDirect(text) {
|
|
7251
7441
|
const llamaCpp = await import("node-llama-cpp");
|
|
7252
7442
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
7253
|
-
const { existsSync:
|
|
7254
|
-
const
|
|
7255
|
-
const modelPath =
|
|
7256
|
-
if (!
|
|
7443
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
7444
|
+
const path21 = await import("path");
|
|
7445
|
+
const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
7446
|
+
if (!existsSync18(modelPath)) {
|
|
7257
7447
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
7258
7448
|
}
|
|
7259
7449
|
const llama = await llamaCpp.getLlama();
|
|
@@ -7511,8 +7701,8 @@ __export(worktree_exports, {
|
|
|
7511
7701
|
worktreePath: () => worktreePath
|
|
7512
7702
|
});
|
|
7513
7703
|
import { execSync as execSync8 } from "child_process";
|
|
7514
|
-
import { existsSync as
|
|
7515
|
-
import
|
|
7704
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, realpathSync } from "fs";
|
|
7705
|
+
import path20 from "path";
|
|
7516
7706
|
function getGitRoot(dir) {
|
|
7517
7707
|
try {
|
|
7518
7708
|
const root = execSync8("git rev-parse --show-toplevel", {
|
|
@@ -7532,14 +7722,14 @@ function getMainRepoRoot(dir) {
|
|
|
7532
7722
|
"git rev-parse --path-format=absolute --git-common-dir",
|
|
7533
7723
|
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
7534
7724
|
).trim();
|
|
7535
|
-
return realpath(
|
|
7725
|
+
return realpath(path20.dirname(commonDir));
|
|
7536
7726
|
} catch {
|
|
7537
7727
|
return null;
|
|
7538
7728
|
}
|
|
7539
7729
|
}
|
|
7540
7730
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
7541
7731
|
const label = instanceLabel(employeeName, instance);
|
|
7542
|
-
return
|
|
7732
|
+
return path20.join(repoRoot, ".worktrees", label);
|
|
7543
7733
|
}
|
|
7544
7734
|
function worktreeBranch(employeeName, instance) {
|
|
7545
7735
|
return `${instanceLabel(employeeName, instance)}-work`;
|
|
@@ -7552,10 +7742,10 @@ function ensureWorktree(projectDir, employeeName, instance) {
|
|
|
7552
7742
|
if (!repoRoot) return null;
|
|
7553
7743
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
7554
7744
|
const branch = worktreeBranch(employeeName, instance);
|
|
7555
|
-
if (
|
|
7745
|
+
if (existsSync17(path20.join(wtPath, ".git"))) {
|
|
7556
7746
|
return wtPath;
|
|
7557
7747
|
}
|
|
7558
|
-
const worktreesDir =
|
|
7748
|
+
const worktreesDir = path20.join(repoRoot, ".worktrees");
|
|
7559
7749
|
mkdirSync8(worktreesDir, { recursive: true });
|
|
7560
7750
|
ensureGitignoreEntry(repoRoot, "/.worktrees/");
|
|
7561
7751
|
try {
|
|
@@ -7611,7 +7801,7 @@ function cleanupWorktree(projectDir, employeeName, instance) {
|
|
|
7611
7801
|
if (!repoRoot) return { cleaned: false, reason: "not a git repo" };
|
|
7612
7802
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
7613
7803
|
const branch = worktreeBranch(employeeName, instance);
|
|
7614
|
-
if (!
|
|
7804
|
+
if (!existsSync17(wtPath)) {
|
|
7615
7805
|
return { cleaned: false, reason: "worktree does not exist" };
|
|
7616
7806
|
}
|
|
7617
7807
|
if (isWorktreeDirty(wtPath)) {
|
|
@@ -7689,9 +7879,9 @@ function realpath(p) {
|
|
|
7689
7879
|
}
|
|
7690
7880
|
function ensureGitignoreEntry(repoRoot, entry) {
|
|
7691
7881
|
try {
|
|
7692
|
-
const gitignorePath =
|
|
7693
|
-
if (
|
|
7694
|
-
const content =
|
|
7882
|
+
const gitignorePath = path20.join(repoRoot, ".gitignore");
|
|
7883
|
+
if (existsSync17(gitignorePath)) {
|
|
7884
|
+
const content = readFileSync13(gitignorePath, "utf-8");
|
|
7695
7885
|
if (content.includes(entry)) return;
|
|
7696
7886
|
appendFileSync2(gitignorePath, `
|
|
7697
7887
|
# Agent worktrees (exe-os)
|
|
@@ -7715,7 +7905,7 @@ init_store();
|
|
|
7715
7905
|
init_database();
|
|
7716
7906
|
init_task_scope();
|
|
7717
7907
|
init_project_name();
|
|
7718
|
-
import
|
|
7908
|
+
import crypto7 from "crypto";
|
|
7719
7909
|
import { execSync as execSync9 } from "child_process";
|
|
7720
7910
|
var agentName = process.argv[2];
|
|
7721
7911
|
var exeSession = process.argv[3];
|
|
@@ -7769,7 +7959,7 @@ try {
|
|
|
7769
7959
|
} catch {
|
|
7770
7960
|
}
|
|
7771
7961
|
await writeMemory({
|
|
7772
|
-
id:
|
|
7962
|
+
id: crypto7.randomUUID(),
|
|
7773
7963
|
agent_id: agentName,
|
|
7774
7964
|
agent_role: "employee",
|
|
7775
7965
|
session_id: `cleanup-${Date.now()}`,
|