@askexenow/exe-os 0.9.65 → 0.9.67
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/deploy/stack-manifests/v0.9.json +54 -5
- package/dist/bin/age-ontology-load.js +61 -0
- package/dist/bin/agentic-ontology-backfill.js +4708 -0
- package/dist/bin/agentic-reflection-backfill.js +4144 -0
- package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1532 -2173
- package/dist/bin/backfill-conversations.js +528 -20
- package/dist/bin/backfill-responses.js +528 -20
- package/dist/bin/backfill-vectors.js +255 -20
- package/dist/bin/bulk-sync-postgres.js +4876 -0
- package/dist/bin/cleanup-stale-review-tasks.js +529 -21
- package/dist/bin/cli.js +3471 -1491
- package/dist/bin/exe-agent-config.js +4 -0
- package/dist/bin/exe-agent.js +16 -0
- package/dist/bin/exe-assign.js +528 -20
- package/dist/bin/exe-boot.js +492 -54
- package/dist/bin/exe-call.js +16 -0
- package/dist/bin/exe-cloud.js +7415 -518
- package/dist/bin/exe-dispatch.js +540 -22
- package/dist/bin/exe-doctor.js +3404 -1225
- package/dist/bin/exe-export-behaviors.js +542 -24
- package/dist/bin/exe-forget.js +529 -21
- package/dist/bin/exe-gateway.js +595 -25
- package/dist/bin/exe-heartbeat.js +541 -24
- package/dist/bin/exe-kill.js +529 -21
- package/dist/bin/exe-launch-agent.js +2334 -1067
- package/dist/bin/exe-new-employee.js +324 -166
- package/dist/bin/exe-pending-messages.js +529 -21
- package/dist/bin/exe-pending-notifications.js +529 -21
- package/dist/bin/exe-pending-reviews.js +529 -21
- package/dist/bin/exe-rename.js +529 -21
- package/dist/bin/exe-review.js +529 -21
- package/dist/bin/exe-search.js +542 -24
- package/dist/bin/exe-session-cleanup.js +540 -22
- package/dist/bin/exe-settings.js +14 -0
- package/dist/bin/exe-start-codex.js +817 -144
- package/dist/bin/exe-start-opencode.js +776 -80
- package/dist/bin/exe-status.js +529 -21
- package/dist/bin/exe-team.js +529 -21
- package/dist/bin/git-sweep.js +540 -22
- package/dist/bin/graph-backfill.js +580 -21
- package/dist/bin/graph-export.js +529 -21
- package/dist/bin/graph-layer-benchmark.js +109 -0
- package/dist/bin/install.js +420 -289
- package/dist/bin/intercom-check.js +540 -22
- package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
- package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
- package/dist/bin/scan-tasks.js +540 -22
- package/dist/bin/setup.js +790 -206
- package/dist/bin/shard-migrate.js +528 -20
- package/dist/bin/update.js +4 -0
- package/dist/gateway/index.js +593 -23
- package/dist/hooks/bug-report-worker.js +651 -64
- package/dist/hooks/codex-stop-task-finalizer.js +540 -22
- package/dist/hooks/commit-complete.js +540 -22
- package/dist/hooks/error-recall.js +542 -24
- package/dist/hooks/exe-heartbeat-hook.js +4 -0
- package/dist/hooks/ingest-worker.js +4 -0
- package/dist/hooks/ingest.js +539 -22
- package/dist/hooks/instructions-loaded.js +529 -21
- package/dist/hooks/notification.js +529 -21
- package/dist/hooks/post-compact.js +529 -21
- package/dist/hooks/post-tool-combined.js +543 -25
- package/dist/hooks/pre-compact.js +772 -127
- package/dist/hooks/pre-tool-use.js +529 -21
- package/dist/hooks/prompt-submit.js +543 -25
- package/dist/hooks/session-end.js +673 -140
- package/dist/hooks/session-start.js +662 -26
- package/dist/hooks/stop.js +540 -23
- package/dist/hooks/subagent-stop.js +529 -21
- package/dist/hooks/summary-worker.js +571 -126
- package/dist/index.js +593 -23
- package/dist/lib/agent-config.js +4 -0
- package/dist/lib/cloud-sync.js +408 -47
- package/dist/lib/config.js +25 -1
- package/dist/lib/consolidation.js +5 -1
- package/dist/lib/database.js +128 -0
- package/dist/lib/db-daemon-client.js +4 -0
- package/dist/lib/db.js +128 -0
- package/dist/lib/device-registry.js +128 -0
- package/dist/lib/embedder.js +25 -1
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +4 -0
- package/dist/lib/exe-daemon-client.js +4 -0
- package/dist/lib/exe-daemon.js +3158 -930
- package/dist/lib/hybrid-search.js +542 -24
- package/dist/lib/identity.js +7 -0
- package/dist/lib/keychain.js +178 -22
- package/dist/lib/license.js +4 -0
- package/dist/lib/messaging.js +7 -0
- package/dist/lib/reminders.js +7 -0
- package/dist/lib/schedules.js +255 -20
- package/dist/lib/skill-learning.js +28 -1
- package/dist/lib/status-brief.js +39 -0
- package/dist/lib/store.js +528 -20
- package/dist/lib/task-router.js +4 -0
- package/dist/lib/tasks.js +28 -1
- package/dist/lib/tmux-routing.js +28 -1
- package/dist/lib/token-spend.js +7 -0
- package/dist/mcp/server.js +2739 -813
- package/dist/mcp/tools/complete-reminder.js +7 -0
- package/dist/mcp/tools/create-reminder.js +7 -0
- package/dist/mcp/tools/create-task.js +28 -1
- package/dist/mcp/tools/deactivate-behavior.js +7 -0
- package/dist/mcp/tools/list-reminders.js +7 -0
- package/dist/mcp/tools/list-tasks.js +7 -0
- package/dist/mcp/tools/send-message.js +7 -0
- package/dist/mcp/tools/update-task.js +28 -1
- package/dist/runtime/index.js +540 -22
- package/dist/tui/App.js +618 -29
- package/package.json +9 -5
- package/src/commands/exe/cloud.md +11 -8
- package/stack.release.json +3 -3
- package/src/commands/exe/link.md +0 -17
|
@@ -214,6 +214,11 @@ function normalizeAutoUpdate(raw) {
|
|
|
214
214
|
const userAU = raw.autoUpdate ?? {};
|
|
215
215
|
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
216
216
|
}
|
|
217
|
+
function normalizeOrchestration(raw) {
|
|
218
|
+
const defaultOrg = DEFAULT_CONFIG.orchestration;
|
|
219
|
+
const userOrg = raw.orchestration ?? {};
|
|
220
|
+
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
221
|
+
}
|
|
217
222
|
async function loadConfig() {
|
|
218
223
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
219
224
|
await ensurePrivateDir(dir);
|
|
@@ -238,10 +243,15 @@ async function loadConfig() {
|
|
|
238
243
|
normalizeScalingRoadmap(migratedCfg);
|
|
239
244
|
normalizeSessionLifecycle(migratedCfg);
|
|
240
245
|
normalizeAutoUpdate(migratedCfg);
|
|
246
|
+
normalizeOrchestration(migratedCfg);
|
|
241
247
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
242
248
|
if (config.dbPath.startsWith("~")) {
|
|
243
249
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
244
250
|
}
|
|
251
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
252
|
+
if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
|
|
253
|
+
config.dbPath = envDbPath;
|
|
254
|
+
}
|
|
245
255
|
return config;
|
|
246
256
|
} catch {
|
|
247
257
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
@@ -261,7 +271,16 @@ function loadConfigSync() {
|
|
|
261
271
|
normalizeScalingRoadmap(migratedCfg);
|
|
262
272
|
normalizeSessionLifecycle(migratedCfg);
|
|
263
273
|
normalizeAutoUpdate(migratedCfg);
|
|
264
|
-
|
|
274
|
+
normalizeOrchestration(migratedCfg);
|
|
275
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
276
|
+
if (config.dbPath.startsWith("~")) {
|
|
277
|
+
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
278
|
+
}
|
|
279
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
280
|
+
if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
|
|
281
|
+
config.dbPath = envDbPath;
|
|
282
|
+
}
|
|
283
|
+
return config;
|
|
265
284
|
} catch {
|
|
266
285
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
267
286
|
}
|
|
@@ -282,6 +301,7 @@ async function loadConfigFrom(configPath) {
|
|
|
282
301
|
normalizeScalingRoadmap(migratedCfg);
|
|
283
302
|
normalizeSessionLifecycle(migratedCfg);
|
|
284
303
|
normalizeAutoUpdate(migratedCfg);
|
|
304
|
+
normalizeOrchestration(migratedCfg);
|
|
285
305
|
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
286
306
|
} catch {
|
|
287
307
|
return { ...DEFAULT_CONFIG };
|
|
@@ -353,6 +373,10 @@ var init_config = __esm({
|
|
|
353
373
|
checkOnBoot: true,
|
|
354
374
|
autoInstall: false,
|
|
355
375
|
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
376
|
+
},
|
|
377
|
+
orchestration: {
|
|
378
|
+
phase: "phase_1_coo",
|
|
379
|
+
phaseSetBy: "default"
|
|
356
380
|
}
|
|
357
381
|
};
|
|
358
382
|
CONFIG_MIGRATIONS = [
|
|
@@ -1050,6 +1074,17 @@ var init_daemon_auth = __esm({
|
|
|
1050
1074
|
});
|
|
1051
1075
|
|
|
1052
1076
|
// src/lib/exe-daemon-client.ts
|
|
1077
|
+
var exe_daemon_client_exports = {};
|
|
1078
|
+
__export(exe_daemon_client_exports, {
|
|
1079
|
+
connectEmbedDaemon: () => connectEmbedDaemon,
|
|
1080
|
+
disconnectClient: () => disconnectClient,
|
|
1081
|
+
embedBatchViaClient: () => embedBatchViaClient,
|
|
1082
|
+
embedViaClient: () => embedViaClient,
|
|
1083
|
+
isClientConnected: () => isClientConnected,
|
|
1084
|
+
pingDaemon: () => pingDaemon,
|
|
1085
|
+
sendDaemonRequest: () => sendDaemonRequest,
|
|
1086
|
+
sendIngestRequest: () => sendIngestRequest
|
|
1087
|
+
});
|
|
1053
1088
|
import net from "net";
|
|
1054
1089
|
import os4 from "os";
|
|
1055
1090
|
import { spawn } from "child_process";
|
|
@@ -1286,6 +1321,9 @@ async function connectEmbedDaemon() {
|
|
|
1286
1321
|
}
|
|
1287
1322
|
return false;
|
|
1288
1323
|
}
|
|
1324
|
+
function sendRequest(texts, priority) {
|
|
1325
|
+
return sendDaemonRequest({ texts, priority });
|
|
1326
|
+
}
|
|
1289
1327
|
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
1290
1328
|
return new Promise((resolve) => {
|
|
1291
1329
|
if (!_socket || !_connected) {
|
|
@@ -1308,10 +1346,170 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1308
1346
|
}
|
|
1309
1347
|
});
|
|
1310
1348
|
}
|
|
1349
|
+
async function pingDaemon() {
|
|
1350
|
+
if (!_socket || !_connected) return null;
|
|
1351
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
1352
|
+
if (response.health) {
|
|
1353
|
+
return response.health;
|
|
1354
|
+
}
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
function killAndRespawnDaemon() {
|
|
1358
|
+
if (!acquireSpawnLock()) {
|
|
1359
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
1360
|
+
if (_socket) {
|
|
1361
|
+
_socket.destroy();
|
|
1362
|
+
_socket = null;
|
|
1363
|
+
}
|
|
1364
|
+
_connected = false;
|
|
1365
|
+
_buffer = "";
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
try {
|
|
1369
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1370
|
+
if (existsSync5(PID_PATH)) {
|
|
1371
|
+
try {
|
|
1372
|
+
const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
|
|
1373
|
+
if (pid > 0) {
|
|
1374
|
+
try {
|
|
1375
|
+
process.kill(pid, "SIGKILL");
|
|
1376
|
+
} catch {
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
} catch {
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
if (_socket) {
|
|
1383
|
+
_socket.destroy();
|
|
1384
|
+
_socket = null;
|
|
1385
|
+
}
|
|
1386
|
+
_connected = false;
|
|
1387
|
+
_buffer = "";
|
|
1388
|
+
try {
|
|
1389
|
+
unlinkSync2(PID_PATH);
|
|
1390
|
+
} catch {
|
|
1391
|
+
}
|
|
1392
|
+
try {
|
|
1393
|
+
unlinkSync2(SOCKET_PATH);
|
|
1394
|
+
} catch {
|
|
1395
|
+
}
|
|
1396
|
+
spawnDaemon();
|
|
1397
|
+
} finally {
|
|
1398
|
+
releaseSpawnLock();
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function isDaemonTooYoung() {
|
|
1402
|
+
try {
|
|
1403
|
+
const stat = statSync(PID_PATH);
|
|
1404
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
1405
|
+
} catch {
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
async function retryThenRestart(doRequest, label) {
|
|
1410
|
+
const result = await doRequest();
|
|
1411
|
+
if (!result.error) {
|
|
1412
|
+
_consecutiveFailures = 0;
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
_consecutiveFailures++;
|
|
1416
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
1417
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
1418
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
1419
|
+
`);
|
|
1420
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1421
|
+
if (!_connected) {
|
|
1422
|
+
if (!await connectToSocket()) continue;
|
|
1423
|
+
}
|
|
1424
|
+
const retry = await doRequest();
|
|
1425
|
+
if (!retry.error) {
|
|
1426
|
+
_consecutiveFailures = 0;
|
|
1427
|
+
return retry;
|
|
1428
|
+
}
|
|
1429
|
+
_consecutiveFailures++;
|
|
1430
|
+
}
|
|
1431
|
+
if (isDaemonTooYoung()) {
|
|
1432
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
1433
|
+
`);
|
|
1434
|
+
return { error: result.error };
|
|
1435
|
+
}
|
|
1436
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
1437
|
+
`);
|
|
1438
|
+
killAndRespawnDaemon();
|
|
1439
|
+
const start = Date.now();
|
|
1440
|
+
let delay2 = 200;
|
|
1441
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1442
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1443
|
+
if (await connectToSocket()) break;
|
|
1444
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1445
|
+
}
|
|
1446
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
1447
|
+
const final = await doRequest();
|
|
1448
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
1449
|
+
return final;
|
|
1450
|
+
}
|
|
1451
|
+
async function embedViaClient(text, priority = "high") {
|
|
1452
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1453
|
+
_requestCount++;
|
|
1454
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
1455
|
+
const health = await pingDaemon();
|
|
1456
|
+
if (!health && !isDaemonTooYoung()) {
|
|
1457
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
1458
|
+
`);
|
|
1459
|
+
killAndRespawnDaemon();
|
|
1460
|
+
const start = Date.now();
|
|
1461
|
+
let d = 200;
|
|
1462
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1463
|
+
await new Promise((r) => setTimeout(r, d));
|
|
1464
|
+
if (await connectToSocket()) break;
|
|
1465
|
+
d = Math.min(d * 2, 3e3);
|
|
1466
|
+
}
|
|
1467
|
+
if (!_connected) return null;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const result = await retryThenRestart(
|
|
1471
|
+
() => sendRequest([text], priority),
|
|
1472
|
+
"Embed"
|
|
1473
|
+
);
|
|
1474
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
1475
|
+
}
|
|
1476
|
+
async function embedBatchViaClient(texts, priority = "high") {
|
|
1477
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
1478
|
+
_requestCount++;
|
|
1479
|
+
const result = await retryThenRestart(
|
|
1480
|
+
() => sendRequest(texts, priority),
|
|
1481
|
+
"Batch embed"
|
|
1482
|
+
);
|
|
1483
|
+
return !result.error && result.vectors ? result.vectors : null;
|
|
1484
|
+
}
|
|
1485
|
+
function disconnectClient() {
|
|
1486
|
+
if (_socket) {
|
|
1487
|
+
_socket.destroy();
|
|
1488
|
+
_socket = null;
|
|
1489
|
+
}
|
|
1490
|
+
_connected = false;
|
|
1491
|
+
_buffer = "";
|
|
1492
|
+
for (const [id, entry] of _pending) {
|
|
1493
|
+
clearTimeout(entry.timer);
|
|
1494
|
+
_pending.delete(id);
|
|
1495
|
+
entry.resolve({ error: "Client disconnected" });
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1311
1498
|
function isClientConnected() {
|
|
1312
1499
|
return _connected;
|
|
1313
1500
|
}
|
|
1314
|
-
|
|
1501
|
+
function sendIngestRequest(payload) {
|
|
1502
|
+
if (!_socket || !_connected) return false;
|
|
1503
|
+
try {
|
|
1504
|
+
const id = randomUUID();
|
|
1505
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1506
|
+
_socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
|
|
1507
|
+
return true;
|
|
1508
|
+
} catch {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
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;
|
|
1315
1513
|
var init_exe_daemon_client = __esm({
|
|
1316
1514
|
"src/lib/exe-daemon-client.ts"() {
|
|
1317
1515
|
"use strict";
|
|
@@ -1327,12 +1525,27 @@ var init_exe_daemon_client = __esm({
|
|
|
1327
1525
|
_socket = null;
|
|
1328
1526
|
_connected = false;
|
|
1329
1527
|
_buffer = "";
|
|
1528
|
+
_requestCount = 0;
|
|
1529
|
+
_consecutiveFailures = 0;
|
|
1530
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
1531
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
1532
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
1533
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
1330
1534
|
_pending = /* @__PURE__ */ new Map();
|
|
1331
1535
|
MAX_BUFFER = 1e7;
|
|
1332
1536
|
}
|
|
1333
1537
|
});
|
|
1334
1538
|
|
|
1335
1539
|
// src/lib/daemon-protocol.ts
|
|
1540
|
+
var daemon_protocol_exports = {};
|
|
1541
|
+
__export(daemon_protocol_exports, {
|
|
1542
|
+
deserializeArgs: () => deserializeArgs,
|
|
1543
|
+
deserializeResultSet: () => deserializeResultSet,
|
|
1544
|
+
deserializeValue: () => deserializeValue,
|
|
1545
|
+
serializeArgs: () => serializeArgs,
|
|
1546
|
+
serializeResultSet: () => serializeResultSet,
|
|
1547
|
+
serializeValue: () => serializeValue
|
|
1548
|
+
});
|
|
1336
1549
|
function serializeValue(v) {
|
|
1337
1550
|
if (v === null || v === void 0) return null;
|
|
1338
1551
|
if (typeof v === "bigint") return Number(v);
|
|
@@ -1357,6 +1570,32 @@ function deserializeValue(v) {
|
|
|
1357
1570
|
}
|
|
1358
1571
|
return v;
|
|
1359
1572
|
}
|
|
1573
|
+
function serializeArgs(args) {
|
|
1574
|
+
return args.map(serializeValue);
|
|
1575
|
+
}
|
|
1576
|
+
function deserializeArgs(args) {
|
|
1577
|
+
return args.map(deserializeValue);
|
|
1578
|
+
}
|
|
1579
|
+
function serializeResultSet(rs) {
|
|
1580
|
+
const rows = [];
|
|
1581
|
+
for (const row of rs.rows) {
|
|
1582
|
+
const obj = {};
|
|
1583
|
+
for (let i = 0; i < rs.columns.length; i++) {
|
|
1584
|
+
const col = rs.columns[i];
|
|
1585
|
+
if (col !== void 0) {
|
|
1586
|
+
obj[col] = serializeValue(row[i]);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
rows.push(obj);
|
|
1590
|
+
}
|
|
1591
|
+
return {
|
|
1592
|
+
columns: [...rs.columns],
|
|
1593
|
+
columnTypes: [...rs.columnTypes ?? []],
|
|
1594
|
+
rows,
|
|
1595
|
+
rowsAffected: typeof rs.rowsAffected === "bigint" ? Number(rs.rowsAffected) : rs.rowsAffected ?? 0,
|
|
1596
|
+
lastInsertRowid: rs.lastInsertRowid != null ? typeof rs.lastInsertRowid === "bigint" ? Number(rs.lastInsertRowid) : rs.lastInsertRowid : null
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1360
1599
|
function deserializeResultSet(srs) {
|
|
1361
1600
|
const rows = srs.rows.map((obj) => {
|
|
1362
1601
|
const values = srs.columns.map(
|
|
@@ -1607,6 +1846,9 @@ function getClient() {
|
|
|
1607
1846
|
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1608
1847
|
return _daemonClient;
|
|
1609
1848
|
}
|
|
1849
|
+
if (!_resilientClient) {
|
|
1850
|
+
return _adapterClient;
|
|
1851
|
+
}
|
|
1610
1852
|
return _resilientClient;
|
|
1611
1853
|
}
|
|
1612
1854
|
async function initDaemonClient() {
|
|
@@ -2639,6 +2881,127 @@ async function ensureSchema() {
|
|
|
2639
2881
|
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
2640
2882
|
END;
|
|
2641
2883
|
`);
|
|
2884
|
+
await client.executeMultiple(`
|
|
2885
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
2886
|
+
id TEXT PRIMARY KEY,
|
|
2887
|
+
agent_id TEXT NOT NULL,
|
|
2888
|
+
project_name TEXT,
|
|
2889
|
+
started_at TEXT NOT NULL,
|
|
2890
|
+
last_event_at TEXT NOT NULL,
|
|
2891
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2892
|
+
properties TEXT DEFAULT '{}'
|
|
2893
|
+
);
|
|
2894
|
+
|
|
2895
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
|
|
2896
|
+
ON agent_sessions(agent_id, started_at);
|
|
2897
|
+
|
|
2898
|
+
CREATE TABLE IF NOT EXISTS agent_goals (
|
|
2899
|
+
id TEXT PRIMARY KEY,
|
|
2900
|
+
statement TEXT NOT NULL,
|
|
2901
|
+
owner_agent_id TEXT,
|
|
2902
|
+
project_name TEXT,
|
|
2903
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
2904
|
+
priority INTEGER NOT NULL DEFAULT 5,
|
|
2905
|
+
success_criteria TEXT,
|
|
2906
|
+
parent_goal_id TEXT,
|
|
2907
|
+
due_at TEXT,
|
|
2908
|
+
achieved_at TEXT,
|
|
2909
|
+
supersedes_id TEXT,
|
|
2910
|
+
created_at TEXT NOT NULL,
|
|
2911
|
+
updated_at TEXT NOT NULL,
|
|
2912
|
+
source_memory_id TEXT
|
|
2913
|
+
);
|
|
2914
|
+
|
|
2915
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
|
|
2916
|
+
ON agent_goals(project_name, status, priority);
|
|
2917
|
+
|
|
2918
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
2919
|
+
id TEXT PRIMARY KEY,
|
|
2920
|
+
event_type TEXT NOT NULL,
|
|
2921
|
+
occurred_at TEXT NOT NULL,
|
|
2922
|
+
sequence_index INTEGER NOT NULL,
|
|
2923
|
+
actor_agent_id TEXT,
|
|
2924
|
+
agent_role TEXT,
|
|
2925
|
+
project_name TEXT,
|
|
2926
|
+
session_id TEXT,
|
|
2927
|
+
task_id TEXT,
|
|
2928
|
+
goal_id TEXT,
|
|
2929
|
+
parent_event_id TEXT,
|
|
2930
|
+
intention TEXT,
|
|
2931
|
+
outcome TEXT,
|
|
2932
|
+
evidence_memory_id TEXT,
|
|
2933
|
+
impact TEXT,
|
|
2934
|
+
payload TEXT DEFAULT '{}',
|
|
2935
|
+
created_at TEXT NOT NULL
|
|
2936
|
+
);
|
|
2937
|
+
|
|
2938
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_time
|
|
2939
|
+
ON agent_events(occurred_at, sequence_index);
|
|
2940
|
+
|
|
2941
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
|
|
2942
|
+
ON agent_events(session_id, sequence_index);
|
|
2943
|
+
|
|
2944
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
|
|
2945
|
+
ON agent_events(goal_id, occurred_at);
|
|
2946
|
+
|
|
2947
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_memory
|
|
2948
|
+
ON agent_events(evidence_memory_id);
|
|
2949
|
+
|
|
2950
|
+
CREATE TABLE IF NOT EXISTS agent_goal_links (
|
|
2951
|
+
id TEXT PRIMARY KEY,
|
|
2952
|
+
goal_id TEXT NOT NULL,
|
|
2953
|
+
link_type TEXT NOT NULL,
|
|
2954
|
+
target_id TEXT NOT NULL,
|
|
2955
|
+
target_type TEXT NOT NULL,
|
|
2956
|
+
created_at TEXT NOT NULL
|
|
2957
|
+
);
|
|
2958
|
+
|
|
2959
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
|
|
2960
|
+
ON agent_goal_links(goal_id, target_type);
|
|
2961
|
+
|
|
2962
|
+
CREATE TABLE IF NOT EXISTS agent_semantic_labels (
|
|
2963
|
+
id TEXT PRIMARY KEY,
|
|
2964
|
+
source_memory_id TEXT NOT NULL,
|
|
2965
|
+
event_id TEXT,
|
|
2966
|
+
labeler TEXT NOT NULL,
|
|
2967
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
2968
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2969
|
+
labels TEXT NOT NULL,
|
|
2970
|
+
created_at TEXT NOT NULL,
|
|
2971
|
+
updated_at TEXT NOT NULL
|
|
2972
|
+
);
|
|
2973
|
+
|
|
2974
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
|
|
2975
|
+
ON agent_semantic_labels(source_memory_id, labeler);
|
|
2976
|
+
|
|
2977
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
|
|
2978
|
+
ON agent_semantic_labels(event_id);
|
|
2979
|
+
|
|
2980
|
+
CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
|
|
2981
|
+
id TEXT PRIMARY KEY,
|
|
2982
|
+
project_name TEXT,
|
|
2983
|
+
session_id TEXT,
|
|
2984
|
+
window_start_at TEXT NOT NULL,
|
|
2985
|
+
window_end_at TEXT NOT NULL,
|
|
2986
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2987
|
+
goal_count INTEGER NOT NULL DEFAULT 0,
|
|
2988
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
2989
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
2990
|
+
risk_count INTEGER NOT NULL DEFAULT 0,
|
|
2991
|
+
summary TEXT NOT NULL,
|
|
2992
|
+
learnings TEXT NOT NULL DEFAULT '[]',
|
|
2993
|
+
next_actions TEXT NOT NULL DEFAULT '[]',
|
|
2994
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2995
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2996
|
+
created_at TEXT NOT NULL
|
|
2997
|
+
);
|
|
2998
|
+
|
|
2999
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
|
|
3000
|
+
ON agent_reflection_checkpoints(project_name, window_end_at);
|
|
3001
|
+
|
|
3002
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
|
|
3003
|
+
ON agent_reflection_checkpoints(session_id, window_end_at);
|
|
3004
|
+
`);
|
|
2642
3005
|
try {
|
|
2643
3006
|
await client.execute({
|
|
2644
3007
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -2786,270 +3149,827 @@ var init_database = __esm({
|
|
|
2786
3149
|
}
|
|
2787
3150
|
});
|
|
2788
3151
|
|
|
2789
|
-
// src/lib/
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
getShardClient: () => getShardClient,
|
|
2798
|
-
getShardsDir: () => getShardsDir,
|
|
2799
|
-
initShardManager: () => initShardManager,
|
|
2800
|
-
isShardingEnabled: () => isShardingEnabled,
|
|
2801
|
-
listShards: () => listShards,
|
|
2802
|
-
shardExists: () => shardExists
|
|
2803
|
-
});
|
|
2804
|
-
import path7 from "path";
|
|
2805
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync2 } from "fs";
|
|
2806
|
-
import { createClient as createClient2 } from "@libsql/client";
|
|
2807
|
-
function initShardManager(encryptionKey) {
|
|
2808
|
-
_encryptionKey = encryptionKey;
|
|
2809
|
-
if (!existsSync7(SHARDS_DIR)) {
|
|
2810
|
-
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2811
|
-
}
|
|
2812
|
-
_shardingEnabled = true;
|
|
2813
|
-
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
2814
|
-
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
2815
|
-
_evictionTimer.unref();
|
|
3152
|
+
// src/lib/keychain.ts
|
|
3153
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3154
|
+
import { existsSync as existsSync6, statSync as statSync2 } from "fs";
|
|
3155
|
+
import { execSync as execSync2 } from "child_process";
|
|
3156
|
+
import path6 from "path";
|
|
3157
|
+
import os5 from "os";
|
|
3158
|
+
function getKeyDir() {
|
|
3159
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
2816
3160
|
}
|
|
2817
|
-
function
|
|
2818
|
-
return
|
|
3161
|
+
function getKeyPath() {
|
|
3162
|
+
return path6.join(getKeyDir(), "master.key");
|
|
2819
3163
|
}
|
|
2820
|
-
function
|
|
2821
|
-
return
|
|
3164
|
+
function nativeKeychainAllowed() {
|
|
3165
|
+
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
2822
3166
|
}
|
|
2823
|
-
function
|
|
2824
|
-
if (!
|
|
2825
|
-
|
|
3167
|
+
function linuxSecretAvailable() {
|
|
3168
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3169
|
+
if (process.platform !== "linux") return false;
|
|
3170
|
+
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
3171
|
+
try {
|
|
3172
|
+
execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
3173
|
+
} catch {
|
|
3174
|
+
linuxSecretAvailability = false;
|
|
3175
|
+
return false;
|
|
2826
3176
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3177
|
+
try {
|
|
3178
|
+
execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
3179
|
+
linuxSecretAvailability = true;
|
|
3180
|
+
} catch {
|
|
3181
|
+
linuxSecretAvailability = false;
|
|
2830
3182
|
}
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3183
|
+
return linuxSecretAvailability;
|
|
3184
|
+
}
|
|
3185
|
+
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3186
|
+
if (process.platform !== "linux") return false;
|
|
3187
|
+
try {
|
|
3188
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3189
|
+
const st = statSync2(keyPath);
|
|
3190
|
+
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3191
|
+
if (uid === 0) return true;
|
|
3192
|
+
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3193
|
+
return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
|
|
3194
|
+
} catch {
|
|
3195
|
+
return false;
|
|
2835
3196
|
}
|
|
2836
|
-
|
|
2837
|
-
|
|
3197
|
+
}
|
|
3198
|
+
function macKeychainGet(service = SERVICE) {
|
|
3199
|
+
if (!nativeKeychainAllowed()) return null;
|
|
3200
|
+
if (process.platform !== "darwin") return null;
|
|
3201
|
+
try {
|
|
3202
|
+
return execSync2(
|
|
3203
|
+
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3204
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3205
|
+
).trim();
|
|
3206
|
+
} catch {
|
|
3207
|
+
return null;
|
|
2838
3208
|
}
|
|
2839
|
-
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
2840
|
-
const client = createClient2({
|
|
2841
|
-
url: `file:${dbPath}`,
|
|
2842
|
-
encryptionKey: _encryptionKey
|
|
2843
|
-
});
|
|
2844
|
-
_shards.set(safeName, client);
|
|
2845
|
-
_shardLastAccess.set(safeName, Date.now());
|
|
2846
|
-
return client;
|
|
2847
3209
|
}
|
|
2848
|
-
function
|
|
2849
|
-
|
|
2850
|
-
|
|
3210
|
+
function macKeychainSet(value, service = SERVICE) {
|
|
3211
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3212
|
+
if (process.platform !== "darwin") return false;
|
|
3213
|
+
try {
|
|
3214
|
+
try {
|
|
3215
|
+
execSync2(
|
|
3216
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3217
|
+
{ timeout: 5e3 }
|
|
3218
|
+
);
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
execSync2(
|
|
3222
|
+
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3223
|
+
{ timeout: 5e3 }
|
|
3224
|
+
);
|
|
3225
|
+
return true;
|
|
3226
|
+
} catch {
|
|
3227
|
+
return false;
|
|
3228
|
+
}
|
|
2851
3229
|
}
|
|
2852
|
-
function
|
|
2853
|
-
|
|
3230
|
+
function macKeychainDelete(service = SERVICE) {
|
|
3231
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3232
|
+
if (process.platform !== "darwin") return false;
|
|
3233
|
+
try {
|
|
3234
|
+
execSync2(
|
|
3235
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3236
|
+
{ timeout: 5e3 }
|
|
3237
|
+
);
|
|
3238
|
+
return true;
|
|
3239
|
+
} catch {
|
|
3240
|
+
return false;
|
|
3241
|
+
}
|
|
2854
3242
|
}
|
|
2855
|
-
function
|
|
2856
|
-
if (!
|
|
2857
|
-
|
|
3243
|
+
function linuxSecretGet(service = SERVICE) {
|
|
3244
|
+
if (!linuxSecretAvailable()) return null;
|
|
3245
|
+
try {
|
|
3246
|
+
return execSync2(
|
|
3247
|
+
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3248
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
3249
|
+
).trim();
|
|
3250
|
+
} catch {
|
|
3251
|
+
return null;
|
|
3252
|
+
}
|
|
2858
3253
|
}
|
|
2859
|
-
|
|
2860
|
-
if (!
|
|
2861
|
-
|
|
3254
|
+
function linuxSecretSet(value, service = SERVICE) {
|
|
3255
|
+
if (!linuxSecretAvailable()) return false;
|
|
3256
|
+
try {
|
|
3257
|
+
execSync2(
|
|
3258
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3259
|
+
{ timeout: 5e3 }
|
|
3260
|
+
);
|
|
3261
|
+
return true;
|
|
3262
|
+
} catch {
|
|
3263
|
+
return false;
|
|
2862
3264
|
}
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
error: null,
|
|
2876
|
-
size: stat.size,
|
|
2877
|
-
mtime: stat.mtime.toISOString(),
|
|
2878
|
-
memoryCount: null
|
|
2879
|
-
};
|
|
2880
|
-
const client = createClient2({
|
|
2881
|
-
url: `file:${dbPath}`,
|
|
2882
|
-
encryptionKey: _encryptionKey
|
|
2883
|
-
});
|
|
2884
|
-
try {
|
|
2885
|
-
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
2886
|
-
const hasMemories = await client.execute(
|
|
2887
|
-
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
2888
|
-
);
|
|
2889
|
-
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
2890
|
-
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
2891
|
-
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
2892
|
-
}
|
|
2893
|
-
item.ok = true;
|
|
2894
|
-
} catch (err) {
|
|
2895
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2896
|
-
item.error = message;
|
|
2897
|
-
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
2898
|
-
if (item.unreadable && repair && !dryRun) {
|
|
2899
|
-
client.close();
|
|
2900
|
-
_shards.delete(name);
|
|
2901
|
-
_shardLastAccess.delete(name);
|
|
2902
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2903
|
-
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
2904
|
-
renameSync3(dbPath, archivedPath);
|
|
2905
|
-
item.archivedPath = archivedPath;
|
|
2906
|
-
}
|
|
2907
|
-
} finally {
|
|
2908
|
-
try {
|
|
2909
|
-
client.close();
|
|
2910
|
-
} catch {
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
shards.push(item);
|
|
3265
|
+
}
|
|
3266
|
+
function linuxSecretDelete(service = SERVICE) {
|
|
3267
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3268
|
+
if (process.platform !== "linux") return false;
|
|
3269
|
+
try {
|
|
3270
|
+
execSync2(
|
|
3271
|
+
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3272
|
+
{ timeout: 5e3 }
|
|
3273
|
+
);
|
|
3274
|
+
return true;
|
|
3275
|
+
} catch {
|
|
3276
|
+
return false;
|
|
2914
3277
|
}
|
|
2915
|
-
return {
|
|
2916
|
-
total: shards.length,
|
|
2917
|
-
ok: shards.filter((s) => s.ok).length,
|
|
2918
|
-
unreadable: shards.filter((s) => s.unreadable).length,
|
|
2919
|
-
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
2920
|
-
shards
|
|
2921
|
-
};
|
|
2922
3278
|
}
|
|
2923
|
-
async function
|
|
2924
|
-
|
|
2925
|
-
await client.execute("PRAGMA busy_timeout = 30000");
|
|
3279
|
+
async function tryKeytar() {
|
|
3280
|
+
if (!nativeKeychainAllowed()) return null;
|
|
2926
3281
|
try {
|
|
2927
|
-
await
|
|
3282
|
+
return await import("keytar");
|
|
2928
3283
|
} catch {
|
|
3284
|
+
return null;
|
|
2929
3285
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
CREATE INDEX IF NOT EXISTS idx_memories_agent ON memories(agent_id);
|
|
2946
|
-
CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);
|
|
2947
|
-
CREATE INDEX IF NOT EXISTS idx_memories_agent_project ON memories(agent_id, project_name);
|
|
2948
|
-
`);
|
|
2949
|
-
await client.executeMultiple(`
|
|
2950
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
2951
|
-
raw_text,
|
|
2952
|
-
content='memories',
|
|
2953
|
-
content_rowid='rowid'
|
|
2954
|
-
);
|
|
2955
|
-
|
|
2956
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_ai AFTER INSERT ON memories BEGIN
|
|
2957
|
-
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
2958
|
-
END;
|
|
2959
|
-
|
|
2960
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_ad AFTER DELETE ON memories BEGIN
|
|
2961
|
-
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
2962
|
-
END;
|
|
2963
|
-
|
|
2964
|
-
CREATE TRIGGER IF NOT EXISTS memories_fts_au AFTER UPDATE ON memories BEGIN
|
|
2965
|
-
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
2966
|
-
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
2967
|
-
END;
|
|
2968
|
-
`);
|
|
2969
|
-
for (const col of [
|
|
2970
|
-
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2971
|
-
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
2972
|
-
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
2973
|
-
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2974
|
-
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2975
|
-
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2976
|
-
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
2977
|
-
"ALTER TABLE memories ADD COLUMN graph_extracted INTEGER DEFAULT 0",
|
|
2978
|
-
"ALTER TABLE memories ADD COLUMN content_hash TEXT",
|
|
2979
|
-
"ALTER TABLE memories ADD COLUMN graph_extracted_hash TEXT",
|
|
2980
|
-
"ALTER TABLE memories ADD COLUMN confidence REAL DEFAULT 0.7",
|
|
2981
|
-
"ALTER TABLE memories ADD COLUMN last_accessed TEXT",
|
|
2982
|
-
// Wiki linkage columns (must match database.ts)
|
|
2983
|
-
"ALTER TABLE memories ADD COLUMN workspace_id TEXT",
|
|
2984
|
-
"ALTER TABLE memories ADD COLUMN document_id TEXT",
|
|
2985
|
-
"ALTER TABLE memories ADD COLUMN user_id TEXT",
|
|
2986
|
-
"ALTER TABLE memories ADD COLUMN char_offset INTEGER",
|
|
2987
|
-
"ALTER TABLE memories ADD COLUMN page_number INTEGER",
|
|
2988
|
-
// Source provenance columns (must match database.ts)
|
|
2989
|
-
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
2990
|
-
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
2991
|
-
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
2992
|
-
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
2993
|
-
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2994
|
-
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2995
|
-
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2996
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
2997
|
-
// Metadata enrichment columns (must match database.ts)
|
|
2998
|
-
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
2999
|
-
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3000
|
-
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3001
|
-
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3002
|
-
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3003
|
-
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3004
|
-
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3005
|
-
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3006
|
-
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3007
|
-
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3008
|
-
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3009
|
-
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3010
|
-
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3011
|
-
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3012
|
-
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
|
|
3013
|
-
"ALTER TABLE memories ADD COLUMN deleted_at TEXT"
|
|
3014
|
-
]) {
|
|
3015
|
-
try {
|
|
3016
|
-
await client.execute(col);
|
|
3017
|
-
} catch {
|
|
3018
|
-
}
|
|
3286
|
+
}
|
|
3287
|
+
function deriveMachineKey() {
|
|
3288
|
+
try {
|
|
3289
|
+
const crypto3 = __require("crypto");
|
|
3290
|
+
const material = [
|
|
3291
|
+
os5.hostname(),
|
|
3292
|
+
os5.userInfo().username,
|
|
3293
|
+
os5.arch(),
|
|
3294
|
+
os5.platform(),
|
|
3295
|
+
// Machine ID on Linux (stable across reboots)
|
|
3296
|
+
process.platform === "linux" ? readMachineId() : ""
|
|
3297
|
+
].join("|");
|
|
3298
|
+
return crypto3.createHash("sha256").update(material).digest();
|
|
3299
|
+
} catch {
|
|
3300
|
+
return null;
|
|
3019
3301
|
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
} catch {
|
|
3028
|
-
}
|
|
3302
|
+
}
|
|
3303
|
+
function readMachineId() {
|
|
3304
|
+
try {
|
|
3305
|
+
const { readFileSync: readFileSync7 } = __require("fs");
|
|
3306
|
+
return readFileSync7("/etc/machine-id", "utf-8").trim();
|
|
3307
|
+
} catch {
|
|
3308
|
+
return "";
|
|
3029
3309
|
}
|
|
3310
|
+
}
|
|
3311
|
+
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3312
|
+
const crypto3 = __require("crypto");
|
|
3313
|
+
const iv = crypto3.randomBytes(12);
|
|
3314
|
+
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3315
|
+
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3316
|
+
encrypted += cipher.final("base64");
|
|
3317
|
+
const authTag = cipher.getAuthTag().toString("base64");
|
|
3318
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3319
|
+
}
|
|
3320
|
+
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3321
|
+
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3030
3322
|
try {
|
|
3031
|
-
|
|
3323
|
+
const crypto3 = __require("crypto");
|
|
3324
|
+
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3325
|
+
if (parts.length !== 3) return null;
|
|
3326
|
+
const [ivB64, tagB64, cipherB64] = parts;
|
|
3327
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
3328
|
+
const authTag = Buffer.from(tagB64, "base64");
|
|
3329
|
+
const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3330
|
+
decipher.setAuthTag(authTag);
|
|
3331
|
+
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3332
|
+
decrypted += decipher.final("utf-8");
|
|
3333
|
+
return decrypted;
|
|
3032
3334
|
} catch {
|
|
3335
|
+
return null;
|
|
3033
3336
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3337
|
+
}
|
|
3338
|
+
async function writeMachineBoundFileFallback(b64) {
|
|
3339
|
+
const dir = getKeyDir();
|
|
3340
|
+
await mkdir3(dir, { recursive: true });
|
|
3341
|
+
const keyPath = getKeyPath();
|
|
3342
|
+
const machineKey = deriveMachineKey();
|
|
3343
|
+
if (machineKey) {
|
|
3344
|
+
const encrypted = encryptWithMachineKey(b64, machineKey);
|
|
3345
|
+
await writeFile3(keyPath, encrypted + "\n", "utf-8");
|
|
3346
|
+
await chmod2(keyPath, 384);
|
|
3347
|
+
return "encrypted";
|
|
3348
|
+
}
|
|
3349
|
+
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
3350
|
+
await chmod2(keyPath, 384);
|
|
3351
|
+
return "plaintext";
|
|
3352
|
+
}
|
|
3353
|
+
async function getMasterKey() {
|
|
3354
|
+
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3355
|
+
if (!nativeValue) {
|
|
3356
|
+
const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
|
|
3357
|
+
if (legacyValue) {
|
|
3358
|
+
const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
|
|
3359
|
+
if (migrated) {
|
|
3360
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3361
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3362
|
+
process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
|
|
3363
|
+
}
|
|
3364
|
+
nativeValue = legacyValue;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
if (nativeValue) {
|
|
3368
|
+
return Buffer.from(nativeValue, "base64");
|
|
3369
|
+
}
|
|
3370
|
+
const keytar = await tryKeytar();
|
|
3371
|
+
if (keytar) {
|
|
3039
3372
|
try {
|
|
3040
|
-
await
|
|
3373
|
+
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3374
|
+
const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
|
|
3375
|
+
if (legacyKeytarValue) {
|
|
3376
|
+
const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
|
|
3377
|
+
if (migrated) {
|
|
3378
|
+
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3379
|
+
try {
|
|
3380
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3381
|
+
} catch {
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return Buffer.from(legacyKeytarValue, "base64");
|
|
3385
|
+
}
|
|
3041
3386
|
} catch {
|
|
3042
3387
|
}
|
|
3043
3388
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3389
|
+
const keyPath = getKeyPath();
|
|
3390
|
+
if (!existsSync6(keyPath)) {
|
|
3391
|
+
process.stderr.write(
|
|
3392
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3393
|
+
`
|
|
3394
|
+
);
|
|
3395
|
+
return null;
|
|
3396
|
+
}
|
|
3397
|
+
try {
|
|
3398
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3399
|
+
let b64Value;
|
|
3400
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3401
|
+
const machineKey = deriveMachineKey();
|
|
3402
|
+
if (!machineKey) {
|
|
3403
|
+
process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
|
|
3404
|
+
return null;
|
|
3405
|
+
}
|
|
3406
|
+
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3407
|
+
if (!decrypted) {
|
|
3408
|
+
process.stderr.write(
|
|
3409
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
|
|
3410
|
+
);
|
|
3411
|
+
return null;
|
|
3412
|
+
}
|
|
3413
|
+
b64Value = decrypted;
|
|
3414
|
+
} else {
|
|
3415
|
+
b64Value = content;
|
|
3416
|
+
}
|
|
3417
|
+
const key = Buffer.from(b64Value, "base64");
|
|
3418
|
+
if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3419
|
+
return key;
|
|
3420
|
+
}
|
|
3421
|
+
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3422
|
+
if (migrated) {
|
|
3423
|
+
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
3424
|
+
try {
|
|
3425
|
+
await unlink(keyPath);
|
|
3426
|
+
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
3427
|
+
} catch {
|
|
3428
|
+
}
|
|
3429
|
+
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3430
|
+
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
3431
|
+
if (fallback === "encrypted") {
|
|
3432
|
+
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
3433
|
+
} else {
|
|
3434
|
+
process.stderr.write(
|
|
3435
|
+
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
return key;
|
|
3440
|
+
} catch (err) {
|
|
3441
|
+
process.stderr.write(
|
|
3442
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
3443
|
+
`
|
|
3444
|
+
);
|
|
3445
|
+
return null;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
|
|
3449
|
+
var init_keychain = __esm({
|
|
3450
|
+
"src/lib/keychain.ts"() {
|
|
3451
|
+
"use strict";
|
|
3452
|
+
SERVICE = "exe-os";
|
|
3453
|
+
LEGACY_SERVICE = "exe-mem";
|
|
3454
|
+
ACCOUNT = "master-key";
|
|
3455
|
+
linuxSecretAvailability = null;
|
|
3456
|
+
ENCRYPTED_PREFIX = "enc:";
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3459
|
+
|
|
3460
|
+
// src/lib/state-bus.ts
|
|
3461
|
+
var StateBus, orgBus;
|
|
3462
|
+
var init_state_bus = __esm({
|
|
3463
|
+
"src/lib/state-bus.ts"() {
|
|
3464
|
+
"use strict";
|
|
3465
|
+
StateBus = class {
|
|
3466
|
+
handlers = /* @__PURE__ */ new Map();
|
|
3467
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
3468
|
+
/** Emit an event to all subscribers */
|
|
3469
|
+
emit(event) {
|
|
3470
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
3471
|
+
if (typeHandlers) {
|
|
3472
|
+
for (const handler of typeHandlers) {
|
|
3473
|
+
try {
|
|
3474
|
+
handler(event);
|
|
3475
|
+
} catch {
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
for (const handler of this.globalHandlers) {
|
|
3480
|
+
try {
|
|
3481
|
+
handler(event);
|
|
3482
|
+
} catch {
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
/** Subscribe to a specific event type */
|
|
3487
|
+
on(type, handler) {
|
|
3488
|
+
if (!this.handlers.has(type)) {
|
|
3489
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
3490
|
+
}
|
|
3491
|
+
this.handlers.get(type).add(handler);
|
|
3492
|
+
}
|
|
3493
|
+
/** Subscribe to ALL events */
|
|
3494
|
+
onAny(handler) {
|
|
3495
|
+
this.globalHandlers.add(handler);
|
|
3496
|
+
}
|
|
3497
|
+
/** Unsubscribe from a specific event type */
|
|
3498
|
+
off(type, handler) {
|
|
3499
|
+
this.handlers.get(type)?.delete(handler);
|
|
3500
|
+
}
|
|
3501
|
+
/** Unsubscribe from ALL events */
|
|
3502
|
+
offAny(handler) {
|
|
3503
|
+
this.globalHandlers.delete(handler);
|
|
3504
|
+
}
|
|
3505
|
+
/** Remove all listeners */
|
|
3506
|
+
clear() {
|
|
3507
|
+
this.handlers.clear();
|
|
3508
|
+
this.globalHandlers.clear();
|
|
3509
|
+
}
|
|
3510
|
+
};
|
|
3511
|
+
orgBus = new StateBus();
|
|
3512
|
+
}
|
|
3513
|
+
});
|
|
3514
|
+
|
|
3515
|
+
// src/lib/memory-write-governor.ts
|
|
3516
|
+
import { createHash } from "crypto";
|
|
3517
|
+
function normalizeMemoryText(text) {
|
|
3518
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3519
|
+
}
|
|
3520
|
+
function classifyMemoryType(input) {
|
|
3521
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3522
|
+
const tool = input.tool_name.toLowerCase();
|
|
3523
|
+
const text = input.raw_text.toLowerCase();
|
|
3524
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3525
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3526
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3527
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3528
|
+
if (tool.includes("checkpoint") || text.startsWith("context checkpoint")) return "checkpoint";
|
|
3529
|
+
if (tool.includes("sessionsummary") || tool.includes("session-summary")) return "summary";
|
|
3530
|
+
if (tool.includes("sessionend") || text.startsWith("session ended")) return "summary";
|
|
3531
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3532
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3533
|
+
return "raw";
|
|
3534
|
+
}
|
|
3535
|
+
function shouldDropMemory(text) {
|
|
3536
|
+
const normalized = normalizeMemoryText(text);
|
|
3537
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3538
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3539
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3540
|
+
}
|
|
3541
|
+
return { drop: false };
|
|
3542
|
+
}
|
|
3543
|
+
function shouldSkipEmbedding(input) {
|
|
3544
|
+
const type = classifyMemoryType(input);
|
|
3545
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3546
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3547
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3548
|
+
return false;
|
|
3549
|
+
}
|
|
3550
|
+
function hashMemoryContent(text) {
|
|
3551
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3552
|
+
}
|
|
3553
|
+
function scopedDedupArgs(input) {
|
|
3554
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3555
|
+
}
|
|
3556
|
+
function governMemoryRecord(record) {
|
|
3557
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3558
|
+
const memoryType = classifyMemoryType({
|
|
3559
|
+
raw_text: normalized,
|
|
3560
|
+
agent_id: record.agent_id,
|
|
3561
|
+
project_name: record.project_name,
|
|
3562
|
+
tool_name: record.tool_name,
|
|
3563
|
+
memory_type: record.memory_type
|
|
3564
|
+
});
|
|
3565
|
+
const drop = shouldDropMemory(normalized);
|
|
3566
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3567
|
+
raw_text: normalized,
|
|
3568
|
+
agent_id: record.agent_id,
|
|
3569
|
+
project_name: record.project_name,
|
|
3570
|
+
tool_name: record.tool_name,
|
|
3571
|
+
memory_type: memoryType
|
|
3572
|
+
});
|
|
3573
|
+
return {
|
|
3574
|
+
record: {
|
|
3575
|
+
...record,
|
|
3576
|
+
raw_text: normalized,
|
|
3577
|
+
memory_type: memoryType,
|
|
3578
|
+
vector: skipEmbedding ? null : record.vector
|
|
3579
|
+
},
|
|
3580
|
+
contentHash: hashMemoryContent(normalized),
|
|
3581
|
+
shouldDrop: drop.drop,
|
|
3582
|
+
dropReason: drop.reason,
|
|
3583
|
+
skipEmbedding,
|
|
3584
|
+
hygiene: {
|
|
3585
|
+
dedup: true,
|
|
3586
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3587
|
+
}
|
|
3588
|
+
};
|
|
3589
|
+
}
|
|
3590
|
+
async function findScopedDuplicate(input) {
|
|
3591
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3592
|
+
const client = getClient2();
|
|
3593
|
+
const args = scopedDedupArgs(input);
|
|
3594
|
+
let sql = `SELECT id FROM memories
|
|
3595
|
+
WHERE content_hash = ?
|
|
3596
|
+
AND agent_id = ?
|
|
3597
|
+
AND project_name = ?
|
|
3598
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3599
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3600
|
+
if (input.excludeId) {
|
|
3601
|
+
sql += " AND id != ?";
|
|
3602
|
+
args.push(input.excludeId);
|
|
3603
|
+
}
|
|
3604
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3605
|
+
const result = await client.execute({ sql, args });
|
|
3606
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3607
|
+
}
|
|
3608
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3609
|
+
try {
|
|
3610
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3611
|
+
const client = getClient2();
|
|
3612
|
+
const current = await client.execute({
|
|
3613
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3614
|
+
importance, timestamp
|
|
3615
|
+
FROM memories
|
|
3616
|
+
WHERE id = ?
|
|
3617
|
+
LIMIT 1`,
|
|
3618
|
+
args: [memoryId]
|
|
3619
|
+
});
|
|
3620
|
+
const row = current.rows[0];
|
|
3621
|
+
if (!row) return;
|
|
3622
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3623
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3624
|
+
const agentId = String(row.agent_id);
|
|
3625
|
+
const projectName = String(row.project_name);
|
|
3626
|
+
if (contentHash) {
|
|
3627
|
+
await client.execute({
|
|
3628
|
+
sql: `UPDATE memories
|
|
3629
|
+
SET status = 'deleted',
|
|
3630
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3631
|
+
WHERE id != ?
|
|
3632
|
+
AND content_hash = ?
|
|
3633
|
+
AND agent_id = ?
|
|
3634
|
+
AND project_name = ?
|
|
3635
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3636
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3637
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3641
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3642
|
+
const old = await client.execute({
|
|
3643
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3644
|
+
args: [supersedesId]
|
|
3645
|
+
});
|
|
3646
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3647
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3648
|
+
await client.batch([
|
|
3649
|
+
{
|
|
3650
|
+
sql: `UPDATE memories
|
|
3651
|
+
SET status = 'archived',
|
|
3652
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3653
|
+
WHERE id = ?`,
|
|
3654
|
+
args: [supersedesId]
|
|
3655
|
+
},
|
|
3656
|
+
{
|
|
3657
|
+
sql: `UPDATE memories
|
|
3658
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3659
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3660
|
+
WHERE id = ?`,
|
|
3661
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3662
|
+
}
|
|
3663
|
+
], "write");
|
|
3664
|
+
}
|
|
3665
|
+
} catch (err) {
|
|
3666
|
+
process.stderr.write(
|
|
3667
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3668
|
+
`
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3673
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3674
|
+
if (memoryIds.length === 0) return;
|
|
3675
|
+
const run = () => {
|
|
3676
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3677
|
+
};
|
|
3678
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3679
|
+
else setTimeout(run, 0);
|
|
3680
|
+
}
|
|
3681
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3682
|
+
var init_memory_write_governor = __esm({
|
|
3683
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3684
|
+
"use strict";
|
|
3685
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3686
|
+
"decision",
|
|
3687
|
+
"adr",
|
|
3688
|
+
"behavior",
|
|
3689
|
+
"procedure"
|
|
3690
|
+
]);
|
|
3691
|
+
NOISE_DROP_PATTERNS = [
|
|
3692
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3693
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3694
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3695
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3696
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3697
|
+
];
|
|
3698
|
+
SKIP_EMBED_PATTERNS = [
|
|
3699
|
+
/tmux capture-pane\b/i,
|
|
3700
|
+
/docker ps\b/i,
|
|
3701
|
+
/docker images\b/i,
|
|
3702
|
+
/git status\b/i,
|
|
3703
|
+
/grep .*node_modules/i,
|
|
3704
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3705
|
+
];
|
|
3706
|
+
}
|
|
3707
|
+
});
|
|
3708
|
+
|
|
3709
|
+
// src/lib/shard-manager.ts
|
|
3710
|
+
var shard_manager_exports = {};
|
|
3711
|
+
__export(shard_manager_exports, {
|
|
3712
|
+
auditShardHealth: () => auditShardHealth,
|
|
3713
|
+
disposeShards: () => disposeShards,
|
|
3714
|
+
ensureShardSchema: () => ensureShardSchema,
|
|
3715
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
3716
|
+
getReadyShardClient: () => getReadyShardClient,
|
|
3717
|
+
getShardClient: () => getShardClient,
|
|
3718
|
+
getShardsDir: () => getShardsDir,
|
|
3719
|
+
initShardManager: () => initShardManager,
|
|
3720
|
+
isShardingEnabled: () => isShardingEnabled,
|
|
3721
|
+
listShards: () => listShards,
|
|
3722
|
+
shardExists: () => shardExists
|
|
3723
|
+
});
|
|
3724
|
+
import path7 from "path";
|
|
3725
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
|
|
3726
|
+
import { createClient as createClient2 } from "@libsql/client";
|
|
3727
|
+
function initShardManager(encryptionKey) {
|
|
3728
|
+
_encryptionKey = encryptionKey;
|
|
3729
|
+
if (!existsSync7(SHARDS_DIR)) {
|
|
3730
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
3731
|
+
}
|
|
3732
|
+
_shardingEnabled = true;
|
|
3733
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3734
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3735
|
+
_evictionTimer.unref();
|
|
3736
|
+
}
|
|
3737
|
+
function isShardingEnabled() {
|
|
3738
|
+
return _shardingEnabled;
|
|
3739
|
+
}
|
|
3740
|
+
function getShardsDir() {
|
|
3741
|
+
return SHARDS_DIR;
|
|
3742
|
+
}
|
|
3743
|
+
function getShardClient(projectName) {
|
|
3744
|
+
if (!_encryptionKey) {
|
|
3745
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3746
|
+
}
|
|
3747
|
+
const safeName = safeShardName(projectName);
|
|
3748
|
+
if (!safeName || safeName === "unknown") {
|
|
3749
|
+
throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
|
|
3750
|
+
}
|
|
3751
|
+
const cached = _shards.get(safeName);
|
|
3752
|
+
if (cached) {
|
|
3753
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3754
|
+
return cached;
|
|
3755
|
+
}
|
|
3756
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3757
|
+
evictLRU();
|
|
3758
|
+
}
|
|
3759
|
+
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
3760
|
+
const client = createClient2({
|
|
3761
|
+
url: `file:${dbPath}`,
|
|
3762
|
+
encryptionKey: _encryptionKey
|
|
3763
|
+
});
|
|
3764
|
+
_shards.set(safeName, client);
|
|
3765
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3766
|
+
return client;
|
|
3767
|
+
}
|
|
3768
|
+
function shardExists(projectName) {
|
|
3769
|
+
const safeName = safeShardName(projectName);
|
|
3770
|
+
return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
|
|
3771
|
+
}
|
|
3772
|
+
function safeShardName(projectName) {
|
|
3773
|
+
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3774
|
+
}
|
|
3775
|
+
function listShards() {
|
|
3776
|
+
if (!existsSync7(SHARDS_DIR)) return [];
|
|
3777
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3778
|
+
}
|
|
3779
|
+
async function auditShardHealth(options = {}) {
|
|
3780
|
+
if (!_encryptionKey) {
|
|
3781
|
+
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
3782
|
+
}
|
|
3783
|
+
const repair = options.repair === true;
|
|
3784
|
+
const dryRun = options.dryRun === true;
|
|
3785
|
+
const names = listShards();
|
|
3786
|
+
const shards = [];
|
|
3787
|
+
for (const name of names) {
|
|
3788
|
+
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
3789
|
+
const stat = statSync3(dbPath);
|
|
3790
|
+
const item = {
|
|
3791
|
+
name,
|
|
3792
|
+
path: dbPath,
|
|
3793
|
+
ok: false,
|
|
3794
|
+
unreadable: false,
|
|
3795
|
+
error: null,
|
|
3796
|
+
size: stat.size,
|
|
3797
|
+
mtime: stat.mtime.toISOString(),
|
|
3798
|
+
memoryCount: null
|
|
3799
|
+
};
|
|
3800
|
+
const client = createClient2({
|
|
3801
|
+
url: `file:${dbPath}`,
|
|
3802
|
+
encryptionKey: _encryptionKey
|
|
3803
|
+
});
|
|
3804
|
+
try {
|
|
3805
|
+
await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
|
|
3806
|
+
const hasMemories = await client.execute(
|
|
3807
|
+
"SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
|
|
3808
|
+
);
|
|
3809
|
+
if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
|
|
3810
|
+
const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
3811
|
+
item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
|
|
3812
|
+
}
|
|
3813
|
+
item.ok = true;
|
|
3814
|
+
} catch (err) {
|
|
3815
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3816
|
+
item.error = message;
|
|
3817
|
+
item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
|
|
3818
|
+
if (item.unreadable && repair && !dryRun) {
|
|
3819
|
+
client.close();
|
|
3820
|
+
_shards.delete(name);
|
|
3821
|
+
_shardLastAccess.delete(name);
|
|
3822
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3823
|
+
const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3824
|
+
renameSync3(dbPath, archivedPath);
|
|
3825
|
+
item.archivedPath = archivedPath;
|
|
3826
|
+
}
|
|
3827
|
+
} finally {
|
|
3828
|
+
try {
|
|
3829
|
+
client.close();
|
|
3830
|
+
} catch {
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
shards.push(item);
|
|
3834
|
+
}
|
|
3835
|
+
return {
|
|
3836
|
+
total: shards.length,
|
|
3837
|
+
ok: shards.filter((s) => s.ok).length,
|
|
3838
|
+
unreadable: shards.filter((s) => s.unreadable).length,
|
|
3839
|
+
archived: shards.filter((s) => Boolean(s.archivedPath)).length,
|
|
3840
|
+
shards
|
|
3841
|
+
};
|
|
3842
|
+
}
|
|
3843
|
+
async function ensureShardSchema(client) {
|
|
3844
|
+
await client.execute("PRAGMA journal_mode = WAL");
|
|
3845
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
3846
|
+
try {
|
|
3847
|
+
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
3848
|
+
} catch {
|
|
3849
|
+
}
|
|
3850
|
+
await client.executeMultiple(`
|
|
3851
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
3852
|
+
id TEXT PRIMARY KEY,
|
|
3853
|
+
agent_id TEXT NOT NULL,
|
|
3854
|
+
agent_role TEXT NOT NULL,
|
|
3855
|
+
session_id TEXT NOT NULL,
|
|
3856
|
+
timestamp TEXT NOT NULL,
|
|
3857
|
+
tool_name TEXT NOT NULL,
|
|
3858
|
+
project_name TEXT NOT NULL,
|
|
3859
|
+
has_error INTEGER NOT NULL DEFAULT 0,
|
|
3860
|
+
raw_text TEXT NOT NULL,
|
|
3861
|
+
vector F32_BLOB(1024),
|
|
3862
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
3863
|
+
);
|
|
3864
|
+
|
|
3865
|
+
CREATE INDEX IF NOT EXISTS idx_memories_agent ON memories(agent_id);
|
|
3866
|
+
CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);
|
|
3867
|
+
CREATE INDEX IF NOT EXISTS idx_memories_agent_project ON memories(agent_id, project_name);
|
|
3868
|
+
`);
|
|
3869
|
+
await client.executeMultiple(`
|
|
3870
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
3871
|
+
raw_text,
|
|
3872
|
+
content='memories',
|
|
3873
|
+
content_rowid='rowid'
|
|
3874
|
+
);
|
|
3875
|
+
|
|
3876
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_ai AFTER INSERT ON memories BEGIN
|
|
3877
|
+
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
3878
|
+
END;
|
|
3879
|
+
|
|
3880
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_ad AFTER DELETE ON memories BEGIN
|
|
3881
|
+
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
3882
|
+
END;
|
|
3883
|
+
|
|
3884
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_au AFTER UPDATE ON memories BEGIN
|
|
3885
|
+
INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
|
|
3886
|
+
INSERT INTO memories_fts(rowid, raw_text) VALUES (new.rowid, new.raw_text);
|
|
3887
|
+
END;
|
|
3888
|
+
`);
|
|
3889
|
+
for (const col of [
|
|
3890
|
+
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
3891
|
+
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3892
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3893
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
3894
|
+
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
3895
|
+
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
3896
|
+
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
3897
|
+
"ALTER TABLE memories ADD COLUMN graph_extracted INTEGER DEFAULT 0",
|
|
3898
|
+
"ALTER TABLE memories ADD COLUMN content_hash TEXT",
|
|
3899
|
+
"ALTER TABLE memories ADD COLUMN graph_extracted_hash TEXT",
|
|
3900
|
+
"ALTER TABLE memories ADD COLUMN confidence REAL DEFAULT 0.7",
|
|
3901
|
+
"ALTER TABLE memories ADD COLUMN last_accessed TEXT",
|
|
3902
|
+
// Wiki linkage columns (must match database.ts)
|
|
3903
|
+
"ALTER TABLE memories ADD COLUMN workspace_id TEXT",
|
|
3904
|
+
"ALTER TABLE memories ADD COLUMN document_id TEXT",
|
|
3905
|
+
"ALTER TABLE memories ADD COLUMN user_id TEXT",
|
|
3906
|
+
"ALTER TABLE memories ADD COLUMN char_offset INTEGER",
|
|
3907
|
+
"ALTER TABLE memories ADD COLUMN page_number INTEGER",
|
|
3908
|
+
// Source provenance columns (must match database.ts)
|
|
3909
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
3910
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
3911
|
+
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
3912
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
|
|
3913
|
+
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
3914
|
+
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
3915
|
+
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
3916
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3917
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3918
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3919
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3920
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3921
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3922
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3923
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3924
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3925
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3926
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3927
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3928
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3929
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3930
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3931
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3932
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
|
|
3933
|
+
"ALTER TABLE memories ADD COLUMN deleted_at TEXT"
|
|
3934
|
+
]) {
|
|
3935
|
+
try {
|
|
3936
|
+
await client.execute(col);
|
|
3937
|
+
} catch {
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
for (const idx of [
|
|
3941
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3942
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3943
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3944
|
+
]) {
|
|
3945
|
+
try {
|
|
3946
|
+
await client.execute(idx);
|
|
3947
|
+
} catch {
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
try {
|
|
3951
|
+
await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
|
|
3952
|
+
} catch {
|
|
3953
|
+
}
|
|
3954
|
+
for (const idx of [
|
|
3955
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_workspace ON memories(workspace_id)",
|
|
3956
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_document ON memories(document_id)",
|
|
3957
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_user ON memories(user_id)"
|
|
3958
|
+
]) {
|
|
3959
|
+
try {
|
|
3960
|
+
await client.execute(idx);
|
|
3961
|
+
} catch {
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
await client.executeMultiple(`
|
|
3965
|
+
CREATE TABLE IF NOT EXISTS entities (
|
|
3966
|
+
id TEXT PRIMARY KEY,
|
|
3967
|
+
name TEXT NOT NULL,
|
|
3968
|
+
type TEXT NOT NULL,
|
|
3969
|
+
first_seen TEXT NOT NULL,
|
|
3970
|
+
last_seen TEXT NOT NULL,
|
|
3971
|
+
properties TEXT DEFAULT '{}',
|
|
3972
|
+
UNIQUE(name, type)
|
|
3053
3973
|
);
|
|
3054
3974
|
|
|
3055
3975
|
CREATE TABLE IF NOT EXISTS relationships (
|
|
@@ -3119,7 +4039,7 @@ async function getReadyShardClient(projectName) {
|
|
|
3119
4039
|
_shardLastAccess.delete(safeName);
|
|
3120
4040
|
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
3121
4041
|
if (existsSync7(dbPath)) {
|
|
3122
|
-
const stat =
|
|
4042
|
+
const stat = statSync3(dbPath);
|
|
3123
4043
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3124
4044
|
const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
3125
4045
|
renameSync3(dbPath, archivedPath);
|
|
@@ -3239,6 +4159,12 @@ var init_platform_procedures = __esm({
|
|
|
3239
4159
|
priority: "p0",
|
|
3240
4160
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3241
4161
|
},
|
|
4162
|
+
{
|
|
4163
|
+
title: "Customer orchestration maturity \u2014 recommend, never trap",
|
|
4164
|
+
domain: "workflow",
|
|
4165
|
+
priority: "p1",
|
|
4166
|
+
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
4167
|
+
},
|
|
3242
4168
|
{
|
|
3243
4169
|
title: "Single dispatch path \u2014 create_task only",
|
|
3244
4170
|
domain: "workflow",
|
|
@@ -3297,6 +4223,12 @@ var init_platform_procedures = __esm({
|
|
|
3297
4223
|
priority: "p0",
|
|
3298
4224
|
content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
|
|
3299
4225
|
},
|
|
4226
|
+
{
|
|
4227
|
+
title: "Commit discipline \u2014 never leave verified work floating",
|
|
4228
|
+
domain: "workflow",
|
|
4229
|
+
priority: "p1",
|
|
4230
|
+
content: "After any code-change batch passes typecheck/tests/build, run git status, summarize changed files, and commit with a clear message before ending the session. If work must remain uncommitted for review/dogfood, explicitly say so, list the files, and state the blocker. Never imply work is complete while verified changes are still floating locally."
|
|
4231
|
+
},
|
|
3300
4232
|
{
|
|
3301
4233
|
title: "Desktop and TUI are the same product",
|
|
3302
4234
|
domain: "architecture",
|
|
@@ -3570,933 +4502,1268 @@ async function searchMemoryCards(queryText, agentId, options) {
|
|
|
3570
4502
|
let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
|
|
3571
4503
|
c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
|
|
3572
4504
|
FROM memory_cards c
|
|
3573
|
-
JOIN memory_cards_fts fts ON c.rowid = fts.rowid
|
|
3574
|
-
WHERE memory_cards_fts MATCH ?
|
|
3575
|
-
AND c.agent_id = ?
|
|
3576
|
-
AND COALESCE(c.active, 1) = 1`;
|
|
3577
|
-
const args = [matchExpr, agentId];
|
|
3578
|
-
if (options?.projectName) {
|
|
3579
|
-
sql += ` AND c.project_name = ?`;
|
|
3580
|
-
args.push(options.projectName);
|
|
3581
|
-
}
|
|
3582
|
-
if (options?.since) {
|
|
3583
|
-
sql += ` AND c.timestamp >= ?`;
|
|
3584
|
-
args.push(options.since);
|
|
3585
|
-
}
|
|
3586
|
-
sql += ` ORDER BY rank LIMIT ?`;
|
|
3587
|
-
args.push(limit);
|
|
3588
|
-
const result = await getClient().execute({ sql, args });
|
|
3589
|
-
return result.rows.map((row) => ({
|
|
3590
|
-
id: `card:${String(row.id)}`,
|
|
3591
|
-
agent_id: String(row.agent_id),
|
|
3592
|
-
agent_role: "memory_card",
|
|
3593
|
-
session_id: String(row.session_id),
|
|
3594
|
-
timestamp: String(row.timestamp),
|
|
3595
|
-
tool_name: `memory_card:${String(row.card_type)}`,
|
|
3596
|
-
project_name: row.project_name == null ? "" : String(row.project_name),
|
|
3597
|
-
has_error: false,
|
|
3598
|
-
raw_text: `[${String(row.card_type)}] ${String(row.content)}
|
|
3599
|
-
Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
3600
|
-
vector: [],
|
|
3601
|
-
importance: 6,
|
|
3602
|
-
status: "active",
|
|
3603
|
-
confidence: Number(row.confidence ?? 0.6),
|
|
3604
|
-
last_accessed: String(row.timestamp)
|
|
3605
|
-
}));
|
|
3606
|
-
}
|
|
3607
|
-
var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
|
|
3608
|
-
var init_memory_cards = __esm({
|
|
3609
|
-
"src/lib/memory-cards.ts"() {
|
|
3610
|
-
"use strict";
|
|
3611
|
-
init_database();
|
|
3612
|
-
MAX_CARDS_PER_MEMORY = 6;
|
|
3613
|
-
MAX_SENTENCE_CHARS = 360;
|
|
3614
|
-
}
|
|
3615
|
-
});
|
|
3616
|
-
|
|
3617
|
-
// src/lib/session-key.ts
|
|
3618
|
-
import { execSync as execSync4 } from "child_process";
|
|
3619
|
-
function normalizeCommand(command) {
|
|
3620
|
-
const trimmed = command.trim().toLowerCase();
|
|
3621
|
-
const parts = trimmed.split(/[\\/]/);
|
|
3622
|
-
return parts[parts.length - 1] ?? trimmed;
|
|
3623
|
-
}
|
|
3624
|
-
function detectRuntimeFromCommand(command) {
|
|
3625
|
-
const normalized = normalizeCommand(command);
|
|
3626
|
-
for (const [runtime, commands] of Object.entries(RUNTIME_COMMANDS)) {
|
|
3627
|
-
if (commands.includes(normalized)) {
|
|
3628
|
-
return runtime;
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
return null;
|
|
3632
|
-
}
|
|
3633
|
-
function resolveRuntimeProcess() {
|
|
3634
|
-
let pid = process.ppid;
|
|
3635
|
-
for (let i = 0; i < 10; i++) {
|
|
3636
|
-
try {
|
|
3637
|
-
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
3638
|
-
encoding: "utf8",
|
|
3639
|
-
timeout: 2e3
|
|
3640
|
-
}).trim();
|
|
3641
|
-
const match = info.match(/^\s*(\d+)\s+(.+)$/);
|
|
3642
|
-
if (!match) break;
|
|
3643
|
-
const [, ppid, cmd] = match;
|
|
3644
|
-
const runtime = detectRuntimeFromCommand(cmd ?? "");
|
|
3645
|
-
if (runtime) {
|
|
3646
|
-
return { pid: String(pid), runtime };
|
|
3647
|
-
}
|
|
3648
|
-
pid = parseInt(ppid, 10);
|
|
3649
|
-
if (pid <= 1) break;
|
|
3650
|
-
} catch {
|
|
3651
|
-
break;
|
|
3652
|
-
}
|
|
3653
|
-
}
|
|
3654
|
-
return null;
|
|
3655
|
-
}
|
|
3656
|
-
function getSessionKey() {
|
|
3657
|
-
if (_cached) return _cached;
|
|
3658
|
-
if (process.env.EXE_SESSION_KEY) {
|
|
3659
|
-
_cached = process.env.EXE_SESSION_KEY;
|
|
3660
|
-
return _cached;
|
|
3661
|
-
}
|
|
3662
|
-
const resolved = resolveRuntimeProcess();
|
|
3663
|
-
if (resolved) {
|
|
3664
|
-
_cachedRuntime = resolved.runtime;
|
|
3665
|
-
_cached = resolved.pid;
|
|
3666
|
-
return _cached;
|
|
4505
|
+
JOIN memory_cards_fts fts ON c.rowid = fts.rowid
|
|
4506
|
+
WHERE memory_cards_fts MATCH ?
|
|
4507
|
+
AND c.agent_id = ?
|
|
4508
|
+
AND COALESCE(c.active, 1) = 1`;
|
|
4509
|
+
const args = [matchExpr, agentId];
|
|
4510
|
+
if (options?.projectName) {
|
|
4511
|
+
sql += ` AND c.project_name = ?`;
|
|
4512
|
+
args.push(options.projectName);
|
|
3667
4513
|
}
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
var _cached, _cachedRuntime, RUNTIME_COMMANDS;
|
|
3672
|
-
var init_session_key = __esm({
|
|
3673
|
-
"src/lib/session-key.ts"() {
|
|
3674
|
-
"use strict";
|
|
3675
|
-
_cached = null;
|
|
3676
|
-
_cachedRuntime = null;
|
|
3677
|
-
RUNTIME_COMMANDS = {
|
|
3678
|
-
claude: ["claude", "claude.exe", "claude-native"],
|
|
3679
|
-
codex: ["codex"],
|
|
3680
|
-
opencode: ["opencode"]
|
|
3681
|
-
};
|
|
4514
|
+
if (options?.since) {
|
|
4515
|
+
sql += ` AND c.timestamp >= ?`;
|
|
4516
|
+
args.push(options.since);
|
|
3682
4517
|
}
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
4518
|
+
sql += ` ORDER BY rank LIMIT ?`;
|
|
4519
|
+
args.push(limit);
|
|
4520
|
+
const result = await getClient().execute({ sql, args });
|
|
4521
|
+
return result.rows.map((row) => ({
|
|
4522
|
+
id: `card:${String(row.id)}`,
|
|
4523
|
+
agent_id: String(row.agent_id),
|
|
4524
|
+
agent_role: "memory_card",
|
|
4525
|
+
session_id: String(row.session_id),
|
|
4526
|
+
timestamp: String(row.timestamp),
|
|
4527
|
+
tool_name: `memory_card:${String(row.card_type)}`,
|
|
4528
|
+
project_name: row.project_name == null ? "" : String(row.project_name),
|
|
4529
|
+
has_error: false,
|
|
4530
|
+
raw_text: `[${String(row.card_type)}] ${String(row.content)}
|
|
4531
|
+
Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
4532
|
+
vector: [],
|
|
4533
|
+
importance: 6,
|
|
4534
|
+
status: "active",
|
|
4535
|
+
confidence: Number(row.confidence ?? 0.6),
|
|
4536
|
+
last_accessed: String(row.timestamp)
|
|
4537
|
+
}));
|
|
3689
4538
|
}
|
|
3690
|
-
var
|
|
3691
|
-
var
|
|
3692
|
-
"src/
|
|
4539
|
+
var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
|
|
4540
|
+
var init_memory_cards = __esm({
|
|
4541
|
+
"src/lib/memory-cards.ts"() {
|
|
3693
4542
|
"use strict";
|
|
3694
|
-
|
|
4543
|
+
init_database();
|
|
4544
|
+
MAX_CARDS_PER_MEMORY = 6;
|
|
4545
|
+
MAX_SENTENCE_CHARS = 360;
|
|
3695
4546
|
}
|
|
3696
4547
|
});
|
|
3697
4548
|
|
|
3698
|
-
// src/lib/
|
|
3699
|
-
var
|
|
3700
|
-
__export(
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
4549
|
+
// src/lib/agentic-ontology.ts
|
|
4550
|
+
var agentic_ontology_exports = {};
|
|
4551
|
+
__export(agentic_ontology_exports, {
|
|
4552
|
+
clean: () => clean,
|
|
4553
|
+
extractGoalCandidates: () => extractGoalCandidates,
|
|
4554
|
+
inferIntention: () => inferIntention,
|
|
4555
|
+
inferOntologyEventType: () => inferOntologyEventType,
|
|
4556
|
+
inferOutcome: () => inferOutcome,
|
|
4557
|
+
inferSemanticLabel: () => inferSemanticLabel,
|
|
4558
|
+
insertOntologyForBatch: () => insertOntologyForBatch,
|
|
4559
|
+
insertOntologyForMemory: () => insertOntologyForMemory,
|
|
4560
|
+
ontologyPayload: () => ontologyPayload,
|
|
4561
|
+
stableId: () => stableId2
|
|
3707
4562
|
});
|
|
3708
|
-
import {
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
4563
|
+
import { createHash as createHash3 } from "crypto";
|
|
4564
|
+
function stableId2(...parts) {
|
|
4565
|
+
return createHash3("sha256").update(parts.map((p) => String(p ?? "")).join("::")).digest("hex").slice(0, 32);
|
|
4566
|
+
}
|
|
4567
|
+
function clean(text, max = 240) {
|
|
4568
|
+
return text.replace(/\u0000/g, "").replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
4569
|
+
}
|
|
4570
|
+
function inferOntologyEventType(row) {
|
|
4571
|
+
const lower = row.raw_text.toLowerCase();
|
|
4572
|
+
if (row.has_error) return "error";
|
|
4573
|
+
if (/\b(done|complete|completed|fixed|resolved|shipped|deployed|pushed|published)\b/.test(lower)) return "milestone";
|
|
4574
|
+
if (/\b(blocked|failed|error|bug|regression|broken)\b/.test(lower)) return "problem";
|
|
4575
|
+
if (/\b(decided|decision|adr|we chose|approved|rejected)\b/.test(lower)) return "decision";
|
|
4576
|
+
if (/\b(goal|need to|we need|want to|trying to|objective)\b/.test(lower)) return "goal_signal";
|
|
4577
|
+
if (["Bash", "Read", "Edit", "Write", "Grep", "Glob"].includes(row.tool_name)) return "tool_action";
|
|
4578
|
+
if (row.tool_name.startsWith("memory_card")) return "memory_card";
|
|
4579
|
+
return "memory_observation";
|
|
4580
|
+
}
|
|
4581
|
+
function inferIntention(row) {
|
|
4582
|
+
if (row.intent) return clean(row.intent, 220);
|
|
4583
|
+
const text = clean(row.raw_text, 1e3);
|
|
4584
|
+
const patterns = [
|
|
4585
|
+
/(?:we need to|need to|let'?s|i want to|we should|goal is to|objective is to|trying to)\s+([^.!?\n]{8,220})/i,
|
|
4586
|
+
/(?:so that|in order to)\s+([^.!?\n]{8,220})/i,
|
|
4587
|
+
/(?:task|plan):\s*([^.!?\n]{8,220})/i
|
|
4588
|
+
];
|
|
4589
|
+
for (const p of patterns) {
|
|
4590
|
+
const m = text.match(p);
|
|
4591
|
+
if (m?.[1]) return clean(m[1], 220);
|
|
4592
|
+
}
|
|
4593
|
+
if (["Bash", "Read", "Edit", "Write", "Grep", "Glob"].includes(row.tool_name)) {
|
|
4594
|
+
return `${row.tool_name} during ${row.project_name}`;
|
|
3722
4595
|
}
|
|
3723
4596
|
return null;
|
|
3724
4597
|
}
|
|
3725
|
-
function
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
const
|
|
3729
|
-
if (
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
4598
|
+
function inferOutcome(row) {
|
|
4599
|
+
if (row.outcome) return clean(row.outcome, 220);
|
|
4600
|
+
if (row.has_error) return "error";
|
|
4601
|
+
const lower = row.raw_text.toLowerCase();
|
|
4602
|
+
if (/\b(done|complete|completed|fixed|resolved|shipped|deployed|pushed|published|passed)\b/.test(lower)) return "success_signal";
|
|
4603
|
+
if (/\b(blocked|failed|error|regression|broken|not working|could not)\b/.test(lower)) return "failure_signal";
|
|
4604
|
+
if (/\b(warning|risk|concern|caveat)\b/.test(lower)) return "risk_signal";
|
|
4605
|
+
return null;
|
|
4606
|
+
}
|
|
4607
|
+
function extractGoalCandidates(row) {
|
|
4608
|
+
const text = clean(row.raw_text, 1600);
|
|
4609
|
+
const patterns = [
|
|
4610
|
+
/(?:we need to|need to|i want to|we should|goal is to|objective is to|trying to|let'?s)\s+([^.!?\n]{12,220})/gi,
|
|
4611
|
+
/(?:success means|success criteria|so that)\s+([^.!?\n]{12,220})/gi
|
|
4612
|
+
];
|
|
4613
|
+
const out = [];
|
|
4614
|
+
for (const pattern of patterns) {
|
|
4615
|
+
for (const m of text.matchAll(pattern)) {
|
|
4616
|
+
const candidate = clean(m[1] ?? "", 220);
|
|
4617
|
+
if (candidate.length >= 12 && !out.some((x) => x.toLowerCase() === candidate.toLowerCase())) out.push(candidate);
|
|
4618
|
+
if (out.length >= 3) return out;
|
|
4619
|
+
}
|
|
3734
4620
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4621
|
+
return out;
|
|
4622
|
+
}
|
|
4623
|
+
function uniq(values, max = 6) {
|
|
4624
|
+
const out = [];
|
|
4625
|
+
for (const value of values.map((v) => clean(v, 220)).filter(Boolean)) {
|
|
4626
|
+
if (!out.some((x) => x.toLowerCase() === value.toLowerCase())) out.push(value);
|
|
4627
|
+
if (out.length >= max) break;
|
|
3740
4628
|
}
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
const
|
|
3748
|
-
|
|
4629
|
+
return out;
|
|
4630
|
+
}
|
|
4631
|
+
function extractMatches(text, patterns, max = 5) {
|
|
4632
|
+
const out = [];
|
|
4633
|
+
for (const pattern of patterns) {
|
|
4634
|
+
for (const match of text.matchAll(pattern)) {
|
|
4635
|
+
const value = match[1] ?? match[0];
|
|
4636
|
+
if (value) out.push(value);
|
|
4637
|
+
if (out.length >= max) return uniq(out, max);
|
|
3749
4638
|
}
|
|
3750
4639
|
}
|
|
3751
|
-
return
|
|
4640
|
+
return uniq(out, max);
|
|
4641
|
+
}
|
|
4642
|
+
function inferSemanticLabel(row) {
|
|
4643
|
+
const text = clean(row.raw_text, 2400);
|
|
4644
|
+
const eventType = inferOntologyEventType(row);
|
|
4645
|
+
const intention = inferIntention(row);
|
|
4646
|
+
const outcome = inferOutcome(row);
|
|
4647
|
+
const goals = extractGoalCandidates(row);
|
|
4648
|
+
const milestones = extractMatches(text, [
|
|
4649
|
+
/\b(?:completed|finished|fixed|resolved|shipped|deployed|published|pushed|passed)\b([^.!?\n]{0,180})/gi,
|
|
4650
|
+
/(?:milestone|done):\s*([^.!?\n]{8,220})/gi
|
|
4651
|
+
]);
|
|
4652
|
+
const problems = extractMatches(text, [
|
|
4653
|
+
/\b(?:blocked by|failed because|bug|regression|broken|not working|error)\b([^.!?\n]{0,180})/gi,
|
|
4654
|
+
/(?:problem|issue|risk):\s*([^.!?\n]{8,220})/gi
|
|
4655
|
+
]);
|
|
4656
|
+
const decisions = extractMatches(text, [
|
|
4657
|
+
/(?:decided|decision|adr|we chose|approved|rejected)\s+([^.!?\n]{8,220})/gi
|
|
4658
|
+
]);
|
|
4659
|
+
const temporalAnchors = extractMatches(text, [
|
|
4660
|
+
/\b(\d{4}-\d{2}-\d{2}(?:[T ][0-9:.+-Z]+)?)\b/g,
|
|
4661
|
+
/\b(today|yesterday|tomorrow|this week|next week|last week|morning|afternoon|tonight)\b/gi
|
|
4662
|
+
], 8);
|
|
4663
|
+
const nextActions = extractMatches(text, [
|
|
4664
|
+
/(?:next|todo|follow[- ]?up|remaining|need to)\s*:?\s*([^.!?\n]{8,220})/gi
|
|
4665
|
+
]);
|
|
4666
|
+
const actors = uniq([
|
|
4667
|
+
row.agent_id,
|
|
4668
|
+
...extractMatches(text, [/\b(?:agent|employee|owner|assignee)[:= ]+([a-zA-Z][a-zA-Z0-9_-]{1,40})/gi], 5)
|
|
4669
|
+
], 6);
|
|
4670
|
+
const successSignals = milestones.length ? milestones : outcome === "success_signal" ? [clean(text, 180)] : [];
|
|
4671
|
+
const failureSignals = problems.length ? problems : outcome === "failure_signal" || row.has_error ? [clean(text, 180)] : [];
|
|
4672
|
+
const impact = successSignals.length && failureSignals.length ? "mixed" : failureSignals.length ? "negative" : successSignals.length ? "positive" : "neutral";
|
|
4673
|
+
const signalCount = goals.length + milestones.length + problems.length + decisions.length + nextActions.length;
|
|
4674
|
+
return {
|
|
4675
|
+
labeler: "deterministic",
|
|
4676
|
+
schemaVersion: 1,
|
|
4677
|
+
eventType,
|
|
4678
|
+
intention,
|
|
4679
|
+
outcome,
|
|
4680
|
+
impact,
|
|
4681
|
+
confidence: Math.min(0.95, 0.45 + signalCount * 0.08 + (intention ? 0.1 : 0) + (outcome ? 0.1 : 0)),
|
|
4682
|
+
goals,
|
|
4683
|
+
milestones,
|
|
4684
|
+
problems,
|
|
4685
|
+
decisions,
|
|
4686
|
+
actors,
|
|
4687
|
+
temporalAnchors,
|
|
4688
|
+
successSignals,
|
|
4689
|
+
failureSignals,
|
|
4690
|
+
nextActions,
|
|
4691
|
+
summary: clean(text, 280)
|
|
4692
|
+
};
|
|
3752
4693
|
}
|
|
3753
|
-
function
|
|
3754
|
-
|
|
4694
|
+
function ontologyPayload(row) {
|
|
4695
|
+
const semantic = inferSemanticLabel(row);
|
|
4696
|
+
return {
|
|
4697
|
+
tool_name: row.tool_name,
|
|
4698
|
+
memory_version: row.version ?? null,
|
|
4699
|
+
domain: row.domain ?? null,
|
|
4700
|
+
trajectory: row.trajectory ? safeJson(row.trajectory) : null,
|
|
4701
|
+
semantic
|
|
4702
|
+
};
|
|
3755
4703
|
}
|
|
3756
|
-
function
|
|
4704
|
+
function safeJson(value) {
|
|
3757
4705
|
try {
|
|
3758
|
-
|
|
3759
|
-
writeFileSync4(
|
|
3760
|
-
getMarkerPath(),
|
|
3761
|
-
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
3762
|
-
);
|
|
4706
|
+
return JSON.parse(value);
|
|
3763
4707
|
} catch {
|
|
4708
|
+
return value.slice(0, 1e3);
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
async function resolveClient(client) {
|
|
4712
|
+
if (client) return client;
|
|
4713
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4714
|
+
return getClient2();
|
|
4715
|
+
}
|
|
4716
|
+
async function insertOntologyForMemory(row, client) {
|
|
4717
|
+
const db = await resolveClient(client);
|
|
4718
|
+
const occurredAt = row.timestamp;
|
|
4719
|
+
const sequence = Number(row.version ?? 0) || Math.floor(new Date(occurredAt).getTime() / 1e3);
|
|
4720
|
+
const eventType = inferOntologyEventType(row);
|
|
4721
|
+
const intention = inferIntention(row);
|
|
4722
|
+
const outcome = inferOutcome(row);
|
|
4723
|
+
const eventId = stableId2("event", row.id);
|
|
4724
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4725
|
+
await db.execute({
|
|
4726
|
+
sql: `INSERT INTO agent_sessions (id, agent_id, project_name, started_at, last_event_at, event_count, properties)
|
|
4727
|
+
VALUES (?, ?, ?, ?, ?, 1, ?)
|
|
4728
|
+
ON CONFLICT(id) DO UPDATE SET last_event_at = MAX(last_event_at, excluded.last_event_at),
|
|
4729
|
+
event_count = event_count + 1`,
|
|
4730
|
+
args: [row.session_id, row.agent_id, row.project_name, occurredAt, occurredAt, JSON.stringify({ agent_role: row.agent_role })]
|
|
4731
|
+
});
|
|
4732
|
+
await db.execute({
|
|
4733
|
+
sql: `INSERT OR IGNORE INTO agent_events
|
|
4734
|
+
(id, event_type, occurred_at, sequence_index, actor_agent_id, agent_role, project_name,
|
|
4735
|
+
session_id, task_id, goal_id, parent_event_id, intention, outcome, evidence_memory_id,
|
|
4736
|
+
impact, payload, created_at)
|
|
4737
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?)`,
|
|
4738
|
+
args: [
|
|
4739
|
+
eventId,
|
|
4740
|
+
eventType,
|
|
4741
|
+
occurredAt,
|
|
4742
|
+
sequence,
|
|
4743
|
+
row.agent_id,
|
|
4744
|
+
row.agent_role,
|
|
4745
|
+
row.project_name,
|
|
4746
|
+
row.session_id,
|
|
4747
|
+
row.task_id ?? null,
|
|
4748
|
+
intention,
|
|
4749
|
+
outcome,
|
|
4750
|
+
row.id,
|
|
4751
|
+
row.has_error ? "negative" : outcome === "success_signal" ? "positive" : "neutral",
|
|
4752
|
+
JSON.stringify(ontologyPayload(row)),
|
|
4753
|
+
now
|
|
4754
|
+
]
|
|
4755
|
+
});
|
|
4756
|
+
const semantic = inferSemanticLabel(row);
|
|
4757
|
+
await db.execute({
|
|
4758
|
+
sql: `INSERT INTO agent_semantic_labels
|
|
4759
|
+
(id, source_memory_id, event_id, labeler, schema_version, confidence, labels, created_at, updated_at)
|
|
4760
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4761
|
+
ON CONFLICT(id) DO UPDATE SET confidence = excluded.confidence,
|
|
4762
|
+
labels = excluded.labels, updated_at = excluded.updated_at`,
|
|
4763
|
+
args: [
|
|
4764
|
+
stableId2("semantic", row.id, semantic.labeler, semantic.schemaVersion),
|
|
4765
|
+
row.id,
|
|
4766
|
+
eventId,
|
|
4767
|
+
semantic.labeler,
|
|
4768
|
+
semantic.schemaVersion,
|
|
4769
|
+
semantic.confidence,
|
|
4770
|
+
JSON.stringify(semantic),
|
|
4771
|
+
now,
|
|
4772
|
+
now
|
|
4773
|
+
]
|
|
4774
|
+
});
|
|
4775
|
+
for (const statement of extractGoalCandidates(row)) {
|
|
4776
|
+
const goalId = stableId2("goal", row.project_name, statement.toLowerCase());
|
|
4777
|
+
await db.execute({
|
|
4778
|
+
sql: `INSERT INTO agent_goals
|
|
4779
|
+
(id, statement, owner_agent_id, project_name, status, priority, success_criteria,
|
|
4780
|
+
parent_goal_id, due_at, achieved_at, supersedes_id, created_at, updated_at, source_memory_id)
|
|
4781
|
+
VALUES (?, ?, ?, ?, 'open', 5, NULL, NULL, NULL, NULL, NULL, ?, ?, ?)
|
|
4782
|
+
ON CONFLICT(id) DO UPDATE SET updated_at = excluded.updated_at`,
|
|
4783
|
+
args: [goalId, statement, row.agent_id, row.project_name, now, now, row.id]
|
|
4784
|
+
});
|
|
4785
|
+
await db.execute({
|
|
4786
|
+
sql: `INSERT OR IGNORE INTO agent_goal_links
|
|
4787
|
+
(id, goal_id, link_type, target_id, target_type, created_at)
|
|
4788
|
+
VALUES (?, ?, 'evidence', ?, 'memory', ?)`,
|
|
4789
|
+
args: [stableId2("goal_link", goalId, row.id, "memory"), goalId, row.id, now]
|
|
4790
|
+
});
|
|
4791
|
+
await db.execute({
|
|
4792
|
+
sql: `INSERT OR IGNORE INTO agent_goal_links
|
|
4793
|
+
(id, goal_id, link_type, target_id, target_type, created_at)
|
|
4794
|
+
VALUES (?, ?, 'event', ?, 'event', ?)`,
|
|
4795
|
+
args: [stableId2("goal_link", goalId, eventId, "event"), goalId, eventId, now]
|
|
4796
|
+
});
|
|
3764
4797
|
}
|
|
3765
4798
|
}
|
|
3766
|
-
function
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
4799
|
+
async function insertOntologyForBatch(rows, client) {
|
|
4800
|
+
const db = await resolveClient(client);
|
|
4801
|
+
let count = 0;
|
|
4802
|
+
for (const row of rows) {
|
|
4803
|
+
try {
|
|
4804
|
+
await insertOntologyForMemory(row, db);
|
|
4805
|
+
count++;
|
|
4806
|
+
} catch {
|
|
4807
|
+
}
|
|
3770
4808
|
}
|
|
4809
|
+
return count;
|
|
3771
4810
|
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
try {
|
|
3776
|
-
const markerPath = getMarkerPath();
|
|
3777
|
-
const raw = readFileSync5(markerPath, "utf8");
|
|
3778
|
-
const data = JSON.parse(raw);
|
|
3779
|
-
if (data.agentId) {
|
|
3780
|
-
if (data.startedAt) {
|
|
3781
|
-
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
3782
|
-
if (age > STALE_MS) {
|
|
3783
|
-
try {
|
|
3784
|
-
unlinkSync4(markerPath);
|
|
3785
|
-
} catch {
|
|
3786
|
-
}
|
|
3787
|
-
} else {
|
|
3788
|
-
return {
|
|
3789
|
-
agentId: data.agentId,
|
|
3790
|
-
agentRole: data.agentRole || "employee"
|
|
3791
|
-
};
|
|
3792
|
-
}
|
|
3793
|
-
} else {
|
|
3794
|
-
return {
|
|
3795
|
-
agentId: data.agentId,
|
|
3796
|
-
agentRole: data.agentRole || "employee"
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
} catch {
|
|
4811
|
+
var init_agentic_ontology = __esm({
|
|
4812
|
+
"src/lib/agentic-ontology.ts"() {
|
|
4813
|
+
"use strict";
|
|
3801
4814
|
}
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
4815
|
+
});
|
|
4816
|
+
|
|
4817
|
+
// src/lib/store.ts
|
|
4818
|
+
var store_exports = {};
|
|
4819
|
+
__export(store_exports, {
|
|
4820
|
+
attachDocumentMetadata: () => attachDocumentMetadata,
|
|
4821
|
+
buildRawVisibilityFilter: () => buildRawVisibilityFilter,
|
|
4822
|
+
buildWikiScopeFilter: () => buildWikiScopeFilter,
|
|
4823
|
+
classifyTier: () => classifyTier,
|
|
4824
|
+
disposeStore: () => disposeStore,
|
|
4825
|
+
flushBatch: () => flushBatch,
|
|
4826
|
+
flushTier3: () => flushTier3,
|
|
4827
|
+
getMemoryCardinality: () => getMemoryCardinality,
|
|
4828
|
+
initStore: () => initStore,
|
|
4829
|
+
reserveVersions: () => reserveVersions,
|
|
4830
|
+
searchMemories: () => searchMemories,
|
|
4831
|
+
updateMemoryStatus: () => updateMemoryStatus,
|
|
4832
|
+
vectorToBlob: () => vectorToBlob,
|
|
4833
|
+
writeMemory: () => writeMemory
|
|
4834
|
+
});
|
|
4835
|
+
function isBusyError2(err) {
|
|
4836
|
+
if (err instanceof Error) {
|
|
4837
|
+
const msg = err.message.toLowerCase();
|
|
4838
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
3810
4839
|
}
|
|
3811
|
-
return
|
|
3812
|
-
agentId: process.env.AGENT_ID || "default",
|
|
3813
|
-
agentRole: process.env.AGENT_ROLE || "employee"
|
|
3814
|
-
};
|
|
4840
|
+
return false;
|
|
3815
4841
|
}
|
|
3816
|
-
function
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
if (!
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
if (!data.agentId) continue;
|
|
3828
|
-
if (data.startedAt) {
|
|
3829
|
-
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
3830
|
-
if (age > STALE_MS) {
|
|
3831
|
-
try {
|
|
3832
|
-
unlinkSync4(path9.join(CACHE_DIR, file));
|
|
3833
|
-
} catch {
|
|
3834
|
-
}
|
|
3835
|
-
continue;
|
|
3836
|
-
}
|
|
3837
|
-
}
|
|
3838
|
-
sessions.push({
|
|
3839
|
-
agentId: data.agentId,
|
|
3840
|
-
agentRole: data.agentRole || "employee",
|
|
3841
|
-
startedAt: data.startedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
3842
|
-
sessionKey: key
|
|
3843
|
-
});
|
|
3844
|
-
} catch {
|
|
3845
|
-
}
|
|
4842
|
+
async function retryOnBusy2(fn, label) {
|
|
4843
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
4844
|
+
try {
|
|
4845
|
+
return await fn();
|
|
4846
|
+
} catch (err) {
|
|
4847
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
4848
|
+
process.stderr.write(
|
|
4849
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
4850
|
+
`
|
|
4851
|
+
);
|
|
4852
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
3846
4853
|
}
|
|
3847
|
-
return sessions;
|
|
3848
|
-
} catch {
|
|
3849
|
-
return [];
|
|
3850
4854
|
}
|
|
4855
|
+
throw new Error("unreachable");
|
|
3851
4856
|
}
|
|
3852
|
-
function
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
} catch {
|
|
4857
|
+
async function initStore(options) {
|
|
4858
|
+
if (_flushTimer !== null) {
|
|
4859
|
+
clearInterval(_flushTimer);
|
|
4860
|
+
_flushTimer = null;
|
|
3857
4861
|
}
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
4862
|
+
_pendingRecords = [];
|
|
4863
|
+
_flushing = false;
|
|
4864
|
+
_batchSize = options?.batchSize ?? 20;
|
|
4865
|
+
_flushIntervalMs = options?.flushIntervalMs ?? 1e4;
|
|
4866
|
+
let dbPath = options?.dbPath;
|
|
4867
|
+
if (!dbPath) {
|
|
4868
|
+
const config = await loadConfig();
|
|
4869
|
+
dbPath = config.dbPath;
|
|
3861
4870
|
}
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
init_employees();
|
|
3871
|
-
CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
|
|
3872
|
-
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4871
|
+
let masterKey = options?.masterKey ?? null;
|
|
4872
|
+
if (!masterKey) {
|
|
4873
|
+
masterKey = await getMasterKey();
|
|
4874
|
+
if (!masterKey) {
|
|
4875
|
+
throw new Error(
|
|
4876
|
+
"No encryption key found. Run /exe-setup to generate one."
|
|
4877
|
+
);
|
|
4878
|
+
}
|
|
3873
4879
|
}
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
import { spawnSync } from "child_process";
|
|
3881
|
-
|
|
3882
|
-
// src/lib/store.ts
|
|
3883
|
-
init_memory();
|
|
3884
|
-
init_database();
|
|
3885
|
-
|
|
3886
|
-
// src/lib/keychain.ts
|
|
3887
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3888
|
-
import { existsSync as existsSync6 } from "fs";
|
|
3889
|
-
import { execSync as execSync2 } from "child_process";
|
|
3890
|
-
import path6 from "path";
|
|
3891
|
-
import os5 from "os";
|
|
3892
|
-
var SERVICE = "exe-mem";
|
|
3893
|
-
var ACCOUNT = "master-key";
|
|
3894
|
-
function getKeyDir() {
|
|
3895
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
3896
|
-
}
|
|
3897
|
-
function getKeyPath() {
|
|
3898
|
-
return path6.join(getKeyDir(), "master.key");
|
|
3899
|
-
}
|
|
3900
|
-
function macKeychainGet() {
|
|
3901
|
-
if (process.platform !== "darwin") return null;
|
|
4880
|
+
const hexKey = masterKey.toString("hex");
|
|
4881
|
+
await initTurso({
|
|
4882
|
+
dbPath,
|
|
4883
|
+
encryptionKey: hexKey
|
|
4884
|
+
});
|
|
4885
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
3902
4886
|
try {
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
{ encoding: "utf-8", timeout: 5e3 }
|
|
3906
|
-
).trim();
|
|
4887
|
+
const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4888
|
+
await initDaemonClient2();
|
|
3907
4889
|
} catch {
|
|
3908
|
-
return null;
|
|
3909
4890
|
}
|
|
3910
|
-
|
|
3911
|
-
function macKeychainSet(value) {
|
|
3912
|
-
if (process.platform !== "darwin") return false;
|
|
3913
|
-
try {
|
|
4891
|
+
if (!options?.lightweight) {
|
|
3914
4892
|
try {
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
{ timeout: 5e3 }
|
|
3918
|
-
);
|
|
4893
|
+
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
4894
|
+
initShardManager2(hexKey);
|
|
3919
4895
|
} catch {
|
|
3920
4896
|
}
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
4897
|
+
const client = getClient();
|
|
4898
|
+
const vResult = await retryOnBusy2(
|
|
4899
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
4900
|
+
"version-query"
|
|
3924
4901
|
);
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
4902
|
+
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
4903
|
+
try {
|
|
4904
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
4905
|
+
await loadGlobalProcedures2();
|
|
4906
|
+
} catch {
|
|
4907
|
+
}
|
|
3928
4908
|
}
|
|
3929
4909
|
}
|
|
3930
|
-
function
|
|
3931
|
-
if (
|
|
4910
|
+
function classifyTier(record) {
|
|
4911
|
+
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
4912
|
+
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
4913
|
+
return 3;
|
|
4914
|
+
}
|
|
4915
|
+
function inferFilePaths(record) {
|
|
4916
|
+
if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
|
|
4917
|
+
const firstLine = record.raw_text.split("\n")[0] ?? "";
|
|
4918
|
+
const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
|
|
4919
|
+
return match ? JSON.stringify([match[1]]) : null;
|
|
4920
|
+
}
|
|
4921
|
+
function inferCommitHash(record) {
|
|
4922
|
+
if (record.tool_name !== "Bash") return null;
|
|
4923
|
+
const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
|
|
4924
|
+
return match ? match[1] : null;
|
|
4925
|
+
}
|
|
4926
|
+
function inferLanguageType(record) {
|
|
4927
|
+
const text = record.raw_text;
|
|
4928
|
+
if (!text || text.length < 10) return null;
|
|
4929
|
+
const trimmed = text.trimStart();
|
|
4930
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
|
|
4931
|
+
if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
|
|
4932
|
+
if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
|
|
4933
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
|
|
4934
|
+
return "mixed";
|
|
4935
|
+
}
|
|
4936
|
+
function inferDomain(record) {
|
|
4937
|
+
const proj = (record.project_name ?? "").toLowerCase();
|
|
4938
|
+
if (proj.includes("marketing") || proj.includes("content")) return "marketing";
|
|
4939
|
+
if (proj.includes("crm") || proj.includes("customer")) return "customer";
|
|
4940
|
+
return null;
|
|
4941
|
+
}
|
|
4942
|
+
async function writeMemory(record) {
|
|
4943
|
+
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
4944
|
+
throw new Error(
|
|
4945
|
+
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4946
|
+
);
|
|
4947
|
+
}
|
|
4948
|
+
const governed = governMemoryRecord(record);
|
|
4949
|
+
if (governed.shouldDrop) return;
|
|
4950
|
+
record = governed.record;
|
|
4951
|
+
const contentHash = governed.contentHash;
|
|
4952
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4953
|
+
if (_pendingRecords.some(
|
|
4954
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4955
|
+
)) {
|
|
4956
|
+
return;
|
|
4957
|
+
}
|
|
3932
4958
|
try {
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
4959
|
+
const existing = await findScopedDuplicate({
|
|
4960
|
+
contentHash,
|
|
4961
|
+
agentId: record.agent_id,
|
|
4962
|
+
projectName: record.project_name,
|
|
4963
|
+
memoryType
|
|
4964
|
+
});
|
|
4965
|
+
if (existing) return;
|
|
3937
4966
|
} catch {
|
|
3938
|
-
|
|
4967
|
+
}
|
|
4968
|
+
const dbRow = {
|
|
4969
|
+
id: record.id,
|
|
4970
|
+
agent_id: record.agent_id,
|
|
4971
|
+
agent_role: record.agent_role,
|
|
4972
|
+
session_id: record.session_id,
|
|
4973
|
+
timestamp: record.timestamp,
|
|
4974
|
+
tool_name: record.tool_name,
|
|
4975
|
+
project_name: record.project_name,
|
|
4976
|
+
has_error: record.has_error ? 1 : 0,
|
|
4977
|
+
raw_text: record.raw_text,
|
|
4978
|
+
vector: record.vector,
|
|
4979
|
+
version: 0,
|
|
4980
|
+
// Placeholder — assigned atomically at flush time
|
|
4981
|
+
task_id: record.task_id ?? null,
|
|
4982
|
+
importance: record.importance ?? 5,
|
|
4983
|
+
status: record.status ?? "active",
|
|
4984
|
+
confidence: record.confidence ?? 0.7,
|
|
4985
|
+
last_accessed: record.last_accessed ?? record.timestamp,
|
|
4986
|
+
workspace_id: record.workspace_id ?? null,
|
|
4987
|
+
document_id: record.document_id ?? null,
|
|
4988
|
+
user_id: record.user_id ?? null,
|
|
4989
|
+
char_offset: record.char_offset ?? null,
|
|
4990
|
+
page_number: record.page_number ?? null,
|
|
4991
|
+
source_path: record.source_path ?? null,
|
|
4992
|
+
source_type: record.source_type ?? null,
|
|
4993
|
+
tier: record.tier ?? classifyTier(record),
|
|
4994
|
+
supersedes_id: record.supersedes_id ?? null,
|
|
4995
|
+
draft: record.draft ? 1 : 0,
|
|
4996
|
+
memory_type: memoryType,
|
|
4997
|
+
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4998
|
+
content_hash: contentHash,
|
|
4999
|
+
intent: record.intent ?? null,
|
|
5000
|
+
outcome: record.outcome ?? null,
|
|
5001
|
+
domain: record.domain ?? inferDomain(record),
|
|
5002
|
+
referenced_entities: record.referenced_entities ?? null,
|
|
5003
|
+
retrieval_count: record.retrieval_count ?? 0,
|
|
5004
|
+
chain_position: record.chain_position ?? null,
|
|
5005
|
+
review_status: record.review_status ?? null,
|
|
5006
|
+
context_window_pct: record.context_window_pct ?? null,
|
|
5007
|
+
file_paths: record.file_paths ?? inferFilePaths(record),
|
|
5008
|
+
commit_hash: record.commit_hash ?? inferCommitHash(record),
|
|
5009
|
+
duration_ms: record.duration_ms ?? null,
|
|
5010
|
+
token_cost: record.token_cost ?? null,
|
|
5011
|
+
audience: record.audience ?? null,
|
|
5012
|
+
language_type: record.language_type ?? inferLanguageType(record),
|
|
5013
|
+
parent_memory_id: record.parent_memory_id ?? null
|
|
5014
|
+
};
|
|
5015
|
+
_pendingRecords.push(dbRow);
|
|
5016
|
+
orgBus.emit({
|
|
5017
|
+
type: "memory_stored",
|
|
5018
|
+
agentId: record.agent_id,
|
|
5019
|
+
project: record.project_name,
|
|
5020
|
+
timestamp: record.timestamp
|
|
5021
|
+
});
|
|
5022
|
+
const MAX_PENDING = 1e3;
|
|
5023
|
+
if (_pendingRecords.length > MAX_PENDING) {
|
|
5024
|
+
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
5025
|
+
_pendingRecords = _pendingRecords.slice(-MAX_PENDING);
|
|
5026
|
+
console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
|
|
5027
|
+
}
|
|
5028
|
+
if (_flushTimer === null) {
|
|
5029
|
+
_flushTimer = setInterval(() => {
|
|
5030
|
+
void flushBatch();
|
|
5031
|
+
}, _flushIntervalMs);
|
|
5032
|
+
if (_flushTimer && typeof _flushTimer === "object" && "unref" in _flushTimer) {
|
|
5033
|
+
_flushTimer.unref();
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
if (_pendingRecords.length >= _batchSize) {
|
|
5037
|
+
await flushBatch();
|
|
3939
5038
|
}
|
|
3940
5039
|
}
|
|
3941
|
-
function
|
|
3942
|
-
if (
|
|
5040
|
+
async function flushBatch() {
|
|
5041
|
+
if (_flushing || _pendingRecords.length === 0) return 0;
|
|
5042
|
+
_flushing = true;
|
|
3943
5043
|
try {
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
);
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
5044
|
+
const batch = _pendingRecords.slice(0);
|
|
5045
|
+
const client = getClient();
|
|
5046
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
5047
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
5048
|
+
for (const row of batch) {
|
|
5049
|
+
row.version = baseVersion++;
|
|
5050
|
+
}
|
|
5051
|
+
_nextVersion = baseVersion;
|
|
5052
|
+
const buildStmt = (row) => {
|
|
5053
|
+
const hasVector = row.vector !== null;
|
|
5054
|
+
const taskId = row.task_id ?? null;
|
|
5055
|
+
const importance = row.importance ?? 5;
|
|
5056
|
+
const status = row.status ?? "active";
|
|
5057
|
+
const confidence = row.confidence ?? 0.7;
|
|
5058
|
+
const lastAccessed = row.last_accessed ?? row.timestamp;
|
|
5059
|
+
const workspaceId = row.workspace_id ?? null;
|
|
5060
|
+
const documentId = row.document_id ?? null;
|
|
5061
|
+
const userId = row.user_id ?? null;
|
|
5062
|
+
const charOffset = row.char_offset ?? null;
|
|
5063
|
+
const pageNumber = row.page_number ?? null;
|
|
5064
|
+
const sourcePath = row.source_path ?? null;
|
|
5065
|
+
const sourceType = row.source_type ?? null;
|
|
5066
|
+
const tier = row.tier ?? 3;
|
|
5067
|
+
const supersedesId = row.supersedes_id ?? null;
|
|
5068
|
+
const draft = row.draft ? 1 : 0;
|
|
5069
|
+
const memoryType = row.memory_type ?? "raw";
|
|
5070
|
+
const trajectory = row.trajectory ?? null;
|
|
5071
|
+
const contentHash = row.content_hash ?? null;
|
|
5072
|
+
const intent = row.intent ?? null;
|
|
5073
|
+
const outcome = row.outcome ?? null;
|
|
5074
|
+
const domain = row.domain ?? null;
|
|
5075
|
+
const referencedEntities = row.referenced_entities ?? null;
|
|
5076
|
+
const retrievalCount = row.retrieval_count ?? 0;
|
|
5077
|
+
const chainPosition = row.chain_position ?? null;
|
|
5078
|
+
const reviewStatus = row.review_status ?? null;
|
|
5079
|
+
const contextWindowPct = row.context_window_pct ?? null;
|
|
5080
|
+
const filePaths = row.file_paths ?? null;
|
|
5081
|
+
const commitHash = row.commit_hash ?? null;
|
|
5082
|
+
const durationMs = row.duration_ms ?? null;
|
|
5083
|
+
const tokenCost = row.token_cost ?? null;
|
|
5084
|
+
const audience = row.audience ?? null;
|
|
5085
|
+
const languageType = row.language_type ?? null;
|
|
5086
|
+
const parentMemoryId = row.parent_memory_id ?? null;
|
|
5087
|
+
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
5088
|
+
tool_name, project_name,
|
|
5089
|
+
has_error, raw_text, vector, version, task_id, importance, status,
|
|
5090
|
+
confidence, last_accessed,
|
|
5091
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
5092
|
+
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
5093
|
+
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
5094
|
+
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
5095
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
5096
|
+
const metaArgs = [
|
|
5097
|
+
intent,
|
|
5098
|
+
outcome,
|
|
5099
|
+
domain,
|
|
5100
|
+
referencedEntities,
|
|
5101
|
+
retrievalCount,
|
|
5102
|
+
chainPosition,
|
|
5103
|
+
reviewStatus,
|
|
5104
|
+
contextWindowPct,
|
|
5105
|
+
filePaths,
|
|
5106
|
+
commitHash,
|
|
5107
|
+
durationMs,
|
|
5108
|
+
tokenCost,
|
|
5109
|
+
audience,
|
|
5110
|
+
languageType,
|
|
5111
|
+
parentMemoryId
|
|
5112
|
+
];
|
|
5113
|
+
const baseArgs = [
|
|
5114
|
+
row.id,
|
|
5115
|
+
row.agent_id,
|
|
5116
|
+
row.agent_role,
|
|
5117
|
+
row.session_id,
|
|
5118
|
+
row.timestamp,
|
|
5119
|
+
row.tool_name,
|
|
5120
|
+
row.project_name,
|
|
5121
|
+
row.has_error,
|
|
5122
|
+
row.raw_text
|
|
5123
|
+
];
|
|
5124
|
+
const sharedArgs = [
|
|
5125
|
+
row.version,
|
|
5126
|
+
taskId,
|
|
5127
|
+
importance,
|
|
5128
|
+
status,
|
|
5129
|
+
confidence,
|
|
5130
|
+
lastAccessed,
|
|
5131
|
+
workspaceId,
|
|
5132
|
+
documentId,
|
|
5133
|
+
userId,
|
|
5134
|
+
charOffset,
|
|
5135
|
+
pageNumber,
|
|
5136
|
+
sourcePath,
|
|
5137
|
+
sourceType,
|
|
5138
|
+
tier,
|
|
5139
|
+
supersedesId,
|
|
5140
|
+
draft,
|
|
5141
|
+
memoryType,
|
|
5142
|
+
trajectory,
|
|
5143
|
+
contentHash
|
|
5144
|
+
];
|
|
5145
|
+
return {
|
|
5146
|
+
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
5147
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
5148
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5149
|
+
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
5150
|
+
};
|
|
5151
|
+
};
|
|
5152
|
+
const globalClient = getClient();
|
|
5153
|
+
const globalStmts = batch.map(buildStmt);
|
|
5154
|
+
await globalClient.batch(globalStmts, "write");
|
|
5155
|
+
try {
|
|
5156
|
+
const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
|
|
5157
|
+
await insertMemoryCardsForBatch2(batch);
|
|
5158
|
+
} catch {
|
|
5159
|
+
}
|
|
5160
|
+
try {
|
|
5161
|
+
const { insertOntologyForBatch: insertOntologyForBatch2 } = await Promise.resolve().then(() => (init_agentic_ontology(), agentic_ontology_exports));
|
|
5162
|
+
await insertOntologyForBatch2(batch);
|
|
5163
|
+
} catch {
|
|
5164
|
+
}
|
|
5165
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
5166
|
+
_pendingRecords.splice(0, batch.length);
|
|
5167
|
+
try {
|
|
5168
|
+
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
5169
|
+
if (isShardingEnabled2()) {
|
|
5170
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
5171
|
+
let skippedUnknown = 0;
|
|
5172
|
+
for (const row of batch) {
|
|
5173
|
+
const proj = row.project_name?.trim();
|
|
5174
|
+
if (!proj) {
|
|
5175
|
+
skippedUnknown++;
|
|
5176
|
+
continue;
|
|
5177
|
+
}
|
|
5178
|
+
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
5179
|
+
byProject.get(proj).push(row);
|
|
5180
|
+
}
|
|
5181
|
+
if (skippedUnknown > 0) {
|
|
5182
|
+
process.stderr.write(
|
|
5183
|
+
`[store] Shard skip: ${skippedUnknown} record(s) with empty project_name (kept in main DB only)
|
|
5184
|
+
`
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
for (const [project, rows] of byProject) {
|
|
5188
|
+
try {
|
|
5189
|
+
const shardClient = await getReadyShardClient2(project);
|
|
5190
|
+
const shardStmts = rows.map(buildStmt);
|
|
5191
|
+
await shardClient.batch(shardStmts, "write");
|
|
5192
|
+
} catch (err) {
|
|
5193
|
+
const fullError = err instanceof Error ? `${err.name}: ${err.message}${err.stack ? `
|
|
5194
|
+
${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
|
|
5195
|
+
process.stderr.write(
|
|
5196
|
+
`[store] Shard write failed for ${project} (${rows.length} records): ${fullError}
|
|
5197
|
+
`
|
|
5198
|
+
);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
} catch {
|
|
5203
|
+
}
|
|
5204
|
+
return batch.length;
|
|
5205
|
+
} finally {
|
|
5206
|
+
_flushing = false;
|
|
3951
5207
|
}
|
|
3952
5208
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
5209
|
+
function buildWikiScopeFilter(options, columnPrefix) {
|
|
5210
|
+
const args = [];
|
|
5211
|
+
let clause = "";
|
|
5212
|
+
if (options?.workspaceId !== void 0) {
|
|
5213
|
+
clause += ` AND ${columnPrefix}workspace_id = ?`;
|
|
5214
|
+
args.push(options.workspaceId);
|
|
3958
5215
|
}
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
os5.userInfo().username,
|
|
3967
|
-
os5.arch(),
|
|
3968
|
-
os5.platform(),
|
|
3969
|
-
// Machine ID on Linux (stable across reboots)
|
|
3970
|
-
process.platform === "linux" ? readMachineId() : ""
|
|
3971
|
-
].join("|");
|
|
3972
|
-
return crypto3.createHash("sha256").update(material).digest();
|
|
3973
|
-
} catch {
|
|
3974
|
-
return null;
|
|
5216
|
+
if (options?.userId === void 0) {
|
|
5217
|
+
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
5218
|
+
} else if (options.userId === null) {
|
|
5219
|
+
clause += ` AND ${columnPrefix}user_id IS NULL`;
|
|
5220
|
+
} else {
|
|
5221
|
+
clause += ` AND (${columnPrefix}user_id = ? OR ${columnPrefix}user_id IS NULL)`;
|
|
5222
|
+
args.push(options.userId);
|
|
3975
5223
|
}
|
|
5224
|
+
return { clause, args };
|
|
3976
5225
|
}
|
|
3977
|
-
function
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
5226
|
+
function buildRawVisibilityFilter(options, columnPrefix) {
|
|
5227
|
+
if (options?.includeRaw === false) {
|
|
5228
|
+
return {
|
|
5229
|
+
clause: ` AND COALESCE(${columnPrefix}memory_type, 'raw') != 'raw'`,
|
|
5230
|
+
args: []
|
|
5231
|
+
};
|
|
3983
5232
|
}
|
|
5233
|
+
return { clause: "", args: [] };
|
|
3984
5234
|
}
|
|
3985
|
-
function
|
|
3986
|
-
|
|
3987
|
-
const iv = crypto3.randomBytes(12);
|
|
3988
|
-
const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3989
|
-
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3990
|
-
encrypted += cipher.final("base64");
|
|
3991
|
-
const authTag = cipher.getAuthTag().toString("base64");
|
|
3992
|
-
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
|
|
3993
|
-
}
|
|
3994
|
-
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3995
|
-
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
5235
|
+
async function searchMemories(queryVector, agentId, options) {
|
|
5236
|
+
let client;
|
|
3996
5237
|
try {
|
|
3997
|
-
const
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
const decipher = crypto3.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
4004
|
-
decipher.setAuthTag(authTag);
|
|
4005
|
-
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
4006
|
-
decrypted += decipher.final("utf-8");
|
|
4007
|
-
return decrypted;
|
|
5238
|
+
const { isShardingEnabled: isShardingEnabled2, shardExists: shardExists2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
5239
|
+
if (isShardingEnabled2() && options?.projectName && shardExists2(options.projectName)) {
|
|
5240
|
+
client = await getReadyShardClient2(options.projectName);
|
|
5241
|
+
} else {
|
|
5242
|
+
client = getClient();
|
|
5243
|
+
}
|
|
4008
5244
|
} catch {
|
|
4009
|
-
|
|
5245
|
+
client = getClient();
|
|
4010
5246
|
}
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
5247
|
+
const limit = options?.limit ?? 10;
|
|
5248
|
+
const statusFilter = options?.includeArchived ? "" : `
|
|
5249
|
+
AND COALESCE(status, 'active') = 'active'`;
|
|
5250
|
+
const draftFilter = options?.includeDrafts ? "" : `
|
|
5251
|
+
AND (draft = 0 OR draft IS NULL)`;
|
|
5252
|
+
let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
5253
|
+
tool_name, project_name,
|
|
5254
|
+
has_error, raw_text, vector, importance, status,
|
|
5255
|
+
confidence, last_accessed,
|
|
5256
|
+
workspace_id, document_id, user_id,
|
|
5257
|
+
char_offset, page_number,
|
|
5258
|
+
source_path, source_type
|
|
5259
|
+
FROM memories
|
|
5260
|
+
WHERE agent_id = ?
|
|
5261
|
+
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
5262
|
+
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
5263
|
+
const args = [agentId];
|
|
5264
|
+
const scope = buildWikiScopeFilter(options, "");
|
|
5265
|
+
sql += scope.clause;
|
|
5266
|
+
args.push(...scope.args);
|
|
5267
|
+
const rawVisibility = buildRawVisibilityFilter(options, "");
|
|
5268
|
+
sql += rawVisibility.clause;
|
|
5269
|
+
args.push(...rawVisibility.args);
|
|
5270
|
+
if (options?.projectName) {
|
|
5271
|
+
sql += ` AND project_name = ?`;
|
|
5272
|
+
args.push(options.projectName);
|
|
4022
5273
|
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
}
|
|
4027
|
-
async function getMasterKey() {
|
|
4028
|
-
const nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
4029
|
-
if (nativeValue) {
|
|
4030
|
-
return Buffer.from(nativeValue, "base64");
|
|
5274
|
+
if (options?.toolName) {
|
|
5275
|
+
sql += ` AND tool_name = ?`;
|
|
5276
|
+
args.push(options.toolName);
|
|
4031
5277
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
4036
|
-
if (keytarValue) {
|
|
4037
|
-
const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
|
|
4038
|
-
if (migrated) {
|
|
4039
|
-
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
4040
|
-
}
|
|
4041
|
-
return Buffer.from(keytarValue, "base64");
|
|
4042
|
-
}
|
|
4043
|
-
} catch {
|
|
4044
|
-
}
|
|
5278
|
+
if (options?.hasError !== void 0) {
|
|
5279
|
+
sql += ` AND has_error = ?`;
|
|
5280
|
+
args.push(options.hasError ? 1 : 0);
|
|
4045
5281
|
}
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4050
|
-
`
|
|
4051
|
-
);
|
|
4052
|
-
return null;
|
|
5282
|
+
if (options?.since) {
|
|
5283
|
+
sql += ` AND timestamp >= ?`;
|
|
5284
|
+
args.push(options.since);
|
|
4053
5285
|
}
|
|
5286
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5287
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5288
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5289
|
+
args.push(...uniqueTypes);
|
|
5290
|
+
} else if (options?.memoryType) {
|
|
5291
|
+
sql += ` AND memory_type = ?`;
|
|
5292
|
+
args.push(options.memoryType);
|
|
5293
|
+
}
|
|
5294
|
+
sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
|
|
5295
|
+
args.push(vectorToBlob(queryVector));
|
|
5296
|
+
sql += ` LIMIT ?`;
|
|
5297
|
+
args.push(limit);
|
|
5298
|
+
const result = await client.execute({ sql, args });
|
|
5299
|
+
return result.rows.map((row) => ({
|
|
5300
|
+
id: row.id,
|
|
5301
|
+
agent_id: row.agent_id,
|
|
5302
|
+
agent_role: row.agent_role,
|
|
5303
|
+
session_id: row.session_id,
|
|
5304
|
+
timestamp: row.timestamp,
|
|
5305
|
+
tool_name: row.tool_name,
|
|
5306
|
+
project_name: row.project_name,
|
|
5307
|
+
has_error: row.has_error === 1,
|
|
5308
|
+
raw_text: row.raw_text,
|
|
5309
|
+
vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
|
|
5310
|
+
importance: row.importance ?? 5,
|
|
5311
|
+
status: row.status ?? "active",
|
|
5312
|
+
confidence: row.confidence ?? 0.7,
|
|
5313
|
+
last_accessed: row.last_accessed ?? row.timestamp,
|
|
5314
|
+
workspace_id: row.workspace_id ?? null,
|
|
5315
|
+
document_id: row.document_id ?? null,
|
|
5316
|
+
user_id: row.user_id ?? null,
|
|
5317
|
+
char_offset: row.char_offset ?? null,
|
|
5318
|
+
page_number: row.page_number ?? null,
|
|
5319
|
+
source_path: row.source_path ?? null,
|
|
5320
|
+
source_type: row.source_type ?? null
|
|
5321
|
+
}));
|
|
5322
|
+
}
|
|
5323
|
+
async function attachDocumentMetadata(records) {
|
|
5324
|
+
const docIds = [
|
|
5325
|
+
...new Set(
|
|
5326
|
+
records.map((r) => r.document_id).filter((id) => typeof id === "string" && id.length > 0)
|
|
5327
|
+
)
|
|
5328
|
+
];
|
|
5329
|
+
if (docIds.length === 0) return records;
|
|
4054
5330
|
try {
|
|
4055
|
-
const
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
5331
|
+
const client = getClient();
|
|
5332
|
+
const placeholders = docIds.map(() => "?").join(",");
|
|
5333
|
+
const result = await client.execute({
|
|
5334
|
+
sql: `SELECT id, filename, mime, source_type, uploaded_at
|
|
5335
|
+
FROM documents
|
|
5336
|
+
WHERE id IN (${placeholders})`,
|
|
5337
|
+
args: docIds
|
|
5338
|
+
});
|
|
5339
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5340
|
+
for (const row of result.rows) {
|
|
5341
|
+
const id = row.id;
|
|
5342
|
+
byId.set(id, {
|
|
5343
|
+
document_id: id,
|
|
5344
|
+
filename: row.filename,
|
|
5345
|
+
mime: row.mime ?? null,
|
|
5346
|
+
source_type: row.source_type ?? null,
|
|
5347
|
+
uploaded_at: row.uploaded_at
|
|
5348
|
+
});
|
|
4073
5349
|
}
|
|
4074
|
-
const
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
4078
|
-
try {
|
|
4079
|
-
await unlink(keyPath);
|
|
4080
|
-
process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
|
|
4081
|
-
} catch {
|
|
4082
|
-
}
|
|
4083
|
-
} else if (!content.startsWith(ENCRYPTED_PREFIX)) {
|
|
4084
|
-
const fallback = await writeMachineBoundFileFallback(b64Value);
|
|
4085
|
-
if (fallback === "encrypted") {
|
|
4086
|
-
process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
|
|
4087
|
-
} else {
|
|
4088
|
-
process.stderr.write(
|
|
4089
|
-
"[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
|
|
4090
|
-
);
|
|
4091
|
-
}
|
|
5350
|
+
for (const record of records) {
|
|
5351
|
+
if (!record.document_id) continue;
|
|
5352
|
+
record.document_metadata = byId.get(record.document_id) ?? null;
|
|
4092
5353
|
}
|
|
4093
|
-
|
|
4094
|
-
} catch (err) {
|
|
4095
|
-
process.stderr.write(
|
|
4096
|
-
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
4097
|
-
`
|
|
4098
|
-
);
|
|
4099
|
-
return null;
|
|
5354
|
+
} catch {
|
|
4100
5355
|
}
|
|
5356
|
+
return records;
|
|
4101
5357
|
}
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
if (typeHandlers) {
|
|
4114
|
-
for (const handler of typeHandlers) {
|
|
4115
|
-
try {
|
|
4116
|
-
handler(event);
|
|
4117
|
-
} catch {
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
}
|
|
4121
|
-
for (const handler of this.globalHandlers) {
|
|
4122
|
-
try {
|
|
4123
|
-
handler(event);
|
|
4124
|
-
} catch {
|
|
4125
|
-
}
|
|
4126
|
-
}
|
|
5358
|
+
async function flushTier3(agentId, options) {
|
|
5359
|
+
const client = getClient();
|
|
5360
|
+
const maxAge = options?.maxAgeHours ?? 72;
|
|
5361
|
+
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
5362
|
+
if (options?.dryRun) {
|
|
5363
|
+
const result2 = await client.execute({
|
|
5364
|
+
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
5365
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5366
|
+
args: [agentId, cutoff]
|
|
5367
|
+
});
|
|
5368
|
+
return { archived: Number(result2.rows[0]?.cnt ?? 0) };
|
|
4127
5369
|
}
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
5370
|
+
const result = await client.execute({
|
|
5371
|
+
sql: `UPDATE memories SET status = 'archived'
|
|
5372
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5373
|
+
args: [agentId, cutoff]
|
|
5374
|
+
});
|
|
5375
|
+
return { archived: result.rowsAffected };
|
|
5376
|
+
}
|
|
5377
|
+
async function disposeStore() {
|
|
5378
|
+
if (_flushTimer !== null) {
|
|
5379
|
+
clearInterval(_flushTimer);
|
|
5380
|
+
_flushTimer = null;
|
|
4134
5381
|
}
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
this.globalHandlers.add(handler);
|
|
5382
|
+
if (_pendingRecords.length > 0) {
|
|
5383
|
+
await flushBatch();
|
|
4138
5384
|
}
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
5385
|
+
await disposeTurso();
|
|
5386
|
+
_pendingRecords = [];
|
|
5387
|
+
_nextVersion = 1;
|
|
5388
|
+
}
|
|
5389
|
+
function vectorToBlob(vector) {
|
|
5390
|
+
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
5391
|
+
return JSON.stringify(Array.from(f32));
|
|
5392
|
+
}
|
|
5393
|
+
async function updateMemoryStatus(id, status) {
|
|
5394
|
+
const client = getClient();
|
|
5395
|
+
await client.execute({
|
|
5396
|
+
sql: `UPDATE memories SET status = ? WHERE id = ?`,
|
|
5397
|
+
args: [status, id]
|
|
5398
|
+
});
|
|
5399
|
+
}
|
|
5400
|
+
function reserveVersions(count) {
|
|
5401
|
+
const reserved = [];
|
|
5402
|
+
for (let i = 0; i < count; i++) {
|
|
5403
|
+
reserved.push(_nextVersion++);
|
|
4142
5404
|
}
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
5405
|
+
return reserved;
|
|
5406
|
+
}
|
|
5407
|
+
async function getMemoryCardinality(agentId) {
|
|
5408
|
+
try {
|
|
5409
|
+
const client = getClient();
|
|
5410
|
+
const result = await client.execute({
|
|
5411
|
+
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
5412
|
+
args: [agentId]
|
|
5413
|
+
});
|
|
5414
|
+
return Number(result.rows[0]?.cnt) || 0;
|
|
5415
|
+
} catch {
|
|
5416
|
+
return 0;
|
|
4146
5417
|
}
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
5418
|
+
}
|
|
5419
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
5420
|
+
var init_store = __esm({
|
|
5421
|
+
"src/lib/store.ts"() {
|
|
5422
|
+
"use strict";
|
|
5423
|
+
init_memory();
|
|
5424
|
+
init_database();
|
|
5425
|
+
init_keychain();
|
|
5426
|
+
init_config();
|
|
5427
|
+
init_state_bus();
|
|
5428
|
+
init_memory_write_governor();
|
|
5429
|
+
INIT_MAX_RETRIES = 3;
|
|
5430
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
5431
|
+
_pendingRecords = [];
|
|
5432
|
+
_batchSize = 20;
|
|
5433
|
+
_flushIntervalMs = 1e4;
|
|
5434
|
+
_flushTimer = null;
|
|
5435
|
+
_flushing = false;
|
|
5436
|
+
_nextVersion = 1;
|
|
4151
5437
|
}
|
|
4152
|
-
};
|
|
4153
|
-
var orgBus = new StateBus();
|
|
5438
|
+
});
|
|
4154
5439
|
|
|
4155
|
-
// src/lib/
|
|
4156
|
-
import {
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
const current = await client.execute({
|
|
4168
|
-
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4169
|
-
importance, timestamp
|
|
4170
|
-
FROM memories
|
|
4171
|
-
WHERE id = ?
|
|
4172
|
-
LIMIT 1`,
|
|
4173
|
-
args: [memoryId]
|
|
4174
|
-
});
|
|
4175
|
-
const row = current.rows[0];
|
|
4176
|
-
if (!row) return;
|
|
4177
|
-
const memoryType = String(row.memory_type ?? "raw");
|
|
4178
|
-
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4179
|
-
const agentId = String(row.agent_id);
|
|
4180
|
-
const projectName = String(row.project_name);
|
|
4181
|
-
if (contentHash) {
|
|
4182
|
-
await client.execute({
|
|
4183
|
-
sql: `UPDATE memories
|
|
4184
|
-
SET status = 'deleted',
|
|
4185
|
-
outcome = COALESCE(outcome, 'superseded')
|
|
4186
|
-
WHERE id != ?
|
|
4187
|
-
AND content_hash = ?
|
|
4188
|
-
AND agent_id = ?
|
|
4189
|
-
AND project_name = ?
|
|
4190
|
-
AND COALESCE(memory_type, 'raw') = ?
|
|
4191
|
-
AND COALESCE(status, 'active') = 'active'`,
|
|
4192
|
-
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4193
|
-
});
|
|
5440
|
+
// src/lib/session-key.ts
|
|
5441
|
+
import { execSync as execSync4 } from "child_process";
|
|
5442
|
+
function normalizeCommand(command) {
|
|
5443
|
+
const trimmed = command.trim().toLowerCase();
|
|
5444
|
+
const parts = trimmed.split(/[\\/]/);
|
|
5445
|
+
return parts[parts.length - 1] ?? trimmed;
|
|
5446
|
+
}
|
|
5447
|
+
function detectRuntimeFromCommand(command) {
|
|
5448
|
+
const normalized = normalizeCommand(command);
|
|
5449
|
+
for (const [runtime, commands] of Object.entries(RUNTIME_COMMANDS)) {
|
|
5450
|
+
if (commands.includes(normalized)) {
|
|
5451
|
+
return runtime;
|
|
4194
5452
|
}
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
const
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
{
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
}
|
|
4218
|
-
], "write");
|
|
5453
|
+
}
|
|
5454
|
+
return null;
|
|
5455
|
+
}
|
|
5456
|
+
function resolveRuntimeProcess() {
|
|
5457
|
+
let pid = process.ppid;
|
|
5458
|
+
for (let i = 0; i < 10; i++) {
|
|
5459
|
+
try {
|
|
5460
|
+
const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
|
|
5461
|
+
encoding: "utf8",
|
|
5462
|
+
timeout: 2e3
|
|
5463
|
+
}).trim();
|
|
5464
|
+
const match = info.match(/^\s*(\d+)\s+(.+)$/);
|
|
5465
|
+
if (!match) break;
|
|
5466
|
+
const [, ppid, cmd] = match;
|
|
5467
|
+
const runtime = detectRuntimeFromCommand(cmd ?? "");
|
|
5468
|
+
if (runtime) {
|
|
5469
|
+
return { pid: String(pid), runtime };
|
|
5470
|
+
}
|
|
5471
|
+
pid = parseInt(ppid, 10);
|
|
5472
|
+
if (pid <= 1) break;
|
|
5473
|
+
} catch {
|
|
5474
|
+
break;
|
|
4219
5475
|
}
|
|
4220
|
-
} catch (err) {
|
|
4221
|
-
process.stderr.write(
|
|
4222
|
-
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4223
|
-
`
|
|
4224
|
-
);
|
|
4225
5476
|
}
|
|
5477
|
+
return null;
|
|
4226
5478
|
}
|
|
4227
|
-
function
|
|
4228
|
-
if (
|
|
4229
|
-
if (
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
}
|
|
4233
|
-
|
|
4234
|
-
|
|
5479
|
+
function getSessionKey() {
|
|
5480
|
+
if (_cached) return _cached;
|
|
5481
|
+
if (process.env.EXE_SESSION_KEY) {
|
|
5482
|
+
_cached = process.env.EXE_SESSION_KEY;
|
|
5483
|
+
return _cached;
|
|
5484
|
+
}
|
|
5485
|
+
const resolved = resolveRuntimeProcess();
|
|
5486
|
+
if (resolved) {
|
|
5487
|
+
_cachedRuntime = resolved.runtime;
|
|
5488
|
+
_cached = resolved.pid;
|
|
5489
|
+
return _cached;
|
|
5490
|
+
}
|
|
5491
|
+
_cached = process.env.CLAUDE_CODE_SSE_PORT ?? String(process.ppid);
|
|
5492
|
+
return _cached;
|
|
4235
5493
|
}
|
|
5494
|
+
var _cached, _cachedRuntime, RUNTIME_COMMANDS;
|
|
5495
|
+
var init_session_key = __esm({
|
|
5496
|
+
"src/lib/session-key.ts"() {
|
|
5497
|
+
"use strict";
|
|
5498
|
+
_cached = null;
|
|
5499
|
+
_cachedRuntime = null;
|
|
5500
|
+
RUNTIME_COMMANDS = {
|
|
5501
|
+
claude: ["claude", "claude.exe", "claude-native"],
|
|
5502
|
+
codex: ["codex"],
|
|
5503
|
+
opencode: ["opencode"]
|
|
5504
|
+
};
|
|
5505
|
+
}
|
|
5506
|
+
});
|
|
4236
5507
|
|
|
4237
|
-
// src/
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
5508
|
+
// src/mcp/agent-context.ts
|
|
5509
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
5510
|
+
function getAgentContext() {
|
|
5511
|
+
return agentStore.getStore();
|
|
5512
|
+
}
|
|
5513
|
+
var agentStore;
|
|
5514
|
+
var init_agent_context = __esm({
|
|
5515
|
+
"src/mcp/agent-context.ts"() {
|
|
5516
|
+
"use strict";
|
|
5517
|
+
agentStore = new AsyncLocalStorage();
|
|
4244
5518
|
}
|
|
4245
|
-
|
|
5519
|
+
});
|
|
5520
|
+
|
|
5521
|
+
// src/lib/active-agent.ts
|
|
5522
|
+
var active_agent_exports = {};
|
|
5523
|
+
__export(active_agent_exports, {
|
|
5524
|
+
cleanupSessionMarkers: () => cleanupSessionMarkers,
|
|
5525
|
+
clearActiveAgent: () => clearActiveAgent,
|
|
5526
|
+
getActiveAgent: () => getActiveAgent,
|
|
5527
|
+
getAllActiveAgents: () => getAllActiveAgents,
|
|
5528
|
+
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
5529
|
+
writeActiveAgent: () => writeActiveAgent
|
|
5530
|
+
});
|
|
5531
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
|
|
5532
|
+
import { execSync as execSync5 } from "child_process";
|
|
5533
|
+
import path9 from "path";
|
|
5534
|
+
function isNameWithOptionalInstance(candidate, baseName) {
|
|
5535
|
+
if (candidate === baseName) return true;
|
|
5536
|
+
if (!candidate.startsWith(baseName)) return false;
|
|
5537
|
+
return /^\d+$/.test(candidate.slice(baseName.length));
|
|
4246
5538
|
}
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
4253
|
-
process.stderr.write(
|
|
4254
|
-
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
4255
|
-
`
|
|
4256
|
-
);
|
|
4257
|
-
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
5539
|
+
function resolveEmployeeFromSessionPrefix(prefix, employees) {
|
|
5540
|
+
const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
|
|
5541
|
+
for (const employee of sorted) {
|
|
5542
|
+
if (isNameWithOptionalInstance(prefix, employee.name)) {
|
|
5543
|
+
return { agentId: employee.name, agentRole: employee.role };
|
|
4258
5544
|
}
|
|
4259
5545
|
}
|
|
4260
|
-
|
|
5546
|
+
return null;
|
|
4261
5547
|
}
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
_flushTimer = null;
|
|
5548
|
+
function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
5549
|
+
const employees = loadEmployeesSync();
|
|
5550
|
+
const coordinator = getCoordinatorEmployee(employees);
|
|
5551
|
+
const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
5552
|
+
if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
|
|
5553
|
+
return {
|
|
5554
|
+
agentId: coordinatorName,
|
|
5555
|
+
agentRole: coordinator?.role ?? "COO"
|
|
5556
|
+
};
|
|
4272
5557
|
}
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
if (!dbPath) {
|
|
4279
|
-
const config = await loadConfig();
|
|
4280
|
-
dbPath = config.dbPath;
|
|
5558
|
+
if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
|
|
5559
|
+
return {
|
|
5560
|
+
agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
5561
|
+
agentRole: coordinator?.role ?? "COO"
|
|
5562
|
+
};
|
|
4281
5563
|
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
if (
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
);
|
|
5564
|
+
if (sessionName.includes("-")) {
|
|
5565
|
+
const prefix = sessionName.split("-")[0] ?? "";
|
|
5566
|
+
const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
|
|
5567
|
+
if (employee) return employee;
|
|
5568
|
+
const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
|
|
5569
|
+
if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
|
|
5570
|
+
const emp = getEmployee(employees, legacy[1]);
|
|
5571
|
+
return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
|
|
4289
5572
|
}
|
|
4290
5573
|
}
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
5574
|
+
return null;
|
|
5575
|
+
}
|
|
5576
|
+
function getMarkerPath() {
|
|
5577
|
+
return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
5578
|
+
}
|
|
5579
|
+
function writeActiveAgent(agentId, agentRole) {
|
|
4297
5580
|
try {
|
|
4298
|
-
|
|
4299
|
-
|
|
5581
|
+
mkdirSync4(CACHE_DIR, { recursive: true });
|
|
5582
|
+
writeFileSync4(
|
|
5583
|
+
getMarkerPath(),
|
|
5584
|
+
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
5585
|
+
);
|
|
4300
5586
|
} catch {
|
|
4301
5587
|
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
5588
|
+
}
|
|
5589
|
+
function clearActiveAgent() {
|
|
5590
|
+
try {
|
|
5591
|
+
unlinkSync4(getMarkerPath());
|
|
5592
|
+
} catch {
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
function getActiveAgent() {
|
|
5596
|
+
const httpCtx = getAgentContext();
|
|
5597
|
+
if (httpCtx) return httpCtx;
|
|
5598
|
+
try {
|
|
5599
|
+
const markerPath = getMarkerPath();
|
|
5600
|
+
const raw = readFileSync5(markerPath, "utf8");
|
|
5601
|
+
const data = JSON.parse(raw);
|
|
5602
|
+
if (data.agentId) {
|
|
5603
|
+
if (data.startedAt) {
|
|
5604
|
+
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
5605
|
+
if (age > STALE_MS) {
|
|
5606
|
+
try {
|
|
5607
|
+
unlinkSync4(markerPath);
|
|
5608
|
+
} catch {
|
|
5609
|
+
}
|
|
5610
|
+
} else {
|
|
5611
|
+
return {
|
|
5612
|
+
agentId: data.agentId,
|
|
5613
|
+
agentRole: data.agentRole || "employee"
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
} else {
|
|
5617
|
+
return {
|
|
5618
|
+
agentId: data.agentId,
|
|
5619
|
+
agentRole: data.agentRole || "employee"
|
|
5620
|
+
};
|
|
5621
|
+
}
|
|
4318
5622
|
}
|
|
5623
|
+
} catch {
|
|
5624
|
+
}
|
|
5625
|
+
try {
|
|
5626
|
+
const sessionName = execSync5(
|
|
5627
|
+
"tmux display-message -p '#{session_name}' 2>/dev/null",
|
|
5628
|
+
{ encoding: "utf8", timeout: 2e3 }
|
|
5629
|
+
).trim();
|
|
5630
|
+
const resolved = resolveActiveAgentFromTmuxSession(sessionName);
|
|
5631
|
+
if (resolved) return resolved;
|
|
5632
|
+
} catch {
|
|
4319
5633
|
}
|
|
5634
|
+
return {
|
|
5635
|
+
agentId: process.env.AGENT_ID || "default",
|
|
5636
|
+
agentRole: process.env.AGENT_ROLE || "employee"
|
|
5637
|
+
};
|
|
4320
5638
|
}
|
|
4321
|
-
|
|
4322
|
-
if (_flushing || _pendingRecords.length === 0) return 0;
|
|
4323
|
-
_flushing = true;
|
|
5639
|
+
function getAllActiveAgents() {
|
|
4324
5640
|
try {
|
|
4325
|
-
const
|
|
4326
|
-
const
|
|
4327
|
-
const
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
const userId = row.user_id ?? null;
|
|
4343
|
-
const charOffset = row.char_offset ?? null;
|
|
4344
|
-
const pageNumber = row.page_number ?? null;
|
|
4345
|
-
const sourcePath = row.source_path ?? null;
|
|
4346
|
-
const sourceType = row.source_type ?? null;
|
|
4347
|
-
const tier = row.tier ?? 3;
|
|
4348
|
-
const supersedesId = row.supersedes_id ?? null;
|
|
4349
|
-
const draft = row.draft ? 1 : 0;
|
|
4350
|
-
const memoryType = row.memory_type ?? "raw";
|
|
4351
|
-
const trajectory = row.trajectory ?? null;
|
|
4352
|
-
const contentHash = row.content_hash ?? null;
|
|
4353
|
-
const intent = row.intent ?? null;
|
|
4354
|
-
const outcome = row.outcome ?? null;
|
|
4355
|
-
const domain = row.domain ?? null;
|
|
4356
|
-
const referencedEntities = row.referenced_entities ?? null;
|
|
4357
|
-
const retrievalCount = row.retrieval_count ?? 0;
|
|
4358
|
-
const chainPosition = row.chain_position ?? null;
|
|
4359
|
-
const reviewStatus = row.review_status ?? null;
|
|
4360
|
-
const contextWindowPct = row.context_window_pct ?? null;
|
|
4361
|
-
const filePaths = row.file_paths ?? null;
|
|
4362
|
-
const commitHash = row.commit_hash ?? null;
|
|
4363
|
-
const durationMs = row.duration_ms ?? null;
|
|
4364
|
-
const tokenCost = row.token_cost ?? null;
|
|
4365
|
-
const audience = row.audience ?? null;
|
|
4366
|
-
const languageType = row.language_type ?? null;
|
|
4367
|
-
const parentMemoryId = row.parent_memory_id ?? null;
|
|
4368
|
-
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
4369
|
-
tool_name, project_name,
|
|
4370
|
-
has_error, raw_text, vector, version, task_id, importance, status,
|
|
4371
|
-
confidence, last_accessed,
|
|
4372
|
-
workspace_id, document_id, user_id, char_offset, page_number,
|
|
4373
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
4374
|
-
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
4375
|
-
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
4376
|
-
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
4377
|
-
const metaArgs = [
|
|
4378
|
-
intent,
|
|
4379
|
-
outcome,
|
|
4380
|
-
domain,
|
|
4381
|
-
referencedEntities,
|
|
4382
|
-
retrievalCount,
|
|
4383
|
-
chainPosition,
|
|
4384
|
-
reviewStatus,
|
|
4385
|
-
contextWindowPct,
|
|
4386
|
-
filePaths,
|
|
4387
|
-
commitHash,
|
|
4388
|
-
durationMs,
|
|
4389
|
-
tokenCost,
|
|
4390
|
-
audience,
|
|
4391
|
-
languageType,
|
|
4392
|
-
parentMemoryId
|
|
4393
|
-
];
|
|
4394
|
-
const baseArgs = [
|
|
4395
|
-
row.id,
|
|
4396
|
-
row.agent_id,
|
|
4397
|
-
row.agent_role,
|
|
4398
|
-
row.session_id,
|
|
4399
|
-
row.timestamp,
|
|
4400
|
-
row.tool_name,
|
|
4401
|
-
row.project_name,
|
|
4402
|
-
row.has_error,
|
|
4403
|
-
row.raw_text
|
|
4404
|
-
];
|
|
4405
|
-
const sharedArgs = [
|
|
4406
|
-
row.version,
|
|
4407
|
-
taskId,
|
|
4408
|
-
importance,
|
|
4409
|
-
status,
|
|
4410
|
-
confidence,
|
|
4411
|
-
lastAccessed,
|
|
4412
|
-
workspaceId,
|
|
4413
|
-
documentId,
|
|
4414
|
-
userId,
|
|
4415
|
-
charOffset,
|
|
4416
|
-
pageNumber,
|
|
4417
|
-
sourcePath,
|
|
4418
|
-
sourceType,
|
|
4419
|
-
tier,
|
|
4420
|
-
supersedesId,
|
|
4421
|
-
draft,
|
|
4422
|
-
memoryType,
|
|
4423
|
-
trajectory,
|
|
4424
|
-
contentHash
|
|
4425
|
-
];
|
|
4426
|
-
return {
|
|
4427
|
-
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
4428
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
4429
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4430
|
-
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
4431
|
-
};
|
|
4432
|
-
};
|
|
4433
|
-
const globalClient = getClient();
|
|
4434
|
-
const globalStmts = batch.map(buildStmt);
|
|
4435
|
-
await globalClient.batch(globalStmts, "write");
|
|
4436
|
-
try {
|
|
4437
|
-
const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
|
|
4438
|
-
await insertMemoryCardsForBatch2(batch);
|
|
4439
|
-
} catch {
|
|
4440
|
-
}
|
|
4441
|
-
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4442
|
-
_pendingRecords.splice(0, batch.length);
|
|
4443
|
-
try {
|
|
4444
|
-
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
4445
|
-
if (isShardingEnabled2()) {
|
|
4446
|
-
const byProject = /* @__PURE__ */ new Map();
|
|
4447
|
-
let skippedUnknown = 0;
|
|
4448
|
-
for (const row of batch) {
|
|
4449
|
-
const proj = row.project_name?.trim();
|
|
4450
|
-
if (!proj) {
|
|
4451
|
-
skippedUnknown++;
|
|
5641
|
+
const files = readdirSync3(CACHE_DIR);
|
|
5642
|
+
const sessions = [];
|
|
5643
|
+
for (const file of files) {
|
|
5644
|
+
if (!file.startsWith("active-agent-") || !file.endsWith(".json")) continue;
|
|
5645
|
+
const key = file.slice("active-agent-".length, -".json".length);
|
|
5646
|
+
if (key === "undefined") continue;
|
|
5647
|
+
try {
|
|
5648
|
+
const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
|
|
5649
|
+
const data = JSON.parse(raw);
|
|
5650
|
+
if (!data.agentId) continue;
|
|
5651
|
+
if (data.startedAt) {
|
|
5652
|
+
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
5653
|
+
if (age > STALE_MS) {
|
|
5654
|
+
try {
|
|
5655
|
+
unlinkSync4(path9.join(CACHE_DIR, file));
|
|
5656
|
+
} catch {
|
|
5657
|
+
}
|
|
4452
5658
|
continue;
|
|
4453
5659
|
}
|
|
4454
|
-
if (!byProject.has(proj)) byProject.set(proj, []);
|
|
4455
|
-
byProject.get(proj).push(row);
|
|
4456
|
-
}
|
|
4457
|
-
if (skippedUnknown > 0) {
|
|
4458
|
-
process.stderr.write(
|
|
4459
|
-
`[store] Shard skip: ${skippedUnknown} record(s) with empty project_name (kept in main DB only)
|
|
4460
|
-
`
|
|
4461
|
-
);
|
|
4462
|
-
}
|
|
4463
|
-
for (const [project, rows] of byProject) {
|
|
4464
|
-
try {
|
|
4465
|
-
const shardClient = await getReadyShardClient2(project);
|
|
4466
|
-
const shardStmts = rows.map(buildStmt);
|
|
4467
|
-
await shardClient.batch(shardStmts, "write");
|
|
4468
|
-
} catch (err) {
|
|
4469
|
-
const fullError = err instanceof Error ? `${err.name}: ${err.message}${err.stack ? `
|
|
4470
|
-
${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
|
|
4471
|
-
process.stderr.write(
|
|
4472
|
-
`[store] Shard write failed for ${project} (${rows.length} records): ${fullError}
|
|
4473
|
-
`
|
|
4474
|
-
);
|
|
4475
|
-
}
|
|
4476
5660
|
}
|
|
5661
|
+
sessions.push({
|
|
5662
|
+
agentId: data.agentId,
|
|
5663
|
+
agentRole: data.agentRole || "employee",
|
|
5664
|
+
startedAt: data.startedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
5665
|
+
sessionKey: key
|
|
5666
|
+
});
|
|
5667
|
+
} catch {
|
|
4477
5668
|
}
|
|
4478
|
-
} catch {
|
|
4479
5669
|
}
|
|
4480
|
-
return
|
|
4481
|
-
}
|
|
4482
|
-
|
|
5670
|
+
return sessions;
|
|
5671
|
+
} catch {
|
|
5672
|
+
return [];
|
|
4483
5673
|
}
|
|
4484
5674
|
}
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
5675
|
+
function cleanupSessionMarkers() {
|
|
5676
|
+
const key = getSessionKey();
|
|
5677
|
+
try {
|
|
5678
|
+
unlinkSync4(path9.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
5679
|
+
} catch {
|
|
4489
5680
|
}
|
|
4490
|
-
|
|
4491
|
-
|
|
5681
|
+
try {
|
|
5682
|
+
unlinkSync4(path9.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
5683
|
+
} catch {
|
|
4492
5684
|
}
|
|
4493
|
-
await disposeTurso();
|
|
4494
|
-
_pendingRecords = [];
|
|
4495
|
-
_nextVersion = 1;
|
|
4496
5685
|
}
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
5686
|
+
var CACHE_DIR, STALE_MS;
|
|
5687
|
+
var init_active_agent = __esm({
|
|
5688
|
+
"src/lib/active-agent.ts"() {
|
|
5689
|
+
"use strict";
|
|
5690
|
+
init_config();
|
|
5691
|
+
init_session_key();
|
|
5692
|
+
init_agent_context();
|
|
5693
|
+
init_employees();
|
|
5694
|
+
CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
|
|
5695
|
+
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
5696
|
+
}
|
|
5697
|
+
});
|
|
5698
|
+
|
|
5699
|
+
// src/bin/exe-launch-agent.ts
|
|
5700
|
+
init_store();
|
|
5701
|
+
import os7 from "os";
|
|
5702
|
+
import path10 from "path";
|
|
5703
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
|
|
5704
|
+
import { spawnSync } from "child_process";
|
|
5705
|
+
|
|
5706
|
+
// src/bin/fast-db-init.ts
|
|
5707
|
+
async function fastDbInit() {
|
|
5708
|
+
const { isInitialized: isInitialized2, getClient: getClient2, setExternalClient: setExternalClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5709
|
+
if (isInitialized2()) {
|
|
5710
|
+
return getClient2();
|
|
5711
|
+
}
|
|
5712
|
+
try {
|
|
5713
|
+
const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
|
|
5714
|
+
const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
|
|
5715
|
+
await connectEmbedDaemon2();
|
|
5716
|
+
if (isClientConnected2()) {
|
|
5717
|
+
const daemonClient = {
|
|
5718
|
+
async execute(stmt) {
|
|
5719
|
+
const sql = typeof stmt === "string" ? stmt : stmt.sql;
|
|
5720
|
+
const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
|
|
5721
|
+
const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
|
|
5722
|
+
if (resp.error) throw new Error(String(resp.error));
|
|
5723
|
+
if (resp.db) return deserializeResultSet2(resp.db);
|
|
5724
|
+
throw new Error("Unexpected daemon response");
|
|
5725
|
+
},
|
|
5726
|
+
async batch(stmts, mode) {
|
|
5727
|
+
const statements = stmts.map((s) => {
|
|
5728
|
+
const sql = typeof s === "string" ? s : s.sql;
|
|
5729
|
+
const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
|
|
5730
|
+
return { sql, args };
|
|
5731
|
+
});
|
|
5732
|
+
const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
|
|
5733
|
+
if (resp.error) throw new Error(String(resp.error));
|
|
5734
|
+
const batchResults = resp["db-batch"];
|
|
5735
|
+
if (batchResults) return batchResults.map(deserializeResultSet2);
|
|
5736
|
+
throw new Error("Unexpected daemon batch response");
|
|
5737
|
+
},
|
|
5738
|
+
async transaction(_mode) {
|
|
5739
|
+
throw new Error("Transactions not supported via daemon socket");
|
|
5740
|
+
},
|
|
5741
|
+
async executeMultiple(_sql) {
|
|
5742
|
+
throw new Error("executeMultiple not supported via daemon socket");
|
|
5743
|
+
},
|
|
5744
|
+
async migrate(_stmts) {
|
|
5745
|
+
throw new Error("migrate not supported via daemon socket");
|
|
5746
|
+
},
|
|
5747
|
+
sync() {
|
|
5748
|
+
return Promise.resolve(void 0);
|
|
5749
|
+
},
|
|
5750
|
+
close() {
|
|
5751
|
+
},
|
|
5752
|
+
get closed() {
|
|
5753
|
+
return false;
|
|
5754
|
+
},
|
|
5755
|
+
get protocol() {
|
|
5756
|
+
return "file";
|
|
5757
|
+
}
|
|
5758
|
+
};
|
|
5759
|
+
setExternalClient2(daemonClient);
|
|
5760
|
+
return daemonClient;
|
|
5761
|
+
}
|
|
5762
|
+
} catch {
|
|
5763
|
+
}
|
|
5764
|
+
const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
5765
|
+
await initStore2({ lightweight: true });
|
|
5766
|
+
return getClient2();
|
|
4500
5767
|
}
|
|
4501
5768
|
|
|
4502
5769
|
// src/lib/behaviors-export.ts
|
|
@@ -4506,7 +5773,7 @@ import {
|
|
|
4506
5773
|
existsSync as existsSync8,
|
|
4507
5774
|
mkdirSync as mkdirSync3,
|
|
4508
5775
|
readdirSync as readdirSync2,
|
|
4509
|
-
statSync as
|
|
5776
|
+
statSync as statSync4,
|
|
4510
5777
|
unlinkSync as unlinkSync3,
|
|
4511
5778
|
writeFileSync as writeFileSync3
|
|
4512
5779
|
} from "fs";
|
|
@@ -4578,7 +5845,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
4578
5845
|
for (const entry of entries) {
|
|
4579
5846
|
const filePath = path8.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
4580
5847
|
try {
|
|
4581
|
-
const stat =
|
|
5848
|
+
const stat = statSync4(filePath);
|
|
4582
5849
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
4583
5850
|
unlinkSync3(filePath);
|
|
4584
5851
|
}
|
|
@@ -5121,7 +6388,7 @@ async function main() {
|
|
|
5121
6388
|
}
|
|
5122
6389
|
let behaviorsPath = null;
|
|
5123
6390
|
try {
|
|
5124
|
-
await
|
|
6391
|
+
await fastDbInit();
|
|
5125
6392
|
behaviorsPath = await exportBehaviorsForAgent(agent, "");
|
|
5126
6393
|
} catch (err) {
|
|
5127
6394
|
process.stderr.write(
|