@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.
Files changed (113) hide show
  1. package/deploy/stack-manifests/v0.9.json +54 -5
  2. package/dist/bin/age-ontology-load.js +61 -0
  3. package/dist/bin/agentic-ontology-backfill.js +4708 -0
  4. package/dist/bin/agentic-reflection-backfill.js +4144 -0
  5. package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1532 -2173
  6. package/dist/bin/backfill-conversations.js +528 -20
  7. package/dist/bin/backfill-responses.js +528 -20
  8. package/dist/bin/backfill-vectors.js +255 -20
  9. package/dist/bin/bulk-sync-postgres.js +4876 -0
  10. package/dist/bin/cleanup-stale-review-tasks.js +529 -21
  11. package/dist/bin/cli.js +3471 -1491
  12. package/dist/bin/exe-agent-config.js +4 -0
  13. package/dist/bin/exe-agent.js +16 -0
  14. package/dist/bin/exe-assign.js +528 -20
  15. package/dist/bin/exe-boot.js +492 -54
  16. package/dist/bin/exe-call.js +16 -0
  17. package/dist/bin/exe-cloud.js +7415 -518
  18. package/dist/bin/exe-dispatch.js +540 -22
  19. package/dist/bin/exe-doctor.js +3404 -1225
  20. package/dist/bin/exe-export-behaviors.js +542 -24
  21. package/dist/bin/exe-forget.js +529 -21
  22. package/dist/bin/exe-gateway.js +595 -25
  23. package/dist/bin/exe-heartbeat.js +541 -24
  24. package/dist/bin/exe-kill.js +529 -21
  25. package/dist/bin/exe-launch-agent.js +2334 -1067
  26. package/dist/bin/exe-new-employee.js +324 -166
  27. package/dist/bin/exe-pending-messages.js +529 -21
  28. package/dist/bin/exe-pending-notifications.js +529 -21
  29. package/dist/bin/exe-pending-reviews.js +529 -21
  30. package/dist/bin/exe-rename.js +529 -21
  31. package/dist/bin/exe-review.js +529 -21
  32. package/dist/bin/exe-search.js +542 -24
  33. package/dist/bin/exe-session-cleanup.js +540 -22
  34. package/dist/bin/exe-settings.js +14 -0
  35. package/dist/bin/exe-start-codex.js +817 -144
  36. package/dist/bin/exe-start-opencode.js +776 -80
  37. package/dist/bin/exe-status.js +529 -21
  38. package/dist/bin/exe-team.js +529 -21
  39. package/dist/bin/git-sweep.js +540 -22
  40. package/dist/bin/graph-backfill.js +580 -21
  41. package/dist/bin/graph-export.js +529 -21
  42. package/dist/bin/graph-layer-benchmark.js +109 -0
  43. package/dist/bin/install.js +420 -289
  44. package/dist/bin/intercom-check.js +540 -22
  45. package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
  46. package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
  47. package/dist/bin/scan-tasks.js +540 -22
  48. package/dist/bin/setup.js +790 -206
  49. package/dist/bin/shard-migrate.js +528 -20
  50. package/dist/bin/update.js +4 -0
  51. package/dist/gateway/index.js +593 -23
  52. package/dist/hooks/bug-report-worker.js +651 -64
  53. package/dist/hooks/codex-stop-task-finalizer.js +540 -22
  54. package/dist/hooks/commit-complete.js +540 -22
  55. package/dist/hooks/error-recall.js +542 -24
  56. package/dist/hooks/exe-heartbeat-hook.js +4 -0
  57. package/dist/hooks/ingest-worker.js +4 -0
  58. package/dist/hooks/ingest.js +539 -22
  59. package/dist/hooks/instructions-loaded.js +529 -21
  60. package/dist/hooks/notification.js +529 -21
  61. package/dist/hooks/post-compact.js +529 -21
  62. package/dist/hooks/post-tool-combined.js +543 -25
  63. package/dist/hooks/pre-compact.js +772 -127
  64. package/dist/hooks/pre-tool-use.js +529 -21
  65. package/dist/hooks/prompt-submit.js +543 -25
  66. package/dist/hooks/session-end.js +673 -140
  67. package/dist/hooks/session-start.js +662 -26
  68. package/dist/hooks/stop.js +540 -23
  69. package/dist/hooks/subagent-stop.js +529 -21
  70. package/dist/hooks/summary-worker.js +571 -126
  71. package/dist/index.js +593 -23
  72. package/dist/lib/agent-config.js +4 -0
  73. package/dist/lib/cloud-sync.js +408 -47
  74. package/dist/lib/config.js +25 -1
  75. package/dist/lib/consolidation.js +5 -1
  76. package/dist/lib/database.js +128 -0
  77. package/dist/lib/db-daemon-client.js +4 -0
  78. package/dist/lib/db.js +128 -0
  79. package/dist/lib/device-registry.js +128 -0
  80. package/dist/lib/embedder.js +25 -1
  81. package/dist/lib/employee-templates.js +16 -0
  82. package/dist/lib/employees.js +4 -0
  83. package/dist/lib/exe-daemon-client.js +4 -0
  84. package/dist/lib/exe-daemon.js +3158 -930
  85. package/dist/lib/hybrid-search.js +542 -24
  86. package/dist/lib/identity.js +7 -0
  87. package/dist/lib/keychain.js +178 -22
  88. package/dist/lib/license.js +4 -0
  89. package/dist/lib/messaging.js +7 -0
  90. package/dist/lib/reminders.js +7 -0
  91. package/dist/lib/schedules.js +255 -20
  92. package/dist/lib/skill-learning.js +28 -1
  93. package/dist/lib/status-brief.js +39 -0
  94. package/dist/lib/store.js +528 -20
  95. package/dist/lib/task-router.js +4 -0
  96. package/dist/lib/tasks.js +28 -1
  97. package/dist/lib/tmux-routing.js +28 -1
  98. package/dist/lib/token-spend.js +7 -0
  99. package/dist/mcp/server.js +2739 -813
  100. package/dist/mcp/tools/complete-reminder.js +7 -0
  101. package/dist/mcp/tools/create-reminder.js +7 -0
  102. package/dist/mcp/tools/create-task.js +28 -1
  103. package/dist/mcp/tools/deactivate-behavior.js +7 -0
  104. package/dist/mcp/tools/list-reminders.js +7 -0
  105. package/dist/mcp/tools/list-tasks.js +7 -0
  106. package/dist/mcp/tools/send-message.js +7 -0
  107. package/dist/mcp/tools/update-task.js +28 -1
  108. package/dist/runtime/index.js +540 -22
  109. package/dist/tui/App.js +618 -29
  110. package/package.json +9 -5
  111. package/src/commands/exe/cloud.md +11 -8
  112. package/stack.release.json +3 -3
  113. 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
- return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
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
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
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/shard-manager.ts
2790
- var shard_manager_exports = {};
2791
- __export(shard_manager_exports, {
2792
- auditShardHealth: () => auditShardHealth,
2793
- disposeShards: () => disposeShards,
2794
- ensureShardSchema: () => ensureShardSchema,
2795
- getOpenShardCount: () => getOpenShardCount,
2796
- getReadyShardClient: () => getReadyShardClient,
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 isShardingEnabled() {
2818
- return _shardingEnabled;
3161
+ function getKeyPath() {
3162
+ return path6.join(getKeyDir(), "master.key");
2819
3163
  }
2820
- function getShardsDir() {
2821
- return SHARDS_DIR;
3164
+ function nativeKeychainAllowed() {
3165
+ return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
2822
3166
  }
2823
- function getShardClient(projectName) {
2824
- if (!_encryptionKey) {
2825
- throw new Error("Shard manager not initialized. Call initShardManager() first.");
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
- const safeName = safeShardName(projectName);
2828
- if (!safeName || safeName === "unknown") {
2829
- throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
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
- const cached = _shards.get(safeName);
2832
- if (cached) {
2833
- _shardLastAccess.set(safeName, Date.now());
2834
- return cached;
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
- while (_shards.size >= MAX_OPEN_SHARDS) {
2837
- evictLRU();
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 shardExists(projectName) {
2849
- const safeName = safeShardName(projectName);
2850
- return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
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 safeShardName(projectName) {
2853
- return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
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 listShards() {
2856
- if (!existsSync7(SHARDS_DIR)) return [];
2857
- return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
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
- async function auditShardHealth(options = {}) {
2860
- if (!_encryptionKey) {
2861
- throw new Error("Shard manager not initialized. Call initShardManager() first.");
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
- const repair = options.repair === true;
2864
- const dryRun = options.dryRun === true;
2865
- const names = listShards();
2866
- const shards = [];
2867
- for (const name of names) {
2868
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2869
- const stat = statSync2(dbPath);
2870
- const item = {
2871
- name,
2872
- path: dbPath,
2873
- ok: false,
2874
- unreadable: false,
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 ensureShardSchema(client) {
2924
- await client.execute("PRAGMA journal_mode = WAL");
2925
- await client.execute("PRAGMA busy_timeout = 30000");
3279
+ async function tryKeytar() {
3280
+ if (!nativeKeychainAllowed()) return null;
2926
3281
  try {
2927
- await client.execute("PRAGMA libsql_vector_search_ef = 128");
3282
+ return await import("keytar");
2928
3283
  } catch {
3284
+ return null;
2929
3285
  }
2930
- await client.executeMultiple(`
2931
- CREATE TABLE IF NOT EXISTS memories (
2932
- id TEXT PRIMARY KEY,
2933
- agent_id TEXT NOT NULL,
2934
- agent_role TEXT NOT NULL,
2935
- session_id TEXT NOT NULL,
2936
- timestamp TEXT NOT NULL,
2937
- tool_name TEXT NOT NULL,
2938
- project_name TEXT NOT NULL,
2939
- has_error INTEGER NOT NULL DEFAULT 0,
2940
- raw_text TEXT NOT NULL,
2941
- vector F32_BLOB(1024),
2942
- version INTEGER NOT NULL DEFAULT 0
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
- for (const idx of [
3021
- "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3022
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3023
- "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"
3024
- ]) {
3025
- try {
3026
- await client.execute(idx);
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
- await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
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
- for (const idx of [
3035
- "CREATE INDEX IF NOT EXISTS idx_memories_workspace ON memories(workspace_id)",
3036
- "CREATE INDEX IF NOT EXISTS idx_memories_document ON memories(document_id)",
3037
- "CREATE INDEX IF NOT EXISTS idx_memories_user ON memories(user_id)"
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 client.execute(idx);
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
- await client.executeMultiple(`
3045
- CREATE TABLE IF NOT EXISTS entities (
3046
- id TEXT PRIMARY KEY,
3047
- name TEXT NOT NULL,
3048
- type TEXT NOT NULL,
3049
- first_seen TEXT NOT NULL,
3050
- last_seen TEXT NOT NULL,
3051
- properties TEXT DEFAULT '{}',
3052
- UNIQUE(name, type)
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 = statSync2(dbPath);
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
- _cached = process.env.CLAUDE_CODE_SSE_PORT ?? String(process.ppid);
3669
- return _cached;
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
- // src/mcp/agent-context.ts
3686
- import { AsyncLocalStorage } from "async_hooks";
3687
- function getAgentContext() {
3688
- return agentStore.getStore();
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 agentStore;
3691
- var init_agent_context = __esm({
3692
- "src/mcp/agent-context.ts"() {
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
- agentStore = new AsyncLocalStorage();
4543
+ init_database();
4544
+ MAX_CARDS_PER_MEMORY = 6;
4545
+ MAX_SENTENCE_CHARS = 360;
3695
4546
  }
3696
4547
  });
3697
4548
 
3698
- // src/lib/active-agent.ts
3699
- var active_agent_exports = {};
3700
- __export(active_agent_exports, {
3701
- cleanupSessionMarkers: () => cleanupSessionMarkers,
3702
- clearActiveAgent: () => clearActiveAgent,
3703
- getActiveAgent: () => getActiveAgent,
3704
- getAllActiveAgents: () => getAllActiveAgents,
3705
- resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
3706
- writeActiveAgent: () => writeActiveAgent
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 { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
3709
- import { execSync as execSync5 } from "child_process";
3710
- import path9 from "path";
3711
- function isNameWithOptionalInstance(candidate, baseName) {
3712
- if (candidate === baseName) return true;
3713
- if (!candidate.startsWith(baseName)) return false;
3714
- return /^\d+$/.test(candidate.slice(baseName.length));
3715
- }
3716
- function resolveEmployeeFromSessionPrefix(prefix, employees) {
3717
- const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
3718
- for (const employee of sorted) {
3719
- if (isNameWithOptionalInstance(prefix, employee.name)) {
3720
- return { agentId: employee.name, agentRole: employee.role };
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 resolveActiveAgentFromTmuxSession(sessionName) {
3726
- const employees = loadEmployeesSync();
3727
- const coordinator = getCoordinatorEmployee(employees);
3728
- const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
3729
- if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
3730
- return {
3731
- agentId: coordinatorName,
3732
- agentRole: coordinator?.role ?? "COO"
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
- if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
3736
- return {
3737
- agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
3738
- agentRole: coordinator?.role ?? "COO"
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
- if (sessionName.includes("-")) {
3742
- const prefix = sessionName.split("-")[0] ?? "";
3743
- const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
3744
- if (employee) return employee;
3745
- const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
3746
- if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
3747
- const emp = getEmployee(employees, legacy[1]);
3748
- return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
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 null;
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 getMarkerPath() {
3754
- return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
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 writeActiveAgent(agentId, agentRole) {
4704
+ function safeJson(value) {
3757
4705
  try {
3758
- mkdirSync4(CACHE_DIR, { recursive: true });
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 clearActiveAgent() {
3767
- try {
3768
- unlinkSync4(getMarkerPath());
3769
- } catch {
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
- function getActiveAgent() {
3773
- const httpCtx = getAgentContext();
3774
- if (httpCtx) return httpCtx;
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
- try {
3803
- const sessionName = execSync5(
3804
- "tmux display-message -p '#{session_name}' 2>/dev/null",
3805
- { encoding: "utf8", timeout: 2e3 }
3806
- ).trim();
3807
- const resolved = resolveActiveAgentFromTmuxSession(sessionName);
3808
- if (resolved) return resolved;
3809
- } catch {
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 getAllActiveAgents() {
3817
- try {
3818
- const files = readdirSync3(CACHE_DIR);
3819
- const sessions = [];
3820
- for (const file of files) {
3821
- if (!file.startsWith("active-agent-") || !file.endsWith(".json")) continue;
3822
- const key = file.slice("active-agent-".length, -".json".length);
3823
- if (key === "undefined") continue;
3824
- try {
3825
- const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
3826
- const data = JSON.parse(raw);
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 cleanupSessionMarkers() {
3853
- const key = getSessionKey();
3854
- try {
3855
- unlinkSync4(path9.join(CACHE_DIR, `active-agent-${key}.json`));
3856
- } catch {
4857
+ async function initStore(options) {
4858
+ if (_flushTimer !== null) {
4859
+ clearInterval(_flushTimer);
4860
+ _flushTimer = null;
3857
4861
  }
3858
- try {
3859
- unlinkSync4(path9.join(CACHE_DIR, "active-agent-undefined.json"));
3860
- } catch {
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
- var CACHE_DIR, STALE_MS;
3864
- var init_active_agent = __esm({
3865
- "src/lib/active-agent.ts"() {
3866
- "use strict";
3867
- init_config();
3868
- init_session_key();
3869
- init_agent_context();
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
- // src/bin/exe-launch-agent.ts
3877
- import os7 from "os";
3878
- import path10 from "path";
3879
- import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
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
- return execSync2(
3904
- `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
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
- execSync2(
3916
- `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
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
- execSync2(
3922
- `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
3923
- { timeout: 5e3 }
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
- return true;
3926
- } catch {
3927
- return false;
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 linuxSecretGet() {
3931
- if (process.platform !== "linux") return null;
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
- return execSync2(
3934
- `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3935
- { encoding: "utf-8", timeout: 5e3 }
3936
- ).trim();
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
- return null;
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 linuxSecretSet(value) {
3942
- if (process.platform !== "linux") return false;
5040
+ async function flushBatch() {
5041
+ if (_flushing || _pendingRecords.length === 0) return 0;
5042
+ _flushing = true;
3943
5043
  try {
3944
- execSync2(
3945
- `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
3946
- { timeout: 5e3 }
3947
- );
3948
- return true;
3949
- } catch {
3950
- return false;
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
- async function tryKeytar() {
3954
- try {
3955
- return await import("keytar");
3956
- } catch {
3957
- return null;
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
- var ENCRYPTED_PREFIX = "enc:";
3961
- function deriveMachineKey() {
3962
- try {
3963
- const crypto3 = __require("crypto");
3964
- const material = [
3965
- os5.hostname(),
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 readMachineId() {
3978
- try {
3979
- const { readFileSync: readFileSync7 } = __require("fs");
3980
- return readFileSync7("/etc/machine-id", "utf-8").trim();
3981
- } catch {
3982
- return "";
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 encryptWithMachineKey(plaintext, machineKey) {
3986
- const crypto3 = __require("crypto");
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 crypto3 = __require("crypto");
3998
- const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
3999
- if (parts.length !== 3) return null;
4000
- const [ivB64, tagB64, cipherB64] = parts;
4001
- const iv = Buffer.from(ivB64, "base64");
4002
- const authTag = Buffer.from(tagB64, "base64");
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
- return null;
5245
+ client = getClient();
4010
5246
  }
4011
- }
4012
- async function writeMachineBoundFileFallback(b64) {
4013
- const dir = getKeyDir();
4014
- await mkdir3(dir, { recursive: true });
4015
- const keyPath = getKeyPath();
4016
- const machineKey = deriveMachineKey();
4017
- if (machineKey) {
4018
- const encrypted = encryptWithMachineKey(b64, machineKey);
4019
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
4020
- await chmod2(keyPath, 384);
4021
- return "encrypted";
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
- await writeFile3(keyPath, b64 + "\n", "utf-8");
4024
- await chmod2(keyPath, 384);
4025
- return "plaintext";
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
- const keytar = await tryKeytar();
4033
- if (keytar) {
4034
- try {
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
- const keyPath = getKeyPath();
4047
- if (!existsSync6(keyPath)) {
4048
- process.stderr.write(
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 content = (await readFile3(keyPath, "utf-8")).trim();
4056
- let b64Value;
4057
- if (content.startsWith(ENCRYPTED_PREFIX)) {
4058
- const machineKey = deriveMachineKey();
4059
- if (!machineKey) {
4060
- process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
4061
- return null;
4062
- }
4063
- const decrypted = decryptWithMachineKey(content, machineKey);
4064
- if (!decrypted) {
4065
- process.stderr.write(
4066
- "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
4067
- );
4068
- return null;
4069
- }
4070
- b64Value = decrypted;
4071
- } else {
4072
- b64Value = content;
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 key = Buffer.from(b64Value, "base64");
4075
- const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
4076
- if (migrated) {
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
- return key;
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
- // src/lib/store.ts
4104
- init_config();
4105
-
4106
- // src/lib/state-bus.ts
4107
- var StateBus = class {
4108
- handlers = /* @__PURE__ */ new Map();
4109
- globalHandlers = /* @__PURE__ */ new Set();
4110
- /** Emit an event to all subscribers */
4111
- emit(event) {
4112
- const typeHandlers = this.handlers.get(event.type);
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
- /** Subscribe to a specific event type */
4129
- on(type, handler) {
4130
- if (!this.handlers.has(type)) {
4131
- this.handlers.set(type, /* @__PURE__ */ new Set());
4132
- }
4133
- this.handlers.get(type).add(handler);
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
- /** Subscribe to ALL events */
4136
- onAny(handler) {
4137
- this.globalHandlers.add(handler);
5382
+ if (_pendingRecords.length > 0) {
5383
+ await flushBatch();
4138
5384
  }
4139
- /** Unsubscribe from a specific event type */
4140
- off(type, handler) {
4141
- this.handlers.get(type)?.delete(handler);
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
- /** Unsubscribe from ALL events */
4144
- offAny(handler) {
4145
- this.globalHandlers.delete(handler);
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
- /** Remove all listeners */
4148
- clear() {
4149
- this.handlers.clear();
4150
- this.globalHandlers.clear();
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/memory-write-governor.ts
4156
- import { createHash } from "crypto";
4157
- var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4158
- "decision",
4159
- "adr",
4160
- "behavior",
4161
- "procedure"
4162
- ]);
4163
- async function runPostWriteMemoryHygiene(memoryId) {
4164
- try {
4165
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4166
- const client = getClient2();
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
- const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4196
- if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4197
- const old = await client.execute({
4198
- sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4199
- args: [supersedesId]
4200
- });
4201
- const oldImportance = Number(old.rows[0]?.importance ?? 0);
4202
- const newImportance = Number(row.importance ?? 0);
4203
- await client.batch([
4204
- {
4205
- sql: `UPDATE memories
4206
- SET status = 'archived',
4207
- outcome = COALESCE(outcome, 'superseded')
4208
- WHERE id = ?`,
4209
- args: [supersedesId]
4210
- },
4211
- {
4212
- sql: `UPDATE memories
4213
- SET importance = MAX(COALESCE(importance, 5), ?),
4214
- parent_memory_id = COALESCE(parent_memory_id, ?)
4215
- WHERE id = ?`,
4216
- args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
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 schedulePostWriteMemoryHygiene(memoryIds) {
4228
- if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
4229
- if (memoryIds.length === 0) return;
4230
- const run = () => {
4231
- void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4232
- };
4233
- if (typeof setImmediate === "function") setImmediate(run);
4234
- else setTimeout(run, 0);
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/lib/store.ts
4238
- var INIT_MAX_RETRIES = 3;
4239
- var INIT_RETRY_DELAY_MS = 1e3;
4240
- function isBusyError2(err) {
4241
- if (err instanceof Error) {
4242
- const msg = err.message.toLowerCase();
4243
- return msg.includes("sqlite_busy") || msg.includes("database is locked");
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
- return false;
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
- async function retryOnBusy2(fn, label) {
4248
- for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
4249
- try {
4250
- return await fn();
4251
- } catch (err) {
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
- throw new Error("unreachable");
5546
+ return null;
4261
5547
  }
4262
- var _pendingRecords = [];
4263
- var _batchSize = 20;
4264
- var _flushIntervalMs = 1e4;
4265
- var _flushTimer = null;
4266
- var _flushing = false;
4267
- var _nextVersion = 1;
4268
- async function initStore(options) {
4269
- if (_flushTimer !== null) {
4270
- clearInterval(_flushTimer);
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
- _pendingRecords = [];
4274
- _flushing = false;
4275
- _batchSize = options?.batchSize ?? 20;
4276
- _flushIntervalMs = options?.flushIntervalMs ?? 1e4;
4277
- let dbPath = options?.dbPath;
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
- let masterKey = options?.masterKey ?? null;
4283
- if (!masterKey) {
4284
- masterKey = await getMasterKey();
4285
- if (!masterKey) {
4286
- throw new Error(
4287
- "No encryption key found. Run /exe-setup to generate one."
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
- const hexKey = masterKey.toString("hex");
4292
- await initTurso({
4293
- dbPath,
4294
- encryptionKey: hexKey
4295
- });
4296
- await retryOnBusy2(() => ensureSchema(), "ensureSchema");
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
- const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4299
- await initDaemonClient2();
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
- if (!options?.lightweight) {
4303
- try {
4304
- const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4305
- initShardManager2(hexKey);
4306
- } catch {
4307
- }
4308
- const client = getClient();
4309
- const vResult = await retryOnBusy2(
4310
- () => client.execute("SELECT MAX(version) as max_v FROM memories"),
4311
- "version-query"
4312
- );
4313
- _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4314
- try {
4315
- const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
4316
- await loadGlobalProcedures2();
4317
- } catch {
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
- async function flushBatch() {
4322
- if (_flushing || _pendingRecords.length === 0) return 0;
4323
- _flushing = true;
5639
+ function getAllActiveAgents() {
4324
5640
  try {
4325
- const batch = _pendingRecords.slice(0);
4326
- const client = getClient();
4327
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
4328
- let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4329
- for (const row of batch) {
4330
- row.version = baseVersion++;
4331
- }
4332
- _nextVersion = baseVersion;
4333
- const buildStmt = (row) => {
4334
- const hasVector = row.vector !== null;
4335
- const taskId = row.task_id ?? null;
4336
- const importance = row.importance ?? 5;
4337
- const status = row.status ?? "active";
4338
- const confidence = row.confidence ?? 0.7;
4339
- const lastAccessed = row.last_accessed ?? row.timestamp;
4340
- const workspaceId = row.workspace_id ?? null;
4341
- const documentId = row.document_id ?? null;
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 batch.length;
4481
- } finally {
4482
- _flushing = false;
5670
+ return sessions;
5671
+ } catch {
5672
+ return [];
4483
5673
  }
4484
5674
  }
4485
- async function disposeStore() {
4486
- if (_flushTimer !== null) {
4487
- clearInterval(_flushTimer);
4488
- _flushTimer = null;
5675
+ function cleanupSessionMarkers() {
5676
+ const key = getSessionKey();
5677
+ try {
5678
+ unlinkSync4(path9.join(CACHE_DIR, `active-agent-${key}.json`));
5679
+ } catch {
4489
5680
  }
4490
- if (_pendingRecords.length > 0) {
4491
- await flushBatch();
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
- function vectorToBlob(vector) {
4498
- const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
4499
- return JSON.stringify(Array.from(f32));
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 statSync3,
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 = statSync3(filePath);
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 initStore();
6391
+ await fastDbInit();
5125
6392
  behaviorsPath = await exportBehaviorsForAgent(agent, "");
5126
6393
  } catch (err) {
5127
6394
  process.stderr.write(