@askexenow/exe-os 0.9.6 → 0.9.8

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 +754 -79
  2. package/dist/bin/backfill-responses.js +752 -77
  3. package/dist/bin/backfill-vectors.js +752 -77
  4. package/dist/bin/cleanup-stale-review-tasks.js +668 -37
  5. package/dist/bin/cli.js +1399 -607
  6. package/dist/bin/exe-agent-config.js +123 -95
  7. package/dist/bin/exe-agent.js +41 -25
  8. package/dist/bin/exe-assign.js +732 -57
  9. package/dist/bin/exe-boot.js +795 -155
  10. package/dist/bin/exe-call.js +209 -138
  11. package/dist/bin/exe-cloud.js +35 -12
  12. package/dist/bin/exe-dispatch.js +703 -72
  13. package/dist/bin/exe-doctor.js +648 -26
  14. package/dist/bin/exe-export-behaviors.js +650 -20
  15. package/dist/bin/exe-forget.js +635 -13
  16. package/dist/bin/exe-gateway.js +1064 -273
  17. package/dist/bin/exe-heartbeat.js +676 -45
  18. package/dist/bin/exe-kill.js +646 -16
  19. package/dist/bin/exe-launch-agent.js +887 -97
  20. package/dist/bin/exe-link.js +658 -43
  21. package/dist/bin/exe-new-employee.js +378 -177
  22. package/dist/bin/exe-pending-messages.js +656 -34
  23. package/dist/bin/exe-pending-notifications.js +635 -13
  24. package/dist/bin/exe-pending-reviews.js +659 -37
  25. package/dist/bin/exe-rename.js +645 -30
  26. package/dist/bin/exe-review.js +635 -13
  27. package/dist/bin/exe-search.js +771 -88
  28. package/dist/bin/exe-session-cleanup.js +845 -152
  29. package/dist/bin/exe-settings.js +127 -91
  30. package/dist/bin/exe-start-codex.js +729 -94
  31. package/dist/bin/exe-start-opencode.js +717 -82
  32. package/dist/bin/exe-status.js +668 -37
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +731 -91
  35. package/dist/bin/graph-backfill.js +643 -13
  36. package/dist/bin/graph-export.js +646 -16
  37. package/dist/bin/install.js +596 -193
  38. package/dist/bin/scan-tasks.js +735 -95
  39. package/dist/bin/setup.js +1038 -210
  40. package/dist/bin/shard-migrate.js +645 -15
  41. package/dist/bin/wiki-sync.js +646 -16
  42. package/dist/gateway/index.js +1038 -247
  43. package/dist/hooks/bug-report-worker.js +902 -172
  44. package/dist/hooks/commit-complete.js +729 -89
  45. package/dist/hooks/error-recall.js +776 -93
  46. package/dist/hooks/exe-heartbeat-hook.js +85 -71
  47. package/dist/hooks/ingest-worker.js +851 -158
  48. package/dist/hooks/ingest.js +90 -73
  49. package/dist/hooks/instructions-loaded.js +669 -38
  50. package/dist/hooks/notification.js +661 -30
  51. package/dist/hooks/post-compact.js +685 -45
  52. package/dist/hooks/pre-compact.js +729 -89
  53. package/dist/hooks/pre-tool-use.js +883 -127
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1071 -321
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +732 -92
  58. package/dist/hooks/session-start.js +1042 -209
  59. package/dist/hooks/stop.js +691 -51
  60. package/dist/hooks/subagent-stop.js +685 -45
  61. package/dist/hooks/summary-worker.js +827 -134
  62. package/dist/index.js +1026 -234
  63. package/dist/lib/cloud-sync.js +663 -48
  64. package/dist/lib/consolidation.js +26 -3
  65. package/dist/lib/database.js +626 -18
  66. package/dist/lib/db.js +2261 -0
  67. package/dist/lib/device-registry.js +640 -25
  68. package/dist/lib/embedder.js +96 -43
  69. package/dist/lib/employee-templates.js +16 -0
  70. package/dist/lib/employees.js +259 -83
  71. package/dist/lib/exe-daemon-client.js +101 -63
  72. package/dist/lib/exe-daemon.js +905 -164
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +66 -30
  76. package/dist/lib/reminders.js +21 -1
  77. package/dist/lib/schedules.js +636 -14
  78. package/dist/lib/skill-learning.js +21 -1
  79. package/dist/lib/store.js +643 -13
  80. package/dist/lib/task-router.js +82 -71
  81. package/dist/lib/tasks.js +109 -73
  82. package/dist/lib/tmux-routing.js +98 -62
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1807 -472
  85. package/dist/mcp/tools/complete-reminder.js +21 -1
  86. package/dist/mcp/tools/create-reminder.js +21 -1
  87. package/dist/mcp/tools/create-task.js +301 -166
  88. package/dist/mcp/tools/deactivate-behavior.js +24 -4
  89. package/dist/mcp/tools/list-reminders.js +21 -1
  90. package/dist/mcp/tools/list-tasks.js +206 -40
  91. package/dist/mcp/tools/send-message.js +69 -33
  92. package/dist/mcp/tools/update-task.js +86 -50
  93. package/dist/runtime/index.js +731 -91
  94. package/dist/tui/App.js +864 -125
  95. package/package.json +3 -2
@@ -388,7 +388,7 @@ function isMultiInstance(agentName2, employees) {
388
388
  if (!emp) return false;
389
389
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
390
390
  }
391
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
391
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
392
392
  var init_employees = __esm({
393
393
  "src/lib/employees.ts"() {
394
394
  "use strict";
@@ -397,16 +397,601 @@ var init_employees = __esm({
397
397
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
398
398
  COORDINATOR_ROLE = "COO";
399
399
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
400
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
401
+ }
402
+ });
403
+
404
+ // src/lib/database-adapter.ts
405
+ import os3 from "os";
406
+ import path3 from "path";
407
+ import { createRequire } from "module";
408
+ import { pathToFileURL } from "url";
409
+ function quotedIdentifier(identifier) {
410
+ return `"${identifier.replace(/"/g, '""')}"`;
411
+ }
412
+ function unqualifiedTableName(name) {
413
+ const raw = name.trim().replace(/^"|"$/g, "");
414
+ const parts = raw.split(".");
415
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
416
+ }
417
+ function stripTrailingSemicolon(sql) {
418
+ return sql.trim().replace(/;+\s*$/u, "");
419
+ }
420
+ function appendClause(sql, clause) {
421
+ const trimmed = stripTrailingSemicolon(sql);
422
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
423
+ if (!returningMatch) {
424
+ return `${trimmed}${clause}`;
425
+ }
426
+ const idx = returningMatch.index;
427
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
428
+ }
429
+ function normalizeStatement(stmt) {
430
+ if (typeof stmt === "string") {
431
+ return { kind: "positional", sql: stmt, args: [] };
432
+ }
433
+ const sql = stmt.sql;
434
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
435
+ return { kind: "positional", sql, args: stmt.args ?? [] };
436
+ }
437
+ return { kind: "named", sql, args: stmt.args };
438
+ }
439
+ function rewriteBooleanLiterals(sql) {
440
+ let out = sql;
441
+ for (const column of BOOLEAN_COLUMN_NAMES) {
442
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
443
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
444
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
445
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
446
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
447
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
448
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
449
+ }
450
+ return out;
451
+ }
452
+ function rewriteInsertOrIgnore(sql) {
453
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
454
+ return sql;
455
+ }
456
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
457
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
458
+ }
459
+ function rewriteInsertOrReplace(sql) {
460
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
461
+ if (!match) {
462
+ return sql;
463
+ }
464
+ const rawTable = match[1];
465
+ const rawColumns = match[2];
466
+ const remainder = match[3];
467
+ const tableName = unqualifiedTableName(rawTable);
468
+ const conflictKeys = UPSERT_KEYS[tableName];
469
+ if (!conflictKeys?.length) {
470
+ return sql;
471
+ }
472
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
473
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
474
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
475
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
476
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
477
+ }
478
+ function rewriteSql(sql) {
479
+ let out = sql;
480
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
481
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
482
+ out = rewriteBooleanLiterals(out);
483
+ out = rewriteInsertOrReplace(out);
484
+ out = rewriteInsertOrIgnore(out);
485
+ return stripTrailingSemicolon(out);
486
+ }
487
+ function toBoolean(value) {
488
+ if (value === null || value === void 0) return value;
489
+ if (typeof value === "boolean") return value;
490
+ if (typeof value === "number") return value !== 0;
491
+ if (typeof value === "bigint") return value !== 0n;
492
+ if (typeof value === "string") {
493
+ const normalized = value.trim().toLowerCase();
494
+ if (normalized === "0" || normalized === "false") return false;
495
+ if (normalized === "1" || normalized === "true") return true;
496
+ }
497
+ return Boolean(value);
498
+ }
499
+ function countQuestionMarks(sql, end) {
500
+ let count = 0;
501
+ let inSingle = false;
502
+ let inDouble = false;
503
+ let inLineComment = false;
504
+ let inBlockComment = false;
505
+ for (let i = 0; i < end; i++) {
506
+ const ch = sql[i];
507
+ const next = sql[i + 1];
508
+ if (inLineComment) {
509
+ if (ch === "\n") inLineComment = false;
510
+ continue;
511
+ }
512
+ if (inBlockComment) {
513
+ if (ch === "*" && next === "/") {
514
+ inBlockComment = false;
515
+ i += 1;
516
+ }
517
+ continue;
518
+ }
519
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
520
+ inLineComment = true;
521
+ i += 1;
522
+ continue;
523
+ }
524
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
525
+ inBlockComment = true;
526
+ i += 1;
527
+ continue;
528
+ }
529
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
530
+ inSingle = !inSingle;
531
+ continue;
532
+ }
533
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
534
+ inDouble = !inDouble;
535
+ continue;
536
+ }
537
+ if (!inSingle && !inDouble && ch === "?") {
538
+ count += 1;
539
+ }
540
+ }
541
+ return count;
542
+ }
543
+ function findBooleanPlaceholderIndexes(sql) {
544
+ const indexes = /* @__PURE__ */ new Set();
545
+ for (const column of BOOLEAN_COLUMN_NAMES) {
546
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
547
+ for (const match of sql.matchAll(pattern)) {
548
+ const matchText = match[0];
549
+ const qIndex = match.index + matchText.lastIndexOf("?");
550
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
551
+ }
552
+ }
553
+ return indexes;
554
+ }
555
+ function coerceInsertBooleanArgs(sql, args) {
556
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
557
+ if (!match) return;
558
+ const rawTable = match[1];
559
+ const rawColumns = match[2];
560
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
561
+ if (!boolColumns?.size) return;
562
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
563
+ for (const [index, column] of columns.entries()) {
564
+ if (boolColumns.has(column) && index < args.length) {
565
+ args[index] = toBoolean(args[index]);
566
+ }
567
+ }
568
+ }
569
+ function coerceUpdateBooleanArgs(sql, args) {
570
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
571
+ if (!match) return;
572
+ const rawTable = match[1];
573
+ const setClause = match[2];
574
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
575
+ if (!boolColumns?.size) return;
576
+ const assignments = setClause.split(",");
577
+ let placeholderIndex = 0;
578
+ for (const assignment of assignments) {
579
+ if (!assignment.includes("?")) continue;
580
+ placeholderIndex += 1;
581
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
582
+ if (colMatch && boolColumns.has(colMatch[1])) {
583
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
584
+ }
585
+ }
586
+ }
587
+ function coerceBooleanArgs(sql, args) {
588
+ const nextArgs = [...args];
589
+ coerceInsertBooleanArgs(sql, nextArgs);
590
+ coerceUpdateBooleanArgs(sql, nextArgs);
591
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
592
+ for (const index of placeholderIndexes) {
593
+ if (index > 0 && index <= nextArgs.length) {
594
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
595
+ }
596
+ }
597
+ return nextArgs;
598
+ }
599
+ function convertQuestionMarksToDollarParams(sql) {
600
+ let out = "";
601
+ let placeholder = 0;
602
+ let inSingle = false;
603
+ let inDouble = false;
604
+ let inLineComment = false;
605
+ let inBlockComment = false;
606
+ for (let i = 0; i < sql.length; i++) {
607
+ const ch = sql[i];
608
+ const next = sql[i + 1];
609
+ if (inLineComment) {
610
+ out += ch;
611
+ if (ch === "\n") inLineComment = false;
612
+ continue;
613
+ }
614
+ if (inBlockComment) {
615
+ out += ch;
616
+ if (ch === "*" && next === "/") {
617
+ out += next;
618
+ inBlockComment = false;
619
+ i += 1;
620
+ }
621
+ continue;
622
+ }
623
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
624
+ out += ch + next;
625
+ inLineComment = true;
626
+ i += 1;
627
+ continue;
628
+ }
629
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
630
+ out += ch + next;
631
+ inBlockComment = true;
632
+ i += 1;
633
+ continue;
634
+ }
635
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
636
+ inSingle = !inSingle;
637
+ out += ch;
638
+ continue;
639
+ }
640
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
641
+ inDouble = !inDouble;
642
+ out += ch;
643
+ continue;
644
+ }
645
+ if (!inSingle && !inDouble && ch === "?") {
646
+ placeholder += 1;
647
+ out += `$${placeholder}`;
648
+ continue;
649
+ }
650
+ out += ch;
651
+ }
652
+ return out;
653
+ }
654
+ function translateStatementForPostgres(stmt) {
655
+ const normalized = normalizeStatement(stmt);
656
+ if (normalized.kind === "named") {
657
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
658
+ }
659
+ const rewrittenSql = rewriteSql(normalized.sql);
660
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
661
+ return {
662
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
663
+ args: coercedArgs
664
+ };
665
+ }
666
+ function shouldBypassPostgres(stmt) {
667
+ const normalized = normalizeStatement(stmt);
668
+ if (normalized.kind === "named") {
669
+ return true;
670
+ }
671
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
672
+ }
673
+ function shouldFallbackOnError(error) {
674
+ const message = error instanceof Error ? error.message : String(error);
675
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
676
+ }
677
+ function isReadQuery(sql) {
678
+ const trimmed = sql.trimStart();
679
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
680
+ }
681
+ function buildRow(row, columns) {
682
+ const values = columns.map((column) => row[column]);
683
+ return Object.assign(values, row);
684
+ }
685
+ function buildResultSet(rows, rowsAffected = 0) {
686
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
687
+ const resultRows = rows.map((row) => buildRow(row, columns));
688
+ return {
689
+ columns,
690
+ columnTypes: columns.map(() => ""),
691
+ rows: resultRows,
692
+ rowsAffected,
693
+ lastInsertRowid: void 0,
694
+ toJSON() {
695
+ return {
696
+ columns,
697
+ columnTypes: columns.map(() => ""),
698
+ rows,
699
+ rowsAffected,
700
+ lastInsertRowid: void 0
701
+ };
702
+ }
703
+ };
704
+ }
705
+ async function loadPrismaClient() {
706
+ if (!prismaClientPromise) {
707
+ prismaClientPromise = (async () => {
708
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
709
+ if (explicitPath) {
710
+ const module2 = await import(pathToFileURL(explicitPath).href);
711
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
712
+ if (!PrismaClient2) {
713
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
714
+ }
715
+ return new PrismaClient2();
716
+ }
717
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
718
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
719
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
720
+ const module = await import(pathToFileURL(prismaEntry).href);
721
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
722
+ if (!PrismaClient) {
723
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
724
+ }
725
+ return new PrismaClient();
726
+ })();
727
+ }
728
+ return prismaClientPromise;
729
+ }
730
+ async function ensureCompatibilityViews(prisma) {
731
+ if (!compatibilityBootstrapPromise) {
732
+ compatibilityBootstrapPromise = (async () => {
733
+ for (const mapping of VIEW_MAPPINGS) {
734
+ const relation = mapping.source.replace(/"/g, "");
735
+ const rows = await prisma.$queryRawUnsafe(
736
+ "SELECT to_regclass($1) AS regclass",
737
+ relation
738
+ );
739
+ if (!rows[0]?.regclass) {
740
+ continue;
741
+ }
742
+ await prisma.$executeRawUnsafe(
743
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
744
+ );
745
+ }
746
+ })();
747
+ }
748
+ return compatibilityBootstrapPromise;
749
+ }
750
+ async function executeOnPrisma(executor, stmt) {
751
+ const translated = translateStatementForPostgres(stmt);
752
+ if (isReadQuery(translated.sql)) {
753
+ const rows = await executor.$queryRawUnsafe(
754
+ translated.sql,
755
+ ...translated.args
756
+ );
757
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
758
+ }
759
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
760
+ return buildResultSet([], rowsAffected);
761
+ }
762
+ function splitSqlStatements(sql) {
763
+ const parts = [];
764
+ let current = "";
765
+ let inSingle = false;
766
+ let inDouble = false;
767
+ let inLineComment = false;
768
+ let inBlockComment = false;
769
+ for (let i = 0; i < sql.length; i++) {
770
+ const ch = sql[i];
771
+ const next = sql[i + 1];
772
+ if (inLineComment) {
773
+ current += ch;
774
+ if (ch === "\n") inLineComment = false;
775
+ continue;
776
+ }
777
+ if (inBlockComment) {
778
+ current += ch;
779
+ if (ch === "*" && next === "/") {
780
+ current += next;
781
+ inBlockComment = false;
782
+ i += 1;
783
+ }
784
+ continue;
785
+ }
786
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
787
+ current += ch + next;
788
+ inLineComment = true;
789
+ i += 1;
790
+ continue;
791
+ }
792
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
793
+ current += ch + next;
794
+ inBlockComment = true;
795
+ i += 1;
796
+ continue;
797
+ }
798
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
799
+ inSingle = !inSingle;
800
+ current += ch;
801
+ continue;
802
+ }
803
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
804
+ inDouble = !inDouble;
805
+ current += ch;
806
+ continue;
807
+ }
808
+ if (!inSingle && !inDouble && ch === ";") {
809
+ if (current.trim()) {
810
+ parts.push(current.trim());
811
+ }
812
+ current = "";
813
+ continue;
814
+ }
815
+ current += ch;
816
+ }
817
+ if (current.trim()) {
818
+ parts.push(current.trim());
819
+ }
820
+ return parts;
821
+ }
822
+ async function createPrismaDbAdapter(fallbackClient) {
823
+ const prisma = await loadPrismaClient();
824
+ await ensureCompatibilityViews(prisma);
825
+ let closed = false;
826
+ let adapter;
827
+ const fallbackExecute = async (stmt, error) => {
828
+ if (!fallbackClient) {
829
+ if (error) throw error;
830
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
831
+ }
832
+ if (error) {
833
+ process.stderr.write(
834
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
835
+ `
836
+ );
837
+ }
838
+ return fallbackClient.execute(stmt);
839
+ };
840
+ adapter = {
841
+ async execute(stmt) {
842
+ if (shouldBypassPostgres(stmt)) {
843
+ return fallbackExecute(stmt);
844
+ }
845
+ try {
846
+ return await executeOnPrisma(prisma, stmt);
847
+ } catch (error) {
848
+ if (shouldFallbackOnError(error)) {
849
+ return fallbackExecute(stmt, error);
850
+ }
851
+ throw error;
852
+ }
853
+ },
854
+ async batch(stmts, mode) {
855
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
856
+ if (!fallbackClient) {
857
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
858
+ }
859
+ return fallbackClient.batch(stmts, mode);
860
+ }
861
+ try {
862
+ if (prisma.$transaction) {
863
+ return await prisma.$transaction(async (tx) => {
864
+ const results2 = [];
865
+ for (const stmt of stmts) {
866
+ results2.push(await executeOnPrisma(tx, stmt));
867
+ }
868
+ return results2;
869
+ });
870
+ }
871
+ const results = [];
872
+ for (const stmt of stmts) {
873
+ results.push(await executeOnPrisma(prisma, stmt));
874
+ }
875
+ return results;
876
+ } catch (error) {
877
+ if (fallbackClient && shouldFallbackOnError(error)) {
878
+ process.stderr.write(
879
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
880
+ `
881
+ );
882
+ return fallbackClient.batch(stmts, mode);
883
+ }
884
+ throw error;
885
+ }
886
+ },
887
+ async migrate(stmts) {
888
+ if (fallbackClient) {
889
+ return fallbackClient.migrate(stmts);
890
+ }
891
+ return adapter.batch(stmts, "deferred");
892
+ },
893
+ async transaction(mode) {
894
+ if (!fallbackClient) {
895
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
896
+ }
897
+ return fallbackClient.transaction(mode);
898
+ },
899
+ async executeMultiple(sql) {
900
+ if (fallbackClient && shouldBypassPostgres(sql)) {
901
+ return fallbackClient.executeMultiple(sql);
902
+ }
903
+ for (const statement of splitSqlStatements(sql)) {
904
+ await adapter.execute(statement);
905
+ }
906
+ },
907
+ async sync() {
908
+ if (fallbackClient) {
909
+ return fallbackClient.sync();
910
+ }
911
+ return { frame_no: 0, frames_synced: 0 };
912
+ },
913
+ close() {
914
+ closed = true;
915
+ prismaClientPromise = null;
916
+ compatibilityBootstrapPromise = null;
917
+ void prisma.$disconnect?.();
918
+ },
919
+ get closed() {
920
+ return closed;
921
+ },
922
+ get protocol() {
923
+ return "prisma-postgres";
924
+ }
925
+ };
926
+ return adapter;
927
+ }
928
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
929
+ var init_database_adapter = __esm({
930
+ "src/lib/database-adapter.ts"() {
931
+ "use strict";
932
+ VIEW_MAPPINGS = [
933
+ { view: "memories", source: "memory.memory_records" },
934
+ { view: "tasks", source: "memory.tasks" },
935
+ { view: "behaviors", source: "memory.behaviors" },
936
+ { view: "entities", source: "memory.entities" },
937
+ { view: "relationships", source: "memory.relationships" },
938
+ { view: "entity_memories", source: "memory.entity_memories" },
939
+ { view: "entity_aliases", source: "memory.entity_aliases" },
940
+ { view: "notifications", source: "memory.notifications" },
941
+ { view: "messages", source: "memory.messages" },
942
+ { view: "users", source: "wiki.users" },
943
+ { view: "workspaces", source: "wiki.workspaces" },
944
+ { view: "workspace_users", source: "wiki.workspace_users" },
945
+ { view: "documents", source: "wiki.workspace_documents" },
946
+ { view: "chats", source: "wiki.workspace_chats" }
947
+ ];
948
+ UPSERT_KEYS = {
949
+ memories: ["id"],
950
+ tasks: ["id"],
951
+ behaviors: ["id"],
952
+ entities: ["id"],
953
+ relationships: ["id"],
954
+ entity_aliases: ["alias"],
955
+ notifications: ["id"],
956
+ messages: ["id"],
957
+ users: ["id"],
958
+ workspaces: ["id"],
959
+ workspace_users: ["id"],
960
+ documents: ["id"],
961
+ chats: ["id"]
962
+ };
963
+ BOOLEAN_COLUMNS_BY_TABLE = {
964
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
965
+ behaviors: /* @__PURE__ */ new Set(["active"]),
966
+ notifications: /* @__PURE__ */ new Set(["read"]),
967
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
968
+ };
969
+ BOOLEAN_COLUMN_NAMES = new Set(
970
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
971
+ );
972
+ IMMEDIATE_FALLBACK_PATTERNS = [
973
+ /\bPRAGMA\b/i,
974
+ /\bsqlite_master\b/i,
975
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
976
+ /\bMATCH\b/i,
977
+ /\bvector_distance_cos\s*\(/i,
978
+ /\bjson_extract\s*\(/i,
979
+ /\bjulianday\s*\(/i,
980
+ /\bstrftime\s*\(/i,
981
+ /\blast_insert_rowid\s*\(/i
982
+ ];
983
+ prismaClientPromise = null;
984
+ compatibilityBootstrapPromise = null;
400
985
  }
401
986
  });
402
987
 
403
988
  // src/lib/exe-daemon-client.ts
404
989
  import net from "net";
405
- import os3 from "os";
990
+ import os4 from "os";
406
991
  import { spawn } from "child_process";
407
992
  import { randomUUID } from "crypto";
408
993
  import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
409
- import path3 from "path";
994
+ import path4 from "path";
410
995
  import { fileURLToPath } from "url";
411
996
  function handleData(chunk) {
412
997
  _buffer += chunk.toString();
@@ -457,17 +1042,17 @@ function cleanupStaleFiles() {
457
1042
  }
458
1043
  }
459
1044
  function findPackageRoot() {
460
- let dir = path3.dirname(fileURLToPath(import.meta.url));
461
- const { root } = path3.parse(dir);
1045
+ let dir = path4.dirname(fileURLToPath(import.meta.url));
1046
+ const { root } = path4.parse(dir);
462
1047
  while (dir !== root) {
463
- if (existsSync3(path3.join(dir, "package.json"))) return dir;
464
- dir = path3.dirname(dir);
1048
+ if (existsSync3(path4.join(dir, "package.json"))) return dir;
1049
+ dir = path4.dirname(dir);
465
1050
  }
466
1051
  return null;
467
1052
  }
468
1053
  function spawnDaemon() {
469
- const freeGB = os3.freemem() / (1024 * 1024 * 1024);
470
- const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
1054
+ const freeGB = os4.freemem() / (1024 * 1024 * 1024);
1055
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
471
1056
  if (totalGB <= 8) {
472
1057
  process.stderr.write(
473
1058
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -487,7 +1072,7 @@ function spawnDaemon() {
487
1072
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
488
1073
  return;
489
1074
  }
490
- const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1075
+ const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
491
1076
  if (!existsSync3(daemonPath)) {
492
1077
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
493
1078
  `);
@@ -496,7 +1081,7 @@ function spawnDaemon() {
496
1081
  const resolvedPath = daemonPath;
497
1082
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
498
1083
  `);
499
- const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
1084
+ const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
500
1085
  let stderrFd = "ignore";
501
1086
  try {
502
1087
  stderrFd = openSync(logPath, "a");
@@ -647,74 +1232,123 @@ async function pingDaemon() {
647
1232
  return null;
648
1233
  }
649
1234
  function killAndRespawnDaemon() {
650
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
651
- if (existsSync3(PID_PATH)) {
652
- try {
653
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
654
- if (pid > 0) {
655
- try {
656
- process.kill(pid, "SIGKILL");
657
- } catch {
1235
+ if (!acquireSpawnLock()) {
1236
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1237
+ if (_socket) {
1238
+ _socket.destroy();
1239
+ _socket = null;
1240
+ }
1241
+ _connected = false;
1242
+ _buffer = "";
1243
+ return;
1244
+ }
1245
+ try {
1246
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1247
+ if (existsSync3(PID_PATH)) {
1248
+ try {
1249
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
1250
+ if (pid > 0) {
1251
+ try {
1252
+ process.kill(pid, "SIGKILL");
1253
+ } catch {
1254
+ }
658
1255
  }
1256
+ } catch {
659
1257
  }
1258
+ }
1259
+ if (_socket) {
1260
+ _socket.destroy();
1261
+ _socket = null;
1262
+ }
1263
+ _connected = false;
1264
+ _buffer = "";
1265
+ try {
1266
+ unlinkSync2(PID_PATH);
660
1267
  } catch {
661
1268
  }
1269
+ try {
1270
+ unlinkSync2(SOCKET_PATH);
1271
+ } catch {
1272
+ }
1273
+ spawnDaemon();
1274
+ } finally {
1275
+ releaseSpawnLock();
662
1276
  }
663
- if (_socket) {
664
- _socket.destroy();
665
- _socket = null;
666
- }
667
- _connected = false;
668
- _buffer = "";
1277
+ }
1278
+ function isDaemonTooYoung() {
669
1279
  try {
670
- unlinkSync2(PID_PATH);
1280
+ const stat = statSync(PID_PATH);
1281
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
671
1282
  } catch {
1283
+ return false;
672
1284
  }
673
- try {
674
- unlinkSync2(SOCKET_PATH);
675
- } catch {
1285
+ }
1286
+ async function retryThenRestart(doRequest, label) {
1287
+ const result = await doRequest();
1288
+ if (!result.error) {
1289
+ _consecutiveFailures = 0;
1290
+ return result;
676
1291
  }
677
- spawnDaemon();
1292
+ _consecutiveFailures++;
1293
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1294
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1295
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1296
+ `);
1297
+ await new Promise((r) => setTimeout(r, delayMs));
1298
+ if (!_connected) {
1299
+ if (!await connectToSocket()) continue;
1300
+ }
1301
+ const retry = await doRequest();
1302
+ if (!retry.error) {
1303
+ _consecutiveFailures = 0;
1304
+ return retry;
1305
+ }
1306
+ _consecutiveFailures++;
1307
+ }
1308
+ if (isDaemonTooYoung()) {
1309
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1310
+ `);
1311
+ return { error: result.error };
1312
+ }
1313
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1314
+ `);
1315
+ killAndRespawnDaemon();
1316
+ const start = Date.now();
1317
+ let delay2 = 200;
1318
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1319
+ await new Promise((r) => setTimeout(r, delay2));
1320
+ if (await connectToSocket()) break;
1321
+ delay2 = Math.min(delay2 * 2, 3e3);
1322
+ }
1323
+ if (!_connected) return { error: "Daemon restart failed" };
1324
+ const final = await doRequest();
1325
+ if (!final.error) _consecutiveFailures = 0;
1326
+ return final;
678
1327
  }
679
1328
  async function embedViaClient(text, priority = "high") {
680
1329
  if (!_connected && !await connectEmbedDaemon()) return null;
681
1330
  _requestCount++;
682
1331
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
683
1332
  const health = await pingDaemon();
684
- if (!health) {
1333
+ if (!health && !isDaemonTooYoung()) {
685
1334
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
686
1335
  `);
687
1336
  killAndRespawnDaemon();
688
1337
  const start = Date.now();
689
- let delay2 = 200;
1338
+ let d = 200;
690
1339
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
691
- await new Promise((r) => setTimeout(r, delay2));
1340
+ await new Promise((r) => setTimeout(r, d));
692
1341
  if (await connectToSocket()) break;
693
- delay2 = Math.min(delay2 * 2, 3e3);
1342
+ d = Math.min(d * 2, 3e3);
694
1343
  }
695
1344
  if (!_connected) return null;
696
1345
  }
697
1346
  }
698
- const result = await sendRequest([text], priority);
699
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
700
- if (result.error) {
701
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
702
- `);
703
- killAndRespawnDaemon();
704
- const start = Date.now();
705
- let delay2 = 200;
706
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
707
- await new Promise((r) => setTimeout(r, delay2));
708
- if (await connectToSocket()) break;
709
- delay2 = Math.min(delay2 * 2, 3e3);
710
- }
711
- if (!_connected) return null;
712
- const retry = await sendRequest([text], priority);
713
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
714
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
715
- `);
716
- }
717
- return null;
1347
+ const result = await retryThenRestart(
1348
+ () => sendRequest([text], priority),
1349
+ "Embed"
1350
+ );
1351
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
718
1352
  }
719
1353
  function disconnectClient() {
720
1354
  if (_socket) {
@@ -732,14 +1366,14 @@ function disconnectClient() {
732
1366
  function isClientConnected() {
733
1367
  return _connected;
734
1368
  }
735
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
1369
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
736
1370
  var init_exe_daemon_client = __esm({
737
1371
  "src/lib/exe-daemon-client.ts"() {
738
1372
  "use strict";
739
1373
  init_config();
740
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
741
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
742
- SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
1374
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
1375
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
1376
+ SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
743
1377
  SPAWN_LOCK_STALE_MS = 3e4;
744
1378
  CONNECT_TIMEOUT_MS = 15e3;
745
1379
  REQUEST_TIMEOUT_MS = 3e4;
@@ -747,7 +1381,11 @@ var init_exe_daemon_client = __esm({
747
1381
  _connected = false;
748
1382
  _buffer = "";
749
1383
  _requestCount = 0;
1384
+ _consecutiveFailures = 0;
750
1385
  HEALTH_CHECK_INTERVAL = 100;
1386
+ MAX_RETRIES_BEFORE_RESTART = 3;
1387
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
1388
+ MIN_DAEMON_AGE_MS = 3e4;
751
1389
  _pending = /* @__PURE__ */ new Map();
752
1390
  MAX_BUFFER = 1e7;
753
1391
  }
@@ -823,7 +1461,7 @@ __export(db_daemon_client_exports, {
823
1461
  createDaemonDbClient: () => createDaemonDbClient,
824
1462
  initDaemonDbClient: () => initDaemonDbClient
825
1463
  });
826
- function normalizeStatement(stmt) {
1464
+ function normalizeStatement2(stmt) {
827
1465
  if (typeof stmt === "string") {
828
1466
  return { sql: stmt, args: [] };
829
1467
  }
@@ -847,7 +1485,7 @@ function createDaemonDbClient(fallbackClient) {
847
1485
  if (!_useDaemon || !isClientConnected()) {
848
1486
  return fallbackClient.execute(stmt);
849
1487
  }
850
- const { sql, args } = normalizeStatement(stmt);
1488
+ const { sql, args } = normalizeStatement2(stmt);
851
1489
  const response = await sendDaemonRequest({
852
1490
  type: "db-execute",
853
1491
  sql,
@@ -872,7 +1510,7 @@ function createDaemonDbClient(fallbackClient) {
872
1510
  if (!_useDaemon || !isClientConnected()) {
873
1511
  return fallbackClient.batch(stmts, mode);
874
1512
  }
875
- const statements = stmts.map(normalizeStatement);
1513
+ const statements = stmts.map(normalizeStatement2);
876
1514
  const response = await sendDaemonRequest({
877
1515
  type: "db-batch",
878
1516
  statements,
@@ -967,6 +1605,18 @@ __export(database_exports, {
967
1605
  });
968
1606
  import { createClient } from "@libsql/client";
969
1607
  async function initDatabase(config) {
1608
+ if (_walCheckpointTimer) {
1609
+ clearInterval(_walCheckpointTimer);
1610
+ _walCheckpointTimer = null;
1611
+ }
1612
+ if (_daemonClient) {
1613
+ _daemonClient.close();
1614
+ _daemonClient = null;
1615
+ }
1616
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1617
+ _adapterClient.close();
1618
+ }
1619
+ _adapterClient = null;
970
1620
  if (_client) {
971
1621
  _client.close();
972
1622
  _client = null;
@@ -980,6 +1630,7 @@ async function initDatabase(config) {
980
1630
  }
981
1631
  _client = createClient(opts);
982
1632
  _resilientClient = wrapWithRetry(_client);
1633
+ _adapterClient = _resilientClient;
983
1634
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
984
1635
  });
985
1636
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -990,14 +1641,20 @@ async function initDatabase(config) {
990
1641
  });
991
1642
  }, 3e4);
992
1643
  _walCheckpointTimer.unref();
1644
+ if (process.env.DATABASE_URL) {
1645
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1646
+ }
993
1647
  }
994
1648
  function isInitialized() {
995
- return _client !== null;
1649
+ return _adapterClient !== null || _client !== null;
996
1650
  }
997
1651
  function getClient() {
998
- if (!_resilientClient) {
1652
+ if (!_adapterClient) {
999
1653
  throw new Error("Database client not initialized. Call initDatabase() first.");
1000
1654
  }
1655
+ if (process.env.DATABASE_URL) {
1656
+ return _adapterClient;
1657
+ }
1001
1658
  if (process.env.EXE_IS_DAEMON === "1") {
1002
1659
  return _resilientClient;
1003
1660
  }
@@ -1007,6 +1664,7 @@ function getClient() {
1007
1664
  return _resilientClient;
1008
1665
  }
1009
1666
  async function initDaemonClient() {
1667
+ if (process.env.DATABASE_URL) return;
1010
1668
  if (process.env.EXE_IS_DAEMON === "1") return;
1011
1669
  if (!_resilientClient) return;
1012
1670
  try {
@@ -1951,26 +2609,36 @@ async function ensureSchema() {
1951
2609
  }
1952
2610
  }
1953
2611
  async function disposeDatabase() {
2612
+ if (_walCheckpointTimer) {
2613
+ clearInterval(_walCheckpointTimer);
2614
+ _walCheckpointTimer = null;
2615
+ }
1954
2616
  if (_daemonClient) {
1955
2617
  _daemonClient.close();
1956
2618
  _daemonClient = null;
1957
2619
  }
2620
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2621
+ _adapterClient.close();
2622
+ }
2623
+ _adapterClient = null;
1958
2624
  if (_client) {
1959
2625
  _client.close();
1960
2626
  _client = null;
1961
2627
  _resilientClient = null;
1962
2628
  }
1963
2629
  }
1964
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2630
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1965
2631
  var init_database = __esm({
1966
2632
  "src/lib/database.ts"() {
1967
2633
  "use strict";
1968
2634
  init_db_retry();
1969
2635
  init_employees();
2636
+ init_database_adapter();
1970
2637
  _client = null;
1971
2638
  _resilientClient = null;
1972
2639
  _walCheckpointTimer = null;
1973
2640
  _daemonClient = null;
2641
+ _adapterClient = null;
1974
2642
  initTurso = initDatabase;
1975
2643
  disposeTurso = disposeDatabase;
1976
2644
  }
@@ -1979,13 +2647,13 @@ var init_database = __esm({
1979
2647
  // src/lib/keychain.ts
1980
2648
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1981
2649
  import { existsSync as existsSync4 } from "fs";
1982
- import path4 from "path";
1983
- import os4 from "os";
2650
+ import path5 from "path";
2651
+ import os5 from "os";
1984
2652
  function getKeyDir() {
1985
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
2653
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os5.homedir(), ".exe-os");
1986
2654
  }
1987
2655
  function getKeyPath() {
1988
- return path4.join(getKeyDir(), "master.key");
2656
+ return path5.join(getKeyDir(), "master.key");
1989
2657
  }
1990
2658
  async function tryKeytar() {
1991
2659
  try {
@@ -2008,7 +2676,7 @@ async function getMasterKey() {
2008
2676
  const keyPath = getKeyPath();
2009
2677
  if (!existsSync4(keyPath)) {
2010
2678
  process.stderr.write(
2011
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2679
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2012
2680
  `
2013
2681
  );
2014
2682
  return null;
@@ -2101,7 +2769,7 @@ __export(shard_manager_exports, {
2101
2769
  listShards: () => listShards,
2102
2770
  shardExists: () => shardExists
2103
2771
  });
2104
- import path5 from "path";
2772
+ import path6 from "path";
2105
2773
  import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
2106
2774
  import { createClient as createClient2 } from "@libsql/client";
2107
2775
  function initShardManager(encryptionKey) {
@@ -2127,7 +2795,7 @@ function getShardClient(projectName) {
2127
2795
  }
2128
2796
  const cached = _shards.get(safeName);
2129
2797
  if (cached) return cached;
2130
- const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
2798
+ const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
2131
2799
  const client = createClient2({
2132
2800
  url: `file:${dbPath}`,
2133
2801
  encryptionKey: _encryptionKey
@@ -2137,7 +2805,7 @@ function getShardClient(projectName) {
2137
2805
  }
2138
2806
  function shardExists(projectName) {
2139
2807
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2140
- return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
2808
+ return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
2141
2809
  }
2142
2810
  function listShards() {
2143
2811
  if (!existsSync5(SHARDS_DIR)) return [];
@@ -2214,7 +2882,23 @@ async function ensureShardSchema(client) {
2214
2882
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
2215
2883
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
2216
2884
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
2217
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2885
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2886
+ // Metadata enrichment columns (must match database.ts)
2887
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2888
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2889
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2890
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2891
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2892
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2893
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2894
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2895
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2896
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2897
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2898
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2899
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2900
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2901
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2218
2902
  ]) {
2219
2903
  try {
2220
2904
  await client.execute(col);
@@ -2326,7 +3010,7 @@ var init_shard_manager = __esm({
2326
3010
  "src/lib/shard-manager.ts"() {
2327
3011
  "use strict";
2328
3012
  init_config();
2329
- SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
3013
+ SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
2330
3014
  _shards = /* @__PURE__ */ new Map();
2331
3015
  _encryptionKey = null;
2332
3016
  _shardingEnabled = false;
@@ -3092,10 +3776,10 @@ var init_store = __esm({
3092
3776
 
3093
3777
  // src/lib/session-registry.ts
3094
3778
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
3095
- import path6 from "path";
3096
- import os5 from "os";
3779
+ import path7 from "path";
3780
+ import os6 from "os";
3097
3781
  function registerSession(entry) {
3098
- const dir = path6.dirname(REGISTRY_PATH);
3782
+ const dir = path7.dirname(REGISTRY_PATH);
3099
3783
  if (!existsSync6(dir)) {
3100
3784
  mkdirSync2(dir, { recursive: true });
3101
3785
  }
@@ -3120,7 +3804,7 @@ var REGISTRY_PATH;
3120
3804
  var init_session_registry = __esm({
3121
3805
  "src/lib/session-registry.ts"() {
3122
3806
  "use strict";
3123
- REGISTRY_PATH = path6.join(os5.homedir(), ".exe-os", "session-registry.json");
3807
+ REGISTRY_PATH = path7.join(os6.homedir(), ".exe-os", "session-registry.json");
3124
3808
  }
3125
3809
  });
3126
3810
 
@@ -3401,7 +4085,7 @@ var init_runtime_table = __esm({
3401
4085
 
3402
4086
  // src/lib/agent-config.ts
3403
4087
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
3404
- import path7 from "path";
4088
+ import path8 from "path";
3405
4089
  function loadAgentConfig() {
3406
4090
  if (!existsSync7(AGENT_CONFIG_PATH)) return {};
3407
4091
  try {
@@ -3424,7 +4108,7 @@ var init_agent_config = __esm({
3424
4108
  "use strict";
3425
4109
  init_config();
3426
4110
  init_runtime_table();
3427
- AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
4111
+ AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
3428
4112
  DEFAULT_MODELS = {
3429
4113
  claude: "claude-opus-4",
3430
4114
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
@@ -3443,10 +4127,10 @@ __export(intercom_queue_exports, {
3443
4127
  readQueue: () => readQueue
3444
4128
  });
3445
4129
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
3446
- import path8 from "path";
3447
- import os6 from "os";
4130
+ import path9 from "path";
4131
+ import os7 from "os";
3448
4132
  function ensureDir() {
3449
- const dir = path8.dirname(QUEUE_PATH);
4133
+ const dir = path9.dirname(QUEUE_PATH);
3450
4134
  if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
3451
4135
  }
3452
4136
  function readQueue() {
@@ -3552,26 +4236,26 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
3552
4236
  var init_intercom_queue = __esm({
3553
4237
  "src/lib/intercom-queue.ts"() {
3554
4238
  "use strict";
3555
- QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
4239
+ QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
3556
4240
  MAX_RETRIES2 = 5;
3557
4241
  TTL_MS = 60 * 60 * 1e3;
3558
- INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
4242
+ INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
3559
4243
  }
3560
4244
  });
3561
4245
 
3562
4246
  // src/lib/license.ts
3563
4247
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
3564
4248
  import { randomUUID as randomUUID3 } from "crypto";
3565
- import path9 from "path";
4249
+ import path10 from "path";
3566
4250
  import { jwtVerify, importSPKI } from "jose";
3567
4251
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
3568
4252
  var init_license = __esm({
3569
4253
  "src/lib/license.ts"() {
3570
4254
  "use strict";
3571
4255
  init_config();
3572
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3573
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3574
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
4256
+ LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
4257
+ CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
4258
+ DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
3575
4259
  PLAN_LIMITS = {
3576
4260
  free: { devices: 1, employees: 1, memories: 5e3 },
3577
4261
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -3584,7 +4268,7 @@ var init_license = __esm({
3584
4268
 
3585
4269
  // src/lib/plan-limits.ts
3586
4270
  import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
3587
- import path10 from "path";
4271
+ import path11 from "path";
3588
4272
  function getLicenseSync() {
3589
4273
  try {
3590
4274
  if (!existsSync10(CACHE_PATH2)) return freeLicense();
@@ -3656,14 +4340,14 @@ var init_plan_limits = __esm({
3656
4340
  this.name = "PlanLimitError";
3657
4341
  }
3658
4342
  };
3659
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
4343
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
3660
4344
  }
3661
4345
  });
3662
4346
 
3663
4347
  // src/lib/notifications.ts
3664
4348
  import crypto from "crypto";
3665
- import path11 from "path";
3666
- import os7 from "os";
4349
+ import path12 from "path";
4350
+ import os8 from "os";
3667
4351
  import {
3668
4352
  readFileSync as readFileSync9,
3669
4353
  readdirSync as readdirSync2,
@@ -3764,8 +4448,8 @@ __export(tasks_crud_exports, {
3764
4448
  writeCheckpoint: () => writeCheckpoint
3765
4449
  });
3766
4450
  import crypto3 from "crypto";
3767
- import path12 from "path";
3768
- import os8 from "os";
4451
+ import path13 from "path";
4452
+ import os9 from "os";
3769
4453
  import { execSync as execSync4 } from "child_process";
3770
4454
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3771
4455
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
@@ -3943,8 +4627,8 @@ ${laneWarning}` : laneWarning;
3943
4627
  }
3944
4628
  if (input.baseDir) {
3945
4629
  try {
3946
- await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
3947
- await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
4630
+ await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
4631
+ await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
3948
4632
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3949
4633
  await ensureGitignoreExe(input.baseDir);
3950
4634
  } catch {
@@ -3980,9 +4664,9 @@ ${laneWarning}` : laneWarning;
3980
4664
  });
3981
4665
  if (input.baseDir) {
3982
4666
  try {
3983
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
3984
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
3985
- const mdDir = path12.dirname(mdPath);
4667
+ const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
4668
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
4669
+ const mdDir = path13.dirname(mdPath);
3986
4670
  if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
3987
4671
  const reviewer = input.reviewer ?? input.assignedBy;
3988
4672
  const mdContent = `# ${input.title}
@@ -4283,7 +4967,7 @@ async function deleteTaskCore(taskId, _baseDir) {
4283
4967
  return { taskFile, assignedTo, assignedBy, taskSlug };
4284
4968
  }
4285
4969
  async function ensureArchitectureDoc(baseDir, projectName) {
4286
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
4970
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
4287
4971
  try {
4288
4972
  if (existsSync12(archPath)) return;
4289
4973
  const template = [
@@ -4318,7 +5002,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
4318
5002
  }
4319
5003
  }
4320
5004
  async function ensureGitignoreExe(baseDir) {
4321
- const gitignorePath = path12.join(baseDir, ".gitignore");
5005
+ const gitignorePath = path13.join(baseDir, ".gitignore");
4322
5006
  try {
4323
5007
  if (existsSync12(gitignorePath)) {
4324
5008
  const content = readFileSync10(gitignorePath, "utf-8");
@@ -4364,7 +5048,7 @@ __export(tasks_review_exports, {
4364
5048
  isStale: () => isStale,
4365
5049
  listPendingReviews: () => listPendingReviews
4366
5050
  });
4367
- import path13 from "path";
5051
+ import path14 from "path";
4368
5052
  import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
4369
5053
  function formatAge(isoTimestamp) {
4370
5054
  if (!isoTimestamp) return "";
@@ -4385,7 +5069,7 @@ async function countPendingReviews(sessionScope) {
4385
5069
  const client = getClient();
4386
5070
  if (sessionScope) {
4387
5071
  const result2 = await client.execute({
4388
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
5072
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
4389
5073
  args: [sessionScope]
4390
5074
  });
4391
5075
  return Number(result2.rows[0]?.cnt) || 0;
@@ -4650,11 +5334,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4650
5334
  );
4651
5335
  }
4652
5336
  try {
4653
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
5337
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4654
5338
  if (existsSync13(cacheDir)) {
4655
5339
  for (const f of readdirSync3(cacheDir)) {
4656
5340
  if (f.startsWith("review-notified-")) {
4657
- unlinkSync4(path13.join(cacheDir, f));
5341
+ unlinkSync4(path14.join(cacheDir, f));
4658
5342
  }
4659
5343
  }
4660
5344
  }
@@ -4675,7 +5359,7 @@ var init_tasks_review = __esm({
4675
5359
  });
4676
5360
 
4677
5361
  // src/lib/tasks-chain.ts
4678
- import path14 from "path";
5362
+ import path15 from "path";
4679
5363
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
4680
5364
  async function cascadeUnblock(taskId, baseDir, now) {
4681
5365
  const client = getClient();
@@ -4692,7 +5376,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
4692
5376
  });
4693
5377
  for (const ur of unblockedRows.rows) {
4694
5378
  try {
4695
- const ubFile = path14.join(baseDir, String(ur.task_file));
5379
+ const ubFile = path15.join(baseDir, String(ur.task_file));
4696
5380
  let ubContent = await readFile4(ubFile, "utf-8");
4697
5381
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
4698
5382
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -4761,7 +5445,7 @@ var init_tasks_chain = __esm({
4761
5445
 
4762
5446
  // src/lib/project-name.ts
4763
5447
  import { execSync as execSync5 } from "child_process";
4764
- import path15 from "path";
5448
+ import path16 from "path";
4765
5449
  function getProjectName(cwd) {
4766
5450
  const dir = cwd ?? process.cwd();
4767
5451
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -4774,7 +5458,7 @@ function getProjectName(cwd) {
4774
5458
  timeout: 2e3,
4775
5459
  stdio: ["pipe", "pipe", "pipe"]
4776
5460
  }).trim();
4777
- repoRoot = path15.dirname(gitCommonDir);
5461
+ repoRoot = path16.dirname(gitCommonDir);
4778
5462
  } catch {
4779
5463
  repoRoot = execSync5("git rev-parse --show-toplevel", {
4780
5464
  cwd: dir,
@@ -4783,11 +5467,11 @@ function getProjectName(cwd) {
4783
5467
  stdio: ["pipe", "pipe", "pipe"]
4784
5468
  }).trim();
4785
5469
  }
4786
- _cached2 = path15.basename(repoRoot);
5470
+ _cached2 = path16.basename(repoRoot);
4787
5471
  _cachedCwd = dir;
4788
5472
  return _cached2;
4789
5473
  } catch {
4790
- _cached2 = path15.basename(dir);
5474
+ _cached2 = path16.basename(dir);
4791
5475
  _cachedCwd = dir;
4792
5476
  return _cached2;
4793
5477
  }
@@ -5260,7 +5944,7 @@ __export(tasks_exports, {
5260
5944
  updateTaskStatus: () => updateTaskStatus,
5261
5945
  writeCheckpoint: () => writeCheckpoint
5262
5946
  });
5263
- import path16 from "path";
5947
+ import path17 from "path";
5264
5948
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
5265
5949
  async function createTask(input) {
5266
5950
  const result = await createTaskCore(input);
@@ -5280,8 +5964,8 @@ async function updateTask(input) {
5280
5964
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
5281
5965
  try {
5282
5966
  const agent = String(row.assigned_to);
5283
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5284
- const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
5967
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5968
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
5285
5969
  if (input.status === "in_progress") {
5286
5970
  mkdirSync6(cacheDir, { recursive: true });
5287
5971
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -5752,12 +6436,12 @@ __export(tmux_routing_exports, {
5752
6436
  });
5753
6437
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
5754
6438
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
5755
- import path17 from "path";
5756
- import os9 from "os";
6439
+ import path18 from "path";
6440
+ import os10 from "os";
5757
6441
  import { fileURLToPath as fileURLToPath2 } from "url";
5758
6442
  import { unlinkSync as unlinkSync6 } from "fs";
5759
6443
  function spawnLockPath(sessionName) {
5760
- return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6444
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5761
6445
  }
5762
6446
  function isProcessAlive(pid) {
5763
6447
  try {
@@ -5794,8 +6478,8 @@ function releaseSpawnLock2(sessionName) {
5794
6478
  function resolveBehaviorsExporterScript() {
5795
6479
  try {
5796
6480
  const thisFile = fileURLToPath2(import.meta.url);
5797
- const scriptPath = path17.join(
5798
- path17.dirname(thisFile),
6481
+ const scriptPath = path18.join(
6482
+ path18.dirname(thisFile),
5799
6483
  "..",
5800
6484
  "bin",
5801
6485
  "exe-export-behaviors.js"
@@ -5870,7 +6554,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5870
6554
  mkdirSync7(SESSION_CACHE, { recursive: true });
5871
6555
  }
5872
6556
  const rootExe = extractRootExe(parentExe) ?? parentExe;
5873
- const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6557
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5874
6558
  writeFileSync7(filePath, JSON.stringify({
5875
6559
  parentExe: rootExe,
5876
6560
  dispatchedBy: dispatchedBy || rootExe,
@@ -5879,7 +6563,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5879
6563
  }
5880
6564
  function getParentExe(sessionKey) {
5881
6565
  try {
5882
- const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6566
+ const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5883
6567
  return data.parentExe || null;
5884
6568
  } catch {
5885
6569
  return null;
@@ -5888,7 +6572,7 @@ function getParentExe(sessionKey) {
5888
6572
  function getDispatchedBy(sessionKey) {
5889
6573
  try {
5890
6574
  const data = JSON.parse(readFileSync11(
5891
- path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6575
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5892
6576
  "utf8"
5893
6577
  ));
5894
6578
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5899,15 +6583,24 @@ function getDispatchedBy(sessionKey) {
5899
6583
  function resolveExeSession() {
5900
6584
  const mySession = getMySession();
5901
6585
  if (!mySession) return null;
6586
+ const fromSessionName = extractRootExe(mySession);
5902
6587
  try {
5903
6588
  const key = getSessionKey();
5904
6589
  const parentExe = getParentExe(key);
5905
6590
  if (parentExe) {
5906
- return extractRootExe(parentExe) ?? parentExe;
6591
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
6592
+ if (fromSessionName && fromCache !== fromSessionName) {
6593
+ process.stderr.write(
6594
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
6595
+ `
6596
+ );
6597
+ return fromSessionName;
6598
+ }
6599
+ return fromCache;
5907
6600
  }
5908
6601
  } catch {
5909
6602
  }
5910
- return extractRootExe(mySession) ?? mySession;
6603
+ return fromSessionName ?? mySession;
5911
6604
  }
5912
6605
  function isEmployeeAlive(sessionName) {
5913
6606
  return getTransport().isAlive(sessionName);
@@ -6065,7 +6758,7 @@ function sendIntercom(targetSession) {
6065
6758
  try {
6066
6759
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6067
6760
  const agent = baseAgentName(rawAgent);
6068
- const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
6761
+ const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
6069
6762
  if (existsSync14(markerPath)) {
6070
6763
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6071
6764
  return "debounced";
@@ -6075,7 +6768,7 @@ function sendIntercom(targetSession) {
6075
6768
  try {
6076
6769
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6077
6770
  const agent = baseAgentName(rawAgent);
6078
- const taskDir = path17.join(process.cwd(), "exe", agent);
6771
+ const taskDir = path18.join(process.cwd(), "exe", agent);
6079
6772
  if (existsSync14(taskDir)) {
6080
6773
  const files = readdirSync4(taskDir).filter(
6081
6774
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -6209,8 +6902,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6209
6902
  const transport = getTransport();
6210
6903
  const sessionName = employeeSessionName(employeeName, exeSession2, opts?.instance);
6211
6904
  const instanceLabel2 = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
6212
- const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
6213
- const logFile = path17.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
6905
+ const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
6906
+ const logFile = path18.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
6214
6907
  if (!existsSync14(logDir)) {
6215
6908
  mkdirSync7(logDir, { recursive: true });
6216
6909
  }
@@ -6218,14 +6911,14 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6218
6911
  let cleanupSuffix = "";
6219
6912
  try {
6220
6913
  const thisFile = fileURLToPath2(import.meta.url);
6221
- const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6914
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6222
6915
  if (existsSync14(cleanupScript)) {
6223
6916
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession2}"`;
6224
6917
  }
6225
6918
  } catch {
6226
6919
  }
6227
6920
  try {
6228
- const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
6921
+ const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
6229
6922
  let claudeJson = {};
6230
6923
  try {
6231
6924
  claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
@@ -6240,10 +6933,10 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6240
6933
  } catch {
6241
6934
  }
6242
6935
  try {
6243
- const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
6936
+ const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
6244
6937
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
6245
- const projSettingsDir = path17.join(settingsDir, normalizedKey);
6246
- const settingsPath = path17.join(projSettingsDir, "settings.json");
6938
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
6939
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
6247
6940
  let settings = {};
6248
6941
  try {
6249
6942
  settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
@@ -6290,8 +6983,8 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6290
6983
  let behaviorsFlag = "";
6291
6984
  let legacyFallbackWarned = false;
6292
6985
  if (!useExeAgent && !useBinSymlink) {
6293
- const identityPath = path17.join(
6294
- os9.homedir(),
6986
+ const identityPath = path18.join(
6987
+ os10.homedir(),
6295
6988
  ".exe-os",
6296
6989
  "identity",
6297
6990
  `${employeeName}.md`
@@ -6306,7 +6999,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6306
6999
  }
6307
7000
  const behaviorsFile = exportBehaviorsSync(
6308
7001
  employeeName,
6309
- path17.basename(spawnCwd),
7002
+ path18.basename(spawnCwd),
6310
7003
  sessionName
6311
7004
  );
6312
7005
  if (behaviorsFile) {
@@ -6321,9 +7014,9 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6321
7014
  }
6322
7015
  let sessionContextFlag = "";
6323
7016
  try {
6324
- const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
7017
+ const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
6325
7018
  mkdirSync7(ctxDir, { recursive: true });
6326
- const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
7019
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
6327
7020
  const ctxContent = [
6328
7021
  `## Session Context`,
6329
7022
  `You are running in tmux session: ${sessionName}.`,
@@ -6407,7 +7100,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6407
7100
  transport.pipeLog(sessionName, logFile);
6408
7101
  try {
6409
7102
  const mySession = getMySession();
6410
- const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7103
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6411
7104
  writeFileSync7(dispatchInfo, JSON.stringify({
6412
7105
  dispatchedBy: mySession,
6413
7106
  rootExe: exeSession2,
@@ -6482,15 +7175,15 @@ var init_tmux_routing = __esm({
6482
7175
  init_intercom_queue();
6483
7176
  init_plan_limits();
6484
7177
  init_employees();
6485
- SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
6486
- SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
7178
+ SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
7179
+ SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
6487
7180
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
6488
7181
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
6489
7182
  VERIFY_PANE_LINES = 200;
6490
7183
  INTERCOM_DEBOUNCE_MS = 3e4;
6491
7184
  CODEX_DEBOUNCE_MS = 12e4;
6492
- INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
6493
- DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
7185
+ INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
7186
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
6494
7187
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
6495
7188
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
6496
7189
  }
@@ -6558,8 +7251,8 @@ async function embedDirect(text) {
6558
7251
  const llamaCpp = await import("node-llama-cpp");
6559
7252
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6560
7253
  const { existsSync: existsSync16 } = await import("fs");
6561
- const path19 = await import("path");
6562
- const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
7254
+ const path20 = await import("path");
7255
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
6563
7256
  if (!existsSync16(modelPath)) {
6564
7257
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
6565
7258
  }
@@ -6819,7 +7512,7 @@ __export(worktree_exports, {
6819
7512
  });
6820
7513
  import { execSync as execSync8 } from "child_process";
6821
7514
  import { existsSync as existsSync15, readFileSync as readFileSync12, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, realpathSync } from "fs";
6822
- import path18 from "path";
7515
+ import path19 from "path";
6823
7516
  function getGitRoot(dir) {
6824
7517
  try {
6825
7518
  const root = execSync8("git rev-parse --show-toplevel", {
@@ -6839,14 +7532,14 @@ function getMainRepoRoot(dir) {
6839
7532
  "git rev-parse --path-format=absolute --git-common-dir",
6840
7533
  { cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
6841
7534
  ).trim();
6842
- return realpath(path18.dirname(commonDir));
7535
+ return realpath(path19.dirname(commonDir));
6843
7536
  } catch {
6844
7537
  return null;
6845
7538
  }
6846
7539
  }
6847
7540
  function worktreePath(repoRoot, employeeName, instance) {
6848
7541
  const label = instanceLabel(employeeName, instance);
6849
- return path18.join(repoRoot, ".worktrees", label);
7542
+ return path19.join(repoRoot, ".worktrees", label);
6850
7543
  }
6851
7544
  function worktreeBranch(employeeName, instance) {
6852
7545
  return `${instanceLabel(employeeName, instance)}-work`;
@@ -6859,10 +7552,10 @@ function ensureWorktree(projectDir, employeeName, instance) {
6859
7552
  if (!repoRoot) return null;
6860
7553
  const wtPath = worktreePath(repoRoot, employeeName, instance);
6861
7554
  const branch = worktreeBranch(employeeName, instance);
6862
- if (existsSync15(path18.join(wtPath, ".git"))) {
7555
+ if (existsSync15(path19.join(wtPath, ".git"))) {
6863
7556
  return wtPath;
6864
7557
  }
6865
- const worktreesDir = path18.join(repoRoot, ".worktrees");
7558
+ const worktreesDir = path19.join(repoRoot, ".worktrees");
6866
7559
  mkdirSync8(worktreesDir, { recursive: true });
6867
7560
  ensureGitignoreEntry(repoRoot, "/.worktrees/");
6868
7561
  try {
@@ -6996,7 +7689,7 @@ function realpath(p) {
6996
7689
  }
6997
7690
  function ensureGitignoreEntry(repoRoot, entry) {
6998
7691
  try {
6999
- const gitignorePath = path18.join(repoRoot, ".gitignore");
7692
+ const gitignorePath = path19.join(repoRoot, ".gitignore");
7000
7693
  if (existsSync15(gitignorePath)) {
7001
7694
  const content = readFileSync12(gitignorePath, "utf-8");
7002
7695
  if (content.includes(entry)) return;