@askexenow/exe-os 0.9.16 → 0.9.18
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 +1242 -909
- package/dist/bin/backfill-responses.js +1245 -912
- package/dist/bin/backfill-vectors.js +1244 -906
- package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
- package/dist/bin/cli.js +217 -107
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +62 -0
- package/dist/bin/exe-assign.js +346 -10
- package/dist/bin/exe-boot.js +387 -32
- package/dist/bin/exe-call.js +72 -2
- package/dist/bin/exe-cloud.js +8 -0
- package/dist/bin/exe-dispatch.js +1821 -225
- package/dist/bin/exe-doctor.js +720 -52
- package/dist/bin/exe-export-behaviors.js +1429 -148
- package/dist/bin/exe-forget.js +1408 -34
- package/dist/bin/exe-gateway.js +1629 -1295
- package/dist/bin/exe-heartbeat.js +1899 -448
- package/dist/bin/exe-kill.js +1624 -346
- package/dist/bin/exe-launch-agent.js +726 -90
- package/dist/bin/exe-link.js +27 -8
- package/dist/bin/exe-new-employee.js +75 -9
- package/dist/bin/exe-pending-messages.js +2769 -1316
- package/dist/bin/exe-pending-notifications.js +2829 -1376
- package/dist/bin/exe-pending-reviews.js +2847 -1392
- package/dist/bin/exe-rename.js +89 -8
- package/dist/bin/exe-review.js +1494 -312
- package/dist/bin/exe-search.js +1608 -1300
- package/dist/bin/exe-session-cleanup.js +194 -91
- package/dist/bin/exe-settings.js +10 -2
- package/dist/bin/exe-start-codex.js +769 -120
- package/dist/bin/exe-start-opencode.js +763 -108
- package/dist/bin/exe-status.js +1887 -434
- package/dist/bin/exe-team.js +1782 -324
- package/dist/bin/git-sweep.js +408 -33
- package/dist/bin/graph-backfill.js +681 -27
- package/dist/bin/graph-export.js +1419 -141
- package/dist/bin/install.js +4 -8
- package/dist/bin/intercom-check.js +8641 -0
- package/dist/bin/scan-tasks.js +553 -38
- package/dist/bin/setup.js +82 -10
- package/dist/bin/shard-migrate.js +682 -28
- package/dist/gateway/index.js +1629 -1295
- package/dist/hooks/bug-report-worker.js +1136 -183
- package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
- package/dist/hooks/commit-complete.js +408 -33
- package/dist/hooks/error-recall.js +1608 -1300
- package/dist/hooks/ingest-worker.js +250 -7966
- package/dist/hooks/ingest.js +707 -119
- package/dist/hooks/instructions-loaded.js +383 -20
- package/dist/hooks/notification.js +383 -20
- package/dist/hooks/post-compact.js +384 -21
- package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
- package/dist/hooks/pre-compact.js +486 -45
- package/dist/hooks/pre-tool-use.js +385 -22
- package/dist/hooks/prompt-submit.js +291 -96
- package/dist/hooks/session-end.js +490 -48
- package/dist/hooks/session-start.js +236 -22
- package/dist/hooks/stop.js +192 -50
- package/dist/hooks/subagent-stop.js +95 -18
- package/dist/hooks/summary-worker.js +205 -361
- package/dist/index.js +221 -105
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +27 -8
- package/dist/lib/consolidation.js +437 -41
- package/dist/lib/database.js +20 -8
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +20 -8
- package/dist/lib/device-registry.js +27 -8
- package/dist/lib/employee-templates.js +62 -0
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +6703 -6259
- package/dist/lib/hybrid-search.js +1608 -1300
- package/dist/lib/identity.js +1 -1
- package/dist/lib/messaging.js +11 -3
- package/dist/lib/reminders.js +1 -1
- package/dist/lib/schedules.js +718 -26
- package/dist/lib/session-registry.js +11 -0
- package/dist/lib/skill-learning.js +639 -9
- package/dist/lib/store.js +676 -27
- package/dist/lib/tasks.js +665 -27
- package/dist/lib/tmux-routing.js +732 -94
- package/dist/lib/token-spend.js +1 -1
- package/dist/mcp/server.js +856 -383
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +616 -46
- package/dist/mcp/tools/deactivate-behavior.js +9 -1
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +10 -2
- package/dist/mcp/tools/send-message.js +11 -3
- package/dist/mcp/tools/update-task.js +677 -39
- package/dist/runtime/index.js +425 -37
- package/dist/tui/App.js +365 -34
- package/package.json +5 -2
- package/src/commands/exe/intercom.md +6 -17
- package/dist/bin/wiki-sync.js +0 -2991
- package/dist/hooks/prompt-ingest-worker.js +0 -3979
|
@@ -25,6 +25,15 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
25
|
};
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
|
|
28
|
+
// src/types/memory.ts
|
|
29
|
+
var EMBEDDING_DIM;
|
|
30
|
+
var init_memory = __esm({
|
|
31
|
+
"src/types/memory.ts"() {
|
|
32
|
+
"use strict";
|
|
33
|
+
EMBEDDING_DIM = 1024;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
28
37
|
// src/lib/db-retry.ts
|
|
29
38
|
function isBusyError(err) {
|
|
30
39
|
if (err instanceof Error) {
|
|
@@ -74,9 +83,9 @@ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
|
74
83
|
var init_db_retry = __esm({
|
|
75
84
|
"src/lib/db-retry.ts"() {
|
|
76
85
|
"use strict";
|
|
77
|
-
MAX_RETRIES =
|
|
78
|
-
BASE_DELAY_MS =
|
|
79
|
-
MAX_JITTER_MS =
|
|
86
|
+
MAX_RETRIES = 5;
|
|
87
|
+
BASE_DELAY_MS = 250;
|
|
88
|
+
MAX_JITTER_MS = 400;
|
|
80
89
|
}
|
|
81
90
|
});
|
|
82
91
|
|
|
@@ -119,6 +128,21 @@ var init_secure_files = __esm({
|
|
|
119
128
|
});
|
|
120
129
|
|
|
121
130
|
// src/lib/config.ts
|
|
131
|
+
var config_exports = {};
|
|
132
|
+
__export(config_exports, {
|
|
133
|
+
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
134
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
135
|
+
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
136
|
+
DB_PATH: () => DB_PATH,
|
|
137
|
+
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
138
|
+
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
139
|
+
MODELS_DIR: () => MODELS_DIR,
|
|
140
|
+
loadConfig: () => loadConfig,
|
|
141
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
142
|
+
loadConfigSync: () => loadConfigSync,
|
|
143
|
+
migrateConfig: () => migrateConfig,
|
|
144
|
+
saveConfig: () => saveConfig
|
|
145
|
+
});
|
|
122
146
|
import { readFile, writeFile } from "fs/promises";
|
|
123
147
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
124
148
|
import path from "path";
|
|
@@ -222,6 +246,46 @@ async function loadConfig() {
|
|
|
222
246
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
223
247
|
}
|
|
224
248
|
}
|
|
249
|
+
function loadConfigSync() {
|
|
250
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
251
|
+
const configPath = path.join(dir, "config.json");
|
|
252
|
+
if (!existsSync2(configPath)) {
|
|
253
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
257
|
+
let parsed = JSON.parse(raw);
|
|
258
|
+
parsed = migrateLegacyConfig(parsed);
|
|
259
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
260
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
261
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
262
|
+
normalizeAutoUpdate(migratedCfg);
|
|
263
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
264
|
+
} catch {
|
|
265
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async function saveConfig(config) {
|
|
269
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
270
|
+
await ensurePrivateDir(dir);
|
|
271
|
+
const configPath = path.join(dir, "config.json");
|
|
272
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
273
|
+
await enforcePrivateFile(configPath);
|
|
274
|
+
}
|
|
275
|
+
async function loadConfigFrom(configPath) {
|
|
276
|
+
const raw = await readFile(configPath, "utf-8");
|
|
277
|
+
try {
|
|
278
|
+
let parsed = JSON.parse(raw);
|
|
279
|
+
parsed = migrateLegacyConfig(parsed);
|
|
280
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
281
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
282
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
283
|
+
normalizeAutoUpdate(migratedCfg);
|
|
284
|
+
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
285
|
+
} catch {
|
|
286
|
+
return { ...DEFAULT_CONFIG };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
225
289
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
226
290
|
var init_config = __esm({
|
|
227
291
|
"src/lib/config.ts"() {
|
|
@@ -402,7 +466,7 @@ var init_agent_config = __esm({
|
|
|
402
466
|
init_secure_files();
|
|
403
467
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
404
468
|
KNOWN_RUNTIMES = {
|
|
405
|
-
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
469
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
406
470
|
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
407
471
|
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
408
472
|
};
|
|
@@ -412,7 +476,7 @@ var init_agent_config = __esm({
|
|
|
412
476
|
opencode: "OpenCode (open source)"
|
|
413
477
|
};
|
|
414
478
|
DEFAULT_MODELS = {
|
|
415
|
-
claude: "claude-opus-4",
|
|
479
|
+
claude: "claude-opus-4.6",
|
|
416
480
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
417
481
|
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
418
482
|
};
|
|
@@ -1259,7 +1323,687 @@ var init_database_adapter = __esm({
|
|
|
1259
1323
|
}
|
|
1260
1324
|
});
|
|
1261
1325
|
|
|
1326
|
+
// src/lib/daemon-auth.ts
|
|
1327
|
+
import crypto from "crypto";
|
|
1328
|
+
import path5 from "path";
|
|
1329
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1330
|
+
function normalizeToken(token) {
|
|
1331
|
+
if (!token) return null;
|
|
1332
|
+
const trimmed = token.trim();
|
|
1333
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1334
|
+
}
|
|
1335
|
+
function readDaemonToken() {
|
|
1336
|
+
try {
|
|
1337
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1338
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
1339
|
+
} catch {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
function ensureDaemonToken(seed) {
|
|
1344
|
+
const existing = readDaemonToken();
|
|
1345
|
+
if (existing) return existing;
|
|
1346
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1347
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1348
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1349
|
+
`, "utf8");
|
|
1350
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1351
|
+
return token;
|
|
1352
|
+
}
|
|
1353
|
+
var DAEMON_TOKEN_PATH;
|
|
1354
|
+
var init_daemon_auth = __esm({
|
|
1355
|
+
"src/lib/daemon-auth.ts"() {
|
|
1356
|
+
"use strict";
|
|
1357
|
+
init_config();
|
|
1358
|
+
init_secure_files();
|
|
1359
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
// src/lib/exe-daemon-client.ts
|
|
1364
|
+
import net from "net";
|
|
1365
|
+
import os4 from "os";
|
|
1366
|
+
import { spawn } from "child_process";
|
|
1367
|
+
import { randomUUID } from "crypto";
|
|
1368
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1369
|
+
import path6 from "path";
|
|
1370
|
+
import { fileURLToPath } from "url";
|
|
1371
|
+
function handleData(chunk) {
|
|
1372
|
+
_buffer += chunk.toString();
|
|
1373
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1374
|
+
_buffer = "";
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
let newlineIdx;
|
|
1378
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1379
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
1380
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
1381
|
+
if (!line) continue;
|
|
1382
|
+
try {
|
|
1383
|
+
const response = JSON.parse(line);
|
|
1384
|
+
const id = response.id;
|
|
1385
|
+
if (!id) continue;
|
|
1386
|
+
const entry = _pending.get(id);
|
|
1387
|
+
if (entry) {
|
|
1388
|
+
clearTimeout(entry.timer);
|
|
1389
|
+
_pending.delete(id);
|
|
1390
|
+
entry.resolve(response);
|
|
1391
|
+
}
|
|
1392
|
+
} catch {
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
function cleanupStaleFiles() {
|
|
1397
|
+
if (existsSync6(PID_PATH)) {
|
|
1398
|
+
try {
|
|
1399
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1400
|
+
if (pid > 0) {
|
|
1401
|
+
try {
|
|
1402
|
+
process.kill(pid, 0);
|
|
1403
|
+
return;
|
|
1404
|
+
} catch {
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
} catch {
|
|
1408
|
+
}
|
|
1409
|
+
try {
|
|
1410
|
+
unlinkSync2(PID_PATH);
|
|
1411
|
+
} catch {
|
|
1412
|
+
}
|
|
1413
|
+
try {
|
|
1414
|
+
unlinkSync2(SOCKET_PATH);
|
|
1415
|
+
} catch {
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
function findPackageRoot() {
|
|
1420
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1421
|
+
const { root } = path6.parse(dir);
|
|
1422
|
+
while (dir !== root) {
|
|
1423
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1424
|
+
dir = path6.dirname(dir);
|
|
1425
|
+
}
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
function getAvailableMemoryGB() {
|
|
1429
|
+
if (process.platform === "darwin") {
|
|
1430
|
+
try {
|
|
1431
|
+
const { execSync: execSync7 } = __require("child_process");
|
|
1432
|
+
const vmstat = execSync7("vm_stat", { encoding: "utf8" });
|
|
1433
|
+
const pageSize = 16384;
|
|
1434
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1435
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
1436
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
1437
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
1438
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
1439
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
1440
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
1441
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
1442
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
1443
|
+
} catch {
|
|
1444
|
+
return os4.freemem() / (1024 * 1024 * 1024);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return os4.freemem() / (1024 * 1024 * 1024);
|
|
1448
|
+
}
|
|
1449
|
+
function spawnDaemon() {
|
|
1450
|
+
const freeGB = getAvailableMemoryGB();
|
|
1451
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1452
|
+
if (totalGB <= 8) {
|
|
1453
|
+
process.stderr.write(
|
|
1454
|
+
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
1455
|
+
`
|
|
1456
|
+
);
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
1460
|
+
process.stderr.write(
|
|
1461
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
1462
|
+
`
|
|
1463
|
+
);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const pkgRoot = findPackageRoot();
|
|
1467
|
+
if (!pkgRoot) {
|
|
1468
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1472
|
+
if (!existsSync6(daemonPath)) {
|
|
1473
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1474
|
+
`);
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
const resolvedPath = daemonPath;
|
|
1478
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1479
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1480
|
+
`);
|
|
1481
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1482
|
+
let stderrFd = "ignore";
|
|
1483
|
+
try {
|
|
1484
|
+
stderrFd = openSync(logPath, "a");
|
|
1485
|
+
} catch {
|
|
1486
|
+
}
|
|
1487
|
+
const heapCapMB = totalGB <= 8 ? 256 : 512;
|
|
1488
|
+
const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
|
|
1489
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
1490
|
+
detached: true,
|
|
1491
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
1492
|
+
env: {
|
|
1493
|
+
...process.env,
|
|
1494
|
+
TMUX: void 0,
|
|
1495
|
+
// Daemon is global — must not inherit session scope
|
|
1496
|
+
TMUX_PANE: void 0,
|
|
1497
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
1498
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1499
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1500
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
child.unref();
|
|
1504
|
+
if (typeof stderrFd === "number") {
|
|
1505
|
+
try {
|
|
1506
|
+
closeSync(stderrFd);
|
|
1507
|
+
} catch {
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
function acquireSpawnLock() {
|
|
1512
|
+
try {
|
|
1513
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1514
|
+
closeSync(fd);
|
|
1515
|
+
return true;
|
|
1516
|
+
} catch {
|
|
1517
|
+
try {
|
|
1518
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
1519
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
1520
|
+
try {
|
|
1521
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1522
|
+
} catch {
|
|
1523
|
+
}
|
|
1524
|
+
try {
|
|
1525
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1526
|
+
closeSync(fd);
|
|
1527
|
+
return true;
|
|
1528
|
+
} catch {
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
} catch {
|
|
1532
|
+
}
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function releaseSpawnLock() {
|
|
1537
|
+
try {
|
|
1538
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1539
|
+
} catch {
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function connectToSocket() {
|
|
1543
|
+
return new Promise((resolve) => {
|
|
1544
|
+
if (_socket && _connected) {
|
|
1545
|
+
resolve(true);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
1549
|
+
const connectTimeout = setTimeout(() => {
|
|
1550
|
+
socket.destroy();
|
|
1551
|
+
resolve(false);
|
|
1552
|
+
}, 2e3);
|
|
1553
|
+
socket.on("connect", () => {
|
|
1554
|
+
clearTimeout(connectTimeout);
|
|
1555
|
+
_socket = socket;
|
|
1556
|
+
_connected = true;
|
|
1557
|
+
_buffer = "";
|
|
1558
|
+
socket.on("data", handleData);
|
|
1559
|
+
socket.on("close", () => {
|
|
1560
|
+
_connected = false;
|
|
1561
|
+
_socket = null;
|
|
1562
|
+
for (const [id, entry] of _pending) {
|
|
1563
|
+
clearTimeout(entry.timer);
|
|
1564
|
+
_pending.delete(id);
|
|
1565
|
+
entry.resolve({ error: "Connection closed" });
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
socket.on("error", () => {
|
|
1569
|
+
_connected = false;
|
|
1570
|
+
_socket = null;
|
|
1571
|
+
});
|
|
1572
|
+
resolve(true);
|
|
1573
|
+
});
|
|
1574
|
+
socket.on("error", () => {
|
|
1575
|
+
clearTimeout(connectTimeout);
|
|
1576
|
+
resolve(false);
|
|
1577
|
+
});
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
async function connectEmbedDaemon() {
|
|
1581
|
+
if (_socket && _connected) return true;
|
|
1582
|
+
if (await connectToSocket()) return true;
|
|
1583
|
+
if (acquireSpawnLock()) {
|
|
1584
|
+
try {
|
|
1585
|
+
cleanupStaleFiles();
|
|
1586
|
+
spawnDaemon();
|
|
1587
|
+
} finally {
|
|
1588
|
+
releaseSpawnLock();
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
const start = Date.now();
|
|
1592
|
+
let delay2 = 100;
|
|
1593
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1594
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1595
|
+
if (await connectToSocket()) return true;
|
|
1596
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1597
|
+
}
|
|
1598
|
+
return false;
|
|
1599
|
+
}
|
|
1600
|
+
function sendRequest(texts, priority) {
|
|
1601
|
+
return sendDaemonRequest({ texts, priority });
|
|
1602
|
+
}
|
|
1603
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1604
|
+
return new Promise((resolve) => {
|
|
1605
|
+
if (!_socket || !_connected) {
|
|
1606
|
+
resolve({ error: "Not connected" });
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const id = randomUUID();
|
|
1610
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1611
|
+
const timer = setTimeout(() => {
|
|
1612
|
+
_pending.delete(id);
|
|
1613
|
+
resolve({ error: "Request timeout" });
|
|
1614
|
+
}, timeoutMs);
|
|
1615
|
+
_pending.set(id, { resolve, timer });
|
|
1616
|
+
try {
|
|
1617
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1618
|
+
} catch {
|
|
1619
|
+
clearTimeout(timer);
|
|
1620
|
+
_pending.delete(id);
|
|
1621
|
+
resolve({ error: "Write failed" });
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
async function pingDaemon() {
|
|
1626
|
+
if (!_socket || !_connected) return null;
|
|
1627
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
1628
|
+
if (response.health) {
|
|
1629
|
+
return response.health;
|
|
1630
|
+
}
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
function killAndRespawnDaemon() {
|
|
1634
|
+
if (!acquireSpawnLock()) {
|
|
1635
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1636
|
+
if (_socket) {
|
|
1637
|
+
_socket.destroy();
|
|
1638
|
+
_socket = null;
|
|
1639
|
+
}
|
|
1640
|
+
_connected = false;
|
|
1641
|
+
_buffer = "";
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
try {
|
|
1645
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1646
|
+
if (existsSync6(PID_PATH)) {
|
|
1647
|
+
try {
|
|
1648
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1649
|
+
if (pid > 0) {
|
|
1650
|
+
try {
|
|
1651
|
+
process.kill(pid, "SIGKILL");
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
} catch {
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
if (_socket) {
|
|
1659
|
+
_socket.destroy();
|
|
1660
|
+
_socket = null;
|
|
1661
|
+
}
|
|
1662
|
+
_connected = false;
|
|
1663
|
+
_buffer = "";
|
|
1664
|
+
try {
|
|
1665
|
+
unlinkSync2(PID_PATH);
|
|
1666
|
+
} catch {
|
|
1667
|
+
}
|
|
1668
|
+
try {
|
|
1669
|
+
unlinkSync2(SOCKET_PATH);
|
|
1670
|
+
} catch {
|
|
1671
|
+
}
|
|
1672
|
+
spawnDaemon();
|
|
1673
|
+
} finally {
|
|
1674
|
+
releaseSpawnLock();
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
function isDaemonTooYoung() {
|
|
1678
|
+
try {
|
|
1679
|
+
const stat = statSync(PID_PATH);
|
|
1680
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
1681
|
+
} catch {
|
|
1682
|
+
return false;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
async function retryThenRestart(doRequest, label) {
|
|
1686
|
+
const result = await doRequest();
|
|
1687
|
+
if (!result.error) {
|
|
1688
|
+
_consecutiveFailures = 0;
|
|
1689
|
+
return result;
|
|
1690
|
+
}
|
|
1691
|
+
_consecutiveFailures++;
|
|
1692
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1693
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1694
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1695
|
+
`);
|
|
1696
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1697
|
+
if (!_connected) {
|
|
1698
|
+
if (!await connectToSocket()) continue;
|
|
1699
|
+
}
|
|
1700
|
+
const retry = await doRequest();
|
|
1701
|
+
if (!retry.error) {
|
|
1702
|
+
_consecutiveFailures = 0;
|
|
1703
|
+
return retry;
|
|
1704
|
+
}
|
|
1705
|
+
_consecutiveFailures++;
|
|
1706
|
+
}
|
|
1707
|
+
if (isDaemonTooYoung()) {
|
|
1708
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1709
|
+
`);
|
|
1710
|
+
return { error: result.error };
|
|
1711
|
+
}
|
|
1712
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1713
|
+
`);
|
|
1714
|
+
killAndRespawnDaemon();
|
|
1715
|
+
const start = Date.now();
|
|
1716
|
+
let delay2 = 200;
|
|
1717
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1718
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1719
|
+
if (await connectToSocket()) break;
|
|
1720
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1721
|
+
}
|
|
1722
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1723
|
+
const final = await doRequest();
|
|
1724
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1725
|
+
return final;
|
|
1726
|
+
}
|
|
1727
|
+
async function embedViaClient(text, priority = "high") {
|
|
1728
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1729
|
+
_requestCount++;
|
|
1730
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1731
|
+
const health = await pingDaemon();
|
|
1732
|
+
if (!health && !isDaemonTooYoung()) {
|
|
1733
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1734
|
+
`);
|
|
1735
|
+
killAndRespawnDaemon();
|
|
1736
|
+
const start = Date.now();
|
|
1737
|
+
let d = 200;
|
|
1738
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1739
|
+
await new Promise((r) => setTimeout(r, d));
|
|
1740
|
+
if (await connectToSocket()) break;
|
|
1741
|
+
d = Math.min(d * 2, 3e3);
|
|
1742
|
+
}
|
|
1743
|
+
if (!_connected) return null;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
const result = await retryThenRestart(
|
|
1747
|
+
() => sendRequest([text], priority),
|
|
1748
|
+
"Embed"
|
|
1749
|
+
);
|
|
1750
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
1751
|
+
}
|
|
1752
|
+
function disconnectClient() {
|
|
1753
|
+
if (_socket) {
|
|
1754
|
+
_socket.destroy();
|
|
1755
|
+
_socket = null;
|
|
1756
|
+
}
|
|
1757
|
+
_connected = false;
|
|
1758
|
+
_buffer = "";
|
|
1759
|
+
for (const [id, entry] of _pending) {
|
|
1760
|
+
clearTimeout(entry.timer);
|
|
1761
|
+
_pending.delete(id);
|
|
1762
|
+
entry.resolve({ error: "Client disconnected" });
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
function isClientConnected() {
|
|
1766
|
+
return _connected;
|
|
1767
|
+
}
|
|
1768
|
+
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;
|
|
1769
|
+
var init_exe_daemon_client = __esm({
|
|
1770
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
1771
|
+
"use strict";
|
|
1772
|
+
init_config();
|
|
1773
|
+
init_daemon_auth();
|
|
1774
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1775
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1776
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1777
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1778
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
1779
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
1780
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1781
|
+
_socket = null;
|
|
1782
|
+
_connected = false;
|
|
1783
|
+
_buffer = "";
|
|
1784
|
+
_requestCount = 0;
|
|
1785
|
+
_consecutiveFailures = 0;
|
|
1786
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
1787
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1788
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1789
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
1790
|
+
_pending = /* @__PURE__ */ new Map();
|
|
1791
|
+
MAX_BUFFER = 1e7;
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
// src/lib/daemon-protocol.ts
|
|
1796
|
+
function serializeValue(v) {
|
|
1797
|
+
if (v === null || v === void 0) return null;
|
|
1798
|
+
if (typeof v === "bigint") return Number(v);
|
|
1799
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
1800
|
+
if (v instanceof Uint8Array) {
|
|
1801
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1802
|
+
}
|
|
1803
|
+
if (ArrayBuffer.isView(v)) {
|
|
1804
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
1805
|
+
}
|
|
1806
|
+
if (v instanceof ArrayBuffer) {
|
|
1807
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1808
|
+
}
|
|
1809
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1810
|
+
return String(v);
|
|
1811
|
+
}
|
|
1812
|
+
function deserializeValue(v) {
|
|
1813
|
+
if (v === null) return null;
|
|
1814
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
1815
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
1816
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
1817
|
+
}
|
|
1818
|
+
return v;
|
|
1819
|
+
}
|
|
1820
|
+
function deserializeResultSet(srs) {
|
|
1821
|
+
const rows = srs.rows.map((obj) => {
|
|
1822
|
+
const values = srs.columns.map(
|
|
1823
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
1824
|
+
);
|
|
1825
|
+
const row = values;
|
|
1826
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
1827
|
+
const col = srs.columns[i];
|
|
1828
|
+
if (col !== void 0) {
|
|
1829
|
+
row[col] = values[i] ?? null;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
Object.defineProperty(row, "length", {
|
|
1833
|
+
value: values.length,
|
|
1834
|
+
enumerable: false
|
|
1835
|
+
});
|
|
1836
|
+
return row;
|
|
1837
|
+
});
|
|
1838
|
+
return {
|
|
1839
|
+
columns: srs.columns,
|
|
1840
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1841
|
+
rows,
|
|
1842
|
+
rowsAffected: srs.rowsAffected,
|
|
1843
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
1844
|
+
toJSON: () => ({
|
|
1845
|
+
columns: srs.columns,
|
|
1846
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1847
|
+
rows: srs.rows,
|
|
1848
|
+
rowsAffected: srs.rowsAffected,
|
|
1849
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
1850
|
+
})
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
var init_daemon_protocol = __esm({
|
|
1854
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1855
|
+
"use strict";
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
// src/lib/db-daemon-client.ts
|
|
1860
|
+
var db_daemon_client_exports = {};
|
|
1861
|
+
__export(db_daemon_client_exports, {
|
|
1862
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1863
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1864
|
+
});
|
|
1865
|
+
function normalizeStatement2(stmt) {
|
|
1866
|
+
if (typeof stmt === "string") {
|
|
1867
|
+
return { sql: stmt, args: [] };
|
|
1868
|
+
}
|
|
1869
|
+
const sql = stmt.sql;
|
|
1870
|
+
let args = [];
|
|
1871
|
+
if (Array.isArray(stmt.args)) {
|
|
1872
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
1873
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1874
|
+
const named = {};
|
|
1875
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1876
|
+
named[key] = serializeValue(val);
|
|
1877
|
+
}
|
|
1878
|
+
return { sql, args: named };
|
|
1879
|
+
}
|
|
1880
|
+
return { sql, args };
|
|
1881
|
+
}
|
|
1882
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1883
|
+
let _useDaemon = false;
|
|
1884
|
+
const client = {
|
|
1885
|
+
async execute(stmt) {
|
|
1886
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1887
|
+
return fallbackClient.execute(stmt);
|
|
1888
|
+
}
|
|
1889
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1890
|
+
const response = await sendDaemonRequest({
|
|
1891
|
+
type: "db-execute",
|
|
1892
|
+
sql,
|
|
1893
|
+
args
|
|
1894
|
+
});
|
|
1895
|
+
if (response.error) {
|
|
1896
|
+
const errMsg = String(response.error);
|
|
1897
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
|
|
1898
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1899
|
+
`);
|
|
1900
|
+
return fallbackClient.execute(stmt);
|
|
1901
|
+
}
|
|
1902
|
+
throw new Error(errMsg);
|
|
1903
|
+
}
|
|
1904
|
+
if (response.db) {
|
|
1905
|
+
return deserializeResultSet(response.db);
|
|
1906
|
+
}
|
|
1907
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1908
|
+
return fallbackClient.execute(stmt);
|
|
1909
|
+
},
|
|
1910
|
+
async batch(stmts, mode) {
|
|
1911
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1912
|
+
return fallbackClient.batch(stmts, mode);
|
|
1913
|
+
}
|
|
1914
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1915
|
+
const response = await sendDaemonRequest({
|
|
1916
|
+
type: "db-batch",
|
|
1917
|
+
statements,
|
|
1918
|
+
mode: mode ?? "deferred"
|
|
1919
|
+
});
|
|
1920
|
+
if (response.error) {
|
|
1921
|
+
const errMsg = String(response.error);
|
|
1922
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
|
|
1923
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1924
|
+
`);
|
|
1925
|
+
return fallbackClient.batch(stmts, mode);
|
|
1926
|
+
}
|
|
1927
|
+
throw new Error(errMsg);
|
|
1928
|
+
}
|
|
1929
|
+
const batchResults = response["db-batch"];
|
|
1930
|
+
if (batchResults) {
|
|
1931
|
+
return batchResults.map(deserializeResultSet);
|
|
1932
|
+
}
|
|
1933
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1934
|
+
return fallbackClient.batch(stmts, mode);
|
|
1935
|
+
},
|
|
1936
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1937
|
+
async transaction(mode) {
|
|
1938
|
+
return fallbackClient.transaction(mode);
|
|
1939
|
+
},
|
|
1940
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1941
|
+
async executeMultiple(sql) {
|
|
1942
|
+
return fallbackClient.executeMultiple(sql);
|
|
1943
|
+
},
|
|
1944
|
+
// migrate — delegate to fallback
|
|
1945
|
+
async migrate(stmts) {
|
|
1946
|
+
return fallbackClient.migrate(stmts);
|
|
1947
|
+
},
|
|
1948
|
+
// Sync mode — delegate to fallback
|
|
1949
|
+
sync() {
|
|
1950
|
+
return fallbackClient.sync();
|
|
1951
|
+
},
|
|
1952
|
+
close() {
|
|
1953
|
+
_useDaemon = false;
|
|
1954
|
+
},
|
|
1955
|
+
get closed() {
|
|
1956
|
+
return fallbackClient.closed;
|
|
1957
|
+
},
|
|
1958
|
+
get protocol() {
|
|
1959
|
+
return fallbackClient.protocol;
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
return {
|
|
1963
|
+
...client,
|
|
1964
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1965
|
+
_enableDaemon() {
|
|
1966
|
+
_useDaemon = true;
|
|
1967
|
+
},
|
|
1968
|
+
/** Check if daemon routing is active */
|
|
1969
|
+
_isDaemonActive() {
|
|
1970
|
+
return _useDaemon && isClientConnected();
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1975
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1976
|
+
const connected = await connectEmbedDaemon();
|
|
1977
|
+
if (!connected) {
|
|
1978
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1979
|
+
return null;
|
|
1980
|
+
}
|
|
1981
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1982
|
+
client._enableDaemon();
|
|
1983
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1984
|
+
return client;
|
|
1985
|
+
}
|
|
1986
|
+
var init_db_daemon_client = __esm({
|
|
1987
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1988
|
+
"use strict";
|
|
1989
|
+
init_exe_daemon_client();
|
|
1990
|
+
init_daemon_protocol();
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1262
1994
|
// src/lib/database.ts
|
|
1995
|
+
var database_exports = {};
|
|
1996
|
+
__export(database_exports, {
|
|
1997
|
+
disposeDatabase: () => disposeDatabase,
|
|
1998
|
+
disposeTurso: () => disposeTurso,
|
|
1999
|
+
ensureSchema: () => ensureSchema,
|
|
2000
|
+
getClient: () => getClient,
|
|
2001
|
+
getRawClient: () => getRawClient,
|
|
2002
|
+
initDaemonClient: () => initDaemonClient,
|
|
2003
|
+
initDatabase: () => initDatabase,
|
|
2004
|
+
initTurso: () => initTurso,
|
|
2005
|
+
isInitialized: () => isInitialized
|
|
2006
|
+
});
|
|
1263
2007
|
import { createClient } from "@libsql/client";
|
|
1264
2008
|
async function initDatabase(config) {
|
|
1265
2009
|
if (_walCheckpointTimer) {
|
|
@@ -1298,15 +2042,18 @@ async function initDatabase(config) {
|
|
|
1298
2042
|
});
|
|
1299
2043
|
}, 3e4);
|
|
1300
2044
|
_walCheckpointTimer.unref();
|
|
1301
|
-
if (process.env.DATABASE_URL) {
|
|
2045
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
1302
2046
|
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1303
2047
|
}
|
|
1304
2048
|
}
|
|
2049
|
+
function isInitialized() {
|
|
2050
|
+
return _adapterClient !== null || _client !== null;
|
|
2051
|
+
}
|
|
1305
2052
|
function getClient() {
|
|
1306
2053
|
if (!_adapterClient) {
|
|
1307
2054
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1308
2055
|
}
|
|
1309
|
-
if (process.env.DATABASE_URL) {
|
|
2056
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
1310
2057
|
return _adapterClient;
|
|
1311
2058
|
}
|
|
1312
2059
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
@@ -1317,6 +2064,22 @@ function getClient() {
|
|
|
1317
2064
|
}
|
|
1318
2065
|
return _resilientClient;
|
|
1319
2066
|
}
|
|
2067
|
+
async function initDaemonClient() {
|
|
2068
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
|
|
2069
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
2070
|
+
if (process.env.VITEST) return;
|
|
2071
|
+
if (!_resilientClient) return;
|
|
2072
|
+
if (_daemonClient) return;
|
|
2073
|
+
try {
|
|
2074
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
2075
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
2076
|
+
} catch (err) {
|
|
2077
|
+
process.stderr.write(
|
|
2078
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
2079
|
+
`
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
1320
2083
|
function getRawClient() {
|
|
1321
2084
|
if (!_client) {
|
|
1322
2085
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1455,6 +2218,13 @@ async function ensureSchema() {
|
|
|
1455
2218
|
});
|
|
1456
2219
|
} catch {
|
|
1457
2220
|
}
|
|
2221
|
+
try {
|
|
2222
|
+
await client.execute({
|
|
2223
|
+
sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
|
|
2224
|
+
args: []
|
|
2225
|
+
});
|
|
2226
|
+
} catch {
|
|
2227
|
+
}
|
|
1458
2228
|
try {
|
|
1459
2229
|
await client.execute({
|
|
1460
2230
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -2299,19 +3069,40 @@ async function ensureSchema() {
|
|
|
2299
3069
|
} catch {
|
|
2300
3070
|
}
|
|
2301
3071
|
}
|
|
2302
|
-
|
|
3072
|
+
async function disposeDatabase() {
|
|
3073
|
+
if (_walCheckpointTimer) {
|
|
3074
|
+
clearInterval(_walCheckpointTimer);
|
|
3075
|
+
_walCheckpointTimer = null;
|
|
3076
|
+
}
|
|
3077
|
+
if (_daemonClient) {
|
|
3078
|
+
_daemonClient.close();
|
|
3079
|
+
_daemonClient = null;
|
|
3080
|
+
}
|
|
3081
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
3082
|
+
_adapterClient.close();
|
|
3083
|
+
}
|
|
3084
|
+
_adapterClient = null;
|
|
3085
|
+
if (_client) {
|
|
3086
|
+
_client.close();
|
|
3087
|
+
_client = null;
|
|
3088
|
+
_resilientClient = null;
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2303
3092
|
var init_database = __esm({
|
|
2304
3093
|
"src/lib/database.ts"() {
|
|
2305
3094
|
"use strict";
|
|
2306
3095
|
init_db_retry();
|
|
2307
3096
|
init_employees();
|
|
2308
3097
|
init_database_adapter();
|
|
3098
|
+
init_memory();
|
|
2309
3099
|
_client = null;
|
|
2310
3100
|
_resilientClient = null;
|
|
2311
3101
|
_walCheckpointTimer = null;
|
|
2312
3102
|
_daemonClient = null;
|
|
2313
3103
|
_adapterClient = null;
|
|
2314
3104
|
initTurso = initDatabase;
|
|
3105
|
+
disposeTurso = disposeDatabase;
|
|
2315
3106
|
}
|
|
2316
3107
|
});
|
|
2317
3108
|
|
|
@@ -2384,12 +3175,12 @@ __export(shard_manager_exports, {
|
|
|
2384
3175
|
listShards: () => listShards,
|
|
2385
3176
|
shardExists: () => shardExists
|
|
2386
3177
|
});
|
|
2387
|
-
import
|
|
2388
|
-
import { existsSync as
|
|
3178
|
+
import path8 from "path";
|
|
3179
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2389
3180
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2390
3181
|
function initShardManager(encryptionKey) {
|
|
2391
3182
|
_encryptionKey = encryptionKey;
|
|
2392
|
-
if (!
|
|
3183
|
+
if (!existsSync8(SHARDS_DIR)) {
|
|
2393
3184
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2394
3185
|
}
|
|
2395
3186
|
_shardingEnabled = true;
|
|
@@ -2419,7 +3210,7 @@ function getShardClient(projectName) {
|
|
|
2419
3210
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2420
3211
|
evictLRU();
|
|
2421
3212
|
}
|
|
2422
|
-
const dbPath =
|
|
3213
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
2423
3214
|
const client = createClient2({
|
|
2424
3215
|
url: `file:${dbPath}`,
|
|
2425
3216
|
encryptionKey: _encryptionKey
|
|
@@ -2430,10 +3221,10 @@ function getShardClient(projectName) {
|
|
|
2430
3221
|
}
|
|
2431
3222
|
function shardExists(projectName) {
|
|
2432
3223
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2433
|
-
return
|
|
3224
|
+
return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
2434
3225
|
}
|
|
2435
3226
|
function listShards() {
|
|
2436
|
-
if (!
|
|
3227
|
+
if (!existsSync8(SHARDS_DIR)) return [];
|
|
2437
3228
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2438
3229
|
}
|
|
2439
3230
|
async function ensureShardSchema(client) {
|
|
@@ -2680,7 +3471,7 @@ var init_shard_manager = __esm({
|
|
|
2680
3471
|
"src/lib/shard-manager.ts"() {
|
|
2681
3472
|
"use strict";
|
|
2682
3473
|
init_config();
|
|
2683
|
-
SHARDS_DIR =
|
|
3474
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
2684
3475
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2685
3476
|
MAX_OPEN_SHARDS = 10;
|
|
2686
3477
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -2800,6 +3591,68 @@ var init_platform_procedures = __esm({
|
|
|
2800
3591
|
domain: "architecture",
|
|
2801
3592
|
priority: "p0",
|
|
2802
3593
|
content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
|
|
3594
|
+
},
|
|
3595
|
+
// --- MCP is the ONLY data interface ---
|
|
3596
|
+
{
|
|
3597
|
+
title: "MCP disconnect \u2014 ask the user, never work around it",
|
|
3598
|
+
domain: "workflow",
|
|
3599
|
+
priority: "p0",
|
|
3600
|
+
content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
|
|
3601
|
+
},
|
|
3602
|
+
// --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
|
|
3603
|
+
{
|
|
3604
|
+
title: "MCP tools \u2014 memory and search",
|
|
3605
|
+
domain: "tool-use",
|
|
3606
|
+
priority: "p1",
|
|
3607
|
+
content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
|
|
3608
|
+
},
|
|
3609
|
+
{
|
|
3610
|
+
title: "MCP tools \u2014 task orchestration",
|
|
3611
|
+
domain: "tool-use",
|
|
3612
|
+
priority: "p1",
|
|
3613
|
+
content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
|
|
3614
|
+
},
|
|
3615
|
+
{
|
|
3616
|
+
title: "MCP tools \u2014 knowledge graph (GraphRAG)",
|
|
3617
|
+
domain: "tool-use",
|
|
3618
|
+
priority: "p1",
|
|
3619
|
+
content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
|
|
3620
|
+
},
|
|
3621
|
+
{
|
|
3622
|
+
title: "MCP tools \u2014 identity, behavior, and decisions",
|
|
3623
|
+
domain: "tool-use",
|
|
3624
|
+
priority: "p1",
|
|
3625
|
+
content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
|
|
3626
|
+
},
|
|
3627
|
+
{
|
|
3628
|
+
title: "MCP tools \u2014 communication and messaging",
|
|
3629
|
+
domain: "tool-use",
|
|
3630
|
+
priority: "p1",
|
|
3631
|
+
content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
|
|
3632
|
+
},
|
|
3633
|
+
{
|
|
3634
|
+
title: "MCP tools \u2014 wiki, documents, and content",
|
|
3635
|
+
domain: "tool-use",
|
|
3636
|
+
priority: "p1",
|
|
3637
|
+
content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
|
|
3638
|
+
},
|
|
3639
|
+
{
|
|
3640
|
+
title: "MCP tools \u2014 system, operations, and admin",
|
|
3641
|
+
domain: "tool-use",
|
|
3642
|
+
priority: "p1",
|
|
3643
|
+
content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
|
|
3644
|
+
},
|
|
3645
|
+
{
|
|
3646
|
+
title: "MCP tools \u2014 config, licensing, and team",
|
|
3647
|
+
domain: "tool-use",
|
|
3648
|
+
priority: "p1",
|
|
3649
|
+
content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
|
|
3650
|
+
},
|
|
3651
|
+
{
|
|
3652
|
+
title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
|
|
3653
|
+
domain: "tool-use",
|
|
3654
|
+
priority: "p1",
|
|
3655
|
+
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
|
|
2803
3656
|
}
|
|
2804
3657
|
];
|
|
2805
3658
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -2816,7 +3669,7 @@ __export(global_procedures_exports, {
|
|
|
2816
3669
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
2817
3670
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
2818
3671
|
});
|
|
2819
|
-
import { randomUUID } from "crypto";
|
|
3672
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2820
3673
|
async function loadGlobalProcedures() {
|
|
2821
3674
|
const client = getClient();
|
|
2822
3675
|
const result = await client.execute({
|
|
@@ -2845,7 +3698,7 @@ ${sections.join("\n\n")}
|
|
|
2845
3698
|
`;
|
|
2846
3699
|
}
|
|
2847
3700
|
async function storeGlobalProcedure(input) {
|
|
2848
|
-
const id =
|
|
3701
|
+
const id = randomUUID2();
|
|
2849
3702
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2850
3703
|
const client = getClient();
|
|
2851
3704
|
await client.execute({
|
|
@@ -2880,12 +3733,12 @@ ${p.content}`).join("\n\n");
|
|
|
2880
3733
|
});
|
|
2881
3734
|
|
|
2882
3735
|
// src/lib/session-registry.ts
|
|
2883
|
-
import { readFileSync as
|
|
2884
|
-
import
|
|
2885
|
-
import
|
|
3736
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
|
|
3737
|
+
import path9 from "path";
|
|
3738
|
+
import os6 from "os";
|
|
2886
3739
|
function registerSession(entry) {
|
|
2887
|
-
const dir =
|
|
2888
|
-
if (!
|
|
3740
|
+
const dir = path9.dirname(REGISTRY_PATH);
|
|
3741
|
+
if (!existsSync9(dir)) {
|
|
2889
3742
|
mkdirSync3(dir, { recursive: true });
|
|
2890
3743
|
}
|
|
2891
3744
|
const sessions = listSessions();
|
|
@@ -2895,11 +3748,11 @@ function registerSession(entry) {
|
|
|
2895
3748
|
} else {
|
|
2896
3749
|
sessions.push(entry);
|
|
2897
3750
|
}
|
|
2898
|
-
|
|
3751
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
2899
3752
|
}
|
|
2900
3753
|
function listSessions() {
|
|
2901
3754
|
try {
|
|
2902
|
-
const raw =
|
|
3755
|
+
const raw = readFileSync6(REGISTRY_PATH, "utf8");
|
|
2903
3756
|
return JSON.parse(raw);
|
|
2904
3757
|
} catch {
|
|
2905
3758
|
return [];
|
|
@@ -2909,7 +3762,7 @@ var REGISTRY_PATH;
|
|
|
2909
3762
|
var init_session_registry = __esm({
|
|
2910
3763
|
"src/lib/session-registry.ts"() {
|
|
2911
3764
|
"use strict";
|
|
2912
|
-
REGISTRY_PATH =
|
|
3765
|
+
REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
|
|
2913
3766
|
}
|
|
2914
3767
|
});
|
|
2915
3768
|
|
|
@@ -3180,17 +4033,17 @@ __export(intercom_queue_exports, {
|
|
|
3180
4033
|
queueIntercom: () => queueIntercom,
|
|
3181
4034
|
readQueue: () => readQueue
|
|
3182
4035
|
});
|
|
3183
|
-
import { readFileSync as
|
|
3184
|
-
import
|
|
3185
|
-
import
|
|
4036
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
|
|
4037
|
+
import path10 from "path";
|
|
4038
|
+
import os7 from "os";
|
|
3186
4039
|
function ensureDir() {
|
|
3187
|
-
const dir =
|
|
3188
|
-
if (!
|
|
4040
|
+
const dir = path10.dirname(QUEUE_PATH);
|
|
4041
|
+
if (!existsSync10(dir)) mkdirSync4(dir, { recursive: true });
|
|
3189
4042
|
}
|
|
3190
4043
|
function readQueue() {
|
|
3191
4044
|
try {
|
|
3192
|
-
if (!
|
|
3193
|
-
return JSON.parse(
|
|
4045
|
+
if (!existsSync10(QUEUE_PATH)) return [];
|
|
4046
|
+
return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
|
|
3194
4047
|
} catch {
|
|
3195
4048
|
return [];
|
|
3196
4049
|
}
|
|
@@ -3198,7 +4051,7 @@ function readQueue() {
|
|
|
3198
4051
|
function writeQueue(queue) {
|
|
3199
4052
|
ensureDir();
|
|
3200
4053
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
3201
|
-
|
|
4054
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
3202
4055
|
renameSync3(tmp, QUEUE_PATH);
|
|
3203
4056
|
}
|
|
3204
4057
|
function queueIntercom(targetSession, reason) {
|
|
@@ -3290,29 +4143,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
3290
4143
|
var init_intercom_queue = __esm({
|
|
3291
4144
|
"src/lib/intercom-queue.ts"() {
|
|
3292
4145
|
"use strict";
|
|
3293
|
-
QUEUE_PATH =
|
|
4146
|
+
QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
|
|
3294
4147
|
MAX_RETRIES2 = 5;
|
|
3295
4148
|
TTL_MS = 60 * 60 * 1e3;
|
|
3296
|
-
INTERCOM_LOG =
|
|
4149
|
+
INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
3297
4150
|
}
|
|
3298
4151
|
});
|
|
3299
4152
|
|
|
3300
4153
|
// src/lib/license.ts
|
|
3301
|
-
import { readFileSync as
|
|
3302
|
-
import { randomUUID as
|
|
4154
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
|
|
4155
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3303
4156
|
import { createRequire as createRequire2 } from "module";
|
|
3304
4157
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3305
|
-
import
|
|
3306
|
-
import
|
|
4158
|
+
import os8 from "os";
|
|
4159
|
+
import path11 from "path";
|
|
3307
4160
|
import { jwtVerify, importSPKI } from "jose";
|
|
3308
4161
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
3309
4162
|
var init_license = __esm({
|
|
3310
4163
|
"src/lib/license.ts"() {
|
|
3311
4164
|
"use strict";
|
|
3312
4165
|
init_config();
|
|
3313
|
-
LICENSE_PATH =
|
|
3314
|
-
CACHE_PATH =
|
|
3315
|
-
DEVICE_ID_PATH =
|
|
4166
|
+
LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
|
|
4167
|
+
CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4168
|
+
DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
|
|
3316
4169
|
PLAN_LIMITS = {
|
|
3317
4170
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
3318
4171
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -3324,12 +4177,12 @@ var init_license = __esm({
|
|
|
3324
4177
|
});
|
|
3325
4178
|
|
|
3326
4179
|
// src/lib/plan-limits.ts
|
|
3327
|
-
import { readFileSync as
|
|
3328
|
-
import
|
|
4180
|
+
import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
|
|
4181
|
+
import path12 from "path";
|
|
3329
4182
|
function getLicenseSync() {
|
|
3330
4183
|
try {
|
|
3331
|
-
if (!
|
|
3332
|
-
const raw = JSON.parse(
|
|
4184
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
4185
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
3333
4186
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3334
4187
|
const parts = raw.token.split(".");
|
|
3335
4188
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3367,8 +4220,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3367
4220
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
3368
4221
|
let count = 0;
|
|
3369
4222
|
try {
|
|
3370
|
-
if (
|
|
3371
|
-
const raw =
|
|
4223
|
+
if (existsSync12(filePath)) {
|
|
4224
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
3372
4225
|
const employees = JSON.parse(raw);
|
|
3373
4226
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3374
4227
|
}
|
|
@@ -3397,12 +4250,12 @@ var init_plan_limits = __esm({
|
|
|
3397
4250
|
this.name = "PlanLimitError";
|
|
3398
4251
|
}
|
|
3399
4252
|
};
|
|
3400
|
-
CACHE_PATH2 =
|
|
4253
|
+
CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
3401
4254
|
}
|
|
3402
4255
|
});
|
|
3403
4256
|
|
|
3404
4257
|
// src/lib/session-kill-telemetry.ts
|
|
3405
|
-
import
|
|
4258
|
+
import crypto2 from "crypto";
|
|
3406
4259
|
async function recordSessionKill(input) {
|
|
3407
4260
|
try {
|
|
3408
4261
|
const client = getClient();
|
|
@@ -3412,7 +4265,7 @@ async function recordSessionKill(input) {
|
|
|
3412
4265
|
ticks_idle, estimated_tokens_saved)
|
|
3413
4266
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3414
4267
|
args: [
|
|
3415
|
-
|
|
4268
|
+
crypto2.randomUUID(),
|
|
3416
4269
|
input.sessionName,
|
|
3417
4270
|
input.agentId,
|
|
3418
4271
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3733,8 +4586,8 @@ __export(tasks_review_exports, {
|
|
|
3733
4586
|
isStale: () => isStale,
|
|
3734
4587
|
listPendingReviews: () => listPendingReviews
|
|
3735
4588
|
});
|
|
3736
|
-
import
|
|
3737
|
-
import { existsSync as
|
|
4589
|
+
import path13 from "path";
|
|
4590
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
3738
4591
|
function formatAge(isoTimestamp) {
|
|
3739
4592
|
if (!isoTimestamp) return "";
|
|
3740
4593
|
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
@@ -4003,11 +4856,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4003
4856
|
);
|
|
4004
4857
|
}
|
|
4005
4858
|
try {
|
|
4006
|
-
const cacheDir =
|
|
4007
|
-
if (
|
|
4859
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
4860
|
+
if (existsSync13(cacheDir)) {
|
|
4008
4861
|
for (const f of readdirSync2(cacheDir)) {
|
|
4009
4862
|
if (f.startsWith("review-notified-")) {
|
|
4010
|
-
|
|
4863
|
+
unlinkSync3(path13.join(cacheDir, f));
|
|
4011
4864
|
}
|
|
4012
4865
|
}
|
|
4013
4866
|
}
|
|
@@ -4031,7 +4884,7 @@ var init_tasks_review = __esm({
|
|
|
4031
4884
|
// src/lib/tmux-routing.ts
|
|
4032
4885
|
var tmux_routing_exports = {};
|
|
4033
4886
|
__export(tmux_routing_exports, {
|
|
4034
|
-
acquireSpawnLock: () =>
|
|
4887
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
4035
4888
|
employeeSessionName: () => employeeSessionName,
|
|
4036
4889
|
ensureEmployee: () => ensureEmployee,
|
|
4037
4890
|
extractRootExe: () => extractRootExe,
|
|
@@ -4047,20 +4900,20 @@ __export(tmux_routing_exports, {
|
|
|
4047
4900
|
notifyParentExe: () => notifyParentExe,
|
|
4048
4901
|
parseParentExe: () => parseParentExe,
|
|
4049
4902
|
registerParentExe: () => registerParentExe,
|
|
4050
|
-
releaseSpawnLock: () =>
|
|
4903
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
4051
4904
|
resolveExeSession: () => resolveExeSession,
|
|
4052
4905
|
sendIntercom: () => sendIntercom,
|
|
4053
4906
|
spawnEmployee: () => spawnEmployee,
|
|
4054
4907
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4055
4908
|
});
|
|
4056
4909
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
4057
|
-
import { readFileSync as
|
|
4058
|
-
import
|
|
4059
|
-
import
|
|
4060
|
-
import { fileURLToPath } from "url";
|
|
4061
|
-
import { unlinkSync as
|
|
4910
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
4911
|
+
import path14 from "path";
|
|
4912
|
+
import os9 from "os";
|
|
4913
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4914
|
+
import { unlinkSync as unlinkSync4 } from "fs";
|
|
4062
4915
|
function spawnLockPath(sessionName) {
|
|
4063
|
-
return
|
|
4916
|
+
return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4064
4917
|
}
|
|
4065
4918
|
function isProcessAlive(pid) {
|
|
4066
4919
|
try {
|
|
@@ -4070,14 +4923,14 @@ function isProcessAlive(pid) {
|
|
|
4070
4923
|
return false;
|
|
4071
4924
|
}
|
|
4072
4925
|
}
|
|
4073
|
-
function
|
|
4074
|
-
if (!
|
|
4926
|
+
function acquireSpawnLock2(sessionName) {
|
|
4927
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
4075
4928
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4076
4929
|
}
|
|
4077
4930
|
const lockFile = spawnLockPath(sessionName);
|
|
4078
|
-
if (
|
|
4931
|
+
if (existsSync14(lockFile)) {
|
|
4079
4932
|
try {
|
|
4080
|
-
const lock = JSON.parse(
|
|
4933
|
+
const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
|
|
4081
4934
|
const age = Date.now() - lock.timestamp;
|
|
4082
4935
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4083
4936
|
return false;
|
|
@@ -4085,25 +4938,25 @@ function acquireSpawnLock(sessionName) {
|
|
|
4085
4938
|
} catch {
|
|
4086
4939
|
}
|
|
4087
4940
|
}
|
|
4088
|
-
|
|
4941
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4089
4942
|
return true;
|
|
4090
4943
|
}
|
|
4091
|
-
function
|
|
4944
|
+
function releaseSpawnLock2(sessionName) {
|
|
4092
4945
|
try {
|
|
4093
|
-
|
|
4946
|
+
unlinkSync4(spawnLockPath(sessionName));
|
|
4094
4947
|
} catch {
|
|
4095
4948
|
}
|
|
4096
4949
|
}
|
|
4097
4950
|
function resolveBehaviorsExporterScript() {
|
|
4098
4951
|
try {
|
|
4099
|
-
const thisFile =
|
|
4100
|
-
const scriptPath =
|
|
4101
|
-
|
|
4952
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
4953
|
+
const scriptPath = path14.join(
|
|
4954
|
+
path14.dirname(thisFile),
|
|
4102
4955
|
"..",
|
|
4103
4956
|
"bin",
|
|
4104
4957
|
"exe-export-behaviors.js"
|
|
4105
4958
|
);
|
|
4106
|
-
return
|
|
4959
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
4107
4960
|
} catch {
|
|
4108
4961
|
return null;
|
|
4109
4962
|
}
|
|
@@ -4169,12 +5022,12 @@ function extractRootExe(name) {
|
|
|
4169
5022
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4170
5023
|
}
|
|
4171
5024
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4172
|
-
if (!
|
|
5025
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
4173
5026
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4174
5027
|
}
|
|
4175
5028
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4176
|
-
const filePath =
|
|
4177
|
-
|
|
5029
|
+
const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5030
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4178
5031
|
parentExe: rootExe,
|
|
4179
5032
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4180
5033
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4182,7 +5035,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4182
5035
|
}
|
|
4183
5036
|
function getParentExe(sessionKey) {
|
|
4184
5037
|
try {
|
|
4185
|
-
const data = JSON.parse(
|
|
5038
|
+
const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4186
5039
|
return data.parentExe || null;
|
|
4187
5040
|
} catch {
|
|
4188
5041
|
return null;
|
|
@@ -4190,8 +5043,8 @@ function getParentExe(sessionKey) {
|
|
|
4190
5043
|
}
|
|
4191
5044
|
function getDispatchedBy(sessionKey) {
|
|
4192
5045
|
try {
|
|
4193
|
-
const data = JSON.parse(
|
|
4194
|
-
|
|
5046
|
+
const data = JSON.parse(readFileSync10(
|
|
5047
|
+
path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4195
5048
|
"utf8"
|
|
4196
5049
|
));
|
|
4197
5050
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4226,10 +5079,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
4226
5079
|
}
|
|
4227
5080
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
4228
5081
|
const base = employeeSessionName(employeeName, exeSession);
|
|
4229
|
-
if (!isAlive(base) &&
|
|
5082
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
4230
5083
|
for (let i = 2; i <= maxInstances; i++) {
|
|
4231
5084
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
4232
|
-
if (!isAlive(candidate) &&
|
|
5085
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
4233
5086
|
}
|
|
4234
5087
|
return null;
|
|
4235
5088
|
}
|
|
@@ -4261,8 +5114,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4261
5114
|
}
|
|
4262
5115
|
function readDebounceState() {
|
|
4263
5116
|
try {
|
|
4264
|
-
if (!
|
|
4265
|
-
const raw = JSON.parse(
|
|
5117
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5118
|
+
const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
|
|
4266
5119
|
const state = {};
|
|
4267
5120
|
for (const [key, val] of Object.entries(raw)) {
|
|
4268
5121
|
if (typeof val === "number") {
|
|
@@ -4278,8 +5131,8 @@ function readDebounceState() {
|
|
|
4278
5131
|
}
|
|
4279
5132
|
function writeDebounceState(state) {
|
|
4280
5133
|
try {
|
|
4281
|
-
if (!
|
|
4282
|
-
|
|
5134
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5135
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4283
5136
|
} catch {
|
|
4284
5137
|
}
|
|
4285
5138
|
}
|
|
@@ -4343,7 +5196,7 @@ function isSessionBusy(sessionName) {
|
|
|
4343
5196
|
function isExeSession(sessionName) {
|
|
4344
5197
|
const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
|
|
4345
5198
|
const coordinatorName = getCoordinatorName();
|
|
4346
|
-
return matchesBaseWithInstance(coordinatorName)
|
|
5199
|
+
return matchesBaseWithInstance(coordinatorName);
|
|
4347
5200
|
}
|
|
4348
5201
|
function sendIntercom(targetSession) {
|
|
4349
5202
|
const transport = getTransport();
|
|
@@ -4378,8 +5231,8 @@ function sendIntercom(targetSession) {
|
|
|
4378
5231
|
try {
|
|
4379
5232
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4380
5233
|
const agent = baseAgentName(rawAgent);
|
|
4381
|
-
const markerPath =
|
|
4382
|
-
if (
|
|
5234
|
+
const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
5235
|
+
if (existsSync14(markerPath)) {
|
|
4383
5236
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
4384
5237
|
return "debounced";
|
|
4385
5238
|
}
|
|
@@ -4389,8 +5242,8 @@ function sendIntercom(targetSession) {
|
|
|
4389
5242
|
try {
|
|
4390
5243
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
4391
5244
|
const agent = baseAgentName(rawAgent);
|
|
4392
|
-
const taskDir =
|
|
4393
|
-
if (
|
|
5245
|
+
const taskDir = path14.join(process.cwd(), "exe", agent);
|
|
5246
|
+
if (existsSync14(taskDir)) {
|
|
4394
5247
|
const files = readdirSync3(taskDir).filter(
|
|
4395
5248
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
4396
5249
|
);
|
|
@@ -4557,26 +5410,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4557
5410
|
const transport = getTransport();
|
|
4558
5411
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4559
5412
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4560
|
-
const logDir =
|
|
4561
|
-
const logFile =
|
|
4562
|
-
if (!
|
|
5413
|
+
const logDir = path14.join(os9.homedir(), ".exe-os", "session-logs");
|
|
5414
|
+
const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5415
|
+
if (!existsSync14(logDir)) {
|
|
4563
5416
|
mkdirSync6(logDir, { recursive: true });
|
|
4564
5417
|
}
|
|
4565
5418
|
transport.kill(sessionName);
|
|
4566
5419
|
let cleanupSuffix = "";
|
|
4567
5420
|
try {
|
|
4568
|
-
const thisFile =
|
|
4569
|
-
const cleanupScript =
|
|
4570
|
-
if (
|
|
5421
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
5422
|
+
const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5423
|
+
if (existsSync14(cleanupScript)) {
|
|
4571
5424
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4572
5425
|
}
|
|
4573
5426
|
} catch {
|
|
4574
5427
|
}
|
|
4575
5428
|
try {
|
|
4576
|
-
const claudeJsonPath =
|
|
5429
|
+
const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
|
|
4577
5430
|
let claudeJson = {};
|
|
4578
5431
|
try {
|
|
4579
|
-
claudeJson = JSON.parse(
|
|
5432
|
+
claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
|
|
4580
5433
|
} catch {
|
|
4581
5434
|
}
|
|
4582
5435
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4584,17 +5437,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4584
5437
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4585
5438
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4586
5439
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4587
|
-
|
|
5440
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4588
5441
|
} catch {
|
|
4589
5442
|
}
|
|
4590
5443
|
try {
|
|
4591
|
-
const settingsDir =
|
|
5444
|
+
const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
|
|
4592
5445
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4593
|
-
const projSettingsDir =
|
|
4594
|
-
const settingsPath =
|
|
5446
|
+
const projSettingsDir = path14.join(settingsDir, normalizedKey);
|
|
5447
|
+
const settingsPath = path14.join(projSettingsDir, "settings.json");
|
|
4595
5448
|
let settings = {};
|
|
4596
5449
|
try {
|
|
4597
|
-
settings = JSON.parse(
|
|
5450
|
+
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
4598
5451
|
} catch {
|
|
4599
5452
|
}
|
|
4600
5453
|
const perms = settings.permissions ?? {};
|
|
@@ -4623,7 +5476,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4623
5476
|
perms.allow = allow;
|
|
4624
5477
|
settings.permissions = perms;
|
|
4625
5478
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4626
|
-
|
|
5479
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4627
5480
|
}
|
|
4628
5481
|
} catch {
|
|
4629
5482
|
}
|
|
@@ -4638,8 +5491,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4638
5491
|
let behaviorsFlag = "";
|
|
4639
5492
|
let legacyFallbackWarned = false;
|
|
4640
5493
|
if (!useExeAgent && !useBinSymlink) {
|
|
4641
|
-
const identityPath =
|
|
4642
|
-
|
|
5494
|
+
const identityPath = path14.join(
|
|
5495
|
+
os9.homedir(),
|
|
4643
5496
|
".exe-os",
|
|
4644
5497
|
"identity",
|
|
4645
5498
|
`${employeeName}.md`
|
|
@@ -4648,13 +5501,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4648
5501
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4649
5502
|
if (hasAgentFlag) {
|
|
4650
5503
|
identityFlag = ` --agent ${employeeName}`;
|
|
4651
|
-
} else if (
|
|
5504
|
+
} else if (existsSync14(identityPath)) {
|
|
4652
5505
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4653
5506
|
legacyFallbackWarned = true;
|
|
4654
5507
|
}
|
|
4655
5508
|
const behaviorsFile = exportBehaviorsSync(
|
|
4656
5509
|
employeeName,
|
|
4657
|
-
|
|
5510
|
+
path14.basename(spawnCwd),
|
|
4658
5511
|
sessionName
|
|
4659
5512
|
);
|
|
4660
5513
|
if (behaviorsFile) {
|
|
@@ -4669,16 +5522,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4669
5522
|
}
|
|
4670
5523
|
let sessionContextFlag = "";
|
|
4671
5524
|
try {
|
|
4672
|
-
const ctxDir =
|
|
5525
|
+
const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4673
5526
|
mkdirSync6(ctxDir, { recursive: true });
|
|
4674
|
-
const ctxFile =
|
|
5527
|
+
const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4675
5528
|
const ctxContent = [
|
|
4676
5529
|
`## Session Context`,
|
|
4677
5530
|
`You are running in tmux session: ${sessionName}.`,
|
|
4678
5531
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4679
5532
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4680
5533
|
].join("\n");
|
|
4681
|
-
|
|
5534
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4682
5535
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4683
5536
|
} catch {
|
|
4684
5537
|
}
|
|
@@ -4752,14 +5605,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4752
5605
|
command: spawnCommand
|
|
4753
5606
|
});
|
|
4754
5607
|
if (spawnResult.error) {
|
|
4755
|
-
|
|
5608
|
+
releaseSpawnLock2(sessionName);
|
|
4756
5609
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
4757
5610
|
}
|
|
4758
5611
|
transport.pipeLog(sessionName, logFile);
|
|
4759
5612
|
try {
|
|
4760
5613
|
const mySession = getMySession();
|
|
4761
|
-
const dispatchInfo =
|
|
4762
|
-
|
|
5614
|
+
const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5615
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4763
5616
|
dispatchedBy: mySession,
|
|
4764
5617
|
rootExe: exeSession,
|
|
4765
5618
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -4797,7 +5650,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4797
5650
|
}
|
|
4798
5651
|
}
|
|
4799
5652
|
if (!booted) {
|
|
4800
|
-
|
|
5653
|
+
releaseSpawnLock2(sessionName);
|
|
4801
5654
|
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4802
5655
|
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4803
5656
|
}
|
|
@@ -4815,7 +5668,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4815
5668
|
pid: 0,
|
|
4816
5669
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4817
5670
|
});
|
|
4818
|
-
|
|
5671
|
+
releaseSpawnLock2(sessionName);
|
|
4819
5672
|
return { sessionName };
|
|
4820
5673
|
}
|
|
4821
5674
|
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
@@ -4833,15 +5686,15 @@ var init_tmux_routing = __esm({
|
|
|
4833
5686
|
init_intercom_queue();
|
|
4834
5687
|
init_plan_limits();
|
|
4835
5688
|
init_employees();
|
|
4836
|
-
SPAWN_LOCK_DIR =
|
|
4837
|
-
SESSION_CACHE =
|
|
5689
|
+
SPAWN_LOCK_DIR = path14.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
5690
|
+
SESSION_CACHE = path14.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4838
5691
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4839
5692
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4840
5693
|
VERIFY_PANE_LINES = 200;
|
|
4841
5694
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4842
5695
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
4843
|
-
INTERCOM_LOG2 =
|
|
4844
|
-
DEBOUNCE_FILE =
|
|
5696
|
+
INTERCOM_LOG2 = path14.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
5697
|
+
DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4845
5698
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4846
5699
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
4847
5700
|
}
|
|
@@ -4881,20 +5734,20 @@ var init_task_scope = __esm({
|
|
|
4881
5734
|
});
|
|
4882
5735
|
|
|
4883
5736
|
// src/lib/notifications.ts
|
|
4884
|
-
import
|
|
4885
|
-
import
|
|
4886
|
-
import
|
|
5737
|
+
import crypto3 from "crypto";
|
|
5738
|
+
import path15 from "path";
|
|
5739
|
+
import os10 from "os";
|
|
4887
5740
|
import {
|
|
4888
|
-
readFileSync as
|
|
5741
|
+
readFileSync as readFileSync11,
|
|
4889
5742
|
readdirSync as readdirSync4,
|
|
4890
|
-
unlinkSync as
|
|
4891
|
-
existsSync as
|
|
5743
|
+
unlinkSync as unlinkSync5,
|
|
5744
|
+
existsSync as existsSync15,
|
|
4892
5745
|
rmdirSync
|
|
4893
5746
|
} from "fs";
|
|
4894
5747
|
async function writeNotification(notification) {
|
|
4895
5748
|
try {
|
|
4896
5749
|
const client = getClient();
|
|
4897
|
-
const id =
|
|
5750
|
+
const id = crypto3.randomUUID();
|
|
4898
5751
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4899
5752
|
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
4900
5753
|
await client.execute({
|
|
@@ -4939,7 +5792,7 @@ var init_notifications = __esm({
|
|
|
4939
5792
|
|
|
4940
5793
|
// src/lib/project-name.ts
|
|
4941
5794
|
import { execSync as execSync5 } from "child_process";
|
|
4942
|
-
import
|
|
5795
|
+
import path16 from "path";
|
|
4943
5796
|
function getProjectName(cwd) {
|
|
4944
5797
|
const dir = cwd ?? process.cwd();
|
|
4945
5798
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4952,7 +5805,7 @@ function getProjectName(cwd) {
|
|
|
4952
5805
|
timeout: 2e3,
|
|
4953
5806
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4954
5807
|
}).trim();
|
|
4955
|
-
repoRoot =
|
|
5808
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4956
5809
|
} catch {
|
|
4957
5810
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
4958
5811
|
cwd: dir,
|
|
@@ -4961,11 +5814,11 @@ function getProjectName(cwd) {
|
|
|
4961
5814
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4962
5815
|
}).trim();
|
|
4963
5816
|
}
|
|
4964
|
-
_cached2 =
|
|
5817
|
+
_cached2 = path16.basename(repoRoot);
|
|
4965
5818
|
_cachedCwd = dir;
|
|
4966
5819
|
return _cached2;
|
|
4967
5820
|
} catch {
|
|
4968
|
-
_cached2 =
|
|
5821
|
+
_cached2 = path16.basename(dir);
|
|
4969
5822
|
_cachedCwd = dir;
|
|
4970
5823
|
return _cached2;
|
|
4971
5824
|
}
|
|
@@ -5042,12 +5895,12 @@ var init_session_scope = __esm({
|
|
|
5042
5895
|
});
|
|
5043
5896
|
|
|
5044
5897
|
// src/lib/tasks-crud.ts
|
|
5045
|
-
import
|
|
5046
|
-
import
|
|
5047
|
-
import
|
|
5898
|
+
import crypto4 from "crypto";
|
|
5899
|
+
import path17 from "path";
|
|
5900
|
+
import os11 from "os";
|
|
5048
5901
|
import { execSync as execSync6 } from "child_process";
|
|
5049
5902
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5050
|
-
import { existsSync as
|
|
5903
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
|
|
5051
5904
|
async function writeCheckpoint(input) {
|
|
5052
5905
|
const client = getClient();
|
|
5053
5906
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -5163,7 +6016,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
5163
6016
|
}
|
|
5164
6017
|
async function createTaskCore(input) {
|
|
5165
6018
|
const client = getClient();
|
|
5166
|
-
const id =
|
|
6019
|
+
const id = crypto4.randomUUID();
|
|
5167
6020
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5168
6021
|
const slug = slugify(input.title);
|
|
5169
6022
|
let earlySessionScope = null;
|
|
@@ -5172,15 +6025,20 @@ async function createTaskCore(input) {
|
|
|
5172
6025
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
5173
6026
|
const resolved = resolveExeSession2();
|
|
5174
6027
|
if (resolved && input.projectName) {
|
|
5175
|
-
const
|
|
5176
|
-
|
|
5177
|
-
if (sessionProject && sessionProject !== input.projectName) {
|
|
5178
|
-
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
5179
|
-
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
5180
|
-
`);
|
|
5181
|
-
earlySessionScope = null;
|
|
5182
|
-
} else {
|
|
6028
|
+
const isCoordinatorSession = !resolved.includes("-");
|
|
6029
|
+
if (isCoordinatorSession) {
|
|
5183
6030
|
earlySessionScope = resolved;
|
|
6031
|
+
} else {
|
|
6032
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
6033
|
+
const sessionProject = getSessionProject2(resolved);
|
|
6034
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
6035
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
6036
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
6037
|
+
`);
|
|
6038
|
+
earlySessionScope = null;
|
|
6039
|
+
} else {
|
|
6040
|
+
earlySessionScope = resolved;
|
|
6041
|
+
}
|
|
5184
6042
|
}
|
|
5185
6043
|
} else {
|
|
5186
6044
|
earlySessionScope = resolved;
|
|
@@ -5241,8 +6099,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
5241
6099
|
}
|
|
5242
6100
|
if (input.baseDir) {
|
|
5243
6101
|
try {
|
|
5244
|
-
await mkdir4(
|
|
5245
|
-
await mkdir4(
|
|
6102
|
+
await mkdir4(path17.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
6103
|
+
await mkdir4(path17.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
5246
6104
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
5247
6105
|
await ensureGitignoreExe(input.baseDir);
|
|
5248
6106
|
} catch {
|
|
@@ -5278,10 +6136,10 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
5278
6136
|
});
|
|
5279
6137
|
if (input.baseDir) {
|
|
5280
6138
|
try {
|
|
5281
|
-
const EXE_OS_DIR =
|
|
5282
|
-
const mdPath =
|
|
5283
|
-
const mdDir =
|
|
5284
|
-
if (!
|
|
6139
|
+
const EXE_OS_DIR = path17.join(os11.homedir(), ".exe-os");
|
|
6140
|
+
const mdPath = path17.join(EXE_OS_DIR, taskFile);
|
|
6141
|
+
const mdDir = path17.dirname(mdPath);
|
|
6142
|
+
if (!existsSync16(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
5285
6143
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
5286
6144
|
const mdContent = `# ${input.title}
|
|
5287
6145
|
|
|
@@ -5581,9 +6439,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5581
6439
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5582
6440
|
}
|
|
5583
6441
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5584
|
-
const archPath =
|
|
6442
|
+
const archPath = path17.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5585
6443
|
try {
|
|
5586
|
-
if (
|
|
6444
|
+
if (existsSync16(archPath)) return;
|
|
5587
6445
|
const template = [
|
|
5588
6446
|
`# ${projectName} \u2014 System Architecture`,
|
|
5589
6447
|
"",
|
|
@@ -5616,10 +6474,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5616
6474
|
}
|
|
5617
6475
|
}
|
|
5618
6476
|
async function ensureGitignoreExe(baseDir) {
|
|
5619
|
-
const gitignorePath =
|
|
6477
|
+
const gitignorePath = path17.join(baseDir, ".gitignore");
|
|
5620
6478
|
try {
|
|
5621
|
-
if (
|
|
5622
|
-
const content =
|
|
6479
|
+
if (existsSync16(gitignorePath)) {
|
|
6480
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
5623
6481
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5624
6482
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5625
6483
|
} else {
|
|
@@ -5650,7 +6508,7 @@ var init_tasks_crud = __esm({
|
|
|
5650
6508
|
});
|
|
5651
6509
|
|
|
5652
6510
|
// src/lib/tasks-chain.ts
|
|
5653
|
-
import
|
|
6511
|
+
import path18 from "path";
|
|
5654
6512
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5655
6513
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5656
6514
|
const client = getClient();
|
|
@@ -5667,7 +6525,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5667
6525
|
});
|
|
5668
6526
|
for (const ur of unblockedRows.rows) {
|
|
5669
6527
|
try {
|
|
5670
|
-
const ubFile =
|
|
6528
|
+
const ubFile = path18.join(baseDir, String(ur.task_file));
|
|
5671
6529
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5672
6530
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5673
6531
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5757,6 +6615,10 @@ async function dispatchTaskToEmployee(input) {
|
|
|
5757
6615
|
if (transport.isAlive(sessionName)) {
|
|
5758
6616
|
const result = sendIntercom(sessionName);
|
|
5759
6617
|
const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
|
|
6618
|
+
process.stderr.write(
|
|
6619
|
+
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
|
|
6620
|
+
`
|
|
6621
|
+
);
|
|
5760
6622
|
return { dispatched, session: sessionName, crossProject };
|
|
5761
6623
|
} else {
|
|
5762
6624
|
const projectDir = input.projectDir ?? process.cwd();
|
|
@@ -5765,11 +6627,15 @@ async function dispatchTaskToEmployee(input) {
|
|
|
5765
6627
|
});
|
|
5766
6628
|
if (result.status === "failed") {
|
|
5767
6629
|
process.stderr.write(
|
|
5768
|
-
`[dispatch]
|
|
6630
|
+
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
|
|
5769
6631
|
`
|
|
5770
6632
|
);
|
|
5771
6633
|
return { dispatched: "session_missing" };
|
|
5772
6634
|
}
|
|
6635
|
+
process.stderr.write(
|
|
6636
|
+
`[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
|
|
6637
|
+
`
|
|
6638
|
+
);
|
|
5773
6639
|
return { dispatched: "spawned", session: result.sessionName, crossProject };
|
|
5774
6640
|
}
|
|
5775
6641
|
} catch {
|
|
@@ -5779,7 +6645,13 @@ async function dispatchTaskToEmployee(input) {
|
|
|
5779
6645
|
function notifyTaskDone() {
|
|
5780
6646
|
try {
|
|
5781
6647
|
const key = getSessionKey();
|
|
5782
|
-
if (key && !process.env.VITEST)
|
|
6648
|
+
if (key && !process.env.VITEST) {
|
|
6649
|
+
notifyParentExe(key);
|
|
6650
|
+
process.stderr.write(
|
|
6651
|
+
`[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
|
|
6652
|
+
`
|
|
6653
|
+
);
|
|
6654
|
+
}
|
|
5783
6655
|
} catch {
|
|
5784
6656
|
}
|
|
5785
6657
|
}
|
|
@@ -5800,16 +6672,91 @@ var init_tasks_notify = __esm({
|
|
|
5800
6672
|
}
|
|
5801
6673
|
});
|
|
5802
6674
|
|
|
6675
|
+
// src/lib/embedder.ts
|
|
6676
|
+
var embedder_exports = {};
|
|
6677
|
+
__export(embedder_exports, {
|
|
6678
|
+
disposeEmbedder: () => disposeEmbedder,
|
|
6679
|
+
embed: () => embed,
|
|
6680
|
+
embedDirect: () => embedDirect,
|
|
6681
|
+
getEmbedder: () => getEmbedder
|
|
6682
|
+
});
|
|
6683
|
+
async function getEmbedder() {
|
|
6684
|
+
const ok = await connectEmbedDaemon();
|
|
6685
|
+
if (!ok) {
|
|
6686
|
+
throw new Error(
|
|
6687
|
+
"Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
|
|
6688
|
+
);
|
|
6689
|
+
}
|
|
6690
|
+
}
|
|
6691
|
+
async function embed(text) {
|
|
6692
|
+
const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
|
|
6693
|
+
const vector = await embedViaClient(text, priority);
|
|
6694
|
+
if (!vector) {
|
|
6695
|
+
throw new Error(
|
|
6696
|
+
"Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
|
|
6697
|
+
);
|
|
6698
|
+
}
|
|
6699
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
6700
|
+
throw new Error(
|
|
6701
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
|
|
6702
|
+
);
|
|
6703
|
+
}
|
|
6704
|
+
return vector;
|
|
6705
|
+
}
|
|
6706
|
+
async function disposeEmbedder() {
|
|
6707
|
+
disconnectClient();
|
|
6708
|
+
}
|
|
6709
|
+
async function embedDirect(text) {
|
|
6710
|
+
const llamaCpp = await import("node-llama-cpp");
|
|
6711
|
+
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6712
|
+
const { existsSync: existsSync17 } = await import("fs");
|
|
6713
|
+
const path20 = await import("path");
|
|
6714
|
+
const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
6715
|
+
if (!existsSync17(modelPath)) {
|
|
6716
|
+
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
6717
|
+
}
|
|
6718
|
+
const llama = await llamaCpp.getLlama();
|
|
6719
|
+
const model = await llama.loadModel({ modelPath });
|
|
6720
|
+
const context = await model.createEmbeddingContext();
|
|
6721
|
+
try {
|
|
6722
|
+
const embedding = await context.getEmbeddingFor(text);
|
|
6723
|
+
const vector = Array.from(embedding.vector);
|
|
6724
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
6725
|
+
throw new Error(
|
|
6726
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
|
|
6727
|
+
);
|
|
6728
|
+
}
|
|
6729
|
+
return vector;
|
|
6730
|
+
} finally {
|
|
6731
|
+
await context.dispose();
|
|
6732
|
+
await model.dispose();
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
var init_embedder = __esm({
|
|
6736
|
+
"src/lib/embedder.ts"() {
|
|
6737
|
+
"use strict";
|
|
6738
|
+
init_memory();
|
|
6739
|
+
init_exe_daemon_client();
|
|
6740
|
+
}
|
|
6741
|
+
});
|
|
6742
|
+
|
|
5803
6743
|
// src/lib/behaviors.ts
|
|
5804
|
-
import
|
|
6744
|
+
import crypto5 from "crypto";
|
|
5805
6745
|
async function storeBehavior(opts) {
|
|
5806
6746
|
const client = getClient();
|
|
5807
|
-
const id =
|
|
6747
|
+
const id = crypto5.randomUUID();
|
|
5808
6748
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6749
|
+
let vector = null;
|
|
6750
|
+
try {
|
|
6751
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
6752
|
+
const vec = await embed2(opts.content);
|
|
6753
|
+
vector = new Float32Array(vec);
|
|
6754
|
+
} catch {
|
|
6755
|
+
}
|
|
5809
6756
|
await client.execute({
|
|
5810
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
5811
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
5812
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
|
|
6757
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
6758
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
6759
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
5813
6760
|
});
|
|
5814
6761
|
return id;
|
|
5815
6762
|
}
|
|
@@ -5833,7 +6780,7 @@ __export(skill_learning_exports, {
|
|
|
5833
6780
|
storeTrajectory: () => storeTrajectory,
|
|
5834
6781
|
sweepTrajectories: () => sweepTrajectories
|
|
5835
6782
|
});
|
|
5836
|
-
import
|
|
6783
|
+
import crypto6 from "crypto";
|
|
5837
6784
|
async function extractTrajectory(taskId, agentId) {
|
|
5838
6785
|
const client = getClient();
|
|
5839
6786
|
const result = await client.execute({
|
|
@@ -5862,11 +6809,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
5862
6809
|
return signature;
|
|
5863
6810
|
}
|
|
5864
6811
|
function hashSignature(signature) {
|
|
5865
|
-
return
|
|
6812
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
5866
6813
|
}
|
|
5867
6814
|
async function storeTrajectory(opts) {
|
|
5868
6815
|
const client = getClient();
|
|
5869
|
-
const id =
|
|
6816
|
+
const id = crypto6.randomUUID();
|
|
5870
6817
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5871
6818
|
const signatureHash = hashSignature(opts.signature);
|
|
5872
6819
|
await client.execute({
|
|
@@ -6131,8 +7078,8 @@ __export(tasks_exports, {
|
|
|
6131
7078
|
updateTaskStatus: () => updateTaskStatus,
|
|
6132
7079
|
writeCheckpoint: () => writeCheckpoint
|
|
6133
7080
|
});
|
|
6134
|
-
import
|
|
6135
|
-
import { writeFileSync as
|
|
7081
|
+
import path19 from "path";
|
|
7082
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
|
|
6136
7083
|
async function createTask(input) {
|
|
6137
7084
|
const result = await createTaskCore(input);
|
|
6138
7085
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -6151,14 +7098,14 @@ async function updateTask(input) {
|
|
|
6151
7098
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
6152
7099
|
try {
|
|
6153
7100
|
const agent = String(row.assigned_to);
|
|
6154
|
-
const cacheDir =
|
|
6155
|
-
const cachePath =
|
|
7101
|
+
const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
|
|
7102
|
+
const cachePath = path19.join(cacheDir, `current-task-${agent}.json`);
|
|
6156
7103
|
if (input.status === "in_progress") {
|
|
6157
7104
|
mkdirSync7(cacheDir, { recursive: true });
|
|
6158
|
-
|
|
7105
|
+
writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6159
7106
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
6160
7107
|
try {
|
|
6161
|
-
|
|
7108
|
+
unlinkSync6(cachePath);
|
|
6162
7109
|
} catch {
|
|
6163
7110
|
}
|
|
6164
7111
|
}
|
|
@@ -6313,21 +7260,22 @@ var init_tasks = __esm({
|
|
|
6313
7260
|
});
|
|
6314
7261
|
|
|
6315
7262
|
// src/lib/store.ts
|
|
6316
|
-
|
|
7263
|
+
init_memory();
|
|
6317
7264
|
init_database();
|
|
7265
|
+
import { createHash } from "crypto";
|
|
6318
7266
|
|
|
6319
7267
|
// src/lib/keychain.ts
|
|
6320
7268
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
6321
|
-
import { existsSync as
|
|
6322
|
-
import
|
|
6323
|
-
import
|
|
7269
|
+
import { existsSync as existsSync7 } from "fs";
|
|
7270
|
+
import path7 from "path";
|
|
7271
|
+
import os5 from "os";
|
|
6324
7272
|
var SERVICE = "exe-mem";
|
|
6325
7273
|
var ACCOUNT = "master-key";
|
|
6326
7274
|
function getKeyDir() {
|
|
6327
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
7275
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
6328
7276
|
}
|
|
6329
7277
|
function getKeyPath() {
|
|
6330
|
-
return
|
|
7278
|
+
return path7.join(getKeyDir(), "master.key");
|
|
6331
7279
|
}
|
|
6332
7280
|
async function tryKeytar() {
|
|
6333
7281
|
try {
|
|
@@ -6348,9 +7296,9 @@ async function getMasterKey() {
|
|
|
6348
7296
|
}
|
|
6349
7297
|
}
|
|
6350
7298
|
const keyPath = getKeyPath();
|
|
6351
|
-
if (!
|
|
7299
|
+
if (!existsSync7(keyPath)) {
|
|
6352
7300
|
process.stderr.write(
|
|
6353
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
7301
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6354
7302
|
`
|
|
6355
7303
|
);
|
|
6356
7304
|
return null;
|
|
@@ -6429,6 +7377,11 @@ async function initStore(options) {
|
|
|
6429
7377
|
encryptionKey: hexKey
|
|
6430
7378
|
});
|
|
6431
7379
|
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
7380
|
+
try {
|
|
7381
|
+
const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
7382
|
+
await initDaemonClient2();
|
|
7383
|
+
} catch {
|
|
7384
|
+
}
|
|
6432
7385
|
if (!options?.lightweight) {
|
|
6433
7386
|
try {
|
|
6434
7387
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|