@askexenow/exe-os 0.8.82 → 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 (97) 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 +14360 -12525
  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 +1260 -323
  9. package/dist/bin/exe-call.js +10 -0
  10. package/dist/bin/exe-cloud.js +32 -9
  11. package/dist/bin/exe-dispatch.js +212 -36
  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 +553 -174
  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 +41 -11
  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 +577 -33
  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 +901 -209
  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 +906 -213
  38. package/dist/bin/setup.js +870 -271
  39. package/dist/bin/shard-migrate.js +175 -72
  40. package/dist/bin/update.js +4 -3
  41. package/dist/bin/wiki-sync.js +175 -72
  42. package/dist/gateway/index.js +550 -168
  43. package/dist/hooks/bug-report-worker.js +210 -25
  44. package/dist/hooks/commit-complete.js +899 -207
  45. package/dist/hooks/error-recall.js +988 -226
  46. package/dist/hooks/ingest-worker.js +1639 -1195
  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 +899 -207
  52. package/dist/hooks/pre-tool-use.js +742 -123
  53. package/dist/hooks/prompt-ingest-worker.js +245 -104
  54. package/dist/hooks/prompt-submit.js +995 -233
  55. package/dist/hooks/response-ingest-worker.js +245 -104
  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 +1970 -1336
  61. package/dist/index.js +1653 -1055
  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 +1957 -924
  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/license.js +3 -3
  76. package/dist/lib/messaging.js +8 -1
  77. package/dist/lib/reminders.js +91 -74
  78. package/dist/lib/schedules.js +96 -2
  79. package/dist/lib/skill-learning.js +103 -85
  80. package/dist/lib/store.js +234 -73
  81. package/dist/lib/tasks.js +113 -24
  82. package/dist/lib/tmux-routing.js +122 -33
  83. package/dist/lib/token-spend.js +273 -0
  84. package/dist/lib/ws-client.js +11 -0
  85. package/dist/mcp/server.js +10874 -5546
  86. package/dist/mcp/tools/complete-reminder.js +94 -77
  87. package/dist/mcp/tools/create-reminder.js +94 -77
  88. package/dist/mcp/tools/create-task.js +810 -27
  89. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  90. package/dist/mcp/tools/list-reminders.js +94 -77
  91. package/dist/mcp/tools/list-tasks.js +31 -1
  92. package/dist/mcp/tools/send-message.js +8 -1
  93. package/dist/mcp/tools/update-task.js +39 -10
  94. package/dist/runtime/index.js +913 -221
  95. package/dist/tui/App.js +1000 -298
  96. package/package.json +6 -1
  97. package/src/commands/exe/build-adv.md +2 -2
@@ -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,28 +1855,31 @@ 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 };
1323
1864
  }
1324
1865
  const employees = await loadEmployees(rosterPath);
1325
- const idx = employees.findIndex((e) => e.name === oldName);
1866
+ const oldNameLower = oldName.toLowerCase();
1867
+ const idx = employees.findIndex((e) => e.name.toLowerCase() === oldNameLower);
1326
1868
  if (idx === -1) {
1327
1869
  return { success: false, error: `Employee '${oldName}' not found` };
1328
1870
  }
1329
- if (employees.some((e) => e.name === newName)) {
1871
+ const newNameLower = newName.toLowerCase();
1872
+ if (employees.some((e) => e.name.toLowerCase() === newNameLower)) {
1330
1873
  return { success: false, error: `Employee '${newName}' already exists` };
1331
1874
  }
1332
1875
  const rollbackStack = [];
1333
1876
  const employee = employees[idx];
1877
+ const rosterOldName = employee.name;
1334
1878
  try {
1335
1879
  const originalName = employee.name;
1336
1880
  const originalPrompt = employee.systemPrompt;
1337
1881
  employee.name = newName;
1338
- employee.systemPrompt = personalizePrompt(originalPrompt, oldName, newName);
1882
+ employee.systemPrompt = personalizePrompt(originalPrompt, rosterOldName, newName);
1339
1883
  await saveEmployees(employees, rosterPath);
1340
1884
  rollbackStack.push({
1341
1885
  description: "restore roster",
@@ -1345,10 +1889,10 @@ async function renameEmployee(oldName, newName, opts = {}) {
1345
1889
  writeFileSync2(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1346
1890
  }
1347
1891
  });
1348
- const oldIdentityPath = path3.join(identityDir, `${oldName}.md`);
1349
- const newIdentityPath = path3.join(identityDir, `${newName}.md`);
1350
- if (existsSync3(oldIdentityPath)) {
1351
- 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");
1352
1896
  const updatedContent = content.replace(
1353
1897
  /^(agent_id:\s*)\S+/m,
1354
1898
  `$1${newName}`
@@ -1358,22 +1902,22 @@ async function renameEmployee(oldName, newName, opts = {}) {
1358
1902
  rollbackStack.push({
1359
1903
  description: "restore identity file",
1360
1904
  undo: () => {
1361
- if (existsSync3(newIdentityPath)) {
1905
+ if (existsSync4(newIdentityPath)) {
1362
1906
  writeFileSync2(newIdentityPath, content, "utf-8");
1363
1907
  renameSync3(newIdentityPath, oldIdentityPath);
1364
1908
  }
1365
1909
  }
1366
1910
  });
1367
1911
  }
1368
- const oldAgentPath = path3.join(agentsDir, `${oldName}.md`);
1369
- const newAgentPath = path3.join(agentsDir, `${newName}.md`);
1370
- if (existsSync3(oldAgentPath)) {
1371
- 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");
1372
1916
  renameSync3(oldAgentPath, newAgentPath);
1373
1917
  rollbackStack.push({
1374
1918
  description: "restore agent file",
1375
1919
  undo: () => {
1376
- if (existsSync3(newAgentPath)) {
1920
+ if (existsSync4(newAgentPath)) {
1377
1921
  renameSync3(newAgentPath, oldAgentPath);
1378
1922
  writeFileSync2(oldAgentPath, agentContent, "utf-8");
1379
1923
  }
@@ -1381,13 +1925,13 @@ async function renameEmployee(oldName, newName, opts = {}) {
1381
1925
  });
1382
1926
  }
1383
1927
  if (!opts.skipSymlinks) {
1384
- removeOldSymlinks(oldName);
1928
+ removeOldSymlinks(rosterOldName);
1385
1929
  const bins = registerBinSymlinks(newName);
1386
1930
  rollbackStack.push({
1387
1931
  description: "restore symlinks",
1388
1932
  undo: () => {
1389
1933
  removeOldSymlinks(newName);
1390
- registerBinSymlinks(oldName);
1934
+ registerBinSymlinks(rosterOldName);
1391
1935
  }
1392
1936
  });
1393
1937
  if (bins.errors.length > 0) {
@@ -1400,23 +1944,23 @@ async function renameEmployee(oldName, newName, opts = {}) {
1400
1944
  const client = getClient2();
1401
1945
  await client.execute({
1402
1946
  sql: "UPDATE memories SET agent_id = ? WHERE agent_id = ?",
1403
- args: [newName, oldName]
1947
+ args: [newName, rosterOldName]
1404
1948
  });
1405
1949
  await client.execute({
1406
1950
  sql: "UPDATE conversations SET agent_name = ? WHERE agent_name = ?",
1407
- args: [newName, oldName]
1951
+ args: [newName, rosterOldName]
1408
1952
  });
1409
1953
  await client.execute({
1410
1954
  sql: "UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?",
1411
- args: [newName, oldName]
1955
+ args: [newName, rosterOldName]
1412
1956
  });
1413
1957
  await client.execute({
1414
1958
  sql: "UPDATE behaviors SET agent_id = ? WHERE agent_id = ?",
1415
- args: [newName, oldName]
1959
+ args: [newName, rosterOldName]
1416
1960
  });
1417
1961
  await client.execute({
1418
1962
  sql: "UPDATE identity SET agent_id = ? WHERE agent_id = ?",
1419
- args: [newName, oldName]
1963
+ args: [newName, rosterOldName]
1420
1964
  });
1421
1965
  } catch (dbErr) {
1422
1966
  console.error(`DB migration failed: ${dbErr instanceof Error ? dbErr.message : String(dbErr)}`);
@@ -1453,12 +1997,12 @@ function removeOldSymlinks(name) {
1453
1997
  try {
1454
1998
  const exeBinPath = findExeBin2();
1455
1999
  if (!exeBinPath) return;
1456
- const binDir = path3.dirname(exeBinPath);
2000
+ const binDir = path4.dirname(exeBinPath);
1457
2001
  for (const suffix of ["", "-opencode"]) {
1458
- const linkPath = path3.join(binDir, `${name}${suffix}`);
1459
- if (existsSync3(linkPath)) {
2002
+ const linkPath = path4.join(binDir, `${name}${suffix}`);
2003
+ if (existsSync4(linkPath)) {
1460
2004
  try {
1461
- unlinkSync2(linkPath);
2005
+ unlinkSync3(linkPath);
1462
2006
  } catch {
1463
2007
  }
1464
2008
  }