@askexenow/exe-os 0.9.11 → 0.9.13
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 +22 -1
- package/dist/bin/backfill-responses.js +22 -1
- package/dist/bin/backfill-vectors.js +22 -1
- package/dist/bin/cleanup-stale-review-tasks.js +22 -1
- package/dist/bin/cli.js +22 -1
- package/dist/bin/exe-assign.js +22 -1
- package/dist/bin/exe-boot.js +22 -1
- package/dist/bin/exe-dispatch.js +22 -1
- package/dist/bin/exe-doctor.js +22 -1
- package/dist/bin/exe-export-behaviors.js +22 -1
- package/dist/bin/exe-forget.js +22 -1
- package/dist/bin/exe-gateway.js +22 -1
- package/dist/bin/exe-heartbeat.js +22 -1
- package/dist/bin/exe-kill.js +22 -1
- package/dist/bin/exe-launch-agent.js +22 -1
- package/dist/bin/exe-link.js +22 -1
- package/dist/bin/exe-pending-messages.js +22 -1
- package/dist/bin/exe-pending-notifications.js +22 -1
- package/dist/bin/exe-pending-reviews.js +22 -1
- package/dist/bin/exe-rename.js +22 -1
- package/dist/bin/exe-review.js +22 -1
- package/dist/bin/exe-search.js +22 -1
- package/dist/bin/exe-session-cleanup.js +22 -1
- package/dist/bin/exe-start-codex.js +22 -1
- package/dist/bin/exe-start-opencode.js +22 -1
- package/dist/bin/exe-status.js +22 -1
- package/dist/bin/exe-team.js +22 -1
- package/dist/bin/git-sweep.js +22 -1
- package/dist/bin/graph-backfill.js +22 -1
- package/dist/bin/graph-export.js +22 -1
- package/dist/bin/scan-tasks.js +22 -1
- package/dist/bin/setup.js +22 -1
- package/dist/bin/shard-migrate.js +22 -1
- package/dist/bin/wiki-sync.js +22 -1
- package/dist/gateway/index.js +22 -1
- package/dist/hooks/bug-report-worker.js +22 -1
- package/dist/hooks/codex-stop-task-finalizer.js +22 -1
- package/dist/hooks/commit-complete.js +22 -1
- package/dist/hooks/error-recall.js +22 -1
- package/dist/hooks/ingest-worker.js +22 -1
- package/dist/hooks/ingest.js +3345 -232
- package/dist/hooks/instructions-loaded.js +22 -1
- package/dist/hooks/notification.js +22 -1
- package/dist/hooks/post-compact.js +22 -1
- package/dist/hooks/pre-compact.js +22 -1
- package/dist/hooks/pre-tool-use.js +22 -1
- package/dist/hooks/prompt-ingest-worker.js +22 -1
- package/dist/hooks/prompt-submit.js +1700 -1396
- package/dist/hooks/response-ingest-worker.js +22 -1
- package/dist/hooks/session-end.js +345 -187
- package/dist/hooks/session-start.js +304 -15
- package/dist/hooks/stop.js +22 -1
- package/dist/hooks/subagent-stop.js +22 -1
- package/dist/hooks/summary-worker.js +22 -1
- package/dist/index.js +22 -1
- package/dist/lib/cloud-sync.js +22 -1
- package/dist/lib/database.js +22 -1
- package/dist/lib/db.js +22 -1
- package/dist/lib/device-registry.js +22 -1
- package/dist/lib/exe-daemon.js +39 -1
- package/dist/lib/hybrid-search.js +22 -1
- package/dist/lib/schedules.js +22 -1
- package/dist/lib/store.js +22 -1
- package/dist/mcp/server.js +126 -1
- package/dist/runtime/index.js +22 -1
- package/dist/tui/App.js +22 -1
- package/package.json +1 -1
|
@@ -1321,450 +1321,1126 @@ var init_database_adapter = __esm({
|
|
|
1321
1321
|
}
|
|
1322
1322
|
});
|
|
1323
1323
|
|
|
1324
|
-
// src/lib/
|
|
1325
|
-
import
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1324
|
+
// src/lib/daemon-auth.ts
|
|
1325
|
+
import crypto from "crypto";
|
|
1326
|
+
import path5 from "path";
|
|
1327
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1328
|
+
function normalizeToken(token) {
|
|
1329
|
+
if (!token) return null;
|
|
1330
|
+
const trimmed = token.trim();
|
|
1331
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1332
|
+
}
|
|
1333
|
+
function readDaemonToken() {
|
|
1334
|
+
try {
|
|
1335
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1336
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
1337
|
+
} catch {
|
|
1338
|
+
return null;
|
|
1330
1339
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1340
|
+
}
|
|
1341
|
+
function ensureDaemonToken(seed) {
|
|
1342
|
+
const existing = readDaemonToken();
|
|
1343
|
+
if (existing) return existing;
|
|
1344
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1345
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1346
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1347
|
+
`, "utf8");
|
|
1348
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1349
|
+
return token;
|
|
1350
|
+
}
|
|
1351
|
+
var DAEMON_TOKEN_PATH;
|
|
1352
|
+
var init_daemon_auth = __esm({
|
|
1353
|
+
"src/lib/daemon-auth.ts"() {
|
|
1354
|
+
"use strict";
|
|
1355
|
+
init_config();
|
|
1356
|
+
init_secure_files();
|
|
1357
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1334
1358
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
// src/lib/exe-daemon-client.ts
|
|
1362
|
+
import net from "net";
|
|
1363
|
+
import os4 from "os";
|
|
1364
|
+
import { spawn } from "child_process";
|
|
1365
|
+
import { randomUUID } from "crypto";
|
|
1366
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1367
|
+
import path6 from "path";
|
|
1368
|
+
import { fileURLToPath } from "url";
|
|
1369
|
+
function handleData(chunk) {
|
|
1370
|
+
_buffer += chunk.toString();
|
|
1371
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
1372
|
+
_buffer = "";
|
|
1373
|
+
return;
|
|
1337
1374
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1375
|
+
let newlineIdx;
|
|
1376
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
1377
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
1378
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
1379
|
+
if (!line) continue;
|
|
1380
|
+
try {
|
|
1381
|
+
const response = JSON.parse(line);
|
|
1382
|
+
const id = response.id;
|
|
1383
|
+
if (!id) continue;
|
|
1384
|
+
const entry = _pending.get(id);
|
|
1385
|
+
if (entry) {
|
|
1386
|
+
clearTimeout(entry.timer);
|
|
1387
|
+
_pending.delete(id);
|
|
1388
|
+
entry.resolve(response);
|
|
1389
|
+
}
|
|
1390
|
+
} catch {
|
|
1391
|
+
}
|
|
1343
1392
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1393
|
+
}
|
|
1394
|
+
function cleanupStaleFiles() {
|
|
1395
|
+
if (existsSync6(PID_PATH)) {
|
|
1396
|
+
try {
|
|
1397
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1398
|
+
if (pid > 0) {
|
|
1399
|
+
try {
|
|
1400
|
+
process.kill(pid, 0);
|
|
1401
|
+
return;
|
|
1402
|
+
} catch {
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
try {
|
|
1408
|
+
unlinkSync2(PID_PATH);
|
|
1409
|
+
} catch {
|
|
1410
|
+
}
|
|
1411
|
+
try {
|
|
1412
|
+
unlinkSync2(SOCKET_PATH);
|
|
1413
|
+
} catch {
|
|
1414
|
+
}
|
|
1349
1415
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
if (_walCheckpointTimer) clearInterval(_walCheckpointTimer);
|
|
1358
|
-
_walCheckpointTimer = setInterval(() => {
|
|
1359
|
-
_client?.execute("PRAGMA wal_checkpoint(PASSIVE)").catch(() => {
|
|
1360
|
-
});
|
|
1361
|
-
}, 3e4);
|
|
1362
|
-
_walCheckpointTimer.unref();
|
|
1363
|
-
if (process.env.DATABASE_URL) {
|
|
1364
|
-
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1416
|
+
}
|
|
1417
|
+
function findPackageRoot() {
|
|
1418
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1419
|
+
const { root } = path6.parse(dir);
|
|
1420
|
+
while (dir !== root) {
|
|
1421
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1422
|
+
dir = path6.dirname(dir);
|
|
1365
1423
|
}
|
|
1424
|
+
return null;
|
|
1366
1425
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
|
|
1426
|
+
function spawnDaemon() {
|
|
1427
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
1428
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1429
|
+
if (totalGB <= 8) {
|
|
1430
|
+
process.stderr.write(
|
|
1431
|
+
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
1432
|
+
`
|
|
1433
|
+
);
|
|
1434
|
+
return;
|
|
1370
1435
|
}
|
|
1371
|
-
if (
|
|
1372
|
-
|
|
1436
|
+
if (totalGB <= 16 && freeGB < 4) {
|
|
1437
|
+
process.stderr.write(
|
|
1438
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB free / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
1439
|
+
`
|
|
1440
|
+
);
|
|
1441
|
+
return;
|
|
1373
1442
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1443
|
+
const pkgRoot = findPackageRoot();
|
|
1444
|
+
if (!pkgRoot) {
|
|
1445
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1446
|
+
return;
|
|
1376
1447
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1448
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1449
|
+
if (!existsSync6(daemonPath)) {
|
|
1450
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1451
|
+
`);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
const resolvedPath = daemonPath;
|
|
1455
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1456
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1457
|
+
`);
|
|
1458
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1459
|
+
let stderrFd = "ignore";
|
|
1460
|
+
try {
|
|
1461
|
+
stderrFd = openSync(logPath, "a");
|
|
1462
|
+
} catch {
|
|
1463
|
+
}
|
|
1464
|
+
const heapCapMB = totalGB <= 8 ? 256 : 512;
|
|
1465
|
+
const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
|
|
1466
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
1467
|
+
detached: true,
|
|
1468
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
1469
|
+
env: {
|
|
1470
|
+
...process.env,
|
|
1471
|
+
TMUX: void 0,
|
|
1472
|
+
// Daemon is global — must not inherit session scope
|
|
1473
|
+
TMUX_PANE: void 0,
|
|
1474
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
1475
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1476
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1477
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
child.unref();
|
|
1481
|
+
if (typeof stderrFd === "number") {
|
|
1482
|
+
try {
|
|
1483
|
+
closeSync(stderrFd);
|
|
1484
|
+
} catch {
|
|
1485
|
+
}
|
|
1379
1486
|
}
|
|
1380
|
-
return _resilientClient;
|
|
1381
1487
|
}
|
|
1382
|
-
function
|
|
1383
|
-
|
|
1384
|
-
|
|
1488
|
+
function acquireSpawnLock() {
|
|
1489
|
+
try {
|
|
1490
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1491
|
+
closeSync(fd);
|
|
1492
|
+
return true;
|
|
1493
|
+
} catch {
|
|
1494
|
+
try {
|
|
1495
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
1496
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
1497
|
+
try {
|
|
1498
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
try {
|
|
1502
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
1503
|
+
closeSync(fd);
|
|
1504
|
+
return true;
|
|
1505
|
+
} catch {
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
} catch {
|
|
1509
|
+
}
|
|
1510
|
+
return false;
|
|
1385
1511
|
}
|
|
1386
|
-
return _client;
|
|
1387
1512
|
}
|
|
1388
|
-
|
|
1389
|
-
const client = getRawClient();
|
|
1390
|
-
await client.execute("PRAGMA journal_mode = WAL");
|
|
1391
|
-
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1392
|
-
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
1513
|
+
function releaseSpawnLock() {
|
|
1393
1514
|
try {
|
|
1394
|
-
|
|
1515
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
1395
1516
|
} catch {
|
|
1396
1517
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
timestamp TEXT NOT NULL,
|
|
1404
|
-
tool_name TEXT NOT NULL,
|
|
1405
|
-
project_name TEXT NOT NULL,
|
|
1406
|
-
has_error INTEGER NOT NULL DEFAULT 0,
|
|
1407
|
-
raw_text TEXT NOT NULL,
|
|
1408
|
-
vector F32_BLOB(1024),
|
|
1409
|
-
version INTEGER NOT NULL DEFAULT 0
|
|
1410
|
-
);
|
|
1411
|
-
|
|
1412
|
-
CREATE INDEX IF NOT EXISTS idx_memories_agent
|
|
1413
|
-
ON memories(agent_id);
|
|
1414
|
-
|
|
1415
|
-
CREATE INDEX IF NOT EXISTS idx_memories_timestamp
|
|
1416
|
-
ON memories(timestamp);
|
|
1417
|
-
|
|
1418
|
-
CREATE INDEX IF NOT EXISTS idx_memories_session
|
|
1419
|
-
ON memories(session_id);
|
|
1420
|
-
|
|
1421
|
-
CREATE INDEX IF NOT EXISTS idx_memories_project
|
|
1422
|
-
ON memories(project_name);
|
|
1423
|
-
|
|
1424
|
-
CREATE INDEX IF NOT EXISTS idx_memories_tool
|
|
1425
|
-
ON memories(tool_name);
|
|
1426
|
-
|
|
1427
|
-
CREATE INDEX IF NOT EXISTS idx_memories_version
|
|
1428
|
-
ON memories(version);
|
|
1429
|
-
|
|
1430
|
-
CREATE INDEX IF NOT EXISTS idx_memories_agent_project
|
|
1431
|
-
ON memories(agent_id, project_name);
|
|
1432
|
-
`);
|
|
1433
|
-
await client.executeMultiple(`
|
|
1434
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
1435
|
-
raw_text,
|
|
1436
|
-
content='memories',
|
|
1437
|
-
content_rowid='rowid'
|
|
1438
|
-
);
|
|
1439
|
-
|
|
1440
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_ai AFTER INSERT ON memories BEGIN
|
|
1441
|
-
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
1442
|
-
END;
|
|
1443
|
-
|
|
1444
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_ad AFTER DELETE ON memories BEGIN
|
|
1445
|
-
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
1446
|
-
END;
|
|
1447
|
-
|
|
1448
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_au AFTER UPDATE ON memories BEGIN
|
|
1449
|
-
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
1450
|
-
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
1451
|
-
END;
|
|
1452
|
-
`);
|
|
1453
|
-
await client.executeMultiple(`
|
|
1454
|
-
CREATE TABLE IF NOT EXISTS sync_meta (
|
|
1455
|
-
key TEXT PRIMARY KEY,
|
|
1456
|
-
value TEXT NOT NULL
|
|
1457
|
-
);
|
|
1458
|
-
`);
|
|
1459
|
-
await client.executeMultiple(`
|
|
1460
|
-
CREATE TABLE IF NOT EXISTS tasks (
|
|
1461
|
-
id TEXT PRIMARY KEY,
|
|
1462
|
-
title TEXT NOT NULL,
|
|
1463
|
-
assigned_to TEXT NOT NULL,
|
|
1464
|
-
assigned_by TEXT NOT NULL,
|
|
1465
|
-
project_name TEXT NOT NULL,
|
|
1466
|
-
priority TEXT NOT NULL DEFAULT 'p1',
|
|
1467
|
-
status TEXT NOT NULL DEFAULT 'open',
|
|
1468
|
-
task_file TEXT,
|
|
1469
|
-
created_at TEXT NOT NULL,
|
|
1470
|
-
updated_at TEXT NOT NULL
|
|
1471
|
-
);
|
|
1472
|
-
|
|
1473
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_assignee_status
|
|
1474
|
-
ON tasks(assigned_to, status);
|
|
1475
|
-
`);
|
|
1476
|
-
await client.executeMultiple(`
|
|
1477
|
-
CREATE TABLE IF NOT EXISTS behaviors (
|
|
1478
|
-
id TEXT PRIMARY KEY,
|
|
1479
|
-
agent_id TEXT NOT NULL,
|
|
1480
|
-
project_name TEXT,
|
|
1481
|
-
domain TEXT,
|
|
1482
|
-
content TEXT NOT NULL,
|
|
1483
|
-
active INTEGER NOT NULL DEFAULT 1,
|
|
1484
|
-
created_at TEXT NOT NULL,
|
|
1485
|
-
updated_at TEXT NOT NULL
|
|
1486
|
-
);
|
|
1487
|
-
|
|
1488
|
-
CREATE INDEX IF NOT EXISTS idx_behaviors_agent
|
|
1489
|
-
ON behaviors(agent_id, active);
|
|
1490
|
-
`);
|
|
1491
|
-
try {
|
|
1492
|
-
const coordinatorName = getCoordinatorName();
|
|
1493
|
-
const existing = await client.execute({
|
|
1494
|
-
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
1495
|
-
args: [coordinatorName]
|
|
1496
|
-
});
|
|
1497
|
-
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
1498
|
-
const seededAt = "2026-03-25T00:00:00Z";
|
|
1499
|
-
for (const [domain, content] of [
|
|
1500
|
-
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
1501
|
-
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
1502
|
-
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
1503
|
-
]) {
|
|
1504
|
-
await client.execute({
|
|
1505
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
1506
|
-
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
1507
|
-
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
1508
|
-
});
|
|
1509
|
-
}
|
|
1518
|
+
}
|
|
1519
|
+
function connectToSocket() {
|
|
1520
|
+
return new Promise((resolve) => {
|
|
1521
|
+
if (_socket && _connected) {
|
|
1522
|
+
resolve(true);
|
|
1523
|
+
return;
|
|
1510
1524
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1525
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
1526
|
+
const connectTimeout = setTimeout(() => {
|
|
1527
|
+
socket.destroy();
|
|
1528
|
+
resolve(false);
|
|
1529
|
+
}, 2e3);
|
|
1530
|
+
socket.on("connect", () => {
|
|
1531
|
+
clearTimeout(connectTimeout);
|
|
1532
|
+
_socket = socket;
|
|
1533
|
+
_connected = true;
|
|
1534
|
+
_buffer = "";
|
|
1535
|
+
socket.on("data", handleData);
|
|
1536
|
+
socket.on("close", () => {
|
|
1537
|
+
_connected = false;
|
|
1538
|
+
_socket = null;
|
|
1539
|
+
for (const [id, entry] of _pending) {
|
|
1540
|
+
clearTimeout(entry.timer);
|
|
1541
|
+
_pending.delete(id);
|
|
1542
|
+
entry.resolve({ error: "Connection closed" });
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
socket.on("error", () => {
|
|
1546
|
+
_connected = false;
|
|
1547
|
+
_socket = null;
|
|
1548
|
+
});
|
|
1549
|
+
resolve(true);
|
|
1524
1550
|
});
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
await client.execute({
|
|
1529
|
-
sql: `ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`,
|
|
1530
|
-
args: []
|
|
1551
|
+
socket.on("error", () => {
|
|
1552
|
+
clearTimeout(connectTimeout);
|
|
1553
|
+
resolve(false);
|
|
1531
1554
|
});
|
|
1532
|
-
}
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
async function connectEmbedDaemon() {
|
|
1558
|
+
if (_socket && _connected) return true;
|
|
1559
|
+
if (await connectToSocket()) return true;
|
|
1560
|
+
if (acquireSpawnLock()) {
|
|
1561
|
+
try {
|
|
1562
|
+
cleanupStaleFiles();
|
|
1563
|
+
spawnDaemon();
|
|
1564
|
+
} finally {
|
|
1565
|
+
releaseSpawnLock();
|
|
1566
|
+
}
|
|
1533
1567
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
});
|
|
1541
|
-
} catch {
|
|
1568
|
+
const start = Date.now();
|
|
1569
|
+
let delay2 = 100;
|
|
1570
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1571
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1572
|
+
if (await connectToSocket()) return true;
|
|
1573
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1542
1574
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
function sendRequest(texts, priority) {
|
|
1578
|
+
return sendDaemonRequest({ texts, priority });
|
|
1579
|
+
}
|
|
1580
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1581
|
+
return new Promise((resolve) => {
|
|
1582
|
+
if (!_socket || !_connected) {
|
|
1583
|
+
resolve({ error: "Not connected" });
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
const id = randomUUID();
|
|
1587
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1588
|
+
const timer = setTimeout(() => {
|
|
1589
|
+
_pending.delete(id);
|
|
1590
|
+
resolve({ error: "Request timeout" });
|
|
1591
|
+
}, timeoutMs);
|
|
1592
|
+
_pending.set(id, { resolve, timer });
|
|
1593
|
+
try {
|
|
1594
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1595
|
+
} catch {
|
|
1596
|
+
clearTimeout(timer);
|
|
1597
|
+
_pending.delete(id);
|
|
1598
|
+
resolve({ error: "Write failed" });
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
async function pingDaemon() {
|
|
1603
|
+
if (!_socket || !_connected) return null;
|
|
1604
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
1605
|
+
if (response.health) {
|
|
1606
|
+
return response.health;
|
|
1549
1607
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1610
|
+
function killAndRespawnDaemon() {
|
|
1611
|
+
if (!acquireSpawnLock()) {
|
|
1612
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1613
|
+
if (_socket) {
|
|
1614
|
+
_socket.destroy();
|
|
1615
|
+
_socket = null;
|
|
1616
|
+
}
|
|
1617
|
+
_connected = false;
|
|
1618
|
+
_buffer = "";
|
|
1619
|
+
return;
|
|
1556
1620
|
}
|
|
1557
1621
|
try {
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1622
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1623
|
+
if (existsSync6(PID_PATH)) {
|
|
1624
|
+
try {
|
|
1625
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1626
|
+
if (pid > 0) {
|
|
1627
|
+
try {
|
|
1628
|
+
process.kill(pid, "SIGKILL");
|
|
1629
|
+
} catch {
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (_socket) {
|
|
1636
|
+
_socket.destroy();
|
|
1637
|
+
_socket = null;
|
|
1638
|
+
}
|
|
1639
|
+
_connected = false;
|
|
1640
|
+
_buffer = "";
|
|
1641
|
+
try {
|
|
1642
|
+
unlinkSync2(PID_PATH);
|
|
1643
|
+
} catch {
|
|
1644
|
+
}
|
|
1645
|
+
try {
|
|
1646
|
+
unlinkSync2(SOCKET_PATH);
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1649
|
+
spawnDaemon();
|
|
1650
|
+
} finally {
|
|
1651
|
+
releaseSpawnLock();
|
|
1563
1652
|
}
|
|
1653
|
+
}
|
|
1654
|
+
function isDaemonTooYoung() {
|
|
1564
1655
|
try {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
args: []
|
|
1568
|
-
});
|
|
1656
|
+
const stat = statSync(PID_PATH);
|
|
1657
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
1569
1658
|
} catch {
|
|
1659
|
+
return false;
|
|
1570
1660
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
}
|
|
1578
|
-
try {
|
|
1579
|
-
await client.execute({
|
|
1580
|
-
sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
|
|
1581
|
-
args: []
|
|
1582
|
-
});
|
|
1583
|
-
} catch {
|
|
1661
|
+
}
|
|
1662
|
+
async function retryThenRestart(doRequest, label) {
|
|
1663
|
+
const result = await doRequest();
|
|
1664
|
+
if (!result.error) {
|
|
1665
|
+
_consecutiveFailures = 0;
|
|
1666
|
+
return result;
|
|
1584
1667
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1668
|
+
_consecutiveFailures++;
|
|
1669
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1670
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1671
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1672
|
+
`);
|
|
1673
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1674
|
+
if (!_connected) {
|
|
1675
|
+
if (!await connectToSocket()) continue;
|
|
1676
|
+
}
|
|
1677
|
+
const retry = await doRequest();
|
|
1678
|
+
if (!retry.error) {
|
|
1679
|
+
_consecutiveFailures = 0;
|
|
1680
|
+
return retry;
|
|
1681
|
+
}
|
|
1682
|
+
_consecutiveFailures++;
|
|
1591
1683
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
});
|
|
1597
|
-
} catch {
|
|
1684
|
+
if (isDaemonTooYoung()) {
|
|
1685
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1686
|
+
`);
|
|
1687
|
+
return { error: result.error };
|
|
1598
1688
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1689
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1690
|
+
`);
|
|
1691
|
+
killAndRespawnDaemon();
|
|
1692
|
+
const start = Date.now();
|
|
1693
|
+
let delay2 = 200;
|
|
1694
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1695
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1696
|
+
if (await connectToSocket()) break;
|
|
1697
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1605
1698
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1699
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1700
|
+
const final = await doRequest();
|
|
1701
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1702
|
+
return final;
|
|
1703
|
+
}
|
|
1704
|
+
async function embedViaClient(text, priority = "high") {
|
|
1705
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1706
|
+
_requestCount++;
|
|
1707
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1708
|
+
const health = await pingDaemon();
|
|
1709
|
+
if (!health && !isDaemonTooYoung()) {
|
|
1710
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1711
|
+
`);
|
|
1712
|
+
killAndRespawnDaemon();
|
|
1713
|
+
const start = Date.now();
|
|
1714
|
+
let d = 200;
|
|
1715
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1716
|
+
await new Promise((r) => setTimeout(r, d));
|
|
1717
|
+
if (await connectToSocket()) break;
|
|
1718
|
+
d = Math.min(d * 2, 3e3);
|
|
1719
|
+
}
|
|
1720
|
+
if (!_connected) return null;
|
|
1721
|
+
}
|
|
1612
1722
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1723
|
+
const result = await retryThenRestart(
|
|
1724
|
+
() => sendRequest([text], priority),
|
|
1725
|
+
"Embed"
|
|
1726
|
+
);
|
|
1727
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
1728
|
+
}
|
|
1729
|
+
function disconnectClient() {
|
|
1730
|
+
if (_socket) {
|
|
1731
|
+
_socket.destroy();
|
|
1732
|
+
_socket = null;
|
|
1619
1733
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1734
|
+
_connected = false;
|
|
1735
|
+
_buffer = "";
|
|
1736
|
+
for (const [id, entry] of _pending) {
|
|
1737
|
+
clearTimeout(entry.timer);
|
|
1738
|
+
_pending.delete(id);
|
|
1739
|
+
entry.resolve({ error: "Client disconnected" });
|
|
1626
1740
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1741
|
+
}
|
|
1742
|
+
function isClientConnected() {
|
|
1743
|
+
return _connected;
|
|
1744
|
+
}
|
|
1745
|
+
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;
|
|
1746
|
+
var init_exe_daemon_client = __esm({
|
|
1747
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
1748
|
+
"use strict";
|
|
1749
|
+
init_config();
|
|
1750
|
+
init_daemon_auth();
|
|
1751
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1752
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1753
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1754
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1755
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
1756
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
1757
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
1758
|
+
_socket = null;
|
|
1759
|
+
_connected = false;
|
|
1760
|
+
_buffer = "";
|
|
1761
|
+
_requestCount = 0;
|
|
1762
|
+
_consecutiveFailures = 0;
|
|
1763
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
1764
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1765
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1766
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
1767
|
+
_pending = /* @__PURE__ */ new Map();
|
|
1768
|
+
MAX_BUFFER = 1e7;
|
|
1633
1769
|
}
|
|
1634
|
-
|
|
1635
|
-
CREATE TABLE IF NOT EXISTS consolidations (
|
|
1636
|
-
id TEXT PRIMARY KEY,
|
|
1637
|
-
consolidated_memory_id TEXT NOT NULL,
|
|
1638
|
-
source_memory_id TEXT NOT NULL,
|
|
1639
|
-
created_at TEXT NOT NULL
|
|
1640
|
-
);
|
|
1641
|
-
|
|
1642
|
-
CREATE INDEX IF NOT EXISTS idx_consolidations_source
|
|
1643
|
-
ON consolidations(source_memory_id);
|
|
1770
|
+
});
|
|
1644
1771
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1772
|
+
// src/lib/daemon-protocol.ts
|
|
1773
|
+
function serializeValue(v) {
|
|
1774
|
+
if (v === null || v === void 0) return null;
|
|
1775
|
+
if (typeof v === "bigint") return Number(v);
|
|
1776
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
1777
|
+
if (v instanceof Uint8Array) {
|
|
1778
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1779
|
+
}
|
|
1780
|
+
if (ArrayBuffer.isView(v)) {
|
|
1781
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
1782
|
+
}
|
|
1783
|
+
if (v instanceof ArrayBuffer) {
|
|
1784
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
1785
|
+
}
|
|
1786
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1787
|
+
return String(v);
|
|
1788
|
+
}
|
|
1789
|
+
function deserializeValue(v) {
|
|
1790
|
+
if (v === null) return null;
|
|
1791
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
1792
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
1793
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
1794
|
+
}
|
|
1795
|
+
return v;
|
|
1796
|
+
}
|
|
1797
|
+
function deserializeResultSet(srs) {
|
|
1798
|
+
const rows = srs.rows.map((obj) => {
|
|
1799
|
+
const values = srs.columns.map(
|
|
1800
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
1669
1801
|
);
|
|
1802
|
+
const row = values;
|
|
1803
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
1804
|
+
const col = srs.columns[i];
|
|
1805
|
+
if (col !== void 0) {
|
|
1806
|
+
row[col] = values[i] ?? null;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
Object.defineProperty(row, "length", {
|
|
1810
|
+
value: values.length,
|
|
1811
|
+
enumerable: false
|
|
1812
|
+
});
|
|
1813
|
+
return row;
|
|
1814
|
+
});
|
|
1815
|
+
return {
|
|
1816
|
+
columns: srs.columns,
|
|
1817
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1818
|
+
rows,
|
|
1819
|
+
rowsAffected: srs.rowsAffected,
|
|
1820
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
1821
|
+
toJSON: () => ({
|
|
1822
|
+
columns: srs.columns,
|
|
1823
|
+
columnTypes: srs.columnTypes ?? [],
|
|
1824
|
+
rows: srs.rows,
|
|
1825
|
+
rowsAffected: srs.rowsAffected,
|
|
1826
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
1827
|
+
})
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
var init_daemon_protocol = __esm({
|
|
1831
|
+
"src/lib/daemon-protocol.ts"() {
|
|
1832
|
+
"use strict";
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1670
1835
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1836
|
+
// src/lib/db-daemon-client.ts
|
|
1837
|
+
var db_daemon_client_exports = {};
|
|
1838
|
+
__export(db_daemon_client_exports, {
|
|
1839
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
1840
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
1841
|
+
});
|
|
1842
|
+
function normalizeStatement2(stmt) {
|
|
1843
|
+
if (typeof stmt === "string") {
|
|
1844
|
+
return { sql: stmt, args: [] };
|
|
1845
|
+
}
|
|
1846
|
+
const sql = stmt.sql;
|
|
1847
|
+
let args = [];
|
|
1848
|
+
if (Array.isArray(stmt.args)) {
|
|
1849
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
1850
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
1851
|
+
const named = {};
|
|
1852
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
1853
|
+
named[key] = serializeValue(val);
|
|
1854
|
+
}
|
|
1855
|
+
return { sql, args: named };
|
|
1856
|
+
}
|
|
1857
|
+
return { sql, args };
|
|
1858
|
+
}
|
|
1859
|
+
function createDaemonDbClient(fallbackClient) {
|
|
1860
|
+
let _useDaemon = false;
|
|
1861
|
+
const client = {
|
|
1862
|
+
async execute(stmt) {
|
|
1863
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1864
|
+
return fallbackClient.execute(stmt);
|
|
1865
|
+
}
|
|
1866
|
+
const { sql, args } = normalizeStatement2(stmt);
|
|
1867
|
+
const response = await sendDaemonRequest({
|
|
1868
|
+
type: "db-execute",
|
|
1869
|
+
sql,
|
|
1870
|
+
args
|
|
1871
|
+
});
|
|
1872
|
+
if (response.error) {
|
|
1873
|
+
const errMsg = String(response.error);
|
|
1874
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1875
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
1876
|
+
`);
|
|
1877
|
+
return fallbackClient.execute(stmt);
|
|
1878
|
+
}
|
|
1879
|
+
throw new Error(errMsg);
|
|
1880
|
+
}
|
|
1881
|
+
if (response.db) {
|
|
1882
|
+
return deserializeResultSet(response.db);
|
|
1883
|
+
}
|
|
1884
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
1885
|
+
return fallbackClient.execute(stmt);
|
|
1886
|
+
},
|
|
1887
|
+
async batch(stmts, mode) {
|
|
1888
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
1889
|
+
return fallbackClient.batch(stmts, mode);
|
|
1890
|
+
}
|
|
1891
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1892
|
+
const response = await sendDaemonRequest({
|
|
1893
|
+
type: "db-batch",
|
|
1894
|
+
statements,
|
|
1895
|
+
mode: mode ?? "deferred"
|
|
1896
|
+
});
|
|
1897
|
+
if (response.error) {
|
|
1898
|
+
const errMsg = String(response.error);
|
|
1899
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
1900
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
1901
|
+
`);
|
|
1902
|
+
return fallbackClient.batch(stmts, mode);
|
|
1903
|
+
}
|
|
1904
|
+
throw new Error(errMsg);
|
|
1905
|
+
}
|
|
1906
|
+
const batchResults = response["db-batch"];
|
|
1907
|
+
if (batchResults) {
|
|
1908
|
+
return batchResults.map(deserializeResultSet);
|
|
1909
|
+
}
|
|
1910
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
1911
|
+
return fallbackClient.batch(stmts, mode);
|
|
1912
|
+
},
|
|
1913
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
1914
|
+
async transaction(mode) {
|
|
1915
|
+
return fallbackClient.transaction(mode);
|
|
1916
|
+
},
|
|
1917
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
1918
|
+
async executeMultiple(sql) {
|
|
1919
|
+
return fallbackClient.executeMultiple(sql);
|
|
1920
|
+
},
|
|
1921
|
+
// migrate — delegate to fallback
|
|
1922
|
+
async migrate(stmts) {
|
|
1923
|
+
return fallbackClient.migrate(stmts);
|
|
1924
|
+
},
|
|
1925
|
+
// Sync mode — delegate to fallback
|
|
1926
|
+
sync() {
|
|
1927
|
+
return fallbackClient.sync();
|
|
1928
|
+
},
|
|
1929
|
+
close() {
|
|
1930
|
+
_useDaemon = false;
|
|
1931
|
+
},
|
|
1932
|
+
get closed() {
|
|
1933
|
+
return fallbackClient.closed;
|
|
1934
|
+
},
|
|
1935
|
+
get protocol() {
|
|
1936
|
+
return fallbackClient.protocol;
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
return {
|
|
1940
|
+
...client,
|
|
1941
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
1942
|
+
_enableDaemon() {
|
|
1943
|
+
_useDaemon = true;
|
|
1944
|
+
},
|
|
1945
|
+
/** Check if daemon routing is active */
|
|
1946
|
+
_isDaemonActive() {
|
|
1947
|
+
return _useDaemon && isClientConnected();
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
1952
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1953
|
+
const connected = await connectEmbedDaemon();
|
|
1954
|
+
if (!connected) {
|
|
1955
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1956
|
+
return null;
|
|
1957
|
+
}
|
|
1958
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1959
|
+
client._enableDaemon();
|
|
1960
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1961
|
+
return client;
|
|
1962
|
+
}
|
|
1963
|
+
var init_db_daemon_client = __esm({
|
|
1964
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1965
|
+
"use strict";
|
|
1966
|
+
init_exe_daemon_client();
|
|
1967
|
+
init_daemon_protocol();
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1673
1970
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1971
|
+
// src/lib/database.ts
|
|
1972
|
+
var database_exports = {};
|
|
1973
|
+
__export(database_exports, {
|
|
1974
|
+
disposeDatabase: () => disposeDatabase,
|
|
1975
|
+
disposeTurso: () => disposeTurso,
|
|
1976
|
+
ensureSchema: () => ensureSchema,
|
|
1977
|
+
getClient: () => getClient,
|
|
1978
|
+
getRawClient: () => getRawClient,
|
|
1979
|
+
initDaemonClient: () => initDaemonClient,
|
|
1980
|
+
initDatabase: () => initDatabase,
|
|
1981
|
+
initTurso: () => initTurso,
|
|
1982
|
+
isInitialized: () => isInitialized
|
|
1983
|
+
});
|
|
1984
|
+
import { createClient } from "@libsql/client";
|
|
1985
|
+
async function initDatabase(config) {
|
|
1986
|
+
if (_walCheckpointTimer) {
|
|
1987
|
+
clearInterval(_walCheckpointTimer);
|
|
1988
|
+
_walCheckpointTimer = null;
|
|
1989
|
+
}
|
|
1990
|
+
if (_daemonClient) {
|
|
1991
|
+
_daemonClient.close();
|
|
1992
|
+
_daemonClient = null;
|
|
1993
|
+
}
|
|
1994
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1995
|
+
_adapterClient.close();
|
|
1996
|
+
}
|
|
1997
|
+
_adapterClient = null;
|
|
1998
|
+
if (_client) {
|
|
1999
|
+
_client.close();
|
|
2000
|
+
_client = null;
|
|
2001
|
+
_resilientClient = null;
|
|
2002
|
+
}
|
|
2003
|
+
const opts = {
|
|
2004
|
+
url: `file:${config.dbPath}`
|
|
2005
|
+
};
|
|
2006
|
+
if (config.encryptionKey) {
|
|
2007
|
+
opts.encryptionKey = config.encryptionKey;
|
|
2008
|
+
}
|
|
2009
|
+
_client = createClient(opts);
|
|
2010
|
+
_resilientClient = wrapWithRetry(_client);
|
|
2011
|
+
_adapterClient = _resilientClient;
|
|
2012
|
+
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
2013
|
+
});
|
|
2014
|
+
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
2015
|
+
});
|
|
2016
|
+
if (_walCheckpointTimer) clearInterval(_walCheckpointTimer);
|
|
2017
|
+
_walCheckpointTimer = setInterval(() => {
|
|
2018
|
+
_client?.execute("PRAGMA wal_checkpoint(PASSIVE)").catch(() => {
|
|
2019
|
+
});
|
|
2020
|
+
}, 3e4);
|
|
2021
|
+
_walCheckpointTimer.unref();
|
|
2022
|
+
if (process.env.DATABASE_URL) {
|
|
2023
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
function isInitialized() {
|
|
2027
|
+
return _adapterClient !== null || _client !== null;
|
|
2028
|
+
}
|
|
2029
|
+
function getClient() {
|
|
2030
|
+
if (!_adapterClient) {
|
|
2031
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
2032
|
+
}
|
|
2033
|
+
if (process.env.DATABASE_URL) {
|
|
2034
|
+
return _adapterClient;
|
|
2035
|
+
}
|
|
2036
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
2037
|
+
return _resilientClient;
|
|
2038
|
+
}
|
|
2039
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
2040
|
+
return _daemonClient;
|
|
2041
|
+
}
|
|
2042
|
+
return _resilientClient;
|
|
2043
|
+
}
|
|
2044
|
+
async function initDaemonClient() {
|
|
2045
|
+
if (process.env.DATABASE_URL) return;
|
|
2046
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
2047
|
+
if (!_resilientClient) return;
|
|
2048
|
+
try {
|
|
2049
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
2050
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
2051
|
+
} catch (err) {
|
|
2052
|
+
process.stderr.write(
|
|
2053
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
2054
|
+
`
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
function getRawClient() {
|
|
2059
|
+
if (!_client) {
|
|
2060
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
2061
|
+
}
|
|
2062
|
+
return _client;
|
|
2063
|
+
}
|
|
2064
|
+
async function ensureSchema() {
|
|
2065
|
+
const client = getRawClient();
|
|
2066
|
+
await client.execute("PRAGMA journal_mode = WAL");
|
|
2067
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
2068
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
2069
|
+
try {
|
|
2070
|
+
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
2071
|
+
} catch {
|
|
2072
|
+
}
|
|
2073
|
+
await client.executeMultiple(`
|
|
2074
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
2075
|
+
id TEXT PRIMARY KEY,
|
|
2076
|
+
agent_id TEXT NOT NULL,
|
|
2077
|
+
agent_role TEXT NOT NULL,
|
|
2078
|
+
session_id TEXT NOT NULL,
|
|
2079
|
+
timestamp TEXT NOT NULL,
|
|
2080
|
+
tool_name TEXT NOT NULL,
|
|
2081
|
+
project_name TEXT NOT NULL,
|
|
2082
|
+
has_error INTEGER NOT NULL DEFAULT 0,
|
|
2083
|
+
raw_text TEXT NOT NULL,
|
|
2084
|
+
vector F32_BLOB(1024),
|
|
2085
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
2086
|
+
);
|
|
1676
2087
|
|
|
1677
|
-
CREATE INDEX IF NOT EXISTS
|
|
1678
|
-
ON
|
|
2088
|
+
CREATE INDEX IF NOT EXISTS idx_memories_agent
|
|
2089
|
+
ON memories(agent_id);
|
|
2090
|
+
|
|
2091
|
+
CREATE INDEX IF NOT EXISTS idx_memories_timestamp
|
|
2092
|
+
ON memories(timestamp);
|
|
2093
|
+
|
|
2094
|
+
CREATE INDEX IF NOT EXISTS idx_memories_session
|
|
2095
|
+
ON memories(session_id);
|
|
2096
|
+
|
|
2097
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project
|
|
2098
|
+
ON memories(project_name);
|
|
2099
|
+
|
|
2100
|
+
CREATE INDEX IF NOT EXISTS idx_memories_tool
|
|
2101
|
+
ON memories(tool_name);
|
|
2102
|
+
|
|
2103
|
+
CREATE INDEX IF NOT EXISTS idx_memories_version
|
|
2104
|
+
ON memories(version);
|
|
2105
|
+
|
|
2106
|
+
CREATE INDEX IF NOT EXISTS idx_memories_agent_project
|
|
2107
|
+
ON memories(agent_id, project_name);
|
|
1679
2108
|
`);
|
|
1680
2109
|
await client.executeMultiple(`
|
|
1681
|
-
CREATE TABLE IF NOT EXISTS
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
job_type TEXT NOT NULL DEFAULT 'report',
|
|
1686
|
-
prompt TEXT,
|
|
1687
|
-
assigned_to TEXT,
|
|
1688
|
-
project_name TEXT,
|
|
1689
|
-
active INTEGER NOT NULL DEFAULT 1,
|
|
1690
|
-
use_crontab INTEGER NOT NULL DEFAULT 0,
|
|
1691
|
-
created_at TEXT NOT NULL
|
|
2110
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
2111
|
+
raw_text,
|
|
2112
|
+
content='memories',
|
|
2113
|
+
content_rowid='rowid'
|
|
1692
2114
|
);
|
|
2115
|
+
|
|
2116
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_ai AFTER INSERT ON memories BEGIN
|
|
2117
|
+
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
2118
|
+
END;
|
|
2119
|
+
|
|
2120
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_ad AFTER DELETE ON memories BEGIN
|
|
2121
|
+
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
2122
|
+
END;
|
|
2123
|
+
|
|
2124
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_au AFTER UPDATE ON memories BEGIN
|
|
2125
|
+
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
2126
|
+
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
2127
|
+
END;
|
|
1693
2128
|
`);
|
|
1694
2129
|
await client.executeMultiple(`
|
|
1695
|
-
CREATE TABLE IF NOT EXISTS
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
hostname TEXT NOT NULL,
|
|
1699
|
-
projects TEXT NOT NULL DEFAULT '[]',
|
|
1700
|
-
agents TEXT NOT NULL DEFAULT '[]',
|
|
1701
|
-
connected INTEGER DEFAULT 0,
|
|
1702
|
-
last_seen TEXT NOT NULL
|
|
2130
|
+
CREATE TABLE IF NOT EXISTS sync_meta (
|
|
2131
|
+
key TEXT PRIMARY KEY,
|
|
2132
|
+
value TEXT NOT NULL
|
|
1703
2133
|
);
|
|
1704
2134
|
`);
|
|
1705
2135
|
await client.executeMultiple(`
|
|
1706
|
-
CREATE TABLE IF NOT EXISTS
|
|
1707
|
-
id
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
server_seq INTEGER,
|
|
1718
|
-
retry_count INTEGER DEFAULT 0,
|
|
1719
|
-
created_at TEXT NOT NULL,
|
|
1720
|
-
delivered_at TEXT,
|
|
1721
|
-
processed_at TEXT,
|
|
1722
|
-
failed_at TEXT,
|
|
1723
|
-
failure_reason TEXT
|
|
2136
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
2137
|
+
id TEXT PRIMARY KEY,
|
|
2138
|
+
title TEXT NOT NULL,
|
|
2139
|
+
assigned_to TEXT NOT NULL,
|
|
2140
|
+
assigned_by TEXT NOT NULL,
|
|
2141
|
+
project_name TEXT NOT NULL,
|
|
2142
|
+
priority TEXT NOT NULL DEFAULT 'p1',
|
|
2143
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
2144
|
+
task_file TEXT,
|
|
2145
|
+
created_at TEXT NOT NULL,
|
|
2146
|
+
updated_at TEXT NOT NULL
|
|
1724
2147
|
);
|
|
1725
2148
|
|
|
1726
|
-
CREATE INDEX IF NOT EXISTS
|
|
1727
|
-
ON
|
|
2149
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_assignee_status
|
|
2150
|
+
ON tasks(assigned_to, status);
|
|
2151
|
+
`);
|
|
2152
|
+
await client.executeMultiple(`
|
|
2153
|
+
CREATE TABLE IF NOT EXISTS behaviors (
|
|
2154
|
+
id TEXT PRIMARY KEY,
|
|
2155
|
+
agent_id TEXT NOT NULL,
|
|
2156
|
+
project_name TEXT,
|
|
2157
|
+
domain TEXT,
|
|
2158
|
+
content TEXT NOT NULL,
|
|
2159
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
2160
|
+
created_at TEXT NOT NULL,
|
|
2161
|
+
updated_at TEXT NOT NULL
|
|
2162
|
+
);
|
|
1728
2163
|
|
|
1729
|
-
CREATE INDEX IF NOT EXISTS
|
|
1730
|
-
ON
|
|
2164
|
+
CREATE INDEX IF NOT EXISTS idx_behaviors_agent
|
|
2165
|
+
ON behaviors(agent_id, active);
|
|
1731
2166
|
`);
|
|
1732
2167
|
try {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2168
|
+
const coordinatorName = getCoordinatorName();
|
|
2169
|
+
const existing = await client.execute({
|
|
2170
|
+
sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
|
|
2171
|
+
args: [coordinatorName]
|
|
1736
2172
|
});
|
|
2173
|
+
if (Number(existing.rows[0]?.cnt) === 0) {
|
|
2174
|
+
const seededAt = "2026-03-25T00:00:00Z";
|
|
2175
|
+
for (const [domain, content] of [
|
|
2176
|
+
["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
|
|
2177
|
+
["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
|
|
2178
|
+
["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
|
|
2179
|
+
]) {
|
|
2180
|
+
await client.execute({
|
|
2181
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
|
|
2182
|
+
VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
|
|
2183
|
+
args: [coordinatorName, domain, content, seededAt, seededAt]
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
1737
2187
|
} catch {
|
|
1738
2188
|
}
|
|
1739
2189
|
try {
|
|
1740
2190
|
await client.execute({
|
|
1741
|
-
sql: `ALTER TABLE
|
|
2191
|
+
sql: `ALTER TABLE behaviors ADD COLUMN priority TEXT DEFAULT 'p1'`,
|
|
1742
2192
|
args: []
|
|
1743
2193
|
});
|
|
1744
2194
|
} catch {
|
|
1745
2195
|
}
|
|
1746
|
-
await client.executeMultiple(`
|
|
1747
|
-
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
1748
|
-
ON notifications(agent_id, session_scope, read, created_at);
|
|
1749
|
-
|
|
1750
|
-
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
1751
|
-
ON messages(target_agent, session_scope, status, created_at);
|
|
1752
|
-
`);
|
|
1753
2196
|
try {
|
|
1754
2197
|
await client.execute({
|
|
1755
|
-
sql: `
|
|
2198
|
+
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
1756
2199
|
args: []
|
|
1757
2200
|
});
|
|
2201
|
+
} catch {
|
|
2202
|
+
}
|
|
2203
|
+
try {
|
|
1758
2204
|
await client.execute({
|
|
1759
|
-
sql: `
|
|
2205
|
+
sql: `ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`,
|
|
1760
2206
|
args: []
|
|
1761
2207
|
});
|
|
2208
|
+
} catch {
|
|
2209
|
+
}
|
|
2210
|
+
try {
|
|
1762
2211
|
await client.execute({
|
|
1763
|
-
sql: `
|
|
2212
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id
|
|
2213
|
+
ON tasks(parent_task_id)
|
|
2214
|
+
WHERE parent_task_id IS NOT NULL`,
|
|
1764
2215
|
args: []
|
|
1765
2216
|
});
|
|
2217
|
+
} catch {
|
|
2218
|
+
}
|
|
2219
|
+
try {
|
|
1766
2220
|
await client.execute({
|
|
1767
|
-
sql: `UPDATE tasks SET
|
|
2221
|
+
sql: `UPDATE tasks SET status = 'done' WHERE status = 'completed'`,
|
|
2222
|
+
args: []
|
|
2223
|
+
});
|
|
2224
|
+
} catch {
|
|
2225
|
+
}
|
|
2226
|
+
try {
|
|
2227
|
+
await client.execute({
|
|
2228
|
+
sql: `ALTER TABLE tasks ADD COLUMN reviewer TEXT`,
|
|
2229
|
+
args: []
|
|
2230
|
+
});
|
|
2231
|
+
} catch {
|
|
2232
|
+
}
|
|
2233
|
+
try {
|
|
2234
|
+
await client.execute({
|
|
2235
|
+
sql: `ALTER TABLE tasks ADD COLUMN context TEXT`,
|
|
2236
|
+
args: []
|
|
2237
|
+
});
|
|
2238
|
+
} catch {
|
|
2239
|
+
}
|
|
2240
|
+
try {
|
|
2241
|
+
await client.execute({
|
|
2242
|
+
sql: `ALTER TABLE tasks ADD COLUMN result TEXT`,
|
|
2243
|
+
args: []
|
|
2244
|
+
});
|
|
2245
|
+
} catch {
|
|
2246
|
+
}
|
|
2247
|
+
try {
|
|
2248
|
+
await client.execute({
|
|
2249
|
+
sql: `ALTER TABLE tasks ADD COLUMN assigned_tmux TEXT`,
|
|
2250
|
+
args: []
|
|
2251
|
+
});
|
|
2252
|
+
} catch {
|
|
2253
|
+
}
|
|
2254
|
+
try {
|
|
2255
|
+
await client.execute({
|
|
2256
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
|
|
2257
|
+
args: []
|
|
2258
|
+
});
|
|
2259
|
+
} catch {
|
|
2260
|
+
}
|
|
2261
|
+
try {
|
|
2262
|
+
await client.execute({
|
|
2263
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
|
|
2264
|
+
args: []
|
|
2265
|
+
});
|
|
2266
|
+
} catch {
|
|
2267
|
+
}
|
|
2268
|
+
try {
|
|
2269
|
+
await client.execute({
|
|
2270
|
+
sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
|
|
2271
|
+
args: []
|
|
2272
|
+
});
|
|
2273
|
+
} catch {
|
|
2274
|
+
}
|
|
2275
|
+
try {
|
|
2276
|
+
await client.execute({
|
|
2277
|
+
sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
|
|
2278
|
+
args: []
|
|
2279
|
+
});
|
|
2280
|
+
} catch {
|
|
2281
|
+
}
|
|
2282
|
+
try {
|
|
2283
|
+
await client.execute({
|
|
2284
|
+
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
2285
|
+
args: []
|
|
2286
|
+
});
|
|
2287
|
+
} catch {
|
|
2288
|
+
}
|
|
2289
|
+
try {
|
|
2290
|
+
await client.execute({
|
|
2291
|
+
sql: `ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0`,
|
|
2292
|
+
args: []
|
|
2293
|
+
});
|
|
2294
|
+
} catch {
|
|
2295
|
+
}
|
|
2296
|
+
try {
|
|
2297
|
+
await client.execute({
|
|
2298
|
+
sql: `ALTER TABLE memories ADD COLUMN author_device_id TEXT`,
|
|
2299
|
+
args: []
|
|
2300
|
+
});
|
|
2301
|
+
} catch {
|
|
2302
|
+
}
|
|
2303
|
+
try {
|
|
2304
|
+
await client.execute({
|
|
2305
|
+
sql: `ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'`,
|
|
2306
|
+
args: []
|
|
2307
|
+
});
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2310
|
+
await client.executeMultiple(`
|
|
2311
|
+
CREATE TABLE IF NOT EXISTS consolidations (
|
|
2312
|
+
id TEXT PRIMARY KEY,
|
|
2313
|
+
consolidated_memory_id TEXT NOT NULL,
|
|
2314
|
+
source_memory_id TEXT NOT NULL,
|
|
2315
|
+
created_at TEXT NOT NULL
|
|
2316
|
+
);
|
|
2317
|
+
|
|
2318
|
+
CREATE INDEX IF NOT EXISTS idx_consolidations_source
|
|
2319
|
+
ON consolidations(source_memory_id);
|
|
2320
|
+
|
|
2321
|
+
CREATE INDEX IF NOT EXISTS idx_consolidations_consolidated
|
|
2322
|
+
ON consolidations(consolidated_memory_id);
|
|
2323
|
+
`);
|
|
2324
|
+
await client.executeMultiple(`
|
|
2325
|
+
CREATE TABLE IF NOT EXISTS reminders (
|
|
2326
|
+
id TEXT PRIMARY KEY,
|
|
2327
|
+
text TEXT NOT NULL,
|
|
2328
|
+
created_at TEXT NOT NULL,
|
|
2329
|
+
due_date TEXT,
|
|
2330
|
+
completed_at TEXT
|
|
2331
|
+
);
|
|
2332
|
+
`);
|
|
2333
|
+
await client.executeMultiple(`
|
|
2334
|
+
CREATE TABLE IF NOT EXISTS notifications (
|
|
2335
|
+
id TEXT PRIMARY KEY,
|
|
2336
|
+
agent_id TEXT NOT NULL,
|
|
2337
|
+
agent_role TEXT NOT NULL,
|
|
2338
|
+
event TEXT NOT NULL,
|
|
2339
|
+
project TEXT NOT NULL,
|
|
2340
|
+
summary TEXT NOT NULL,
|
|
2341
|
+
task_file TEXT,
|
|
2342
|
+
session_scope TEXT,
|
|
2343
|
+
read INTEGER NOT NULL DEFAULT 0,
|
|
2344
|
+
created_at TEXT NOT NULL
|
|
2345
|
+
);
|
|
2346
|
+
|
|
2347
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_read
|
|
2348
|
+
ON notifications(read);
|
|
2349
|
+
|
|
2350
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2351
|
+
ON notifications(agent_id, session_scope);
|
|
2352
|
+
|
|
2353
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2354
|
+
ON notifications(task_file);
|
|
2355
|
+
`);
|
|
2356
|
+
await client.executeMultiple(`
|
|
2357
|
+
CREATE TABLE IF NOT EXISTS schedules (
|
|
2358
|
+
id TEXT PRIMARY KEY,
|
|
2359
|
+
cron TEXT NOT NULL,
|
|
2360
|
+
description TEXT NOT NULL,
|
|
2361
|
+
job_type TEXT NOT NULL DEFAULT 'report',
|
|
2362
|
+
prompt TEXT,
|
|
2363
|
+
assigned_to TEXT,
|
|
2364
|
+
project_name TEXT,
|
|
2365
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
2366
|
+
use_crontab INTEGER NOT NULL DEFAULT 0,
|
|
2367
|
+
created_at TEXT NOT NULL
|
|
2368
|
+
);
|
|
2369
|
+
`);
|
|
2370
|
+
await client.executeMultiple(`
|
|
2371
|
+
CREATE TABLE IF NOT EXISTS device_registry (
|
|
2372
|
+
device_id TEXT PRIMARY KEY,
|
|
2373
|
+
friendly_name TEXT NOT NULL,
|
|
2374
|
+
hostname TEXT NOT NULL,
|
|
2375
|
+
projects TEXT NOT NULL DEFAULT '[]',
|
|
2376
|
+
agents TEXT NOT NULL DEFAULT '[]',
|
|
2377
|
+
connected INTEGER DEFAULT 0,
|
|
2378
|
+
last_seen TEXT NOT NULL
|
|
2379
|
+
);
|
|
2380
|
+
`);
|
|
2381
|
+
await client.executeMultiple(`
|
|
2382
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
2383
|
+
id TEXT PRIMARY KEY,
|
|
2384
|
+
from_agent TEXT NOT NULL,
|
|
2385
|
+
from_device TEXT NOT NULL DEFAULT 'local',
|
|
2386
|
+
target_agent TEXT NOT NULL,
|
|
2387
|
+
target_project TEXT,
|
|
2388
|
+
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2389
|
+
session_scope TEXT,
|
|
2390
|
+
content TEXT NOT NULL,
|
|
2391
|
+
priority TEXT DEFAULT 'normal',
|
|
2392
|
+
status TEXT DEFAULT 'pending',
|
|
2393
|
+
server_seq INTEGER,
|
|
2394
|
+
retry_count INTEGER DEFAULT 0,
|
|
2395
|
+
created_at TEXT NOT NULL,
|
|
2396
|
+
delivered_at TEXT,
|
|
2397
|
+
processed_at TEXT,
|
|
2398
|
+
failed_at TEXT,
|
|
2399
|
+
failure_reason TEXT
|
|
2400
|
+
);
|
|
2401
|
+
|
|
2402
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2403
|
+
ON messages(target_agent, session_scope, status);
|
|
2404
|
+
|
|
2405
|
+
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2406
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2407
|
+
`);
|
|
2408
|
+
try {
|
|
2409
|
+
await client.execute({
|
|
2410
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2411
|
+
args: []
|
|
2412
|
+
});
|
|
2413
|
+
} catch {
|
|
2414
|
+
}
|
|
2415
|
+
try {
|
|
2416
|
+
await client.execute({
|
|
2417
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2418
|
+
args: []
|
|
2419
|
+
});
|
|
2420
|
+
} catch {
|
|
2421
|
+
}
|
|
2422
|
+
await client.executeMultiple(`
|
|
2423
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2424
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2425
|
+
|
|
2426
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2427
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2428
|
+
`);
|
|
2429
|
+
try {
|
|
2430
|
+
await client.execute({
|
|
2431
|
+
sql: `UPDATE memories SET project_name = 'exe-create' WHERE project_name = 'web'`,
|
|
2432
|
+
args: []
|
|
2433
|
+
});
|
|
2434
|
+
await client.execute({
|
|
2435
|
+
sql: `UPDATE memories SET project_name = 'exe-os' WHERE project_name = 'worker'`,
|
|
2436
|
+
args: []
|
|
2437
|
+
});
|
|
2438
|
+
await client.execute({
|
|
2439
|
+
sql: `UPDATE tasks SET project_name = 'exe-create' WHERE project_name = 'web'`,
|
|
2440
|
+
args: []
|
|
2441
|
+
});
|
|
2442
|
+
await client.execute({
|
|
2443
|
+
sql: `UPDATE tasks SET project_name = 'exe-os' WHERE project_name = 'worker'`,
|
|
1768
2444
|
args: []
|
|
1769
2445
|
});
|
|
1770
2446
|
} catch {
|
|
@@ -2157,12 +2833,26 @@ async function ensureSchema() {
|
|
|
2157
2833
|
session_name TEXT,
|
|
2158
2834
|
task_id TEXT,
|
|
2159
2835
|
project_name TEXT,
|
|
2160
|
-
started_at TEXT NOT NULL
|
|
2836
|
+
started_at TEXT NOT NULL,
|
|
2837
|
+
cache_cold_count INTEGER NOT NULL DEFAULT 0
|
|
2161
2838
|
);
|
|
2162
2839
|
|
|
2163
2840
|
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
2164
2841
|
ON session_agent_map(agent_id);
|
|
2165
2842
|
`);
|
|
2843
|
+
await client.executeMultiple(`
|
|
2844
|
+
CREATE TABLE IF NOT EXISTS agent_file_reads (
|
|
2845
|
+
session_uuid TEXT NOT NULL,
|
|
2846
|
+
agent_id TEXT NOT NULL,
|
|
2847
|
+
file_path TEXT NOT NULL,
|
|
2848
|
+
read_at TEXT NOT NULL,
|
|
2849
|
+
commit_hash TEXT,
|
|
2850
|
+
PRIMARY KEY (session_uuid, file_path)
|
|
2851
|
+
);
|
|
2852
|
+
|
|
2853
|
+
CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
|
|
2854
|
+
ON agent_file_reads(agent_id, read_at);
|
|
2855
|
+
`);
|
|
2166
2856
|
try {
|
|
2167
2857
|
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
2168
2858
|
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
@@ -2177,6 +2867,13 @@ async function ensureSchema() {
|
|
|
2177
2867
|
}
|
|
2178
2868
|
} catch {
|
|
2179
2869
|
}
|
|
2870
|
+
try {
|
|
2871
|
+
await client.execute({
|
|
2872
|
+
sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
|
|
2873
|
+
args: []
|
|
2874
|
+
});
|
|
2875
|
+
} catch {
|
|
2876
|
+
}
|
|
2180
2877
|
try {
|
|
2181
2878
|
await client.execute({
|
|
2182
2879
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -2378,14 +3075,14 @@ var init_database = __esm({
|
|
|
2378
3075
|
|
|
2379
3076
|
// src/lib/keychain.ts
|
|
2380
3077
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2381
|
-
import { existsSync as
|
|
2382
|
-
import
|
|
2383
|
-
import
|
|
3078
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3079
|
+
import path7 from "path";
|
|
3080
|
+
import os5 from "os";
|
|
2384
3081
|
function getKeyDir() {
|
|
2385
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3082
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
2386
3083
|
}
|
|
2387
3084
|
function getKeyPath() {
|
|
2388
|
-
return
|
|
3085
|
+
return path7.join(getKeyDir(), "master.key");
|
|
2389
3086
|
}
|
|
2390
3087
|
async function tryKeytar() {
|
|
2391
3088
|
try {
|
|
@@ -2406,9 +3103,9 @@ async function getMasterKey() {
|
|
|
2406
3103
|
}
|
|
2407
3104
|
}
|
|
2408
3105
|
const keyPath = getKeyPath();
|
|
2409
|
-
if (!
|
|
3106
|
+
if (!existsSync7(keyPath)) {
|
|
2410
3107
|
process.stderr.write(
|
|
2411
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3108
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2412
3109
|
`
|
|
2413
3110
|
);
|
|
2414
3111
|
return null;
|
|
@@ -2502,12 +3199,12 @@ __export(shard_manager_exports, {
|
|
|
2502
3199
|
listShards: () => listShards,
|
|
2503
3200
|
shardExists: () => shardExists
|
|
2504
3201
|
});
|
|
2505
|
-
import
|
|
2506
|
-
import { existsSync as
|
|
3202
|
+
import path8 from "path";
|
|
3203
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2507
3204
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2508
3205
|
function initShardManager(encryptionKey) {
|
|
2509
3206
|
_encryptionKey = encryptionKey;
|
|
2510
|
-
if (!
|
|
3207
|
+
if (!existsSync8(SHARDS_DIR)) {
|
|
2511
3208
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2512
3209
|
}
|
|
2513
3210
|
_shardingEnabled = true;
|
|
@@ -2537,7 +3234,7 @@ function getShardClient(projectName) {
|
|
|
2537
3234
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
2538
3235
|
evictLRU();
|
|
2539
3236
|
}
|
|
2540
|
-
const dbPath =
|
|
3237
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
2541
3238
|
const client = createClient2({
|
|
2542
3239
|
url: `file:${dbPath}`,
|
|
2543
3240
|
encryptionKey: _encryptionKey
|
|
@@ -2548,10 +3245,10 @@ function getShardClient(projectName) {
|
|
|
2548
3245
|
}
|
|
2549
3246
|
function shardExists(projectName) {
|
|
2550
3247
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2551
|
-
return
|
|
3248
|
+
return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
2552
3249
|
}
|
|
2553
3250
|
function listShards() {
|
|
2554
|
-
if (!
|
|
3251
|
+
if (!existsSync8(SHARDS_DIR)) return [];
|
|
2555
3252
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2556
3253
|
}
|
|
2557
3254
|
async function ensureShardSchema(client) {
|
|
@@ -2798,7 +3495,7 @@ var init_shard_manager = __esm({
|
|
|
2798
3495
|
"src/lib/shard-manager.ts"() {
|
|
2799
3496
|
"use strict";
|
|
2800
3497
|
init_config();
|
|
2801
|
-
SHARDS_DIR =
|
|
3498
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
2802
3499
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
2803
3500
|
MAX_OPEN_SHARDS = 10;
|
|
2804
3501
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -2934,7 +3631,7 @@ __export(global_procedures_exports, {
|
|
|
2934
3631
|
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
2935
3632
|
storeGlobalProcedure: () => storeGlobalProcedure
|
|
2936
3633
|
});
|
|
2937
|
-
import { randomUUID } from "crypto";
|
|
3634
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2938
3635
|
async function loadGlobalProcedures() {
|
|
2939
3636
|
const client = getClient();
|
|
2940
3637
|
const result = await client.execute({
|
|
@@ -2963,7 +3660,7 @@ ${sections.join("\n\n")}
|
|
|
2963
3660
|
`;
|
|
2964
3661
|
}
|
|
2965
3662
|
async function storeGlobalProcedure(input2) {
|
|
2966
|
-
const id =
|
|
3663
|
+
const id = randomUUID2();
|
|
2967
3664
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2968
3665
|
const client = getClient();
|
|
2969
3666
|
await client.execute({
|
|
@@ -3313,920 +4010,475 @@ async function flushBatch() {
|
|
|
3313
4010
|
trajectory,
|
|
3314
4011
|
contentHash
|
|
3315
4012
|
];
|
|
3316
|
-
return {
|
|
3317
|
-
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
3318
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
3319
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3320
|
-
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
3321
|
-
};
|
|
3322
|
-
};
|
|
3323
|
-
const globalClient = getClient();
|
|
3324
|
-
const globalStmts = batch.map(buildStmt);
|
|
3325
|
-
await globalClient.batch(globalStmts, "write");
|
|
3326
|
-
_pendingRecords.splice(0, batch.length);
|
|
3327
|
-
try {
|
|
3328
|
-
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
3329
|
-
if (isShardingEnabled2()) {
|
|
3330
|
-
const byProject = /* @__PURE__ */ new Map();
|
|
3331
|
-
for (const row of batch) {
|
|
3332
|
-
const proj = row.project_name || "unknown";
|
|
3333
|
-
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
3334
|
-
byProject.get(proj).push(row);
|
|
3335
|
-
}
|
|
3336
|
-
for (const [project, rows] of byProject) {
|
|
3337
|
-
try {
|
|
3338
|
-
const shardClient = await getReadyShardClient2(project);
|
|
3339
|
-
const shardStmts = rows.map(buildStmt);
|
|
3340
|
-
await shardClient.batch(shardStmts, "write");
|
|
3341
|
-
} catch (err) {
|
|
3342
|
-
process.stderr.write(
|
|
3343
|
-
`[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
|
|
3344
|
-
`
|
|
3345
|
-
);
|
|
3346
|
-
}
|
|
3347
|
-
}
|
|
3348
|
-
}
|
|
3349
|
-
} catch {
|
|
3350
|
-
}
|
|
3351
|
-
return batch.length;
|
|
3352
|
-
} finally {
|
|
3353
|
-
_flushing = false;
|
|
3354
|
-
}
|
|
3355
|
-
}
|
|
3356
|
-
function buildWikiScopeFilter(options, columnPrefix) {
|
|
3357
|
-
const args = [];
|
|
3358
|
-
let clause = "";
|
|
3359
|
-
if (options?.workspaceId !== void 0) {
|
|
3360
|
-
clause += ` AND ${columnPrefix}workspace_id = ?`;
|
|
3361
|
-
args.push(options.workspaceId);
|
|
3362
|
-
}
|
|
3363
|
-
if (options?.userId === void 0) {
|
|
3364
|
-
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
3365
|
-
} else if (options.userId === null) {
|
|
3366
|
-
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
3367
|
-
} else {
|
|
3368
|
-
clause += ` AND (${columnPrefix}user_id = ? OR ${columnPrefix}user_id IS NULL)`;
|
|
3369
|
-
args.push(options.userId);
|
|
3370
|
-
}
|
|
3371
|
-
return { clause, args };
|
|
3372
|
-
}
|
|
3373
|
-
async function searchMemories(queryVector, agentId, options) {
|
|
3374
|
-
let client;
|
|
3375
|
-
try {
|
|
3376
|
-
const { isShardingEnabled: isShardingEnabled2, shardExists: shardExists2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
3377
|
-
if (isShardingEnabled2() && options?.projectName && shardExists2(options.projectName)) {
|
|
3378
|
-
client = await getReadyShardClient2(options.projectName);
|
|
3379
|
-
} else {
|
|
3380
|
-
client = getClient();
|
|
3381
|
-
}
|
|
3382
|
-
} catch {
|
|
3383
|
-
client = getClient();
|
|
3384
|
-
}
|
|
3385
|
-
const limit = options?.limit ?? 10;
|
|
3386
|
-
const statusFilter = options?.includeArchived ? "" : `
|
|
3387
|
-
AND COALESCE(status, 'active') = 'active'`;
|
|
3388
|
-
const draftFilter = options?.includeDrafts ? "" : `
|
|
3389
|
-
AND (draft = 0 OR draft IS NULL)`;
|
|
3390
|
-
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
3391
|
-
tool_name, project_name,
|
|
3392
|
-
has_error, raw_text, vector, importance, status,
|
|
3393
|
-
confidence, last_accessed,
|
|
3394
|
-
workspace_id, document_id, user_id,
|
|
3395
|
-
char_offset, page_number,
|
|
3396
|
-
source_path, source_type
|
|
3397
|
-
FROM memories
|
|
3398
|
-
WHERE agent_id = ?
|
|
3399
|
-
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
3400
|
-
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
3401
|
-
const args = [agentId];
|
|
3402
|
-
const scope = buildWikiScopeFilter(options, "");
|
|
3403
|
-
sql += scope.clause;
|
|
3404
|
-
args.push(...scope.args);
|
|
3405
|
-
if (options?.projectName) {
|
|
3406
|
-
sql += ` AND project_name = ?`;
|
|
3407
|
-
args.push(options.projectName);
|
|
3408
|
-
}
|
|
3409
|
-
if (options?.toolName) {
|
|
3410
|
-
sql += ` AND tool_name = ?`;
|
|
3411
|
-
args.push(options.toolName);
|
|
3412
|
-
}
|
|
3413
|
-
if (options?.hasError !== void 0) {
|
|
3414
|
-
sql += ` AND has_error = ?`;
|
|
3415
|
-
args.push(options.hasError ? 1 : 0);
|
|
3416
|
-
}
|
|
3417
|
-
if (options?.since) {
|
|
3418
|
-
sql += ` AND timestamp >= ?`;
|
|
3419
|
-
args.push(options.since);
|
|
3420
|
-
}
|
|
3421
|
-
if (options?.memoryType) {
|
|
3422
|
-
sql += ` AND memory_type = ?`;
|
|
3423
|
-
args.push(options.memoryType);
|
|
3424
|
-
}
|
|
3425
|
-
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
3426
|
-
args.push(vectorToBlob(queryVector));
|
|
3427
|
-
sql += ` LIMIT ?`;
|
|
3428
|
-
args.push(limit);
|
|
3429
|
-
const result = await client.execute({ sql, args });
|
|
3430
|
-
return result.rows.map((row) => ({
|
|
3431
|
-
id: row.id,
|
|
3432
|
-
agent_id: row.agent_id,
|
|
3433
|
-
agent_role: row.agent_role,
|
|
3434
|
-
session_id: row.session_id,
|
|
3435
|
-
timestamp: row.timestamp,
|
|
3436
|
-
tool_name: row.tool_name,
|
|
3437
|
-
project_name: row.project_name,
|
|
3438
|
-
has_error: row.has_error === 1,
|
|
3439
|
-
raw_text: row.raw_text,
|
|
3440
|
-
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
3441
|
-
importance: row.importance ?? 5,
|
|
3442
|
-
status: row.status ?? "active",
|
|
3443
|
-
confidence: row.confidence ?? 0.7,
|
|
3444
|
-
last_accessed: row.last_accessed ?? row.timestamp,
|
|
3445
|
-
workspace_id: row.workspace_id ?? null,
|
|
3446
|
-
document_id: row.document_id ?? null,
|
|
3447
|
-
user_id: row.user_id ?? null,
|
|
3448
|
-
char_offset: row.char_offset ?? null,
|
|
3449
|
-
page_number: row.page_number ?? null,
|
|
3450
|
-
source_path: row.source_path ?? null,
|
|
3451
|
-
source_type: row.source_type ?? null
|
|
3452
|
-
}));
|
|
3453
|
-
}
|
|
3454
|
-
async function attachDocumentMetadata(records) {
|
|
3455
|
-
const docIds = [
|
|
3456
|
-
...new Set(
|
|
3457
|
-
records.map((r) => r.document_id).filter((id) => typeof id === "string" && id.length > 0)
|
|
3458
|
-
)
|
|
3459
|
-
];
|
|
3460
|
-
if (docIds.length === 0) return records;
|
|
3461
|
-
try {
|
|
3462
|
-
const client = getClient();
|
|
3463
|
-
const placeholders = docIds.map(() => "?").join(",");
|
|
3464
|
-
const result = await client.execute({
|
|
3465
|
-
sql: `SELECT id, filename, mime, source_type, uploaded_at
|
|
3466
|
-
FROM documents
|
|
3467
|
-
WHERE id IN (${placeholders})`,
|
|
3468
|
-
args: docIds
|
|
3469
|
-
});
|
|
3470
|
-
const byId = /* @__PURE__ */ new Map();
|
|
3471
|
-
for (const row of result.rows) {
|
|
3472
|
-
const id = row.id;
|
|
3473
|
-
byId.set(id, {
|
|
3474
|
-
document_id: id,
|
|
3475
|
-
filename: row.filename,
|
|
3476
|
-
mime: row.mime ?? null,
|
|
3477
|
-
source_type: row.source_type ?? null,
|
|
3478
|
-
uploaded_at: row.uploaded_at
|
|
3479
|
-
});
|
|
3480
|
-
}
|
|
3481
|
-
for (const record of records) {
|
|
3482
|
-
if (!record.document_id) continue;
|
|
3483
|
-
record.document_metadata = byId.get(record.document_id) ?? null;
|
|
3484
|
-
}
|
|
3485
|
-
} catch {
|
|
3486
|
-
}
|
|
3487
|
-
return records;
|
|
3488
|
-
}
|
|
3489
|
-
async function flushTier3(agentId, options) {
|
|
3490
|
-
const client = getClient();
|
|
3491
|
-
const maxAge = options?.maxAgeHours ?? 72;
|
|
3492
|
-
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
3493
|
-
if (options?.dryRun) {
|
|
3494
|
-
const result2 = await client.execute({
|
|
3495
|
-
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
3496
|
-
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
3497
|
-
args: [agentId, cutoff]
|
|
3498
|
-
});
|
|
3499
|
-
return { archived: Number(result2.rows[0]?.cnt ?? 0) };
|
|
3500
|
-
}
|
|
3501
|
-
const result = await client.execute({
|
|
3502
|
-
sql: `UPDATE memories SET status = 'archived'
|
|
3503
|
-
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
3504
|
-
args: [agentId, cutoff]
|
|
3505
|
-
});
|
|
3506
|
-
return { archived: result.rowsAffected };
|
|
3507
|
-
}
|
|
3508
|
-
async function disposeStore() {
|
|
3509
|
-
if (_flushTimer !== null) {
|
|
3510
|
-
clearInterval(_flushTimer);
|
|
3511
|
-
_flushTimer = null;
|
|
3512
|
-
}
|
|
3513
|
-
if (_pendingRecords.length > 0) {
|
|
3514
|
-
await flushBatch();
|
|
3515
|
-
}
|
|
3516
|
-
await disposeTurso();
|
|
3517
|
-
_pendingRecords = [];
|
|
3518
|
-
_nextVersion = 1;
|
|
3519
|
-
}
|
|
3520
|
-
function vectorToBlob(vector) {
|
|
3521
|
-
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
3522
|
-
return JSON.stringify(Array.from(f32));
|
|
3523
|
-
}
|
|
3524
|
-
async function updateMemoryStatus(id, status) {
|
|
3525
|
-
const client = getClient();
|
|
3526
|
-
await client.execute({
|
|
3527
|
-
sql: `UPDATE memories SET status = ? WHERE id = ?`,
|
|
3528
|
-
args: [status, id]
|
|
3529
|
-
});
|
|
3530
|
-
}
|
|
3531
|
-
function reserveVersions(count) {
|
|
3532
|
-
const reserved = [];
|
|
3533
|
-
for (let i = 0; i < count; i++) {
|
|
3534
|
-
reserved.push(_nextVersion++);
|
|
3535
|
-
}
|
|
3536
|
-
return reserved;
|
|
3537
|
-
}
|
|
3538
|
-
async function getMemoryCardinality(agentId) {
|
|
3539
|
-
try {
|
|
3540
|
-
const client = getClient();
|
|
3541
|
-
const result = await client.execute({
|
|
3542
|
-
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
3543
|
-
args: [agentId]
|
|
3544
|
-
});
|
|
3545
|
-
return Number(result.rows[0]?.cnt) || 0;
|
|
3546
|
-
} catch {
|
|
3547
|
-
return 0;
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
3551
|
-
var init_store = __esm({
|
|
3552
|
-
"src/lib/store.ts"() {
|
|
3553
|
-
"use strict";
|
|
3554
|
-
init_memory();
|
|
3555
|
-
init_database();
|
|
3556
|
-
init_keychain();
|
|
3557
|
-
init_config();
|
|
3558
|
-
init_state_bus();
|
|
3559
|
-
INIT_MAX_RETRIES = 3;
|
|
3560
|
-
INIT_RETRY_DELAY_MS = 1e3;
|
|
3561
|
-
_pendingRecords = [];
|
|
3562
|
-
_batchSize = 20;
|
|
3563
|
-
_flushIntervalMs = 1e4;
|
|
3564
|
-
_flushTimer = null;
|
|
3565
|
-
_flushing = false;
|
|
3566
|
-
_nextVersion = 1;
|
|
3567
|
-
}
|
|
3568
|
-
});
|
|
3569
|
-
|
|
3570
|
-
// src/lib/self-query-router.ts
|
|
3571
|
-
var self_query_router_exports = {};
|
|
3572
|
-
__export(self_query_router_exports, {
|
|
3573
|
-
routeQuery: () => routeQuery
|
|
3574
|
-
});
|
|
3575
|
-
async function routeQuery(query, model = "claude-haiku-4-5-20251001") {
|
|
3576
|
-
if (query.length < 10) {
|
|
3577
|
-
return {
|
|
3578
|
-
semanticQuery: query,
|
|
3579
|
-
projectFilter: null,
|
|
3580
|
-
roleFilter: null,
|
|
3581
|
-
timeFilter: null,
|
|
3582
|
-
isBroadQuery: false
|
|
3583
|
-
};
|
|
3584
|
-
}
|
|
3585
|
-
try {
|
|
3586
|
-
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
3587
|
-
const client = new Anthropic();
|
|
3588
|
-
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3589
|
-
const response = await client.messages.create({
|
|
3590
|
-
model,
|
|
3591
|
-
max_tokens: 256,
|
|
3592
|
-
system: `You are a search query router. Extract metadata filters from the user's memory search query. Today is ${now}. Convert relative time references (yesterday, last week) to ISO timestamps.`,
|
|
3593
|
-
messages: [{ role: "user", content: query }],
|
|
3594
|
-
tools: [EXTRACT_TOOL],
|
|
3595
|
-
tool_choice: { type: "tool", name: "extract_search_filters" }
|
|
3596
|
-
});
|
|
3597
|
-
const toolBlock = response.content.find((b) => b.type === "tool_use");
|
|
3598
|
-
if (toolBlock && toolBlock.type === "tool_use") {
|
|
3599
|
-
const input2 = toolBlock.input;
|
|
3600
|
-
return {
|
|
3601
|
-
semanticQuery: input2.semantic_query || query,
|
|
3602
|
-
projectFilter: input2.project_filter || null,
|
|
3603
|
-
roleFilter: input2.role_filter || null,
|
|
3604
|
-
timeFilter: input2.time_filter || null,
|
|
3605
|
-
isBroadQuery: Boolean(input2.is_broad_query)
|
|
3606
|
-
};
|
|
3607
|
-
}
|
|
3608
|
-
} catch (err) {
|
|
3609
|
-
process.stderr.write(
|
|
3610
|
-
`[self-query-router] LLM extraction failed, using passthrough: ${err instanceof Error ? err.message : String(err)}
|
|
3611
|
-
`
|
|
3612
|
-
);
|
|
3613
|
-
}
|
|
3614
|
-
return {
|
|
3615
|
-
semanticQuery: query,
|
|
3616
|
-
projectFilter: null,
|
|
3617
|
-
roleFilter: null,
|
|
3618
|
-
timeFilter: null,
|
|
3619
|
-
isBroadQuery: false
|
|
3620
|
-
};
|
|
3621
|
-
}
|
|
3622
|
-
var EXTRACT_TOOL;
|
|
3623
|
-
var init_self_query_router = __esm({
|
|
3624
|
-
"src/lib/self-query-router.ts"() {
|
|
3625
|
-
"use strict";
|
|
3626
|
-
EXTRACT_TOOL = {
|
|
3627
|
-
name: "extract_search_filters",
|
|
3628
|
-
description: "Extract metadata filters from a memory search query to improve retrieval precision.",
|
|
3629
|
-
input_schema: {
|
|
3630
|
-
type: "object",
|
|
3631
|
-
properties: {
|
|
3632
|
-
semantic_query: {
|
|
3633
|
-
type: "string",
|
|
3634
|
-
description: "The core semantic meaning of the query, stripped of metadata references. This is used for embedding search."
|
|
3635
|
-
},
|
|
3636
|
-
project_filter: {
|
|
3637
|
-
type: ["string", "null"],
|
|
3638
|
-
description: "Project name if the query references a specific project (e.g., 'exe-os', 'exe-create'). Null if no project specified."
|
|
3639
|
-
},
|
|
3640
|
-
role_filter: {
|
|
3641
|
-
type: ["string", "null"],
|
|
3642
|
-
description: "Agent role if the query targets a specific role (e.g., 'CTO', 'CMO'). Null if no role specified."
|
|
3643
|
-
},
|
|
3644
|
-
time_filter: {
|
|
3645
|
-
type: ["string", "null"],
|
|
3646
|
-
description: "ISO 8601 timestamp lower bound if the query references a time period (e.g., 'last week', 'yesterday'). Null if no time reference."
|
|
3647
|
-
},
|
|
3648
|
-
is_broad_query: {
|
|
3649
|
-
type: "boolean",
|
|
3650
|
-
description: "True if the query is exploratory/broad (e.g., 'what has the CTO been working on', 'summarize recent activity'). False if targeted (e.g., 'how did we fix the auth bug')."
|
|
3651
|
-
}
|
|
3652
|
-
},
|
|
3653
|
-
required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
|
|
3654
|
-
}
|
|
3655
|
-
};
|
|
3656
|
-
}
|
|
3657
|
-
});
|
|
3658
|
-
|
|
3659
|
-
// src/lib/reranker.ts
|
|
3660
|
-
var reranker_exports = {};
|
|
3661
|
-
__export(reranker_exports, {
|
|
3662
|
-
disposeReranker: () => disposeReranker,
|
|
3663
|
-
getRerankerModelPath: () => getRerankerModelPath,
|
|
3664
|
-
isRerankerAvailable: () => isRerankerAvailable,
|
|
3665
|
-
rerank: () => rerank,
|
|
3666
|
-
rerankWithContext: () => rerankWithContext,
|
|
3667
|
-
rerankWithScores: () => rerankWithScores
|
|
3668
|
-
});
|
|
3669
|
-
import path7 from "path";
|
|
3670
|
-
import { existsSync as existsSync7 } from "fs";
|
|
3671
|
-
function resetIdleTimer() {
|
|
3672
|
-
if (_idleTimer) clearTimeout(_idleTimer);
|
|
3673
|
-
_idleTimer = setTimeout(() => {
|
|
3674
|
-
void disposeReranker();
|
|
3675
|
-
}, IDLE_TIMEOUT_MS);
|
|
3676
|
-
if (_idleTimer && typeof _idleTimer === "object" && "unref" in _idleTimer) {
|
|
3677
|
-
_idleTimer.unref();
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3680
|
-
function isRerankerAvailable() {
|
|
3681
|
-
return existsSync7(path7.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
3682
|
-
}
|
|
3683
|
-
function getRerankerModelPath() {
|
|
3684
|
-
return path7.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3685
|
-
}
|
|
3686
|
-
async function ensureLoaded() {
|
|
3687
|
-
if (_rerankerContext) {
|
|
3688
|
-
resetIdleTimer();
|
|
3689
|
-
return;
|
|
3690
|
-
}
|
|
3691
|
-
const modelPath = path7.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
3692
|
-
if (!existsSync7(modelPath)) {
|
|
3693
|
-
throw new Error(
|
|
3694
|
-
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
3695
|
-
);
|
|
3696
|
-
}
|
|
3697
|
-
process.stderr.write("[reranker] Loading Jina Reranker v3...\n");
|
|
3698
|
-
const { getLlama } = await import("node-llama-cpp");
|
|
3699
|
-
const llama = await getLlama();
|
|
3700
|
-
_rerankerModel = await llama.loadModel({ modelPath });
|
|
3701
|
-
_rerankerContext = await _rerankerModel.createEmbeddingContext();
|
|
3702
|
-
process.stderr.write("[reranker] Jina Reranker v3 loaded.\n");
|
|
3703
|
-
resetIdleTimer();
|
|
3704
|
-
}
|
|
3705
|
-
async function disposeReranker() {
|
|
3706
|
-
if (_idleTimer) {
|
|
3707
|
-
clearTimeout(_idleTimer);
|
|
3708
|
-
_idleTimer = null;
|
|
3709
|
-
}
|
|
3710
|
-
if (_rerankerContext) {
|
|
3711
|
-
try {
|
|
3712
|
-
await _rerankerContext.dispose();
|
|
3713
|
-
} catch {
|
|
3714
|
-
}
|
|
3715
|
-
_rerankerContext = null;
|
|
3716
|
-
}
|
|
3717
|
-
if (_rerankerModel) {
|
|
3718
|
-
try {
|
|
3719
|
-
await _rerankerModel.dispose();
|
|
3720
|
-
} catch {
|
|
3721
|
-
}
|
|
3722
|
-
_rerankerModel = null;
|
|
3723
|
-
}
|
|
3724
|
-
process.stderr.write("[reranker] Unloaded (idle timeout).\n");
|
|
3725
|
-
}
|
|
3726
|
-
async function rerankWithScores(query, texts, topK) {
|
|
3727
|
-
if (texts.length === 0) return [];
|
|
3728
|
-
await ensureLoaded();
|
|
3729
|
-
const ctx = _rerankerContext;
|
|
3730
|
-
const scored = [];
|
|
3731
|
-
for (let i = 0; i < texts.length; i++) {
|
|
3732
|
-
const text = texts[i] ?? "";
|
|
3733
|
-
try {
|
|
3734
|
-
const input2 = `query: ${query} document: ${text.slice(0, 512)}`;
|
|
3735
|
-
const embedding = await ctx.getEmbeddingFor(input2);
|
|
3736
|
-
const score = embedding.vector[0] ?? 0;
|
|
3737
|
-
scored.push({ text, score, index: i });
|
|
3738
|
-
} catch {
|
|
3739
|
-
scored.push({ text, score: -1, index: i });
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
scored.sort((a, b) => b.score - a.score);
|
|
3743
|
-
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
3744
|
-
}
|
|
3745
|
-
async function rerank(query, candidates, topK = 5) {
|
|
3746
|
-
if (candidates.length === 0) return [];
|
|
3747
|
-
if (candidates.length <= topK) return candidates;
|
|
3748
|
-
const scored = await rerankWithScores(
|
|
3749
|
-
query,
|
|
3750
|
-
candidates.map((c) => c.raw_text),
|
|
3751
|
-
topK
|
|
3752
|
-
);
|
|
3753
|
-
return scored.map((s) => candidates[s.index]);
|
|
3754
|
-
}
|
|
3755
|
-
async function rerankWithContext(query, candidates, topK) {
|
|
3756
|
-
if (candidates.length === 0) return [];
|
|
3757
|
-
await ensureLoaded();
|
|
3758
|
-
const ctx = _rerankerContext;
|
|
3759
|
-
const scored = [];
|
|
3760
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
3761
|
-
const candidate = candidates[i];
|
|
3762
|
-
try {
|
|
3763
|
-
const docText = candidate.context ? `[${candidate.context}] ${candidate.text.slice(0, 460)}` : candidate.text.slice(0, 512);
|
|
3764
|
-
const input2 = `query: ${query} document: ${docText}`;
|
|
3765
|
-
const embedding = await ctx.getEmbeddingFor(input2);
|
|
3766
|
-
const score = embedding.vector[0] ?? 0;
|
|
3767
|
-
scored.push({ text: candidate.text, score, index: i });
|
|
3768
|
-
} catch {
|
|
3769
|
-
scored.push({ text: candidate.text, score: -1, index: i });
|
|
3770
|
-
}
|
|
3771
|
-
}
|
|
3772
|
-
scored.sort((a, b) => b.score - a.score);
|
|
3773
|
-
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
3774
|
-
}
|
|
3775
|
-
var RERANKER_MODEL_FILE, IDLE_TIMEOUT_MS, _rerankerContext, _rerankerModel, _idleTimer;
|
|
3776
|
-
var init_reranker = __esm({
|
|
3777
|
-
"src/lib/reranker.ts"() {
|
|
3778
|
-
"use strict";
|
|
3779
|
-
init_config();
|
|
3780
|
-
RERANKER_MODEL_FILE = "jina-reranker-v3-q4_k_m.gguf";
|
|
3781
|
-
IDLE_TIMEOUT_MS = 6e4;
|
|
3782
|
-
_rerankerContext = null;
|
|
3783
|
-
_rerankerModel = null;
|
|
3784
|
-
_idleTimer = null;
|
|
3785
|
-
}
|
|
3786
|
-
});
|
|
3787
|
-
|
|
3788
|
-
// src/lib/daemon-auth.ts
|
|
3789
|
-
import crypto from "crypto";
|
|
3790
|
-
import path8 from "path";
|
|
3791
|
-
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3792
|
-
function normalizeToken(token) {
|
|
3793
|
-
if (!token) return null;
|
|
3794
|
-
const trimmed = token.trim();
|
|
3795
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
3796
|
-
}
|
|
3797
|
-
function readDaemonToken() {
|
|
3798
|
-
try {
|
|
3799
|
-
if (!existsSync8(DAEMON_TOKEN_PATH)) return null;
|
|
3800
|
-
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
3801
|
-
} catch {
|
|
3802
|
-
return null;
|
|
3803
|
-
}
|
|
3804
|
-
}
|
|
3805
|
-
function ensureDaemonToken(seed) {
|
|
3806
|
-
const existing = readDaemonToken();
|
|
3807
|
-
if (existing) return existing;
|
|
3808
|
-
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
3809
|
-
ensurePrivateDirSync(EXE_AI_DIR);
|
|
3810
|
-
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
3811
|
-
`, "utf8");
|
|
3812
|
-
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
3813
|
-
return token;
|
|
3814
|
-
}
|
|
3815
|
-
var DAEMON_TOKEN_PATH;
|
|
3816
|
-
var init_daemon_auth = __esm({
|
|
3817
|
-
"src/lib/daemon-auth.ts"() {
|
|
3818
|
-
"use strict";
|
|
3819
|
-
init_config();
|
|
3820
|
-
init_secure_files();
|
|
3821
|
-
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
3822
|
-
}
|
|
3823
|
-
});
|
|
3824
|
-
|
|
3825
|
-
// src/lib/exe-daemon-client.ts
|
|
3826
|
-
import net from "net";
|
|
3827
|
-
import os5 from "os";
|
|
3828
|
-
import { spawn } from "child_process";
|
|
3829
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
3830
|
-
import { existsSync as existsSync9, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
3831
|
-
import path9 from "path";
|
|
3832
|
-
import { fileURLToPath } from "url";
|
|
3833
|
-
function handleData(chunk) {
|
|
3834
|
-
_buffer += chunk.toString();
|
|
3835
|
-
if (_buffer.length > MAX_BUFFER) {
|
|
3836
|
-
_buffer = "";
|
|
3837
|
-
return;
|
|
3838
|
-
}
|
|
3839
|
-
let newlineIdx;
|
|
3840
|
-
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
3841
|
-
const line = _buffer.slice(0, newlineIdx).trim();
|
|
3842
|
-
_buffer = _buffer.slice(newlineIdx + 1);
|
|
3843
|
-
if (!line) continue;
|
|
3844
|
-
try {
|
|
3845
|
-
const response = JSON.parse(line);
|
|
3846
|
-
const id = response.id;
|
|
3847
|
-
if (!id) continue;
|
|
3848
|
-
const entry = _pending.get(id);
|
|
3849
|
-
if (entry) {
|
|
3850
|
-
clearTimeout(entry.timer);
|
|
3851
|
-
_pending.delete(id);
|
|
3852
|
-
entry.resolve(response);
|
|
3853
|
-
}
|
|
3854
|
-
} catch {
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
}
|
|
3858
|
-
function cleanupStaleFiles() {
|
|
3859
|
-
if (existsSync9(PID_PATH)) {
|
|
4013
|
+
return {
|
|
4014
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
4015
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
4016
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4017
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
4018
|
+
};
|
|
4019
|
+
};
|
|
4020
|
+
const globalClient = getClient();
|
|
4021
|
+
const globalStmts = batch.map(buildStmt);
|
|
4022
|
+
await globalClient.batch(globalStmts, "write");
|
|
4023
|
+
_pendingRecords.splice(0, batch.length);
|
|
3860
4024
|
try {
|
|
3861
|
-
const
|
|
3862
|
-
if (
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
4025
|
+
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
4026
|
+
if (isShardingEnabled2()) {
|
|
4027
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
4028
|
+
for (const row of batch) {
|
|
4029
|
+
const proj = row.project_name || "unknown";
|
|
4030
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
4031
|
+
byProject.get(proj).push(row);
|
|
4032
|
+
}
|
|
4033
|
+
for (const [project, rows] of byProject) {
|
|
4034
|
+
try {
|
|
4035
|
+
const shardClient = await getReadyShardClient2(project);
|
|
4036
|
+
const shardStmts = rows.map(buildStmt);
|
|
4037
|
+
await shardClient.batch(shardStmts, "write");
|
|
4038
|
+
} catch (err) {
|
|
4039
|
+
process.stderr.write(
|
|
4040
|
+
`[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
|
|
4041
|
+
`
|
|
4042
|
+
);
|
|
4043
|
+
}
|
|
3867
4044
|
}
|
|
3868
4045
|
}
|
|
3869
4046
|
} catch {
|
|
3870
4047
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
}
|
|
3875
|
-
try {
|
|
3876
|
-
unlinkSync2(SOCKET_PATH);
|
|
3877
|
-
} catch {
|
|
3878
|
-
}
|
|
4048
|
+
return batch.length;
|
|
4049
|
+
} finally {
|
|
4050
|
+
_flushing = false;
|
|
3879
4051
|
}
|
|
3880
4052
|
}
|
|
3881
|
-
function
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4053
|
+
function buildWikiScopeFilter(options, columnPrefix) {
|
|
4054
|
+
const args = [];
|
|
4055
|
+
let clause = "";
|
|
4056
|
+
if (options?.workspaceId !== void 0) {
|
|
4057
|
+
clause += ` AND ${columnPrefix}workspace_id = ?`;
|
|
4058
|
+
args.push(options.workspaceId);
|
|
3887
4059
|
}
|
|
3888
|
-
|
|
4060
|
+
if (options?.userId === void 0) {
|
|
4061
|
+
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
4062
|
+
} else if (options.userId === null) {
|
|
4063
|
+
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
4064
|
+
} else {
|
|
4065
|
+
clause += ` AND (${columnPrefix}user_id = ? OR ${columnPrefix}user_id IS NULL)`;
|
|
4066
|
+
args.push(options.userId);
|
|
4067
|
+
}
|
|
4068
|
+
return { clause, args };
|
|
3889
4069
|
}
|
|
3890
|
-
function
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
4070
|
+
async function searchMemories(queryVector, agentId, options) {
|
|
4071
|
+
let client;
|
|
4072
|
+
try {
|
|
4073
|
+
const { isShardingEnabled: isShardingEnabled2, shardExists: shardExists2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
4074
|
+
if (isShardingEnabled2() && options?.projectName && shardExists2(options.projectName)) {
|
|
4075
|
+
client = await getReadyShardClient2(options.projectName);
|
|
4076
|
+
} else {
|
|
4077
|
+
client = getClient();
|
|
4078
|
+
}
|
|
4079
|
+
} catch {
|
|
4080
|
+
client = getClient();
|
|
3899
4081
|
}
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
`
|
|
3904
|
-
|
|
3905
|
-
|
|
4082
|
+
const limit = options?.limit ?? 10;
|
|
4083
|
+
const statusFilter = options?.includeArchived ? "" : `
|
|
4084
|
+
AND COALESCE(status, 'active') = 'active'`;
|
|
4085
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
4086
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
4087
|
+
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
4088
|
+
tool_name, project_name,
|
|
4089
|
+
has_error, raw_text, vector, importance, status,
|
|
4090
|
+
confidence, last_accessed,
|
|
4091
|
+
workspace_id, document_id, user_id,
|
|
4092
|
+
char_offset, page_number,
|
|
4093
|
+
source_path, source_type
|
|
4094
|
+
FROM memories
|
|
4095
|
+
WHERE agent_id = ?
|
|
4096
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
4097
|
+
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
4098
|
+
const args = [agentId];
|
|
4099
|
+
const scope = buildWikiScopeFilter(options, "");
|
|
4100
|
+
sql += scope.clause;
|
|
4101
|
+
args.push(...scope.args);
|
|
4102
|
+
if (options?.projectName) {
|
|
4103
|
+
sql += ` AND project_name = ?`;
|
|
4104
|
+
args.push(options.projectName);
|
|
3906
4105
|
}
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
return;
|
|
4106
|
+
if (options?.toolName) {
|
|
4107
|
+
sql += ` AND tool_name = ?`;
|
|
4108
|
+
args.push(options.toolName);
|
|
3911
4109
|
}
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
`);
|
|
3916
|
-
return;
|
|
4110
|
+
if (options?.hasError !== void 0) {
|
|
4111
|
+
sql += ` AND has_error = ?`;
|
|
4112
|
+
args.push(options.hasError ? 1 : 0);
|
|
3917
4113
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
4114
|
+
if (options?.since) {
|
|
4115
|
+
sql += ` AND timestamp >= ?`;
|
|
4116
|
+
args.push(options.since);
|
|
4117
|
+
}
|
|
4118
|
+
if (options?.memoryType) {
|
|
4119
|
+
sql += ` AND memory_type = ?`;
|
|
4120
|
+
args.push(options.memoryType);
|
|
4121
|
+
}
|
|
4122
|
+
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
4123
|
+
args.push(vectorToBlob(queryVector));
|
|
4124
|
+
sql += ` LIMIT ?`;
|
|
4125
|
+
args.push(limit);
|
|
4126
|
+
const result = await client.execute({ sql, args });
|
|
4127
|
+
return result.rows.map((row) => ({
|
|
4128
|
+
id: row.id,
|
|
4129
|
+
agent_id: row.agent_id,
|
|
4130
|
+
agent_role: row.agent_role,
|
|
4131
|
+
session_id: row.session_id,
|
|
4132
|
+
timestamp: row.timestamp,
|
|
4133
|
+
tool_name: row.tool_name,
|
|
4134
|
+
project_name: row.project_name,
|
|
4135
|
+
has_error: row.has_error === 1,
|
|
4136
|
+
raw_text: row.raw_text,
|
|
4137
|
+
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
4138
|
+
importance: row.importance ?? 5,
|
|
4139
|
+
status: row.status ?? "active",
|
|
4140
|
+
confidence: row.confidence ?? 0.7,
|
|
4141
|
+
last_accessed: row.last_accessed ?? row.timestamp,
|
|
4142
|
+
workspace_id: row.workspace_id ?? null,
|
|
4143
|
+
document_id: row.document_id ?? null,
|
|
4144
|
+
user_id: row.user_id ?? null,
|
|
4145
|
+
char_offset: row.char_offset ?? null,
|
|
4146
|
+
page_number: row.page_number ?? null,
|
|
4147
|
+
source_path: row.source_path ?? null,
|
|
4148
|
+
source_type: row.source_type ?? null
|
|
4149
|
+
}));
|
|
4150
|
+
}
|
|
4151
|
+
async function attachDocumentMetadata(records) {
|
|
4152
|
+
const docIds = [
|
|
4153
|
+
...new Set(
|
|
4154
|
+
records.map((r) => r.document_id).filter((id) => typeof id === "string" && id.length > 0)
|
|
4155
|
+
)
|
|
4156
|
+
];
|
|
4157
|
+
if (docIds.length === 0) return records;
|
|
3924
4158
|
try {
|
|
3925
|
-
|
|
4159
|
+
const client = getClient();
|
|
4160
|
+
const placeholders = docIds.map(() => "?").join(",");
|
|
4161
|
+
const result = await client.execute({
|
|
4162
|
+
sql: `SELECT id, filename, mime, source_type, uploaded_at
|
|
4163
|
+
FROM documents
|
|
4164
|
+
WHERE id IN (${placeholders})`,
|
|
4165
|
+
args: docIds
|
|
4166
|
+
});
|
|
4167
|
+
const byId = /* @__PURE__ */ new Map();
|
|
4168
|
+
for (const row of result.rows) {
|
|
4169
|
+
const id = row.id;
|
|
4170
|
+
byId.set(id, {
|
|
4171
|
+
document_id: id,
|
|
4172
|
+
filename: row.filename,
|
|
4173
|
+
mime: row.mime ?? null,
|
|
4174
|
+
source_type: row.source_type ?? null,
|
|
4175
|
+
uploaded_at: row.uploaded_at
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
for (const record of records) {
|
|
4179
|
+
if (!record.document_id) continue;
|
|
4180
|
+
record.document_metadata = byId.get(record.document_id) ?? null;
|
|
4181
|
+
}
|
|
3926
4182
|
} catch {
|
|
3927
4183
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
4184
|
+
return records;
|
|
4185
|
+
}
|
|
4186
|
+
async function flushTier3(agentId, options) {
|
|
4187
|
+
const client = getClient();
|
|
4188
|
+
const maxAge = options?.maxAgeHours ?? 72;
|
|
4189
|
+
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
4190
|
+
if (options?.dryRun) {
|
|
4191
|
+
const result2 = await client.execute({
|
|
4192
|
+
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
4193
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
4194
|
+
args: [agentId, cutoff]
|
|
4195
|
+
});
|
|
4196
|
+
return { archived: Number(result2.rows[0]?.cnt ?? 0) };
|
|
4197
|
+
}
|
|
4198
|
+
const result = await client.execute({
|
|
4199
|
+
sql: `UPDATE memories SET status = 'archived'
|
|
4200
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
4201
|
+
args: [agentId, cutoff]
|
|
3943
4202
|
});
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
4203
|
+
return { archived: result.rowsAffected };
|
|
4204
|
+
}
|
|
4205
|
+
async function disposeStore() {
|
|
4206
|
+
if (_flushTimer !== null) {
|
|
4207
|
+
clearInterval(_flushTimer);
|
|
4208
|
+
_flushTimer = null;
|
|
4209
|
+
}
|
|
4210
|
+
if (_pendingRecords.length > 0) {
|
|
4211
|
+
await flushBatch();
|
|
3950
4212
|
}
|
|
4213
|
+
await disposeTurso();
|
|
4214
|
+
_pendingRecords = [];
|
|
4215
|
+
_nextVersion = 1;
|
|
4216
|
+
}
|
|
4217
|
+
function vectorToBlob(vector) {
|
|
4218
|
+
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
4219
|
+
return JSON.stringify(Array.from(f32));
|
|
4220
|
+
}
|
|
4221
|
+
async function updateMemoryStatus(id, status) {
|
|
4222
|
+
const client = getClient();
|
|
4223
|
+
await client.execute({
|
|
4224
|
+
sql: `UPDATE memories SET status = ? WHERE id = ?`,
|
|
4225
|
+
args: [status, id]
|
|
4226
|
+
});
|
|
3951
4227
|
}
|
|
3952
|
-
function
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
return true;
|
|
3957
|
-
} catch {
|
|
3958
|
-
try {
|
|
3959
|
-
const stat = statSync(SPAWN_LOCK_PATH);
|
|
3960
|
-
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
3961
|
-
try {
|
|
3962
|
-
unlinkSync2(SPAWN_LOCK_PATH);
|
|
3963
|
-
} catch {
|
|
3964
|
-
}
|
|
3965
|
-
try {
|
|
3966
|
-
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
3967
|
-
closeSync(fd);
|
|
3968
|
-
return true;
|
|
3969
|
-
} catch {
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
} catch {
|
|
3973
|
-
}
|
|
3974
|
-
return false;
|
|
4228
|
+
function reserveVersions(count) {
|
|
4229
|
+
const reserved = [];
|
|
4230
|
+
for (let i = 0; i < count; i++) {
|
|
4231
|
+
reserved.push(_nextVersion++);
|
|
3975
4232
|
}
|
|
4233
|
+
return reserved;
|
|
3976
4234
|
}
|
|
3977
|
-
function
|
|
4235
|
+
async function getMemoryCardinality(agentId) {
|
|
3978
4236
|
try {
|
|
3979
|
-
|
|
4237
|
+
const client = getClient();
|
|
4238
|
+
const result = await client.execute({
|
|
4239
|
+
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
4240
|
+
args: [agentId]
|
|
4241
|
+
});
|
|
4242
|
+
return Number(result.rows[0]?.cnt) || 0;
|
|
3980
4243
|
} catch {
|
|
4244
|
+
return 0;
|
|
3981
4245
|
}
|
|
3982
4246
|
}
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4247
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
4248
|
+
var init_store = __esm({
|
|
4249
|
+
"src/lib/store.ts"() {
|
|
4250
|
+
"use strict";
|
|
4251
|
+
init_memory();
|
|
4252
|
+
init_database();
|
|
4253
|
+
init_keychain();
|
|
4254
|
+
init_config();
|
|
4255
|
+
init_state_bus();
|
|
4256
|
+
INIT_MAX_RETRIES = 3;
|
|
4257
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
4258
|
+
_pendingRecords = [];
|
|
4259
|
+
_batchSize = 20;
|
|
4260
|
+
_flushIntervalMs = 1e4;
|
|
4261
|
+
_flushTimer = null;
|
|
4262
|
+
_flushing = false;
|
|
4263
|
+
_nextVersion = 1;
|
|
4264
|
+
}
|
|
4265
|
+
});
|
|
4266
|
+
|
|
4267
|
+
// src/lib/self-query-router.ts
|
|
4268
|
+
var self_query_router_exports = {};
|
|
4269
|
+
__export(self_query_router_exports, {
|
|
4270
|
+
routeQuery: () => routeQuery
|
|
4271
|
+
});
|
|
4272
|
+
async function routeQuery(query, model = "claude-haiku-4-5-20251001") {
|
|
4273
|
+
if (query.length < 10) {
|
|
4274
|
+
return {
|
|
4275
|
+
semanticQuery: query,
|
|
4276
|
+
projectFilter: null,
|
|
4277
|
+
roleFilter: null,
|
|
4278
|
+
timeFilter: null,
|
|
4279
|
+
isBroadQuery: false
|
|
4280
|
+
};
|
|
4281
|
+
}
|
|
4282
|
+
try {
|
|
4283
|
+
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
4284
|
+
const client = new Anthropic();
|
|
4285
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4286
|
+
const response = await client.messages.create({
|
|
4287
|
+
model,
|
|
4288
|
+
max_tokens: 256,
|
|
4289
|
+
system: `You are a search query router. Extract metadata filters from the user's memory search query. Today is ${now}. Convert relative time references (yesterday, last week) to ISO timestamps.`,
|
|
4290
|
+
messages: [{ role: "user", content: query }],
|
|
4291
|
+
tools: [EXTRACT_TOOL],
|
|
4292
|
+
tool_choice: { type: "tool", name: "extract_search_filters" }
|
|
4018
4293
|
});
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
releaseSpawnLock();
|
|
4294
|
+
const toolBlock = response.content.find((b) => b.type === "tool_use");
|
|
4295
|
+
if (toolBlock && toolBlock.type === "tool_use") {
|
|
4296
|
+
const input2 = toolBlock.input;
|
|
4297
|
+
return {
|
|
4298
|
+
semanticQuery: input2.semantic_query || query,
|
|
4299
|
+
projectFilter: input2.project_filter || null,
|
|
4300
|
+
roleFilter: input2.role_filter || null,
|
|
4301
|
+
timeFilter: input2.time_filter || null,
|
|
4302
|
+
isBroadQuery: Boolean(input2.is_broad_query)
|
|
4303
|
+
};
|
|
4030
4304
|
}
|
|
4305
|
+
} catch (err) {
|
|
4306
|
+
process.stderr.write(
|
|
4307
|
+
`[self-query-router] LLM extraction failed, using passthrough: ${err instanceof Error ? err.message : String(err)}
|
|
4308
|
+
`
|
|
4309
|
+
);
|
|
4031
4310
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4311
|
+
return {
|
|
4312
|
+
semanticQuery: query,
|
|
4313
|
+
projectFilter: null,
|
|
4314
|
+
roleFilter: null,
|
|
4315
|
+
timeFilter: null,
|
|
4316
|
+
isBroadQuery: false
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
var EXTRACT_TOOL;
|
|
4320
|
+
var init_self_query_router = __esm({
|
|
4321
|
+
"src/lib/self-query-router.ts"() {
|
|
4322
|
+
"use strict";
|
|
4323
|
+
EXTRACT_TOOL = {
|
|
4324
|
+
name: "extract_search_filters",
|
|
4325
|
+
description: "Extract metadata filters from a memory search query to improve retrieval precision.",
|
|
4326
|
+
input_schema: {
|
|
4327
|
+
type: "object",
|
|
4328
|
+
properties: {
|
|
4329
|
+
semantic_query: {
|
|
4330
|
+
type: "string",
|
|
4331
|
+
description: "The core semantic meaning of the query, stripped of metadata references. This is used for embedding search."
|
|
4332
|
+
},
|
|
4333
|
+
project_filter: {
|
|
4334
|
+
type: ["string", "null"],
|
|
4335
|
+
description: "Project name if the query references a specific project (e.g., 'exe-os', 'exe-create'). Null if no project specified."
|
|
4336
|
+
},
|
|
4337
|
+
role_filter: {
|
|
4338
|
+
type: ["string", "null"],
|
|
4339
|
+
description: "Agent role if the query targets a specific role (e.g., 'CTO', 'CMO'). Null if no role specified."
|
|
4340
|
+
},
|
|
4341
|
+
time_filter: {
|
|
4342
|
+
type: ["string", "null"],
|
|
4343
|
+
description: "ISO 8601 timestamp lower bound if the query references a time period (e.g., 'last week', 'yesterday'). Null if no time reference."
|
|
4344
|
+
},
|
|
4345
|
+
is_broad_query: {
|
|
4346
|
+
type: "boolean",
|
|
4347
|
+
description: "True if the query is exploratory/broad (e.g., 'what has the CTO been working on', 'summarize recent activity'). False if targeted (e.g., 'how did we fix the auth bug')."
|
|
4348
|
+
}
|
|
4349
|
+
},
|
|
4350
|
+
required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
|
|
4351
|
+
}
|
|
4352
|
+
};
|
|
4353
|
+
}
|
|
4354
|
+
});
|
|
4355
|
+
|
|
4356
|
+
// src/lib/reranker.ts
|
|
4357
|
+
var reranker_exports = {};
|
|
4358
|
+
__export(reranker_exports, {
|
|
4359
|
+
disposeReranker: () => disposeReranker,
|
|
4360
|
+
getRerankerModelPath: () => getRerankerModelPath,
|
|
4361
|
+
isRerankerAvailable: () => isRerankerAvailable,
|
|
4362
|
+
rerank: () => rerank,
|
|
4363
|
+
rerankWithContext: () => rerankWithContext,
|
|
4364
|
+
rerankWithScores: () => rerankWithScores
|
|
4365
|
+
});
|
|
4366
|
+
import path9 from "path";
|
|
4367
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4368
|
+
function resetIdleTimer() {
|
|
4369
|
+
if (_idleTimer) clearTimeout(_idleTimer);
|
|
4370
|
+
_idleTimer = setTimeout(() => {
|
|
4371
|
+
void disposeReranker();
|
|
4372
|
+
}, IDLE_TIMEOUT_MS);
|
|
4373
|
+
if (_idleTimer && typeof _idleTimer === "object" && "unref" in _idleTimer) {
|
|
4374
|
+
_idleTimer.unref();
|
|
4038
4375
|
}
|
|
4039
|
-
return false;
|
|
4040
4376
|
}
|
|
4041
|
-
function
|
|
4042
|
-
return
|
|
4377
|
+
function isRerankerAvailable() {
|
|
4378
|
+
return existsSync9(path9.join(MODELS_DIR, RERANKER_MODEL_FILE));
|
|
4043
4379
|
}
|
|
4044
|
-
function
|
|
4045
|
-
return
|
|
4046
|
-
if (!_socket || !_connected) {
|
|
4047
|
-
resolve({ error: "Not connected" });
|
|
4048
|
-
return;
|
|
4049
|
-
}
|
|
4050
|
-
const id = randomUUID2();
|
|
4051
|
-
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
4052
|
-
const timer = setTimeout(() => {
|
|
4053
|
-
_pending.delete(id);
|
|
4054
|
-
resolve({ error: "Request timeout" });
|
|
4055
|
-
}, timeoutMs);
|
|
4056
|
-
_pending.set(id, { resolve, timer });
|
|
4057
|
-
try {
|
|
4058
|
-
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
4059
|
-
} catch {
|
|
4060
|
-
clearTimeout(timer);
|
|
4061
|
-
_pending.delete(id);
|
|
4062
|
-
resolve({ error: "Write failed" });
|
|
4063
|
-
}
|
|
4064
|
-
});
|
|
4380
|
+
function getRerankerModelPath() {
|
|
4381
|
+
return path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
4065
4382
|
}
|
|
4066
|
-
async function
|
|
4067
|
-
if (
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4383
|
+
async function ensureLoaded() {
|
|
4384
|
+
if (_rerankerContext) {
|
|
4385
|
+
resetIdleTimer();
|
|
4386
|
+
return;
|
|
4387
|
+
}
|
|
4388
|
+
const modelPath = path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
|
|
4389
|
+
if (!existsSync9(modelPath)) {
|
|
4390
|
+
throw new Error(
|
|
4391
|
+
`Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
|
|
4392
|
+
);
|
|
4071
4393
|
}
|
|
4072
|
-
|
|
4394
|
+
process.stderr.write("[reranker] Loading Jina Reranker v3...\n");
|
|
4395
|
+
const { getLlama } = await import("node-llama-cpp");
|
|
4396
|
+
const llama = await getLlama();
|
|
4397
|
+
_rerankerModel = await llama.loadModel({ modelPath });
|
|
4398
|
+
_rerankerContext = await _rerankerModel.createEmbeddingContext();
|
|
4399
|
+
process.stderr.write("[reranker] Jina Reranker v3 loaded.\n");
|
|
4400
|
+
resetIdleTimer();
|
|
4073
4401
|
}
|
|
4074
|
-
function
|
|
4075
|
-
if (
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
_socket.destroy();
|
|
4079
|
-
_socket = null;
|
|
4080
|
-
}
|
|
4081
|
-
_connected = false;
|
|
4082
|
-
_buffer = "";
|
|
4083
|
-
return;
|
|
4402
|
+
async function disposeReranker() {
|
|
4403
|
+
if (_idleTimer) {
|
|
4404
|
+
clearTimeout(_idleTimer);
|
|
4405
|
+
_idleTimer = null;
|
|
4084
4406
|
}
|
|
4085
|
-
|
|
4086
|
-
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
4087
|
-
if (existsSync9(PID_PATH)) {
|
|
4088
|
-
try {
|
|
4089
|
-
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
4090
|
-
if (pid > 0) {
|
|
4091
|
-
try {
|
|
4092
|
-
process.kill(pid, "SIGKILL");
|
|
4093
|
-
} catch {
|
|
4094
|
-
}
|
|
4095
|
-
}
|
|
4096
|
-
} catch {
|
|
4097
|
-
}
|
|
4098
|
-
}
|
|
4099
|
-
if (_socket) {
|
|
4100
|
-
_socket.destroy();
|
|
4101
|
-
_socket = null;
|
|
4102
|
-
}
|
|
4103
|
-
_connected = false;
|
|
4104
|
-
_buffer = "";
|
|
4407
|
+
if (_rerankerContext) {
|
|
4105
4408
|
try {
|
|
4106
|
-
|
|
4409
|
+
await _rerankerContext.dispose();
|
|
4107
4410
|
} catch {
|
|
4108
4411
|
}
|
|
4412
|
+
_rerankerContext = null;
|
|
4413
|
+
}
|
|
4414
|
+
if (_rerankerModel) {
|
|
4109
4415
|
try {
|
|
4110
|
-
|
|
4416
|
+
await _rerankerModel.dispose();
|
|
4111
4417
|
} catch {
|
|
4112
4418
|
}
|
|
4113
|
-
|
|
4114
|
-
} finally {
|
|
4115
|
-
releaseSpawnLock();
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
function isDaemonTooYoung() {
|
|
4119
|
-
try {
|
|
4120
|
-
const stat = statSync(PID_PATH);
|
|
4121
|
-
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
4122
|
-
} catch {
|
|
4123
|
-
return false;
|
|
4419
|
+
_rerankerModel = null;
|
|
4124
4420
|
}
|
|
4421
|
+
process.stderr.write("[reranker] Unloaded (idle timeout).\n");
|
|
4125
4422
|
}
|
|
4126
|
-
async function
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
}
|
|
4141
|
-
const retry = await doRequest();
|
|
4142
|
-
if (!retry.error) {
|
|
4143
|
-
_consecutiveFailures = 0;
|
|
4144
|
-
return retry;
|
|
4423
|
+
async function rerankWithScores(query, texts, topK) {
|
|
4424
|
+
if (texts.length === 0) return [];
|
|
4425
|
+
await ensureLoaded();
|
|
4426
|
+
const ctx = _rerankerContext;
|
|
4427
|
+
const scored = [];
|
|
4428
|
+
for (let i = 0; i < texts.length; i++) {
|
|
4429
|
+
const text = texts[i] ?? "";
|
|
4430
|
+
try {
|
|
4431
|
+
const input2 = `query: ${query} document: ${text.slice(0, 512)}`;
|
|
4432
|
+
const embedding = await ctx.getEmbeddingFor(input2);
|
|
4433
|
+
const score = embedding.vector[0] ?? 0;
|
|
4434
|
+
scored.push({ text, score, index: i });
|
|
4435
|
+
} catch {
|
|
4436
|
+
scored.push({ text, score: -1, index: i });
|
|
4145
4437
|
}
|
|
4146
|
-
_consecutiveFailures++;
|
|
4147
4438
|
}
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
`);
|
|
4151
|
-
return { error: result.error };
|
|
4152
|
-
}
|
|
4153
|
-
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
4154
|
-
`);
|
|
4155
|
-
killAndRespawnDaemon();
|
|
4156
|
-
const start = Date.now();
|
|
4157
|
-
let delay2 = 200;
|
|
4158
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
4159
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
4160
|
-
if (await connectToSocket()) break;
|
|
4161
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
4162
|
-
}
|
|
4163
|
-
if (!_connected) return { error: "Daemon restart failed" };
|
|
4164
|
-
const final = await doRequest();
|
|
4165
|
-
if (!final.error) _consecutiveFailures = 0;
|
|
4166
|
-
return final;
|
|
4439
|
+
scored.sort((a, b) => b.score - a.score);
|
|
4440
|
+
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
4167
4441
|
}
|
|
4168
|
-
async function
|
|
4169
|
-
if (
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
`);
|
|
4176
|
-
killAndRespawnDaemon();
|
|
4177
|
-
const start = Date.now();
|
|
4178
|
-
let d = 200;
|
|
4179
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
4180
|
-
await new Promise((r) => setTimeout(r, d));
|
|
4181
|
-
if (await connectToSocket()) break;
|
|
4182
|
-
d = Math.min(d * 2, 3e3);
|
|
4183
|
-
}
|
|
4184
|
-
if (!_connected) return null;
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
const result = await retryThenRestart(
|
|
4188
|
-
() => sendRequest([text], priority),
|
|
4189
|
-
"Embed"
|
|
4442
|
+
async function rerank(query, candidates, topK = 5) {
|
|
4443
|
+
if (candidates.length === 0) return [];
|
|
4444
|
+
if (candidates.length <= topK) return candidates;
|
|
4445
|
+
const scored = await rerankWithScores(
|
|
4446
|
+
query,
|
|
4447
|
+
candidates.map((c) => c.raw_text),
|
|
4448
|
+
topK
|
|
4190
4449
|
);
|
|
4191
|
-
return
|
|
4450
|
+
return scored.map((s) => candidates[s.index]);
|
|
4192
4451
|
}
|
|
4193
|
-
function
|
|
4194
|
-
if (
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4452
|
+
async function rerankWithContext(query, candidates, topK) {
|
|
4453
|
+
if (candidates.length === 0) return [];
|
|
4454
|
+
await ensureLoaded();
|
|
4455
|
+
const ctx = _rerankerContext;
|
|
4456
|
+
const scored = [];
|
|
4457
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
4458
|
+
const candidate = candidates[i];
|
|
4459
|
+
try {
|
|
4460
|
+
const docText = candidate.context ? `[${candidate.context}] ${candidate.text.slice(0, 460)}` : candidate.text.slice(0, 512);
|
|
4461
|
+
const input2 = `query: ${query} document: ${docText}`;
|
|
4462
|
+
const embedding = await ctx.getEmbeddingFor(input2);
|
|
4463
|
+
const score = embedding.vector[0] ?? 0;
|
|
4464
|
+
scored.push({ text: candidate.text, score, index: i });
|
|
4465
|
+
} catch {
|
|
4466
|
+
scored.push({ text: candidate.text, score: -1, index: i });
|
|
4467
|
+
}
|
|
4204
4468
|
}
|
|
4469
|
+
scored.sort((a, b) => b.score - a.score);
|
|
4470
|
+
return typeof topK === "number" ? scored.slice(0, topK) : scored;
|
|
4205
4471
|
}
|
|
4206
|
-
var
|
|
4207
|
-
var
|
|
4208
|
-
"src/lib/
|
|
4472
|
+
var RERANKER_MODEL_FILE, IDLE_TIMEOUT_MS, _rerankerContext, _rerankerModel, _idleTimer;
|
|
4473
|
+
var init_reranker = __esm({
|
|
4474
|
+
"src/lib/reranker.ts"() {
|
|
4209
4475
|
"use strict";
|
|
4210
4476
|
init_config();
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
CONNECT_TIMEOUT_MS = 15e3;
|
|
4217
|
-
REQUEST_TIMEOUT_MS = 3e4;
|
|
4218
|
-
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
4219
|
-
_socket = null;
|
|
4220
|
-
_connected = false;
|
|
4221
|
-
_buffer = "";
|
|
4222
|
-
_requestCount = 0;
|
|
4223
|
-
_consecutiveFailures = 0;
|
|
4224
|
-
HEALTH_CHECK_INTERVAL = 100;
|
|
4225
|
-
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
4226
|
-
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
4227
|
-
MIN_DAEMON_AGE_MS = 3e4;
|
|
4228
|
-
_pending = /* @__PURE__ */ new Map();
|
|
4229
|
-
MAX_BUFFER = 1e7;
|
|
4477
|
+
RERANKER_MODEL_FILE = "jina-reranker-v3-q4_k_m.gguf";
|
|
4478
|
+
IDLE_TIMEOUT_MS = 6e4;
|
|
4479
|
+
_rerankerContext = null;
|
|
4480
|
+
_rerankerModel = null;
|
|
4481
|
+
_idleTimer = null;
|
|
4230
4482
|
}
|
|
4231
4483
|
});
|
|
4232
4484
|
|
|
@@ -4267,10 +4519,10 @@ async function disposeEmbedder() {
|
|
|
4267
4519
|
async function embedDirect(text) {
|
|
4268
4520
|
const llamaCpp = await import("node-llama-cpp");
|
|
4269
4521
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4270
|
-
const { existsSync:
|
|
4271
|
-
const
|
|
4272
|
-
const modelPath =
|
|
4273
|
-
if (!
|
|
4522
|
+
const { existsSync: existsSync21 } = await import("fs");
|
|
4523
|
+
const path25 = await import("path");
|
|
4524
|
+
const modelPath = path25.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
4525
|
+
if (!existsSync21(modelPath)) {
|
|
4274
4526
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
4275
4527
|
}
|
|
4276
4528
|
const llama = await llamaCpp.getLlama();
|
|
@@ -5212,13 +5464,13 @@ var init_session_key = __esm({
|
|
|
5212
5464
|
});
|
|
5213
5465
|
|
|
5214
5466
|
// src/lib/session-registry.ts
|
|
5215
|
-
import { readFileSync as
|
|
5216
|
-
import
|
|
5217
|
-
import
|
|
5467
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync12 } from "fs";
|
|
5468
|
+
import path14 from "path";
|
|
5469
|
+
import os7 from "os";
|
|
5218
5470
|
function registerSession(entry) {
|
|
5219
|
-
const dir =
|
|
5220
|
-
if (!
|
|
5221
|
-
|
|
5471
|
+
const dir = path14.dirname(REGISTRY_PATH);
|
|
5472
|
+
if (!existsSync12(dir)) {
|
|
5473
|
+
mkdirSync5(dir, { recursive: true });
|
|
5222
5474
|
}
|
|
5223
5475
|
const sessions = listSessions();
|
|
5224
5476
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -5227,11 +5479,11 @@ function registerSession(entry) {
|
|
|
5227
5479
|
} else {
|
|
5228
5480
|
sessions.push(entry);
|
|
5229
5481
|
}
|
|
5230
|
-
|
|
5482
|
+
writeFileSync6(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
5231
5483
|
}
|
|
5232
5484
|
function listSessions() {
|
|
5233
5485
|
try {
|
|
5234
|
-
const raw =
|
|
5486
|
+
const raw = readFileSync9(REGISTRY_PATH, "utf8");
|
|
5235
5487
|
return JSON.parse(raw);
|
|
5236
5488
|
} catch {
|
|
5237
5489
|
return [];
|
|
@@ -5241,7 +5493,7 @@ var REGISTRY_PATH;
|
|
|
5241
5493
|
var init_session_registry = __esm({
|
|
5242
5494
|
"src/lib/session-registry.ts"() {
|
|
5243
5495
|
"use strict";
|
|
5244
|
-
REGISTRY_PATH =
|
|
5496
|
+
REGISTRY_PATH = path14.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
5245
5497
|
}
|
|
5246
5498
|
});
|
|
5247
5499
|
|
|
@@ -5442,17 +5694,17 @@ __export(intercom_queue_exports, {
|
|
|
5442
5694
|
queueIntercom: () => queueIntercom,
|
|
5443
5695
|
readQueue: () => readQueue
|
|
5444
5696
|
});
|
|
5445
|
-
import { readFileSync as
|
|
5446
|
-
import
|
|
5447
|
-
import
|
|
5697
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
5698
|
+
import path15 from "path";
|
|
5699
|
+
import os8 from "os";
|
|
5448
5700
|
function ensureDir() {
|
|
5449
|
-
const dir =
|
|
5450
|
-
if (!
|
|
5701
|
+
const dir = path15.dirname(QUEUE_PATH);
|
|
5702
|
+
if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
|
|
5451
5703
|
}
|
|
5452
5704
|
function readQueue() {
|
|
5453
5705
|
try {
|
|
5454
|
-
if (!
|
|
5455
|
-
return JSON.parse(
|
|
5706
|
+
if (!existsSync13(QUEUE_PATH)) return [];
|
|
5707
|
+
return JSON.parse(readFileSync10(QUEUE_PATH, "utf8"));
|
|
5456
5708
|
} catch {
|
|
5457
5709
|
return [];
|
|
5458
5710
|
}
|
|
@@ -5460,7 +5712,7 @@ function readQueue() {
|
|
|
5460
5712
|
function writeQueue(queue) {
|
|
5461
5713
|
ensureDir();
|
|
5462
5714
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
5463
|
-
|
|
5715
|
+
writeFileSync7(tmp, JSON.stringify(queue, null, 2));
|
|
5464
5716
|
renameSync3(tmp, QUEUE_PATH);
|
|
5465
5717
|
}
|
|
5466
5718
|
function queueIntercom(targetSession, reason) {
|
|
@@ -5552,29 +5804,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
5552
5804
|
var init_intercom_queue = __esm({
|
|
5553
5805
|
"src/lib/intercom-queue.ts"() {
|
|
5554
5806
|
"use strict";
|
|
5555
|
-
QUEUE_PATH =
|
|
5807
|
+
QUEUE_PATH = path15.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
5556
5808
|
MAX_RETRIES2 = 5;
|
|
5557
5809
|
TTL_MS = 60 * 60 * 1e3;
|
|
5558
|
-
INTERCOM_LOG =
|
|
5810
|
+
INTERCOM_LOG = path15.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
5559
5811
|
}
|
|
5560
5812
|
});
|
|
5561
5813
|
|
|
5562
5814
|
// src/lib/license.ts
|
|
5563
|
-
import { readFileSync as
|
|
5815
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
|
|
5564
5816
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5565
5817
|
import { createRequire as createRequire2 } from "module";
|
|
5566
5818
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5567
|
-
import
|
|
5568
|
-
import
|
|
5819
|
+
import os9 from "os";
|
|
5820
|
+
import path16 from "path";
|
|
5569
5821
|
import { jwtVerify, importSPKI } from "jose";
|
|
5570
5822
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
5571
5823
|
var init_license = __esm({
|
|
5572
5824
|
"src/lib/license.ts"() {
|
|
5573
5825
|
"use strict";
|
|
5574
5826
|
init_config();
|
|
5575
|
-
LICENSE_PATH =
|
|
5576
|
-
CACHE_PATH =
|
|
5577
|
-
DEVICE_ID_PATH =
|
|
5827
|
+
LICENSE_PATH = path16.join(EXE_AI_DIR, "license.key");
|
|
5828
|
+
CACHE_PATH = path16.join(EXE_AI_DIR, "license-cache.json");
|
|
5829
|
+
DEVICE_ID_PATH = path16.join(EXE_AI_DIR, "device-id");
|
|
5578
5830
|
PLAN_LIMITS = {
|
|
5579
5831
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
5580
5832
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -5586,12 +5838,12 @@ var init_license = __esm({
|
|
|
5586
5838
|
});
|
|
5587
5839
|
|
|
5588
5840
|
// src/lib/plan-limits.ts
|
|
5589
|
-
import { readFileSync as
|
|
5590
|
-
import
|
|
5841
|
+
import { readFileSync as readFileSync12, existsSync as existsSync15 } from "fs";
|
|
5842
|
+
import path17 from "path";
|
|
5591
5843
|
function getLicenseSync() {
|
|
5592
5844
|
try {
|
|
5593
|
-
if (!
|
|
5594
|
-
const raw = JSON.parse(
|
|
5845
|
+
if (!existsSync15(CACHE_PATH2)) return freeLicense();
|
|
5846
|
+
const raw = JSON.parse(readFileSync12(CACHE_PATH2, "utf8"));
|
|
5595
5847
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
5596
5848
|
const parts = raw.token.split(".");
|
|
5597
5849
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -5629,8 +5881,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
5629
5881
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
5630
5882
|
let count = 0;
|
|
5631
5883
|
try {
|
|
5632
|
-
if (
|
|
5633
|
-
const raw =
|
|
5884
|
+
if (existsSync15(filePath)) {
|
|
5885
|
+
const raw = readFileSync12(filePath, "utf8");
|
|
5634
5886
|
const employees = JSON.parse(raw);
|
|
5635
5887
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
5636
5888
|
}
|
|
@@ -5659,7 +5911,7 @@ var init_plan_limits = __esm({
|
|
|
5659
5911
|
this.name = "PlanLimitError";
|
|
5660
5912
|
}
|
|
5661
5913
|
};
|
|
5662
|
-
CACHE_PATH2 =
|
|
5914
|
+
CACHE_PATH2 = path17.join(EXE_AI_DIR, "license-cache.json");
|
|
5663
5915
|
}
|
|
5664
5916
|
});
|
|
5665
5917
|
|
|
@@ -5698,13 +5950,13 @@ var init_task_scope = __esm({
|
|
|
5698
5950
|
|
|
5699
5951
|
// src/lib/notifications.ts
|
|
5700
5952
|
import crypto3 from "crypto";
|
|
5701
|
-
import
|
|
5702
|
-
import
|
|
5953
|
+
import path18 from "path";
|
|
5954
|
+
import os10 from "os";
|
|
5703
5955
|
import {
|
|
5704
|
-
readFileSync as
|
|
5956
|
+
readFileSync as readFileSync13,
|
|
5705
5957
|
readdirSync as readdirSync4,
|
|
5706
|
-
unlinkSync as
|
|
5707
|
-
existsSync as
|
|
5958
|
+
unlinkSync as unlinkSync5,
|
|
5959
|
+
existsSync as existsSync16,
|
|
5708
5960
|
rmdirSync
|
|
5709
5961
|
} from "fs";
|
|
5710
5962
|
async function writeNotification(notification) {
|
|
@@ -5851,11 +6103,11 @@ var init_session_scope = __esm({
|
|
|
5851
6103
|
|
|
5852
6104
|
// src/lib/tasks-crud.ts
|
|
5853
6105
|
import crypto5 from "crypto";
|
|
5854
|
-
import
|
|
5855
|
-
import
|
|
6106
|
+
import path19 from "path";
|
|
6107
|
+
import os11 from "os";
|
|
5856
6108
|
import { execSync as execSync7 } from "child_process";
|
|
5857
6109
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5858
|
-
import { existsSync as
|
|
6110
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
5859
6111
|
async function writeCheckpoint(input2) {
|
|
5860
6112
|
const client = getClient();
|
|
5861
6113
|
const row = await resolveTask(client, input2.taskId);
|
|
@@ -6049,8 +6301,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
6049
6301
|
}
|
|
6050
6302
|
if (input2.baseDir) {
|
|
6051
6303
|
try {
|
|
6052
|
-
await mkdir4(
|
|
6053
|
-
await mkdir4(
|
|
6304
|
+
await mkdir4(path19.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
6305
|
+
await mkdir4(path19.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
6054
6306
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
6055
6307
|
await ensureGitignoreExe(input2.baseDir);
|
|
6056
6308
|
} catch {
|
|
@@ -6086,10 +6338,10 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
6086
6338
|
});
|
|
6087
6339
|
if (input2.baseDir) {
|
|
6088
6340
|
try {
|
|
6089
|
-
const EXE_OS_DIR =
|
|
6090
|
-
const mdPath =
|
|
6091
|
-
const mdDir =
|
|
6092
|
-
if (!
|
|
6341
|
+
const EXE_OS_DIR = path19.join(os11.homedir(), ".exe-os");
|
|
6342
|
+
const mdPath = path19.join(EXE_OS_DIR, taskFile);
|
|
6343
|
+
const mdDir = path19.dirname(mdPath);
|
|
6344
|
+
if (!existsSync17(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
6093
6345
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
6094
6346
|
const mdContent = `# ${input2.title}
|
|
6095
6347
|
|
|
@@ -6389,9 +6641,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
6389
6641
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
6390
6642
|
}
|
|
6391
6643
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
6392
|
-
const archPath =
|
|
6644
|
+
const archPath = path19.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
6393
6645
|
try {
|
|
6394
|
-
if (
|
|
6646
|
+
if (existsSync17(archPath)) return;
|
|
6395
6647
|
const template = [
|
|
6396
6648
|
`# ${projectName} \u2014 System Architecture`,
|
|
6397
6649
|
"",
|
|
@@ -6424,10 +6676,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
6424
6676
|
}
|
|
6425
6677
|
}
|
|
6426
6678
|
async function ensureGitignoreExe(baseDir) {
|
|
6427
|
-
const gitignorePath =
|
|
6679
|
+
const gitignorePath = path19.join(baseDir, ".gitignore");
|
|
6428
6680
|
try {
|
|
6429
|
-
if (
|
|
6430
|
-
const content =
|
|
6681
|
+
if (existsSync17(gitignorePath)) {
|
|
6682
|
+
const content = readFileSync14(gitignorePath, "utf-8");
|
|
6431
6683
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
6432
6684
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
6433
6685
|
} else {
|
|
@@ -6470,8 +6722,8 @@ __export(tasks_review_exports, {
|
|
|
6470
6722
|
isStale: () => isStale,
|
|
6471
6723
|
listPendingReviews: () => listPendingReviews
|
|
6472
6724
|
});
|
|
6473
|
-
import
|
|
6474
|
-
import { existsSync as
|
|
6725
|
+
import path20 from "path";
|
|
6726
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
6475
6727
|
function formatAge(isoTimestamp) {
|
|
6476
6728
|
if (!isoTimestamp) return "";
|
|
6477
6729
|
const ms = Date.now() - new Date(isoTimestamp).getTime();
|
|
@@ -6740,11 +6992,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
6740
6992
|
);
|
|
6741
6993
|
}
|
|
6742
6994
|
try {
|
|
6743
|
-
const cacheDir =
|
|
6744
|
-
if (
|
|
6995
|
+
const cacheDir = path20.join(EXE_AI_DIR, "session-cache");
|
|
6996
|
+
if (existsSync18(cacheDir)) {
|
|
6745
6997
|
for (const f of readdirSync5(cacheDir)) {
|
|
6746
6998
|
if (f.startsWith("review-notified-")) {
|
|
6747
|
-
|
|
6999
|
+
unlinkSync6(path20.join(cacheDir, f));
|
|
6748
7000
|
}
|
|
6749
7001
|
}
|
|
6750
7002
|
}
|
|
@@ -6766,7 +7018,7 @@ var init_tasks_review = __esm({
|
|
|
6766
7018
|
});
|
|
6767
7019
|
|
|
6768
7020
|
// src/lib/tasks-chain.ts
|
|
6769
|
-
import
|
|
7021
|
+
import path21 from "path";
|
|
6770
7022
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
6771
7023
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
6772
7024
|
const client = getClient();
|
|
@@ -6783,7 +7035,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
6783
7035
|
});
|
|
6784
7036
|
for (const ur of unblockedRows.rows) {
|
|
6785
7037
|
try {
|
|
6786
|
-
const ubFile =
|
|
7038
|
+
const ubFile = path21.join(baseDir, String(ur.task_file));
|
|
6787
7039
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
6788
7040
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
6789
7041
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -7247,8 +7499,8 @@ __export(tasks_exports, {
|
|
|
7247
7499
|
updateTaskStatus: () => updateTaskStatus,
|
|
7248
7500
|
writeCheckpoint: () => writeCheckpoint
|
|
7249
7501
|
});
|
|
7250
|
-
import
|
|
7251
|
-
import { writeFileSync as
|
|
7502
|
+
import path22 from "path";
|
|
7503
|
+
import { writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
7252
7504
|
async function createTask(input2) {
|
|
7253
7505
|
const result = await createTaskCore(input2);
|
|
7254
7506
|
if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -7267,14 +7519,14 @@ async function updateTask(input2) {
|
|
|
7267
7519
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
7268
7520
|
try {
|
|
7269
7521
|
const agent = String(row.assigned_to);
|
|
7270
|
-
const cacheDir =
|
|
7271
|
-
const cachePath =
|
|
7522
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
7523
|
+
const cachePath = path22.join(cacheDir, `current-task-${agent}.json`);
|
|
7272
7524
|
if (input2.status === "in_progress") {
|
|
7273
|
-
|
|
7274
|
-
|
|
7525
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
7526
|
+
writeFileSync9(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
7275
7527
|
} else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled" || input2.status === "closed") {
|
|
7276
7528
|
try {
|
|
7277
|
-
|
|
7529
|
+
unlinkSync7(cachePath);
|
|
7278
7530
|
} catch {
|
|
7279
7531
|
}
|
|
7280
7532
|
}
|
|
@@ -7739,13 +7991,13 @@ __export(tmux_routing_exports, {
|
|
|
7739
7991
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
7740
7992
|
});
|
|
7741
7993
|
import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
|
|
7742
|
-
import { readFileSync as
|
|
7743
|
-
import
|
|
7744
|
-
import
|
|
7994
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, existsSync as existsSync19, appendFileSync, readdirSync as readdirSync6 } from "fs";
|
|
7995
|
+
import path23 from "path";
|
|
7996
|
+
import os12 from "os";
|
|
7745
7997
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7746
|
-
import { unlinkSync as
|
|
7998
|
+
import { unlinkSync as unlinkSync8 } from "fs";
|
|
7747
7999
|
function spawnLockPath(sessionName) {
|
|
7748
|
-
return
|
|
8000
|
+
return path23.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
7749
8001
|
}
|
|
7750
8002
|
function isProcessAlive(pid) {
|
|
7751
8003
|
try {
|
|
@@ -7756,13 +8008,13 @@ function isProcessAlive(pid) {
|
|
|
7756
8008
|
}
|
|
7757
8009
|
}
|
|
7758
8010
|
function acquireSpawnLock2(sessionName) {
|
|
7759
|
-
if (!
|
|
7760
|
-
|
|
8011
|
+
if (!existsSync19(SPAWN_LOCK_DIR)) {
|
|
8012
|
+
mkdirSync9(SPAWN_LOCK_DIR, { recursive: true });
|
|
7761
8013
|
}
|
|
7762
8014
|
const lockFile = spawnLockPath(sessionName);
|
|
7763
|
-
if (
|
|
8015
|
+
if (existsSync19(lockFile)) {
|
|
7764
8016
|
try {
|
|
7765
|
-
const lock = JSON.parse(
|
|
8017
|
+
const lock = JSON.parse(readFileSync15(lockFile, "utf8"));
|
|
7766
8018
|
const age = Date.now() - lock.timestamp;
|
|
7767
8019
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
7768
8020
|
return false;
|
|
@@ -7770,25 +8022,25 @@ function acquireSpawnLock2(sessionName) {
|
|
|
7770
8022
|
} catch {
|
|
7771
8023
|
}
|
|
7772
8024
|
}
|
|
7773
|
-
|
|
8025
|
+
writeFileSync10(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
7774
8026
|
return true;
|
|
7775
8027
|
}
|
|
7776
8028
|
function releaseSpawnLock2(sessionName) {
|
|
7777
8029
|
try {
|
|
7778
|
-
|
|
8030
|
+
unlinkSync8(spawnLockPath(sessionName));
|
|
7779
8031
|
} catch {
|
|
7780
8032
|
}
|
|
7781
8033
|
}
|
|
7782
8034
|
function resolveBehaviorsExporterScript() {
|
|
7783
8035
|
try {
|
|
7784
8036
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7785
|
-
const scriptPath =
|
|
7786
|
-
|
|
8037
|
+
const scriptPath = path23.join(
|
|
8038
|
+
path23.dirname(thisFile),
|
|
7787
8039
|
"..",
|
|
7788
8040
|
"bin",
|
|
7789
8041
|
"exe-export-behaviors.js"
|
|
7790
8042
|
);
|
|
7791
|
-
return
|
|
8043
|
+
return existsSync19(scriptPath) ? scriptPath : null;
|
|
7792
8044
|
} catch {
|
|
7793
8045
|
return null;
|
|
7794
8046
|
}
|
|
@@ -7854,12 +8106,12 @@ function extractRootExe(name) {
|
|
|
7854
8106
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
7855
8107
|
}
|
|
7856
8108
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
7857
|
-
if (!
|
|
7858
|
-
|
|
8109
|
+
if (!existsSync19(SESSION_CACHE)) {
|
|
8110
|
+
mkdirSync9(SESSION_CACHE, { recursive: true });
|
|
7859
8111
|
}
|
|
7860
8112
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
7861
|
-
const filePath =
|
|
7862
|
-
|
|
8113
|
+
const filePath = path23.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
8114
|
+
writeFileSync10(filePath, JSON.stringify({
|
|
7863
8115
|
parentExe: rootExe,
|
|
7864
8116
|
dispatchedBy: dispatchedBy || rootExe,
|
|
7865
8117
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7867,7 +8119,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
7867
8119
|
}
|
|
7868
8120
|
function getParentExe(sessionKey) {
|
|
7869
8121
|
try {
|
|
7870
|
-
const data = JSON.parse(
|
|
8122
|
+
const data = JSON.parse(readFileSync15(path23.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
7871
8123
|
return data.parentExe || null;
|
|
7872
8124
|
} catch {
|
|
7873
8125
|
return null;
|
|
@@ -7875,8 +8127,8 @@ function getParentExe(sessionKey) {
|
|
|
7875
8127
|
}
|
|
7876
8128
|
function getDispatchedBy(sessionKey) {
|
|
7877
8129
|
try {
|
|
7878
|
-
const data = JSON.parse(
|
|
7879
|
-
|
|
8130
|
+
const data = JSON.parse(readFileSync15(
|
|
8131
|
+
path23.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
7880
8132
|
"utf8"
|
|
7881
8133
|
));
|
|
7882
8134
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -7946,8 +8198,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
7946
8198
|
}
|
|
7947
8199
|
function readDebounceState() {
|
|
7948
8200
|
try {
|
|
7949
|
-
if (!
|
|
7950
|
-
const raw = JSON.parse(
|
|
8201
|
+
if (!existsSync19(DEBOUNCE_FILE)) return {};
|
|
8202
|
+
const raw = JSON.parse(readFileSync15(DEBOUNCE_FILE, "utf8"));
|
|
7951
8203
|
const state = {};
|
|
7952
8204
|
for (const [key, val] of Object.entries(raw)) {
|
|
7953
8205
|
if (typeof val === "number") {
|
|
@@ -7963,8 +8215,8 @@ function readDebounceState() {
|
|
|
7963
8215
|
}
|
|
7964
8216
|
function writeDebounceState(state) {
|
|
7965
8217
|
try {
|
|
7966
|
-
if (!
|
|
7967
|
-
|
|
8218
|
+
if (!existsSync19(SESSION_CACHE)) mkdirSync9(SESSION_CACHE, { recursive: true });
|
|
8219
|
+
writeFileSync10(DEBOUNCE_FILE, JSON.stringify(state));
|
|
7968
8220
|
} catch {
|
|
7969
8221
|
}
|
|
7970
8222
|
}
|
|
@@ -8063,8 +8315,8 @@ function sendIntercom(targetSession) {
|
|
|
8063
8315
|
try {
|
|
8064
8316
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8065
8317
|
const agent = baseAgentName(rawAgent);
|
|
8066
|
-
const markerPath =
|
|
8067
|
-
if (
|
|
8318
|
+
const markerPath = path23.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
8319
|
+
if (existsSync19(markerPath)) {
|
|
8068
8320
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
8069
8321
|
return "debounced";
|
|
8070
8322
|
}
|
|
@@ -8074,8 +8326,8 @@ function sendIntercom(targetSession) {
|
|
|
8074
8326
|
try {
|
|
8075
8327
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8076
8328
|
const agent = baseAgentName(rawAgent);
|
|
8077
|
-
const taskDir =
|
|
8078
|
-
if (
|
|
8329
|
+
const taskDir = path23.join(process.cwd(), "exe", agent);
|
|
8330
|
+
if (existsSync19(taskDir)) {
|
|
8079
8331
|
const files = readdirSync6(taskDir).filter(
|
|
8080
8332
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
8081
8333
|
);
|
|
@@ -8241,26 +8493,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8241
8493
|
const transport = getTransport();
|
|
8242
8494
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
8243
8495
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
8244
|
-
const logDir =
|
|
8245
|
-
const logFile =
|
|
8246
|
-
if (!
|
|
8247
|
-
|
|
8496
|
+
const logDir = path23.join(os12.homedir(), ".exe-os", "session-logs");
|
|
8497
|
+
const logFile = path23.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
8498
|
+
if (!existsSync19(logDir)) {
|
|
8499
|
+
mkdirSync9(logDir, { recursive: true });
|
|
8248
8500
|
}
|
|
8249
8501
|
transport.kill(sessionName);
|
|
8250
8502
|
let cleanupSuffix = "";
|
|
8251
8503
|
try {
|
|
8252
8504
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
8253
|
-
const cleanupScript =
|
|
8254
|
-
if (
|
|
8505
|
+
const cleanupScript = path23.join(path23.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
8506
|
+
if (existsSync19(cleanupScript)) {
|
|
8255
8507
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
8256
8508
|
}
|
|
8257
8509
|
} catch {
|
|
8258
8510
|
}
|
|
8259
8511
|
try {
|
|
8260
|
-
const claudeJsonPath =
|
|
8512
|
+
const claudeJsonPath = path23.join(os12.homedir(), ".claude.json");
|
|
8261
8513
|
let claudeJson = {};
|
|
8262
8514
|
try {
|
|
8263
|
-
claudeJson = JSON.parse(
|
|
8515
|
+
claudeJson = JSON.parse(readFileSync15(claudeJsonPath, "utf8"));
|
|
8264
8516
|
} catch {
|
|
8265
8517
|
}
|
|
8266
8518
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -8268,17 +8520,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8268
8520
|
const trustDir = opts?.cwd ?? projectDir;
|
|
8269
8521
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
8270
8522
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
8271
|
-
|
|
8523
|
+
writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
8272
8524
|
} catch {
|
|
8273
8525
|
}
|
|
8274
8526
|
try {
|
|
8275
|
-
const settingsDir =
|
|
8527
|
+
const settingsDir = path23.join(os12.homedir(), ".claude", "projects");
|
|
8276
8528
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
8277
|
-
const projSettingsDir =
|
|
8278
|
-
const settingsPath =
|
|
8529
|
+
const projSettingsDir = path23.join(settingsDir, normalizedKey);
|
|
8530
|
+
const settingsPath = path23.join(projSettingsDir, "settings.json");
|
|
8279
8531
|
let settings = {};
|
|
8280
8532
|
try {
|
|
8281
|
-
settings = JSON.parse(
|
|
8533
|
+
settings = JSON.parse(readFileSync15(settingsPath, "utf8"));
|
|
8282
8534
|
} catch {
|
|
8283
8535
|
}
|
|
8284
8536
|
const perms = settings.permissions ?? {};
|
|
@@ -8306,8 +8558,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8306
8558
|
if (changed) {
|
|
8307
8559
|
perms.allow = allow;
|
|
8308
8560
|
settings.permissions = perms;
|
|
8309
|
-
|
|
8310
|
-
|
|
8561
|
+
mkdirSync9(projSettingsDir, { recursive: true });
|
|
8562
|
+
writeFileSync10(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
8311
8563
|
}
|
|
8312
8564
|
} catch {
|
|
8313
8565
|
}
|
|
@@ -8322,8 +8574,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8322
8574
|
let behaviorsFlag = "";
|
|
8323
8575
|
let legacyFallbackWarned = false;
|
|
8324
8576
|
if (!useExeAgent && !useBinSymlink) {
|
|
8325
|
-
const identityPath =
|
|
8326
|
-
|
|
8577
|
+
const identityPath = path23.join(
|
|
8578
|
+
os12.homedir(),
|
|
8327
8579
|
".exe-os",
|
|
8328
8580
|
"identity",
|
|
8329
8581
|
`${employeeName}.md`
|
|
@@ -8332,13 +8584,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8332
8584
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
8333
8585
|
if (hasAgentFlag) {
|
|
8334
8586
|
identityFlag = ` --agent ${employeeName}`;
|
|
8335
|
-
} else if (
|
|
8587
|
+
} else if (existsSync19(identityPath)) {
|
|
8336
8588
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
8337
8589
|
legacyFallbackWarned = true;
|
|
8338
8590
|
}
|
|
8339
8591
|
const behaviorsFile = exportBehaviorsSync(
|
|
8340
8592
|
employeeName,
|
|
8341
|
-
|
|
8593
|
+
path23.basename(spawnCwd),
|
|
8342
8594
|
sessionName
|
|
8343
8595
|
);
|
|
8344
8596
|
if (behaviorsFile) {
|
|
@@ -8353,16 +8605,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8353
8605
|
}
|
|
8354
8606
|
let sessionContextFlag = "";
|
|
8355
8607
|
try {
|
|
8356
|
-
const ctxDir =
|
|
8357
|
-
|
|
8358
|
-
const ctxFile =
|
|
8608
|
+
const ctxDir = path23.join(os12.homedir(), ".exe-os", "session-cache");
|
|
8609
|
+
mkdirSync9(ctxDir, { recursive: true });
|
|
8610
|
+
const ctxFile = path23.join(ctxDir, `session-context-${sessionName}.md`);
|
|
8359
8611
|
const ctxContent = [
|
|
8360
8612
|
`## Session Context`,
|
|
8361
8613
|
`You are running in tmux session: ${sessionName}.`,
|
|
8362
8614
|
`Your parent coordinator session is ${exeSession}.`,
|
|
8363
8615
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
8364
8616
|
].join("\n");
|
|
8365
|
-
|
|
8617
|
+
writeFileSync10(ctxFile, ctxContent);
|
|
8366
8618
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
8367
8619
|
} catch {
|
|
8368
8620
|
}
|
|
@@ -8439,8 +8691,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8439
8691
|
transport.pipeLog(sessionName, logFile);
|
|
8440
8692
|
try {
|
|
8441
8693
|
const mySession = getMySession();
|
|
8442
|
-
const dispatchInfo =
|
|
8443
|
-
|
|
8694
|
+
const dispatchInfo = path23.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
8695
|
+
writeFileSync10(dispatchInfo, JSON.stringify({
|
|
8444
8696
|
dispatchedBy: mySession,
|
|
8445
8697
|
rootExe: exeSession,
|
|
8446
8698
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -8514,15 +8766,15 @@ var init_tmux_routing = __esm({
|
|
|
8514
8766
|
init_intercom_queue();
|
|
8515
8767
|
init_plan_limits();
|
|
8516
8768
|
init_employees();
|
|
8517
|
-
SPAWN_LOCK_DIR =
|
|
8518
|
-
SESSION_CACHE =
|
|
8769
|
+
SPAWN_LOCK_DIR = path23.join(os12.homedir(), ".exe-os", "spawn-locks");
|
|
8770
|
+
SESSION_CACHE = path23.join(os12.homedir(), ".exe-os", "session-cache");
|
|
8519
8771
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
8520
8772
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
8521
8773
|
VERIFY_PANE_LINES = 200;
|
|
8522
8774
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
8523
8775
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
8524
|
-
INTERCOM_LOG2 =
|
|
8525
|
-
DEBOUNCE_FILE =
|
|
8776
|
+
INTERCOM_LOG2 = path23.join(os12.homedir(), ".exe-os", "intercom.log");
|
|
8777
|
+
DEBOUNCE_FILE = path23.join(SESSION_CACHE, "intercom-debounce.json");
|
|
8526
8778
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
8527
8779
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
8528
8780
|
}
|
|
@@ -8806,8 +9058,8 @@ init_config();
|
|
|
8806
9058
|
init_config();
|
|
8807
9059
|
init_store();
|
|
8808
9060
|
import { spawn as spawn2 } from "child_process";
|
|
8809
|
-
import { readFileSync as
|
|
8810
|
-
import
|
|
9061
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync20, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
9062
|
+
import path24 from "path";
|
|
8811
9063
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8812
9064
|
|
|
8813
9065
|
// src/lib/hybrid-search.ts
|
|
@@ -9343,14 +9595,44 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
|
9343
9595
|
}
|
|
9344
9596
|
}
|
|
9345
9597
|
|
|
9598
|
+
// src/lib/cache-warmth.ts
|
|
9599
|
+
import os6 from "os";
|
|
9600
|
+
import path12 from "path";
|
|
9601
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
9602
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
9603
|
+
var CACHE_DIR = path12.join(
|
|
9604
|
+
process.env.EXE_OS_DIR ?? path12.join(os6.homedir(), ".exe-os"),
|
|
9605
|
+
"session-cache"
|
|
9606
|
+
);
|
|
9607
|
+
function getStatePath(sessionKey) {
|
|
9608
|
+
return path12.join(CACHE_DIR, `cache-warmth-${sessionKey}.json`);
|
|
9609
|
+
}
|
|
9610
|
+
function isCacheCold(sessionKey) {
|
|
9611
|
+
const statePath = getStatePath(sessionKey);
|
|
9612
|
+
if (!existsSync11(statePath)) {
|
|
9613
|
+
return { cold: true, idleMs: Number.POSITIVE_INFINITY };
|
|
9614
|
+
}
|
|
9615
|
+
try {
|
|
9616
|
+
const state = JSON.parse(readFileSync7(statePath, "utf-8"));
|
|
9617
|
+
const lastApiCallAt = new Date(state.lastApiCallAt).getTime();
|
|
9618
|
+
if (!Number.isFinite(lastApiCallAt)) {
|
|
9619
|
+
return { cold: true, idleMs: Number.POSITIVE_INFINITY };
|
|
9620
|
+
}
|
|
9621
|
+
const idleMs = Date.now() - lastApiCallAt;
|
|
9622
|
+
return { cold: idleMs > CACHE_TTL_MS, idleMs };
|
|
9623
|
+
} catch {
|
|
9624
|
+
return { cold: true, idleMs: Number.POSITIVE_INFINITY };
|
|
9625
|
+
}
|
|
9626
|
+
}
|
|
9627
|
+
|
|
9346
9628
|
// src/lib/active-agent.ts
|
|
9347
9629
|
init_config();
|
|
9348
9630
|
init_session_key();
|
|
9349
9631
|
init_employees();
|
|
9350
|
-
import { readFileSync as
|
|
9632
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
|
|
9351
9633
|
import { execSync as execSync5 } from "child_process";
|
|
9352
|
-
import
|
|
9353
|
-
var
|
|
9634
|
+
import path13 from "path";
|
|
9635
|
+
var CACHE_DIR2 = path13.join(EXE_AI_DIR, "session-cache");
|
|
9354
9636
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
9355
9637
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
9356
9638
|
if (candidate === baseName) return true;
|
|
@@ -9395,19 +9677,19 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
9395
9677
|
return null;
|
|
9396
9678
|
}
|
|
9397
9679
|
function getMarkerPath() {
|
|
9398
|
-
return
|
|
9680
|
+
return path13.join(CACHE_DIR2, `active-agent-${getSessionKey()}.json`);
|
|
9399
9681
|
}
|
|
9400
9682
|
function getActiveAgent() {
|
|
9401
9683
|
try {
|
|
9402
9684
|
const markerPath = getMarkerPath();
|
|
9403
|
-
const raw =
|
|
9685
|
+
const raw = readFileSync8(markerPath, "utf8");
|
|
9404
9686
|
const data = JSON.parse(raw);
|
|
9405
9687
|
if (data.agentId) {
|
|
9406
9688
|
if (data.startedAt) {
|
|
9407
9689
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
9408
9690
|
if (age > STALE_MS) {
|
|
9409
9691
|
try {
|
|
9410
|
-
|
|
9692
|
+
unlinkSync4(markerPath);
|
|
9411
9693
|
} catch {
|
|
9412
9694
|
}
|
|
9413
9695
|
} else {
|
|
@@ -9453,7 +9735,7 @@ if (!process.env.AGENT_ID) {
|
|
|
9453
9735
|
if (!loadConfigSync().autoRetrieval) {
|
|
9454
9736
|
process.exit(0);
|
|
9455
9737
|
}
|
|
9456
|
-
var WORKER_LOG_PATH =
|
|
9738
|
+
var WORKER_LOG_PATH = path24.join(EXE_AI_DIR, "workers.log");
|
|
9457
9739
|
function openWorkerLog() {
|
|
9458
9740
|
try {
|
|
9459
9741
|
return openSync2(WORKER_LOG_PATH, "a");
|
|
@@ -9461,10 +9743,10 @@ function openWorkerLog() {
|
|
|
9461
9743
|
return "ignore";
|
|
9462
9744
|
}
|
|
9463
9745
|
}
|
|
9464
|
-
var
|
|
9746
|
+
var CACHE_DIR3 = path24.join(EXE_AI_DIR, "session-cache");
|
|
9465
9747
|
function loadInjectedIds(sessionId) {
|
|
9466
9748
|
try {
|
|
9467
|
-
const raw =
|
|
9749
|
+
const raw = readFileSync16(path24.join(CACHE_DIR3, `${sessionId}.json`), "utf8");
|
|
9468
9750
|
return new Set(JSON.parse(raw));
|
|
9469
9751
|
} catch {
|
|
9470
9752
|
return /* @__PURE__ */ new Set();
|
|
@@ -9472,9 +9754,9 @@ function loadInjectedIds(sessionId) {
|
|
|
9472
9754
|
}
|
|
9473
9755
|
function saveInjectedIds(sessionId, ids) {
|
|
9474
9756
|
try {
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9757
|
+
mkdirSync10(CACHE_DIR3, { recursive: true });
|
|
9758
|
+
writeFileSync11(
|
|
9759
|
+
path24.join(CACHE_DIR3, `${sessionId}.json`),
|
|
9478
9760
|
JSON.stringify([...ids])
|
|
9479
9761
|
);
|
|
9480
9762
|
} catch {
|
|
@@ -9569,6 +9851,28 @@ process.stdin.on("end", async () => {
|
|
|
9569
9851
|
}
|
|
9570
9852
|
const config = await loadConfig();
|
|
9571
9853
|
const search = config.hookSearchMode === "hybrid" ? hybridSearch : lightweightSearch;
|
|
9854
|
+
let cacheContext = "";
|
|
9855
|
+
try {
|
|
9856
|
+
const sessionKey = getSessionKey();
|
|
9857
|
+
const cacheStatus = isCacheCold(sessionKey);
|
|
9858
|
+
if (cacheStatus.cold) {
|
|
9859
|
+
const idleMinutes = Number.isFinite(cacheStatus.idleMs) ? `${Math.max(1, Math.floor(cacheStatus.idleMs / 6e4))}m` : "5m+";
|
|
9860
|
+
cacheContext = `## Cache Status
|
|
9861
|
+
\u26A0\uFE0F Cache cold (idle ${idleMinutes}). Next response will re-process the full context (higher cost). This is informational \u2014 no action needed.`;
|
|
9862
|
+
try {
|
|
9863
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
9864
|
+
const client = getClient2();
|
|
9865
|
+
await client.execute({
|
|
9866
|
+
sql: `UPDATE session_agent_map
|
|
9867
|
+
SET cache_cold_count = COALESCE(cache_cold_count, 0) + 1
|
|
9868
|
+
WHERE session_uuid = ?`,
|
|
9869
|
+
args: [data.session_id]
|
|
9870
|
+
});
|
|
9871
|
+
} catch {
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
} catch {
|
|
9875
|
+
}
|
|
9572
9876
|
const memories = await search(
|
|
9573
9877
|
prompt.slice(0, 200),
|
|
9574
9878
|
agent.agentId,
|
|
@@ -9592,7 +9896,7 @@ ${fresh.map(
|
|
|
9592
9896
|
try {
|
|
9593
9897
|
const { countPendingReviews: countPendingReviews2, countNewPendingReviewsSince: countNewPendingReviewsSince2 } = await Promise.resolve().then(() => (init_tasks_review(), tasks_review_exports));
|
|
9594
9898
|
const sessionKey = getSessionKey();
|
|
9595
|
-
const lastCheckPath =
|
|
9899
|
+
const lastCheckPath = path24.join(CACHE_DIR3, `review-lastcheck-${sessionKey}.json`);
|
|
9596
9900
|
let sessionScope;
|
|
9597
9901
|
try {
|
|
9598
9902
|
const { execSync: execSync9 } = await import("child_process");
|
|
@@ -9602,7 +9906,7 @@ ${fresh.map(
|
|
|
9602
9906
|
}
|
|
9603
9907
|
let lastCheckedAt = "";
|
|
9604
9908
|
try {
|
|
9605
|
-
lastCheckedAt =
|
|
9909
|
+
lastCheckedAt = readFileSync16(lastCheckPath, "utf8").trim();
|
|
9606
9910
|
} catch {
|
|
9607
9911
|
}
|
|
9608
9912
|
const totalCount = await countPendingReviews2(sessionScope);
|
|
@@ -9637,12 +9941,12 @@ IMPORTANT: After completing your current task, you MUST address the pending revi
|
|
|
9637
9941
|
}
|
|
9638
9942
|
}
|
|
9639
9943
|
const { writeFileSync: wf, mkdirSync: md } = await import("fs");
|
|
9640
|
-
md(
|
|
9944
|
+
md(CACHE_DIR3, { recursive: true });
|
|
9641
9945
|
wf(lastCheckPath, (/* @__PURE__ */ new Date()).toISOString());
|
|
9642
9946
|
} catch {
|
|
9643
9947
|
}
|
|
9644
9948
|
}
|
|
9645
|
-
const combined = [memoryContext, reviewContext].filter(Boolean).join("\n");
|
|
9949
|
+
const combined = [cacheContext, memoryContext, reviewContext].filter(Boolean).join("\n\n");
|
|
9646
9950
|
if (combined.length > 0) {
|
|
9647
9951
|
const output = JSON.stringify({
|
|
9648
9952
|
hookSpecificOutput: {
|
|
@@ -9660,11 +9964,11 @@ IMPORTANT: After completing your current task, you MUST address the pending revi
|
|
|
9660
9964
|
function spawnPromptWorker(prompt, sessionId, agent) {
|
|
9661
9965
|
if (!loadConfigSync().autoIngestion) return;
|
|
9662
9966
|
try {
|
|
9663
|
-
const workerPath =
|
|
9664
|
-
|
|
9967
|
+
const workerPath = path24.resolve(
|
|
9968
|
+
path24.dirname(fileURLToPath3(import.meta.url)),
|
|
9665
9969
|
"prompt-ingest-worker.js"
|
|
9666
9970
|
);
|
|
9667
|
-
if (!
|
|
9971
|
+
if (!existsSync20(workerPath)) {
|
|
9668
9972
|
process.stderr.write(`[prompt-submit] WARN: prompt-ingest-worker not found at ${workerPath}
|
|
9669
9973
|
`);
|
|
9670
9974
|
return;
|