@askexenow/exe-os 0.8.83 → 0.8.85

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 (95) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +97 -2
  5. package/dist/bin/cli.js +14350 -12518
  6. package/dist/bin/exe-agent.js +97 -88
  7. package/dist/bin/exe-assign.js +1003 -854
  8. package/dist/bin/exe-boot.js +1257 -320
  9. package/dist/bin/exe-call.js +10 -0
  10. package/dist/bin/exe-cloud.js +29 -6
  11. package/dist/bin/exe-dispatch.js +210 -34
  12. package/dist/bin/exe-doctor.js +403 -6
  13. package/dist/bin/exe-export-behaviors.js +175 -72
  14. package/dist/bin/exe-forget.js +97 -2
  15. package/dist/bin/exe-gateway.js +550 -171
  16. package/dist/bin/exe-healthcheck.js +1 -0
  17. package/dist/bin/exe-heartbeat.js +100 -5
  18. package/dist/bin/exe-kill.js +175 -72
  19. package/dist/bin/exe-launch-agent.js +189 -76
  20. package/dist/bin/exe-link.js +902 -80
  21. package/dist/bin/exe-new-employee.js +38 -8
  22. package/dist/bin/exe-pending-messages.js +96 -2
  23. package/dist/bin/exe-pending-notifications.js +97 -2
  24. package/dist/bin/exe-pending-reviews.js +98 -3
  25. package/dist/bin/exe-rename.js +564 -23
  26. package/dist/bin/exe-review.js +231 -73
  27. package/dist/bin/exe-search.js +989 -226
  28. package/dist/bin/exe-session-cleanup.js +4806 -1665
  29. package/dist/bin/exe-settings.js +20 -5
  30. package/dist/bin/exe-status.js +97 -2
  31. package/dist/bin/exe-team.js +97 -2
  32. package/dist/bin/git-sweep.js +899 -207
  33. package/dist/bin/graph-backfill.js +175 -72
  34. package/dist/bin/graph-export.js +175 -72
  35. package/dist/bin/install.js +38 -7
  36. package/dist/bin/list-providers.js +1 -0
  37. package/dist/bin/scan-tasks.js +904 -211
  38. package/dist/bin/setup.js +867 -268
  39. package/dist/bin/shard-migrate.js +175 -72
  40. package/dist/bin/update.js +1 -0
  41. package/dist/bin/wiki-sync.js +175 -72
  42. package/dist/gateway/index.js +548 -166
  43. package/dist/hooks/bug-report-worker.js +208 -23
  44. package/dist/hooks/commit-complete.js +897 -205
  45. package/dist/hooks/error-recall.js +988 -226
  46. package/dist/hooks/ingest-worker.js +1638 -1194
  47. package/dist/hooks/ingest.js +3 -0
  48. package/dist/hooks/instructions-loaded.js +707 -97
  49. package/dist/hooks/notification.js +699 -89
  50. package/dist/hooks/post-compact.js +714 -104
  51. package/dist/hooks/pre-compact.js +897 -205
  52. package/dist/hooks/pre-tool-use.js +742 -123
  53. package/dist/hooks/prompt-ingest-worker.js +242 -101
  54. package/dist/hooks/prompt-submit.js +995 -233
  55. package/dist/hooks/response-ingest-worker.js +242 -101
  56. package/dist/hooks/session-end.js +3941 -400
  57. package/dist/hooks/session-start.js +1001 -226
  58. package/dist/hooks/stop.js +725 -115
  59. package/dist/hooks/subagent-stop.js +714 -104
  60. package/dist/hooks/summary-worker.js +1964 -1330
  61. package/dist/index.js +1651 -1053
  62. package/dist/lib/cloud-sync.js +907 -86
  63. package/dist/lib/consolidation.js +2 -1
  64. package/dist/lib/database.js +642 -87
  65. package/dist/lib/db-daemon-client.js +503 -0
  66. package/dist/lib/device-registry.js +547 -7
  67. package/dist/lib/embedder.js +14 -28
  68. package/dist/lib/employee-templates.js +84 -74
  69. package/dist/lib/employees.js +9 -0
  70. package/dist/lib/exe-daemon-client.js +16 -29
  71. package/dist/lib/exe-daemon.js +1955 -922
  72. package/dist/lib/hybrid-search.js +988 -226
  73. package/dist/lib/identity.js +87 -67
  74. package/dist/lib/keychain.js +9 -1
  75. package/dist/lib/messaging.js +8 -1
  76. package/dist/lib/reminders.js +91 -74
  77. package/dist/lib/schedules.js +96 -2
  78. package/dist/lib/skill-learning.js +103 -85
  79. package/dist/lib/store.js +234 -73
  80. package/dist/lib/tasks.js +111 -22
  81. package/dist/lib/tmux-routing.js +120 -31
  82. package/dist/lib/token-spend.js +273 -0
  83. package/dist/lib/ws-client.js +11 -0
  84. package/dist/mcp/server.js +5222 -475
  85. package/dist/mcp/tools/complete-reminder.js +94 -77
  86. package/dist/mcp/tools/create-reminder.js +94 -77
  87. package/dist/mcp/tools/create-task.js +120 -22
  88. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  89. package/dist/mcp/tools/list-reminders.js +94 -77
  90. package/dist/mcp/tools/list-tasks.js +31 -1
  91. package/dist/mcp/tools/send-message.js +8 -1
  92. package/dist/mcp/tools/update-task.js +39 -10
  93. package/dist/runtime/index.js +911 -219
  94. package/dist/tui/App.js +997 -295
  95. package/package.json +6 -1
@@ -262,6 +262,443 @@ var init_db_retry = __esm({
262
262
  }
263
263
  });
264
264
 
265
+ // src/lib/exe-daemon-client.ts
266
+ import net from "net";
267
+ import { spawn } from "child_process";
268
+ import { randomUUID } from "crypto";
269
+ import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
270
+ import path3 from "path";
271
+ import { fileURLToPath } from "url";
272
+ function handleData(chunk) {
273
+ _buffer += chunk.toString();
274
+ if (_buffer.length > MAX_BUFFER) {
275
+ _buffer = "";
276
+ return;
277
+ }
278
+ let newlineIdx;
279
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
280
+ const line = _buffer.slice(0, newlineIdx).trim();
281
+ _buffer = _buffer.slice(newlineIdx + 1);
282
+ if (!line) continue;
283
+ try {
284
+ const response = JSON.parse(line);
285
+ const id = response.id;
286
+ if (!id) continue;
287
+ const entry = _pending.get(id);
288
+ if (entry) {
289
+ clearTimeout(entry.timer);
290
+ _pending.delete(id);
291
+ entry.resolve(response);
292
+ }
293
+ } catch {
294
+ }
295
+ }
296
+ }
297
+ function cleanupStaleFiles() {
298
+ if (existsSync3(PID_PATH)) {
299
+ try {
300
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
301
+ if (pid > 0) {
302
+ try {
303
+ process.kill(pid, 0);
304
+ return;
305
+ } catch {
306
+ }
307
+ }
308
+ } catch {
309
+ }
310
+ try {
311
+ unlinkSync2(PID_PATH);
312
+ } catch {
313
+ }
314
+ try {
315
+ unlinkSync2(SOCKET_PATH);
316
+ } catch {
317
+ }
318
+ }
319
+ }
320
+ function findPackageRoot() {
321
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
322
+ const { root } = path3.parse(dir);
323
+ while (dir !== root) {
324
+ if (existsSync3(path3.join(dir, "package.json"))) return dir;
325
+ dir = path3.dirname(dir);
326
+ }
327
+ return null;
328
+ }
329
+ function spawnDaemon() {
330
+ const pkgRoot = findPackageRoot();
331
+ if (!pkgRoot) {
332
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
333
+ return;
334
+ }
335
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
336
+ if (!existsSync3(daemonPath)) {
337
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
338
+ `);
339
+ return;
340
+ }
341
+ const resolvedPath = daemonPath;
342
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
343
+ `);
344
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
345
+ let stderrFd = "ignore";
346
+ try {
347
+ stderrFd = openSync(logPath, "a");
348
+ } catch {
349
+ }
350
+ const child = spawn(process.execPath, [resolvedPath], {
351
+ detached: true,
352
+ stdio: ["ignore", "ignore", stderrFd],
353
+ env: {
354
+ ...process.env,
355
+ TMUX: void 0,
356
+ // Daemon is global — must not inherit session scope
357
+ TMUX_PANE: void 0,
358
+ // Prevents resolveExeSession() from scoping to one session
359
+ EXE_DAEMON_SOCK: SOCKET_PATH,
360
+ EXE_DAEMON_PID: PID_PATH
361
+ }
362
+ });
363
+ child.unref();
364
+ if (typeof stderrFd === "number") {
365
+ try {
366
+ closeSync(stderrFd);
367
+ } catch {
368
+ }
369
+ }
370
+ }
371
+ function acquireSpawnLock() {
372
+ try {
373
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
374
+ closeSync(fd);
375
+ return true;
376
+ } catch {
377
+ try {
378
+ const stat = statSync(SPAWN_LOCK_PATH);
379
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
380
+ try {
381
+ unlinkSync2(SPAWN_LOCK_PATH);
382
+ } catch {
383
+ }
384
+ try {
385
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
386
+ closeSync(fd);
387
+ return true;
388
+ } catch {
389
+ }
390
+ }
391
+ } catch {
392
+ }
393
+ return false;
394
+ }
395
+ }
396
+ function releaseSpawnLock() {
397
+ try {
398
+ unlinkSync2(SPAWN_LOCK_PATH);
399
+ } catch {
400
+ }
401
+ }
402
+ function connectToSocket() {
403
+ return new Promise((resolve) => {
404
+ if (_socket && _connected) {
405
+ resolve(true);
406
+ return;
407
+ }
408
+ const socket = net.createConnection({ path: SOCKET_PATH });
409
+ const connectTimeout = setTimeout(() => {
410
+ socket.destroy();
411
+ resolve(false);
412
+ }, 2e3);
413
+ socket.on("connect", () => {
414
+ clearTimeout(connectTimeout);
415
+ _socket = socket;
416
+ _connected = true;
417
+ _buffer = "";
418
+ socket.on("data", handleData);
419
+ socket.on("close", () => {
420
+ _connected = false;
421
+ _socket = null;
422
+ for (const [id, entry] of _pending) {
423
+ clearTimeout(entry.timer);
424
+ _pending.delete(id);
425
+ entry.resolve({ error: "Connection closed" });
426
+ }
427
+ });
428
+ socket.on("error", () => {
429
+ _connected = false;
430
+ _socket = null;
431
+ });
432
+ resolve(true);
433
+ });
434
+ socket.on("error", () => {
435
+ clearTimeout(connectTimeout);
436
+ resolve(false);
437
+ });
438
+ });
439
+ }
440
+ async function connectEmbedDaemon() {
441
+ if (_socket && _connected) return true;
442
+ if (await connectToSocket()) return true;
443
+ if (acquireSpawnLock()) {
444
+ try {
445
+ cleanupStaleFiles();
446
+ spawnDaemon();
447
+ } finally {
448
+ releaseSpawnLock();
449
+ }
450
+ }
451
+ const start = Date.now();
452
+ let delay2 = 100;
453
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
454
+ await new Promise((r) => setTimeout(r, delay2));
455
+ if (await connectToSocket()) return true;
456
+ delay2 = Math.min(delay2 * 2, 3e3);
457
+ }
458
+ return false;
459
+ }
460
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
461
+ return new Promise((resolve) => {
462
+ if (!_socket || !_connected) {
463
+ resolve({ error: "Not connected" });
464
+ return;
465
+ }
466
+ const id = randomUUID();
467
+ const timer = setTimeout(() => {
468
+ _pending.delete(id);
469
+ resolve({ error: "Request timeout" });
470
+ }, timeoutMs);
471
+ _pending.set(id, { resolve, timer });
472
+ try {
473
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
474
+ } catch {
475
+ clearTimeout(timer);
476
+ _pending.delete(id);
477
+ resolve({ error: "Write failed" });
478
+ }
479
+ });
480
+ }
481
+ function isClientConnected() {
482
+ return _connected;
483
+ }
484
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
485
+ var init_exe_daemon_client = __esm({
486
+ "src/lib/exe-daemon-client.ts"() {
487
+ "use strict";
488
+ init_config();
489
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
490
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
491
+ SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
492
+ SPAWN_LOCK_STALE_MS = 3e4;
493
+ CONNECT_TIMEOUT_MS = 15e3;
494
+ REQUEST_TIMEOUT_MS = 3e4;
495
+ _socket = null;
496
+ _connected = false;
497
+ _buffer = "";
498
+ _pending = /* @__PURE__ */ new Map();
499
+ MAX_BUFFER = 1e7;
500
+ }
501
+ });
502
+
503
+ // src/lib/daemon-protocol.ts
504
+ function serializeValue(v) {
505
+ if (v === null || v === void 0) return null;
506
+ if (typeof v === "bigint") return Number(v);
507
+ if (typeof v === "boolean") return v ? 1 : 0;
508
+ if (v instanceof Uint8Array) {
509
+ return { __blob: Buffer.from(v).toString("base64") };
510
+ }
511
+ if (ArrayBuffer.isView(v)) {
512
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
513
+ }
514
+ if (v instanceof ArrayBuffer) {
515
+ return { __blob: Buffer.from(v).toString("base64") };
516
+ }
517
+ if (typeof v === "string" || typeof v === "number") return v;
518
+ return String(v);
519
+ }
520
+ function deserializeValue(v) {
521
+ if (v === null) return null;
522
+ if (typeof v === "object" && v !== null && "__blob" in v) {
523
+ const buf = Buffer.from(v.__blob, "base64");
524
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
525
+ }
526
+ return v;
527
+ }
528
+ function deserializeResultSet(srs) {
529
+ const rows = srs.rows.map((obj) => {
530
+ const values = srs.columns.map(
531
+ (col) => deserializeValue(obj[col] ?? null)
532
+ );
533
+ const row = values;
534
+ for (let i = 0; i < srs.columns.length; i++) {
535
+ const col = srs.columns[i];
536
+ if (col !== void 0) {
537
+ row[col] = values[i] ?? null;
538
+ }
539
+ }
540
+ Object.defineProperty(row, "length", {
541
+ value: values.length,
542
+ enumerable: false
543
+ });
544
+ return row;
545
+ });
546
+ return {
547
+ columns: srs.columns,
548
+ columnTypes: srs.columnTypes ?? [],
549
+ rows,
550
+ rowsAffected: srs.rowsAffected,
551
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
552
+ toJSON: () => ({
553
+ columns: srs.columns,
554
+ columnTypes: srs.columnTypes ?? [],
555
+ rows: srs.rows,
556
+ rowsAffected: srs.rowsAffected,
557
+ lastInsertRowid: srs.lastInsertRowid
558
+ })
559
+ };
560
+ }
561
+ var init_daemon_protocol = __esm({
562
+ "src/lib/daemon-protocol.ts"() {
563
+ "use strict";
564
+ }
565
+ });
566
+
567
+ // src/lib/db-daemon-client.ts
568
+ var db_daemon_client_exports = {};
569
+ __export(db_daemon_client_exports, {
570
+ createDaemonDbClient: () => createDaemonDbClient,
571
+ initDaemonDbClient: () => initDaemonDbClient
572
+ });
573
+ function normalizeStatement(stmt) {
574
+ if (typeof stmt === "string") {
575
+ return { sql: stmt, args: [] };
576
+ }
577
+ const sql = stmt.sql;
578
+ let args = [];
579
+ if (Array.isArray(stmt.args)) {
580
+ args = stmt.args.map((v) => serializeValue(v));
581
+ } else if (stmt.args && typeof stmt.args === "object") {
582
+ const named = {};
583
+ for (const [key, val] of Object.entries(stmt.args)) {
584
+ named[key] = serializeValue(val);
585
+ }
586
+ return { sql, args: named };
587
+ }
588
+ return { sql, args };
589
+ }
590
+ function createDaemonDbClient(fallbackClient) {
591
+ let _useDaemon = false;
592
+ const client = {
593
+ async execute(stmt) {
594
+ if (!_useDaemon || !isClientConnected()) {
595
+ return fallbackClient.execute(stmt);
596
+ }
597
+ const { sql, args } = normalizeStatement(stmt);
598
+ const response = await sendDaemonRequest({
599
+ type: "db-execute",
600
+ sql,
601
+ args
602
+ });
603
+ if (response.error) {
604
+ const errMsg = String(response.error);
605
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
606
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
607
+ `);
608
+ return fallbackClient.execute(stmt);
609
+ }
610
+ throw new Error(errMsg);
611
+ }
612
+ if (response.db) {
613
+ return deserializeResultSet(response.db);
614
+ }
615
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
616
+ return fallbackClient.execute(stmt);
617
+ },
618
+ async batch(stmts, mode) {
619
+ if (!_useDaemon || !isClientConnected()) {
620
+ return fallbackClient.batch(stmts, mode);
621
+ }
622
+ const statements = stmts.map(normalizeStatement);
623
+ const response = await sendDaemonRequest({
624
+ type: "db-batch",
625
+ statements,
626
+ mode: mode ?? "deferred"
627
+ });
628
+ if (response.error) {
629
+ const errMsg = String(response.error);
630
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
631
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
632
+ `);
633
+ return fallbackClient.batch(stmts, mode);
634
+ }
635
+ throw new Error(errMsg);
636
+ }
637
+ const batchResults = response["db-batch"];
638
+ if (batchResults) {
639
+ return batchResults.map(deserializeResultSet);
640
+ }
641
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
642
+ return fallbackClient.batch(stmts, mode);
643
+ },
644
+ // Transaction support — delegate to fallback (transactions need direct connection)
645
+ async transaction(mode) {
646
+ return fallbackClient.transaction(mode);
647
+ },
648
+ // executeMultiple — delegate to fallback (used only for schema migrations)
649
+ async executeMultiple(sql) {
650
+ return fallbackClient.executeMultiple(sql);
651
+ },
652
+ // migrate — delegate to fallback
653
+ async migrate(stmts) {
654
+ return fallbackClient.migrate(stmts);
655
+ },
656
+ // Sync mode — delegate to fallback
657
+ sync() {
658
+ return fallbackClient.sync();
659
+ },
660
+ close() {
661
+ _useDaemon = false;
662
+ },
663
+ get closed() {
664
+ return fallbackClient.closed;
665
+ },
666
+ get protocol() {
667
+ return fallbackClient.protocol;
668
+ }
669
+ };
670
+ return {
671
+ ...client,
672
+ /** Enable daemon routing (call after confirming daemon is connected) */
673
+ _enableDaemon() {
674
+ _useDaemon = true;
675
+ },
676
+ /** Check if daemon routing is active */
677
+ _isDaemonActive() {
678
+ return _useDaemon && isClientConnected();
679
+ }
680
+ };
681
+ }
682
+ async function initDaemonDbClient(fallbackClient) {
683
+ if (process.env.EXE_IS_DAEMON === "1") return null;
684
+ const connected = await connectEmbedDaemon();
685
+ if (!connected) {
686
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
687
+ return null;
688
+ }
689
+ const client = createDaemonDbClient(fallbackClient);
690
+ client._enableDaemon();
691
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
692
+ return client;
693
+ }
694
+ var init_db_daemon_client = __esm({
695
+ "src/lib/db-daemon-client.ts"() {
696
+ "use strict";
697
+ init_exe_daemon_client();
698
+ init_daemon_protocol();
699
+ }
700
+ });
701
+
265
702
  // src/lib/database.ts
266
703
  var database_exports = {};
267
704
  __export(database_exports, {
@@ -270,6 +707,7 @@ __export(database_exports, {
270
707
  ensureSchema: () => ensureSchema,
271
708
  getClient: () => getClient,
272
709
  getRawClient: () => getRawClient,
710
+ initDaemonClient: () => initDaemonClient,
273
711
  initDatabase: () => initDatabase,
274
712
  initTurso: () => initTurso,
275
713
  isInitialized: () => isInitialized
@@ -297,8 +735,27 @@ function getClient() {
297
735
  if (!_resilientClient) {
298
736
  throw new Error("Database client not initialized. Call initDatabase() first.");
299
737
  }
738
+ if (process.env.EXE_IS_DAEMON === "1") {
739
+ return _resilientClient;
740
+ }
741
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
742
+ return _daemonClient;
743
+ }
300
744
  return _resilientClient;
301
745
  }
746
+ async function initDaemonClient() {
747
+ if (process.env.EXE_IS_DAEMON === "1") return;
748
+ if (!_resilientClient) return;
749
+ try {
750
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
751
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
752
+ } catch (err) {
753
+ process.stderr.write(
754
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
755
+ `
756
+ );
757
+ }
758
+ }
302
759
  function getRawClient() {
303
760
  if (!_client) {
304
761
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -785,6 +1242,12 @@ async function ensureSchema() {
785
1242
  } catch {
786
1243
  }
787
1244
  }
1245
+ try {
1246
+ await client.execute(
1247
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1248
+ );
1249
+ } catch {
1250
+ }
788
1251
  await client.executeMultiple(`
789
1252
  CREATE TABLE IF NOT EXISTS entities (
790
1253
  id TEXT PRIMARY KEY,
@@ -837,7 +1300,30 @@ async function ensureSchema() {
837
1300
  entity_id TEXT NOT NULL,
838
1301
  PRIMARY KEY (hyperedge_id, entity_id)
839
1302
  );
1303
+
1304
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1305
+ name,
1306
+ content=entities,
1307
+ content_rowid=rowid
1308
+ );
1309
+
1310
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1311
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1312
+ END;
1313
+
1314
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1315
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1316
+ END;
1317
+
1318
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1319
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1320
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1321
+ END;
840
1322
  `);
1323
+ try {
1324
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1325
+ } catch {
1326
+ }
841
1327
  await client.executeMultiple(`
842
1328
  CREATE TABLE IF NOT EXISTS entity_aliases (
843
1329
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1018,6 +1504,33 @@ async function ensureSchema() {
1018
1504
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1019
1505
  ON conversations(channel_id);
1020
1506
  `);
1507
+ await client.executeMultiple(`
1508
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1509
+ session_uuid TEXT PRIMARY KEY,
1510
+ agent_id TEXT NOT NULL,
1511
+ session_name TEXT,
1512
+ task_id TEXT,
1513
+ project_name TEXT,
1514
+ started_at TEXT NOT NULL
1515
+ );
1516
+
1517
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1518
+ ON session_agent_map(agent_id);
1519
+ `);
1520
+ try {
1521
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1522
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1523
+ await client.execute({
1524
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1525
+ SELECT session_id, agent_id, '', MIN(timestamp)
1526
+ FROM memories
1527
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1528
+ GROUP BY session_id, agent_id`,
1529
+ args: []
1530
+ });
1531
+ }
1532
+ } catch {
1533
+ }
1021
1534
  try {
1022
1535
  await client.execute({
1023
1536
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1151,15 +1664,41 @@ async function ensureSchema() {
1151
1664
  });
1152
1665
  } catch {
1153
1666
  }
1667
+ for (const col of [
1668
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
1669
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
1670
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
1671
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
1672
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
1673
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
1674
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
1675
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
1676
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
1677
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
1678
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
1679
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
1680
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
1681
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
1682
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1683
+ ]) {
1684
+ try {
1685
+ await client.execute(col);
1686
+ } catch {
1687
+ }
1688
+ }
1154
1689
  }
1155
1690
  async function disposeDatabase() {
1691
+ if (_daemonClient) {
1692
+ _daemonClient.close();
1693
+ _daemonClient = null;
1694
+ }
1156
1695
  if (_client) {
1157
1696
  _client.close();
1158
1697
  _client = null;
1159
1698
  _resilientClient = null;
1160
1699
  }
1161
1700
  }
1162
- var _client, _resilientClient, initTurso, disposeTurso;
1701
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1163
1702
  var init_database = __esm({
1164
1703
  "src/lib/database.ts"() {
1165
1704
  "use strict";
@@ -1167,6 +1706,7 @@ var init_database = __esm({
1167
1706
  init_employees();
1168
1707
  _client = null;
1169
1708
  _resilientClient = null;
1709
+ _daemonClient = null;
1170
1710
  initTurso = initDatabase;
1171
1711
  disposeTurso = disposeDatabase;
1172
1712
  }
@@ -1174,14 +1714,14 @@ var init_database = __esm({
1174
1714
 
1175
1715
  // src/bin/exe-rename.ts
1176
1716
  init_employees();
1177
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync3, unlinkSync as unlinkSync2, existsSync as existsSync3 } from "fs";
1717
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, unlinkSync as unlinkSync3, existsSync as existsSync4 } from "fs";
1178
1718
  import { execSync as execSync2 } from "child_process";
1179
- import path3 from "path";
1719
+ import path4 from "path";
1180
1720
  import { homedir } from "os";
1181
1721
 
1182
1722
  // src/lib/global-procedures.ts
1183
1723
  init_database();
1184
- import { randomUUID } from "crypto";
1724
+ import { randomUUID as randomUUID2 } from "crypto";
1185
1725
 
1186
1726
  // src/lib/platform-procedures.ts
1187
1727
  var PLATFORM_PROCEDURES = [
@@ -1300,12 +1840,13 @@ function personalizePrompt(prompt, templateName, actualName) {
1300
1840
 
1301
1841
  // src/lib/is-main.ts
1302
1842
  import { realpathSync } from "fs";
1303
- import { fileURLToPath } from "url";
1843
+ import { fileURLToPath as fileURLToPath2 } from "url";
1304
1844
  function isMainModule(importMetaUrl) {
1305
1845
  if (process.argv[1] == null) return false;
1846
+ if (process.argv[1].includes("mcp/server")) return false;
1306
1847
  try {
1307
1848
  const scriptPath = realpathSync(process.argv[1]);
1308
- const modulePath = realpathSync(fileURLToPath(importMetaUrl));
1849
+ const modulePath = realpathSync(fileURLToPath2(importMetaUrl));
1309
1850
  return scriptPath === modulePath;
1310
1851
  } catch {
1311
1852
  return importMetaUrl === `file://${process.argv[1]}` || importMetaUrl === new URL(process.argv[1], "file://").href;
@@ -1314,9 +1855,9 @@ function isMainModule(importMetaUrl) {
1314
1855
 
1315
1856
  // src/bin/exe-rename.ts
1316
1857
  async function renameEmployee(oldName, newName, opts = {}) {
1317
- const rosterPath = opts.rosterPath ?? path3.join(homedir(), ".exe-os", "exe-employees.json");
1318
- const identityDir = opts.identityDir ?? path3.join(homedir(), ".exe-os", "identity");
1319
- const agentsDir = opts.agentsDir ?? path3.join(homedir(), ".claude", "agents");
1858
+ const rosterPath = opts.rosterPath ?? path4.join(homedir(), ".exe-os", "exe-employees.json");
1859
+ const identityDir = opts.identityDir ?? path4.join(homedir(), ".exe-os", "identity");
1860
+ const agentsDir = opts.agentsDir ?? path4.join(homedir(), ".claude", "agents");
1320
1861
  const validation = validateEmployeeName(newName);
1321
1862
  if (!validation.valid) {
1322
1863
  return { success: false, error: validation.error };
@@ -1348,10 +1889,10 @@ async function renameEmployee(oldName, newName, opts = {}) {
1348
1889
  writeFileSync2(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1349
1890
  }
1350
1891
  });
1351
- const oldIdentityPath = path3.join(identityDir, `${rosterOldName}.md`);
1352
- const newIdentityPath = path3.join(identityDir, `${newName}.md`);
1353
- if (existsSync3(oldIdentityPath)) {
1354
- const content = readFileSync3(oldIdentityPath, "utf-8");
1892
+ const oldIdentityPath = path4.join(identityDir, `${rosterOldName}.md`);
1893
+ const newIdentityPath = path4.join(identityDir, `${newName}.md`);
1894
+ if (existsSync4(oldIdentityPath)) {
1895
+ const content = readFileSync4(oldIdentityPath, "utf-8");
1355
1896
  const updatedContent = content.replace(
1356
1897
  /^(agent_id:\s*)\S+/m,
1357
1898
  `$1${newName}`
@@ -1361,22 +1902,22 @@ async function renameEmployee(oldName, newName, opts = {}) {
1361
1902
  rollbackStack.push({
1362
1903
  description: "restore identity file",
1363
1904
  undo: () => {
1364
- if (existsSync3(newIdentityPath)) {
1905
+ if (existsSync4(newIdentityPath)) {
1365
1906
  writeFileSync2(newIdentityPath, content, "utf-8");
1366
1907
  renameSync3(newIdentityPath, oldIdentityPath);
1367
1908
  }
1368
1909
  }
1369
1910
  });
1370
1911
  }
1371
- const oldAgentPath = path3.join(agentsDir, `${rosterOldName}.md`);
1372
- const newAgentPath = path3.join(agentsDir, `${newName}.md`);
1373
- if (existsSync3(oldAgentPath)) {
1374
- const agentContent = readFileSync3(oldAgentPath, "utf-8");
1912
+ const oldAgentPath = path4.join(agentsDir, `${rosterOldName}.md`);
1913
+ const newAgentPath = path4.join(agentsDir, `${newName}.md`);
1914
+ if (existsSync4(oldAgentPath)) {
1915
+ const agentContent = readFileSync4(oldAgentPath, "utf-8");
1375
1916
  renameSync3(oldAgentPath, newAgentPath);
1376
1917
  rollbackStack.push({
1377
1918
  description: "restore agent file",
1378
1919
  undo: () => {
1379
- if (existsSync3(newAgentPath)) {
1920
+ if (existsSync4(newAgentPath)) {
1380
1921
  renameSync3(newAgentPath, oldAgentPath);
1381
1922
  writeFileSync2(oldAgentPath, agentContent, "utf-8");
1382
1923
  }
@@ -1456,12 +1997,12 @@ function removeOldSymlinks(name) {
1456
1997
  try {
1457
1998
  const exeBinPath = findExeBin2();
1458
1999
  if (!exeBinPath) return;
1459
- const binDir = path3.dirname(exeBinPath);
2000
+ const binDir = path4.dirname(exeBinPath);
1460
2001
  for (const suffix of ["", "-opencode"]) {
1461
- const linkPath = path3.join(binDir, `${name}${suffix}`);
1462
- if (existsSync3(linkPath)) {
2002
+ const linkPath = path4.join(binDir, `${name}${suffix}`);
2003
+ if (existsSync4(linkPath)) {
1463
2004
  try {
1464
- unlinkSync2(linkPath);
2005
+ unlinkSync3(linkPath);
1465
2006
  } catch {
1466
2007
  }
1467
2008
  }