@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
@@ -454,7 +454,7 @@ function isMultiInstance(agentName, employees) {
454
454
  if (!emp) return false;
455
455
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
456
456
  }
457
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
457
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
458
458
  var init_employees = __esm({
459
459
  "src/lib/employees.ts"() {
460
460
  "use strict";
@@ -463,16 +463,601 @@ var init_employees = __esm({
463
463
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
464
464
  COORDINATOR_ROLE = "COO";
465
465
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
466
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
467
+ }
468
+ });
469
+
470
+ // src/lib/database-adapter.ts
471
+ import os3 from "os";
472
+ import path4 from "path";
473
+ import { createRequire } from "module";
474
+ import { pathToFileURL } from "url";
475
+ function quotedIdentifier(identifier) {
476
+ return `"${identifier.replace(/"/g, '""')}"`;
477
+ }
478
+ function unqualifiedTableName(name) {
479
+ const raw = name.trim().replace(/^"|"$/g, "");
480
+ const parts = raw.split(".");
481
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
482
+ }
483
+ function stripTrailingSemicolon(sql) {
484
+ return sql.trim().replace(/;+\s*$/u, "");
485
+ }
486
+ function appendClause(sql, clause) {
487
+ const trimmed = stripTrailingSemicolon(sql);
488
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
489
+ if (!returningMatch) {
490
+ return `${trimmed}${clause}`;
491
+ }
492
+ const idx = returningMatch.index;
493
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
494
+ }
495
+ function normalizeStatement(stmt) {
496
+ if (typeof stmt === "string") {
497
+ return { kind: "positional", sql: stmt, args: [] };
498
+ }
499
+ const sql = stmt.sql;
500
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
501
+ return { kind: "positional", sql, args: stmt.args ?? [] };
502
+ }
503
+ return { kind: "named", sql, args: stmt.args };
504
+ }
505
+ function rewriteBooleanLiterals(sql) {
506
+ let out = sql;
507
+ for (const column of BOOLEAN_COLUMN_NAMES) {
508
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
509
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
510
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
511
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
512
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
513
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
514
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
515
+ }
516
+ return out;
517
+ }
518
+ function rewriteInsertOrIgnore(sql) {
519
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
520
+ return sql;
521
+ }
522
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
523
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
524
+ }
525
+ function rewriteInsertOrReplace(sql) {
526
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
527
+ if (!match) {
528
+ return sql;
529
+ }
530
+ const rawTable = match[1];
531
+ const rawColumns = match[2];
532
+ const remainder = match[3];
533
+ const tableName = unqualifiedTableName(rawTable);
534
+ const conflictKeys = UPSERT_KEYS[tableName];
535
+ if (!conflictKeys?.length) {
536
+ return sql;
537
+ }
538
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
539
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
540
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
541
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
542
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
543
+ }
544
+ function rewriteSql(sql) {
545
+ let out = sql;
546
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
547
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
548
+ out = rewriteBooleanLiterals(out);
549
+ out = rewriteInsertOrReplace(out);
550
+ out = rewriteInsertOrIgnore(out);
551
+ return stripTrailingSemicolon(out);
552
+ }
553
+ function toBoolean(value) {
554
+ if (value === null || value === void 0) return value;
555
+ if (typeof value === "boolean") return value;
556
+ if (typeof value === "number") return value !== 0;
557
+ if (typeof value === "bigint") return value !== 0n;
558
+ if (typeof value === "string") {
559
+ const normalized = value.trim().toLowerCase();
560
+ if (normalized === "0" || normalized === "false") return false;
561
+ if (normalized === "1" || normalized === "true") return true;
562
+ }
563
+ return Boolean(value);
564
+ }
565
+ function countQuestionMarks(sql, end) {
566
+ let count = 0;
567
+ let inSingle = false;
568
+ let inDouble = false;
569
+ let inLineComment = false;
570
+ let inBlockComment = false;
571
+ for (let i = 0; i < end; i++) {
572
+ const ch = sql[i];
573
+ const next = sql[i + 1];
574
+ if (inLineComment) {
575
+ if (ch === "\n") inLineComment = false;
576
+ continue;
577
+ }
578
+ if (inBlockComment) {
579
+ if (ch === "*" && next === "/") {
580
+ inBlockComment = false;
581
+ i += 1;
582
+ }
583
+ continue;
584
+ }
585
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
586
+ inLineComment = true;
587
+ i += 1;
588
+ continue;
589
+ }
590
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
591
+ inBlockComment = true;
592
+ i += 1;
593
+ continue;
594
+ }
595
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
596
+ inSingle = !inSingle;
597
+ continue;
598
+ }
599
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
600
+ inDouble = !inDouble;
601
+ continue;
602
+ }
603
+ if (!inSingle && !inDouble && ch === "?") {
604
+ count += 1;
605
+ }
606
+ }
607
+ return count;
608
+ }
609
+ function findBooleanPlaceholderIndexes(sql) {
610
+ const indexes = /* @__PURE__ */ new Set();
611
+ for (const column of BOOLEAN_COLUMN_NAMES) {
612
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
613
+ for (const match of sql.matchAll(pattern)) {
614
+ const matchText = match[0];
615
+ const qIndex = match.index + matchText.lastIndexOf("?");
616
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
617
+ }
618
+ }
619
+ return indexes;
620
+ }
621
+ function coerceInsertBooleanArgs(sql, args) {
622
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
623
+ if (!match) return;
624
+ const rawTable = match[1];
625
+ const rawColumns = match[2];
626
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
627
+ if (!boolColumns?.size) return;
628
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
629
+ for (const [index, column] of columns.entries()) {
630
+ if (boolColumns.has(column) && index < args.length) {
631
+ args[index] = toBoolean(args[index]);
632
+ }
633
+ }
634
+ }
635
+ function coerceUpdateBooleanArgs(sql, args) {
636
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
637
+ if (!match) return;
638
+ const rawTable = match[1];
639
+ const setClause = match[2];
640
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
641
+ if (!boolColumns?.size) return;
642
+ const assignments = setClause.split(",");
643
+ let placeholderIndex = 0;
644
+ for (const assignment of assignments) {
645
+ if (!assignment.includes("?")) continue;
646
+ placeholderIndex += 1;
647
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
648
+ if (colMatch && boolColumns.has(colMatch[1])) {
649
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
650
+ }
651
+ }
652
+ }
653
+ function coerceBooleanArgs(sql, args) {
654
+ const nextArgs = [...args];
655
+ coerceInsertBooleanArgs(sql, nextArgs);
656
+ coerceUpdateBooleanArgs(sql, nextArgs);
657
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
658
+ for (const index of placeholderIndexes) {
659
+ if (index > 0 && index <= nextArgs.length) {
660
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
661
+ }
662
+ }
663
+ return nextArgs;
664
+ }
665
+ function convertQuestionMarksToDollarParams(sql) {
666
+ let out = "";
667
+ let placeholder = 0;
668
+ let inSingle = false;
669
+ let inDouble = false;
670
+ let inLineComment = false;
671
+ let inBlockComment = false;
672
+ for (let i = 0; i < sql.length; i++) {
673
+ const ch = sql[i];
674
+ const next = sql[i + 1];
675
+ if (inLineComment) {
676
+ out += ch;
677
+ if (ch === "\n") inLineComment = false;
678
+ continue;
679
+ }
680
+ if (inBlockComment) {
681
+ out += ch;
682
+ if (ch === "*" && next === "/") {
683
+ out += next;
684
+ inBlockComment = false;
685
+ i += 1;
686
+ }
687
+ continue;
688
+ }
689
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
690
+ out += ch + next;
691
+ inLineComment = true;
692
+ i += 1;
693
+ continue;
694
+ }
695
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
696
+ out += ch + next;
697
+ inBlockComment = true;
698
+ i += 1;
699
+ continue;
700
+ }
701
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
702
+ inSingle = !inSingle;
703
+ out += ch;
704
+ continue;
705
+ }
706
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
707
+ inDouble = !inDouble;
708
+ out += ch;
709
+ continue;
710
+ }
711
+ if (!inSingle && !inDouble && ch === "?") {
712
+ placeholder += 1;
713
+ out += `$${placeholder}`;
714
+ continue;
715
+ }
716
+ out += ch;
717
+ }
718
+ return out;
719
+ }
720
+ function translateStatementForPostgres(stmt) {
721
+ const normalized = normalizeStatement(stmt);
722
+ if (normalized.kind === "named") {
723
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
724
+ }
725
+ const rewrittenSql = rewriteSql(normalized.sql);
726
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
727
+ return {
728
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
729
+ args: coercedArgs
730
+ };
731
+ }
732
+ function shouldBypassPostgres(stmt) {
733
+ const normalized = normalizeStatement(stmt);
734
+ if (normalized.kind === "named") {
735
+ return true;
736
+ }
737
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
738
+ }
739
+ function shouldFallbackOnError(error) {
740
+ const message = error instanceof Error ? error.message : String(error);
741
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
742
+ }
743
+ function isReadQuery(sql) {
744
+ const trimmed = sql.trimStart();
745
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
746
+ }
747
+ function buildRow(row, columns) {
748
+ const values = columns.map((column) => row[column]);
749
+ return Object.assign(values, row);
750
+ }
751
+ function buildResultSet(rows, rowsAffected = 0) {
752
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
753
+ const resultRows = rows.map((row) => buildRow(row, columns));
754
+ return {
755
+ columns,
756
+ columnTypes: columns.map(() => ""),
757
+ rows: resultRows,
758
+ rowsAffected,
759
+ lastInsertRowid: void 0,
760
+ toJSON() {
761
+ return {
762
+ columns,
763
+ columnTypes: columns.map(() => ""),
764
+ rows,
765
+ rowsAffected,
766
+ lastInsertRowid: void 0
767
+ };
768
+ }
769
+ };
770
+ }
771
+ async function loadPrismaClient() {
772
+ if (!prismaClientPromise) {
773
+ prismaClientPromise = (async () => {
774
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
775
+ if (explicitPath) {
776
+ const module2 = await import(pathToFileURL(explicitPath).href);
777
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
778
+ if (!PrismaClient2) {
779
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
780
+ }
781
+ return new PrismaClient2();
782
+ }
783
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
784
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
785
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
786
+ const module = await import(pathToFileURL(prismaEntry).href);
787
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
788
+ if (!PrismaClient) {
789
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
790
+ }
791
+ return new PrismaClient();
792
+ })();
793
+ }
794
+ return prismaClientPromise;
795
+ }
796
+ async function ensureCompatibilityViews(prisma) {
797
+ if (!compatibilityBootstrapPromise) {
798
+ compatibilityBootstrapPromise = (async () => {
799
+ for (const mapping of VIEW_MAPPINGS) {
800
+ const relation = mapping.source.replace(/"/g, "");
801
+ const rows = await prisma.$queryRawUnsafe(
802
+ "SELECT to_regclass($1) AS regclass",
803
+ relation
804
+ );
805
+ if (!rows[0]?.regclass) {
806
+ continue;
807
+ }
808
+ await prisma.$executeRawUnsafe(
809
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
810
+ );
811
+ }
812
+ })();
813
+ }
814
+ return compatibilityBootstrapPromise;
815
+ }
816
+ async function executeOnPrisma(executor, stmt) {
817
+ const translated = translateStatementForPostgres(stmt);
818
+ if (isReadQuery(translated.sql)) {
819
+ const rows = await executor.$queryRawUnsafe(
820
+ translated.sql,
821
+ ...translated.args
822
+ );
823
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
824
+ }
825
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
826
+ return buildResultSet([], rowsAffected);
827
+ }
828
+ function splitSqlStatements(sql) {
829
+ const parts = [];
830
+ let current = "";
831
+ let inSingle = false;
832
+ let inDouble = false;
833
+ let inLineComment = false;
834
+ let inBlockComment = false;
835
+ for (let i = 0; i < sql.length; i++) {
836
+ const ch = sql[i];
837
+ const next = sql[i + 1];
838
+ if (inLineComment) {
839
+ current += ch;
840
+ if (ch === "\n") inLineComment = false;
841
+ continue;
842
+ }
843
+ if (inBlockComment) {
844
+ current += ch;
845
+ if (ch === "*" && next === "/") {
846
+ current += next;
847
+ inBlockComment = false;
848
+ i += 1;
849
+ }
850
+ continue;
851
+ }
852
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
853
+ current += ch + next;
854
+ inLineComment = true;
855
+ i += 1;
856
+ continue;
857
+ }
858
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
859
+ current += ch + next;
860
+ inBlockComment = true;
861
+ i += 1;
862
+ continue;
863
+ }
864
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
865
+ inSingle = !inSingle;
866
+ current += ch;
867
+ continue;
868
+ }
869
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
870
+ inDouble = !inDouble;
871
+ current += ch;
872
+ continue;
873
+ }
874
+ if (!inSingle && !inDouble && ch === ";") {
875
+ if (current.trim()) {
876
+ parts.push(current.trim());
877
+ }
878
+ current = "";
879
+ continue;
880
+ }
881
+ current += ch;
882
+ }
883
+ if (current.trim()) {
884
+ parts.push(current.trim());
885
+ }
886
+ return parts;
887
+ }
888
+ async function createPrismaDbAdapter(fallbackClient) {
889
+ const prisma = await loadPrismaClient();
890
+ await ensureCompatibilityViews(prisma);
891
+ let closed = false;
892
+ let adapter;
893
+ const fallbackExecute = async (stmt, error) => {
894
+ if (!fallbackClient) {
895
+ if (error) throw error;
896
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
897
+ }
898
+ if (error) {
899
+ process.stderr.write(
900
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
901
+ `
902
+ );
903
+ }
904
+ return fallbackClient.execute(stmt);
905
+ };
906
+ adapter = {
907
+ async execute(stmt) {
908
+ if (shouldBypassPostgres(stmt)) {
909
+ return fallbackExecute(stmt);
910
+ }
911
+ try {
912
+ return await executeOnPrisma(prisma, stmt);
913
+ } catch (error) {
914
+ if (shouldFallbackOnError(error)) {
915
+ return fallbackExecute(stmt, error);
916
+ }
917
+ throw error;
918
+ }
919
+ },
920
+ async batch(stmts, mode) {
921
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
922
+ if (!fallbackClient) {
923
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
924
+ }
925
+ return fallbackClient.batch(stmts, mode);
926
+ }
927
+ try {
928
+ if (prisma.$transaction) {
929
+ return await prisma.$transaction(async (tx) => {
930
+ const results2 = [];
931
+ for (const stmt of stmts) {
932
+ results2.push(await executeOnPrisma(tx, stmt));
933
+ }
934
+ return results2;
935
+ });
936
+ }
937
+ const results = [];
938
+ for (const stmt of stmts) {
939
+ results.push(await executeOnPrisma(prisma, stmt));
940
+ }
941
+ return results;
942
+ } catch (error) {
943
+ if (fallbackClient && shouldFallbackOnError(error)) {
944
+ process.stderr.write(
945
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
946
+ `
947
+ );
948
+ return fallbackClient.batch(stmts, mode);
949
+ }
950
+ throw error;
951
+ }
952
+ },
953
+ async migrate(stmts) {
954
+ if (fallbackClient) {
955
+ return fallbackClient.migrate(stmts);
956
+ }
957
+ return adapter.batch(stmts, "deferred");
958
+ },
959
+ async transaction(mode) {
960
+ if (!fallbackClient) {
961
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
962
+ }
963
+ return fallbackClient.transaction(mode);
964
+ },
965
+ async executeMultiple(sql) {
966
+ if (fallbackClient && shouldBypassPostgres(sql)) {
967
+ return fallbackClient.executeMultiple(sql);
968
+ }
969
+ for (const statement of splitSqlStatements(sql)) {
970
+ await adapter.execute(statement);
971
+ }
972
+ },
973
+ async sync() {
974
+ if (fallbackClient) {
975
+ return fallbackClient.sync();
976
+ }
977
+ return { frame_no: 0, frames_synced: 0 };
978
+ },
979
+ close() {
980
+ closed = true;
981
+ prismaClientPromise = null;
982
+ compatibilityBootstrapPromise = null;
983
+ void prisma.$disconnect?.();
984
+ },
985
+ get closed() {
986
+ return closed;
987
+ },
988
+ get protocol() {
989
+ return "prisma-postgres";
990
+ }
991
+ };
992
+ return adapter;
993
+ }
994
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
995
+ var init_database_adapter = __esm({
996
+ "src/lib/database-adapter.ts"() {
997
+ "use strict";
998
+ VIEW_MAPPINGS = [
999
+ { view: "memories", source: "memory.memory_records" },
1000
+ { view: "tasks", source: "memory.tasks" },
1001
+ { view: "behaviors", source: "memory.behaviors" },
1002
+ { view: "entities", source: "memory.entities" },
1003
+ { view: "relationships", source: "memory.relationships" },
1004
+ { view: "entity_memories", source: "memory.entity_memories" },
1005
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1006
+ { view: "notifications", source: "memory.notifications" },
1007
+ { view: "messages", source: "memory.messages" },
1008
+ { view: "users", source: "wiki.users" },
1009
+ { view: "workspaces", source: "wiki.workspaces" },
1010
+ { view: "workspace_users", source: "wiki.workspace_users" },
1011
+ { view: "documents", source: "wiki.workspace_documents" },
1012
+ { view: "chats", source: "wiki.workspace_chats" }
1013
+ ];
1014
+ UPSERT_KEYS = {
1015
+ memories: ["id"],
1016
+ tasks: ["id"],
1017
+ behaviors: ["id"],
1018
+ entities: ["id"],
1019
+ relationships: ["id"],
1020
+ entity_aliases: ["alias"],
1021
+ notifications: ["id"],
1022
+ messages: ["id"],
1023
+ users: ["id"],
1024
+ workspaces: ["id"],
1025
+ workspace_users: ["id"],
1026
+ documents: ["id"],
1027
+ chats: ["id"]
1028
+ };
1029
+ BOOLEAN_COLUMNS_BY_TABLE = {
1030
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1031
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1032
+ notifications: /* @__PURE__ */ new Set(["read"]),
1033
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1034
+ };
1035
+ BOOLEAN_COLUMN_NAMES = new Set(
1036
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1037
+ );
1038
+ IMMEDIATE_FALLBACK_PATTERNS = [
1039
+ /\bPRAGMA\b/i,
1040
+ /\bsqlite_master\b/i,
1041
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1042
+ /\bMATCH\b/i,
1043
+ /\bvector_distance_cos\s*\(/i,
1044
+ /\bjson_extract\s*\(/i,
1045
+ /\bjulianday\s*\(/i,
1046
+ /\bstrftime\s*\(/i,
1047
+ /\blast_insert_rowid\s*\(/i
1048
+ ];
1049
+ prismaClientPromise = null;
1050
+ compatibilityBootstrapPromise = null;
466
1051
  }
467
1052
  });
468
1053
 
469
1054
  // src/lib/exe-daemon-client.ts
470
1055
  import net from "net";
471
- import os3 from "os";
1056
+ import os4 from "os";
472
1057
  import { spawn } from "child_process";
473
1058
  import { randomUUID } from "crypto";
474
1059
  import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
475
- import path4 from "path";
1060
+ import path5 from "path";
476
1061
  import { fileURLToPath } from "url";
477
1062
  function handleData(chunk) {
478
1063
  _buffer += chunk.toString();
@@ -523,17 +1108,17 @@ function cleanupStaleFiles() {
523
1108
  }
524
1109
  }
525
1110
  function findPackageRoot() {
526
- let dir = path4.dirname(fileURLToPath(import.meta.url));
527
- const { root } = path4.parse(dir);
1111
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
1112
+ const { root } = path5.parse(dir);
528
1113
  while (dir !== root) {
529
- if (existsSync3(path4.join(dir, "package.json"))) return dir;
530
- dir = path4.dirname(dir);
1114
+ if (existsSync3(path5.join(dir, "package.json"))) return dir;
1115
+ dir = path5.dirname(dir);
531
1116
  }
532
1117
  return null;
533
1118
  }
534
1119
  function spawnDaemon() {
535
- const freeGB = os3.freemem() / (1024 * 1024 * 1024);
536
- const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
1120
+ const freeGB = os4.freemem() / (1024 * 1024 * 1024);
1121
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
537
1122
  if (totalGB <= 8) {
538
1123
  process.stderr.write(
539
1124
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -553,7 +1138,7 @@ function spawnDaemon() {
553
1138
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
554
1139
  return;
555
1140
  }
556
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1141
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
557
1142
  if (!existsSync3(daemonPath)) {
558
1143
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
559
1144
  `);
@@ -562,7 +1147,7 @@ function spawnDaemon() {
562
1147
  const resolvedPath = daemonPath;
563
1148
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
564
1149
  `);
565
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
1150
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
566
1151
  let stderrFd = "ignore";
567
1152
  try {
568
1153
  stderrFd = openSync(logPath, "a");
@@ -713,74 +1298,123 @@ async function pingDaemon() {
713
1298
  return null;
714
1299
  }
715
1300
  function killAndRespawnDaemon() {
716
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
717
- if (existsSync3(PID_PATH)) {
718
- try {
719
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
720
- if (pid > 0) {
721
- try {
722
- process.kill(pid, "SIGKILL");
723
- } catch {
1301
+ if (!acquireSpawnLock()) {
1302
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1303
+ if (_socket) {
1304
+ _socket.destroy();
1305
+ _socket = null;
1306
+ }
1307
+ _connected = false;
1308
+ _buffer = "";
1309
+ return;
1310
+ }
1311
+ try {
1312
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1313
+ if (existsSync3(PID_PATH)) {
1314
+ try {
1315
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
1316
+ if (pid > 0) {
1317
+ try {
1318
+ process.kill(pid, "SIGKILL");
1319
+ } catch {
1320
+ }
724
1321
  }
1322
+ } catch {
725
1323
  }
1324
+ }
1325
+ if (_socket) {
1326
+ _socket.destroy();
1327
+ _socket = null;
1328
+ }
1329
+ _connected = false;
1330
+ _buffer = "";
1331
+ try {
1332
+ unlinkSync2(PID_PATH);
726
1333
  } catch {
727
1334
  }
1335
+ try {
1336
+ unlinkSync2(SOCKET_PATH);
1337
+ } catch {
1338
+ }
1339
+ spawnDaemon();
1340
+ } finally {
1341
+ releaseSpawnLock();
728
1342
  }
729
- if (_socket) {
730
- _socket.destroy();
731
- _socket = null;
732
- }
733
- _connected = false;
734
- _buffer = "";
1343
+ }
1344
+ function isDaemonTooYoung() {
735
1345
  try {
736
- unlinkSync2(PID_PATH);
1346
+ const stat = statSync(PID_PATH);
1347
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
737
1348
  } catch {
1349
+ return false;
738
1350
  }
739
- try {
740
- unlinkSync2(SOCKET_PATH);
741
- } catch {
1351
+ }
1352
+ async function retryThenRestart(doRequest, label) {
1353
+ const result = await doRequest();
1354
+ if (!result.error) {
1355
+ _consecutiveFailures = 0;
1356
+ return result;
742
1357
  }
743
- spawnDaemon();
1358
+ _consecutiveFailures++;
1359
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1360
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1361
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1362
+ `);
1363
+ await new Promise((r) => setTimeout(r, delayMs));
1364
+ if (!_connected) {
1365
+ if (!await connectToSocket()) continue;
1366
+ }
1367
+ const retry = await doRequest();
1368
+ if (!retry.error) {
1369
+ _consecutiveFailures = 0;
1370
+ return retry;
1371
+ }
1372
+ _consecutiveFailures++;
1373
+ }
1374
+ if (isDaemonTooYoung()) {
1375
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1376
+ `);
1377
+ return { error: result.error };
1378
+ }
1379
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1380
+ `);
1381
+ killAndRespawnDaemon();
1382
+ const start = Date.now();
1383
+ let delay2 = 200;
1384
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1385
+ await new Promise((r) => setTimeout(r, delay2));
1386
+ if (await connectToSocket()) break;
1387
+ delay2 = Math.min(delay2 * 2, 3e3);
1388
+ }
1389
+ if (!_connected) return { error: "Daemon restart failed" };
1390
+ const final = await doRequest();
1391
+ if (!final.error) _consecutiveFailures = 0;
1392
+ return final;
744
1393
  }
745
1394
  async function embedViaClient(text, priority = "high") {
746
1395
  if (!_connected && !await connectEmbedDaemon()) return null;
747
1396
  _requestCount++;
748
1397
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
749
1398
  const health = await pingDaemon();
750
- if (!health) {
1399
+ if (!health && !isDaemonTooYoung()) {
751
1400
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
752
1401
  `);
753
1402
  killAndRespawnDaemon();
754
1403
  const start = Date.now();
755
- let delay2 = 200;
1404
+ let d = 200;
756
1405
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
757
- await new Promise((r) => setTimeout(r, delay2));
1406
+ await new Promise((r) => setTimeout(r, d));
758
1407
  if (await connectToSocket()) break;
759
- delay2 = Math.min(delay2 * 2, 3e3);
1408
+ d = Math.min(d * 2, 3e3);
760
1409
  }
761
1410
  if (!_connected) return null;
762
1411
  }
763
1412
  }
764
- const result = await sendRequest([text], priority);
765
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
766
- if (result.error) {
767
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
768
- `);
769
- killAndRespawnDaemon();
770
- const start = Date.now();
771
- let delay2 = 200;
772
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
773
- await new Promise((r) => setTimeout(r, delay2));
774
- if (await connectToSocket()) break;
775
- delay2 = Math.min(delay2 * 2, 3e3);
776
- }
777
- if (!_connected) return null;
778
- const retry = await sendRequest([text], priority);
779
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
780
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
781
- `);
782
- }
783
- return null;
1413
+ const result = await retryThenRestart(
1414
+ () => sendRequest([text], priority),
1415
+ "Embed"
1416
+ );
1417
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
784
1418
  }
785
1419
  function disconnectClient() {
786
1420
  if (_socket) {
@@ -798,14 +1432,14 @@ function disconnectClient() {
798
1432
  function isClientConnected() {
799
1433
  return _connected;
800
1434
  }
801
- 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;
1435
+ 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;
802
1436
  var init_exe_daemon_client = __esm({
803
1437
  "src/lib/exe-daemon-client.ts"() {
804
1438
  "use strict";
805
1439
  init_config();
806
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
807
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
808
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
1440
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1441
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1442
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
809
1443
  SPAWN_LOCK_STALE_MS = 3e4;
810
1444
  CONNECT_TIMEOUT_MS = 15e3;
811
1445
  REQUEST_TIMEOUT_MS = 3e4;
@@ -813,7 +1447,11 @@ var init_exe_daemon_client = __esm({
813
1447
  _connected = false;
814
1448
  _buffer = "";
815
1449
  _requestCount = 0;
1450
+ _consecutiveFailures = 0;
816
1451
  HEALTH_CHECK_INTERVAL = 100;
1452
+ MAX_RETRIES_BEFORE_RESTART = 3;
1453
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
1454
+ MIN_DAEMON_AGE_MS = 3e4;
817
1455
  _pending = /* @__PURE__ */ new Map();
818
1456
  MAX_BUFFER = 1e7;
819
1457
  }
@@ -889,7 +1527,7 @@ __export(db_daemon_client_exports, {
889
1527
  createDaemonDbClient: () => createDaemonDbClient,
890
1528
  initDaemonDbClient: () => initDaemonDbClient
891
1529
  });
892
- function normalizeStatement(stmt) {
1530
+ function normalizeStatement2(stmt) {
893
1531
  if (typeof stmt === "string") {
894
1532
  return { sql: stmt, args: [] };
895
1533
  }
@@ -913,7 +1551,7 @@ function createDaemonDbClient(fallbackClient) {
913
1551
  if (!_useDaemon || !isClientConnected()) {
914
1552
  return fallbackClient.execute(stmt);
915
1553
  }
916
- const { sql, args } = normalizeStatement(stmt);
1554
+ const { sql, args } = normalizeStatement2(stmt);
917
1555
  const response = await sendDaemonRequest({
918
1556
  type: "db-execute",
919
1557
  sql,
@@ -938,7 +1576,7 @@ function createDaemonDbClient(fallbackClient) {
938
1576
  if (!_useDaemon || !isClientConnected()) {
939
1577
  return fallbackClient.batch(stmts, mode);
940
1578
  }
941
- const statements = stmts.map(normalizeStatement);
1579
+ const statements = stmts.map(normalizeStatement2);
942
1580
  const response = await sendDaemonRequest({
943
1581
  type: "db-batch",
944
1582
  statements,
@@ -1033,6 +1671,18 @@ __export(database_exports, {
1033
1671
  });
1034
1672
  import { createClient } from "@libsql/client";
1035
1673
  async function initDatabase(config) {
1674
+ if (_walCheckpointTimer) {
1675
+ clearInterval(_walCheckpointTimer);
1676
+ _walCheckpointTimer = null;
1677
+ }
1678
+ if (_daemonClient) {
1679
+ _daemonClient.close();
1680
+ _daemonClient = null;
1681
+ }
1682
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1683
+ _adapterClient.close();
1684
+ }
1685
+ _adapterClient = null;
1036
1686
  if (_client) {
1037
1687
  _client.close();
1038
1688
  _client = null;
@@ -1046,6 +1696,7 @@ async function initDatabase(config) {
1046
1696
  }
1047
1697
  _client = createClient(opts);
1048
1698
  _resilientClient = wrapWithRetry(_client);
1699
+ _adapterClient = _resilientClient;
1049
1700
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1050
1701
  });
1051
1702
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1056,14 +1707,20 @@ async function initDatabase(config) {
1056
1707
  });
1057
1708
  }, 3e4);
1058
1709
  _walCheckpointTimer.unref();
1710
+ if (process.env.DATABASE_URL) {
1711
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1712
+ }
1059
1713
  }
1060
1714
  function isInitialized() {
1061
- return _client !== null;
1715
+ return _adapterClient !== null || _client !== null;
1062
1716
  }
1063
1717
  function getClient() {
1064
- if (!_resilientClient) {
1718
+ if (!_adapterClient) {
1065
1719
  throw new Error("Database client not initialized. Call initDatabase() first.");
1066
1720
  }
1721
+ if (process.env.DATABASE_URL) {
1722
+ return _adapterClient;
1723
+ }
1067
1724
  if (process.env.EXE_IS_DAEMON === "1") {
1068
1725
  return _resilientClient;
1069
1726
  }
@@ -1073,6 +1730,7 @@ function getClient() {
1073
1730
  return _resilientClient;
1074
1731
  }
1075
1732
  async function initDaemonClient() {
1733
+ if (process.env.DATABASE_URL) return;
1076
1734
  if (process.env.EXE_IS_DAEMON === "1") return;
1077
1735
  if (!_resilientClient) return;
1078
1736
  try {
@@ -2017,26 +2675,36 @@ async function ensureSchema() {
2017
2675
  }
2018
2676
  }
2019
2677
  async function disposeDatabase() {
2678
+ if (_walCheckpointTimer) {
2679
+ clearInterval(_walCheckpointTimer);
2680
+ _walCheckpointTimer = null;
2681
+ }
2020
2682
  if (_daemonClient) {
2021
2683
  _daemonClient.close();
2022
2684
  _daemonClient = null;
2023
2685
  }
2686
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2687
+ _adapterClient.close();
2688
+ }
2689
+ _adapterClient = null;
2024
2690
  if (_client) {
2025
2691
  _client.close();
2026
2692
  _client = null;
2027
2693
  _resilientClient = null;
2028
2694
  }
2029
2695
  }
2030
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2696
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2031
2697
  var init_database = __esm({
2032
2698
  "src/lib/database.ts"() {
2033
2699
  "use strict";
2034
2700
  init_db_retry();
2035
2701
  init_employees();
2702
+ init_database_adapter();
2036
2703
  _client = null;
2037
2704
  _resilientClient = null;
2038
2705
  _walCheckpointTimer = null;
2039
2706
  _daemonClient = null;
2707
+ _adapterClient = null;
2040
2708
  initTurso = initDatabase;
2041
2709
  disposeTurso = disposeDatabase;
2042
2710
  }
@@ -2110,7 +2778,7 @@ __export(shard_manager_exports, {
2110
2778
  listShards: () => listShards,
2111
2779
  shardExists: () => shardExists
2112
2780
  });
2113
- import path6 from "path";
2781
+ import path7 from "path";
2114
2782
  import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
2115
2783
  import { createClient as createClient2 } from "@libsql/client";
2116
2784
  function initShardManager(encryptionKey) {
@@ -2136,7 +2804,7 @@ function getShardClient(projectName) {
2136
2804
  }
2137
2805
  const cached = _shards.get(safeName);
2138
2806
  if (cached) return cached;
2139
- const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
2807
+ const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
2140
2808
  const client = createClient2({
2141
2809
  url: `file:${dbPath}`,
2142
2810
  encryptionKey: _encryptionKey
@@ -2146,7 +2814,7 @@ function getShardClient(projectName) {
2146
2814
  }
2147
2815
  function shardExists(projectName) {
2148
2816
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2149
- return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
2817
+ return existsSync5(path7.join(SHARDS_DIR, `${safeName}.db`));
2150
2818
  }
2151
2819
  function listShards() {
2152
2820
  if (!existsSync5(SHARDS_DIR)) return [];
@@ -2223,7 +2891,23 @@ async function ensureShardSchema(client) {
2223
2891
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
2224
2892
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
2225
2893
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
2226
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2894
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2895
+ // Metadata enrichment columns (must match database.ts)
2896
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2897
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2898
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2899
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2900
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2901
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2902
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2903
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2904
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2905
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2906
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2907
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2908
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2909
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2910
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2227
2911
  ]) {
2228
2912
  try {
2229
2913
  await client.execute(col);
@@ -2335,7 +3019,7 @@ var init_shard_manager = __esm({
2335
3019
  "src/lib/shard-manager.ts"() {
2336
3020
  "use strict";
2337
3021
  init_config();
2338
- SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
3022
+ SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
2339
3023
  _shards = /* @__PURE__ */ new Map();
2340
3024
  _encryptionKey = null;
2341
3025
  _shardingEnabled = false;
@@ -2531,8 +3215,8 @@ ${p.content}`).join("\n\n");
2531
3215
 
2532
3216
  // src/lib/notifications.ts
2533
3217
  import crypto2 from "crypto";
2534
- import path7 from "path";
2535
- import os5 from "os";
3218
+ import path8 from "path";
3219
+ import os6 from "os";
2536
3220
  import {
2537
3221
  readFileSync as readFileSync4,
2538
3222
  readdirSync as readdirSync2,
@@ -2584,7 +3268,7 @@ var init_notifications = __esm({
2584
3268
  // src/lib/license.ts
2585
3269
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
2586
3270
  import { randomUUID as randomUUID3 } from "crypto";
2587
- import path8 from "path";
3271
+ import path9 from "path";
2588
3272
  import { jwtVerify, importSPKI } from "jose";
2589
3273
  async function fetchRetry(url, init) {
2590
3274
  try {
@@ -2595,7 +3279,7 @@ async function fetchRetry(url, init) {
2595
3279
  }
2596
3280
  }
2597
3281
  function loadDeviceId() {
2598
- const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
3282
+ const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
2599
3283
  try {
2600
3284
  if (existsSync7(deviceJsonPath)) {
2601
3285
  const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
@@ -2760,7 +3444,7 @@ async function checkLicense() {
2760
3444
  let key = loadLicense();
2761
3445
  if (!key) {
2762
3446
  try {
2763
- const configPath = path8.join(EXE_AI_DIR, "config.json");
3447
+ const configPath = path9.join(EXE_AI_DIR, "config.json");
2764
3448
  if (existsSync7(configPath)) {
2765
3449
  const raw = JSON.parse(readFileSync5(configPath, "utf8"));
2766
3450
  const cloud = raw.cloud;
@@ -2783,9 +3467,9 @@ var init_license = __esm({
2783
3467
  "src/lib/license.ts"() {
2784
3468
  "use strict";
2785
3469
  init_config();
2786
- LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2787
- CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2788
- DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
3470
+ LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3471
+ CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3472
+ DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2789
3473
  API_BASE = "https://askexe.com/cloud";
2790
3474
  RETRY_DELAY_MS = 500;
2791
3475
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2815,7 +3499,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2815
3499
 
2816
3500
  // src/lib/plan-limits.ts
2817
3501
  import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2818
- import path9 from "path";
3502
+ import path10 from "path";
2819
3503
  function getLicenseSync() {
2820
3504
  try {
2821
3505
  if (!existsSync8(CACHE_PATH2)) return freeLicense();
@@ -2906,7 +3590,7 @@ var init_plan_limits = __esm({
2906
3590
  this.name = "PlanLimitError";
2907
3591
  }
2908
3592
  };
2909
- CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
3593
+ CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2910
3594
  }
2911
3595
  });
2912
3596
 
@@ -2948,8 +3632,8 @@ async function embedDirect(text) {
2948
3632
  const llamaCpp = await import("node-llama-cpp");
2949
3633
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2950
3634
  const { existsSync: existsSync16 } = await import("fs");
2951
- const path20 = await import("path");
2952
- const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
3635
+ const path21 = await import("path");
3636
+ const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2953
3637
  if (!existsSync16(modelPath)) {
2954
3638
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2955
3639
  }
@@ -2980,10 +3664,10 @@ var init_embedder = __esm({
2980
3664
 
2981
3665
  // src/lib/session-registry.ts
2982
3666
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
2983
- import path10 from "path";
2984
- import os6 from "os";
3667
+ import path11 from "path";
3668
+ import os7 from "os";
2985
3669
  function registerSession(entry) {
2986
- const dir = path10.dirname(REGISTRY_PATH);
3670
+ const dir = path11.dirname(REGISTRY_PATH);
2987
3671
  if (!existsSync9(dir)) {
2988
3672
  mkdirSync3(dir, { recursive: true });
2989
3673
  }
@@ -3008,7 +3692,7 @@ var REGISTRY_PATH;
3008
3692
  var init_session_registry = __esm({
3009
3693
  "src/lib/session-registry.ts"() {
3010
3694
  "use strict";
3011
- REGISTRY_PATH = path10.join(os6.homedir(), ".exe-os", "session-registry.json");
3695
+ REGISTRY_PATH = path11.join(os7.homedir(), ".exe-os", "session-registry.json");
3012
3696
  }
3013
3697
  });
3014
3698
 
@@ -3266,7 +3950,7 @@ var init_runtime_table = __esm({
3266
3950
 
3267
3951
  // src/lib/agent-config.ts
3268
3952
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
3269
- import path11 from "path";
3953
+ import path12 from "path";
3270
3954
  function loadAgentConfig() {
3271
3955
  if (!existsSync10(AGENT_CONFIG_PATH)) return {};
3272
3956
  try {
@@ -3289,7 +3973,7 @@ var init_agent_config = __esm({
3289
3973
  "use strict";
3290
3974
  init_config();
3291
3975
  init_runtime_table();
3292
- AGENT_CONFIG_PATH = path11.join(EXE_AI_DIR, "agent-config.json");
3976
+ AGENT_CONFIG_PATH = path12.join(EXE_AI_DIR, "agent-config.json");
3293
3977
  DEFAULT_MODELS = {
3294
3978
  claude: "claude-opus-4",
3295
3979
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
@@ -3308,10 +3992,10 @@ __export(intercom_queue_exports, {
3308
3992
  readQueue: () => readQueue
3309
3993
  });
3310
3994
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
3311
- import path12 from "path";
3312
- import os7 from "os";
3995
+ import path13 from "path";
3996
+ import os8 from "os";
3313
3997
  function ensureDir() {
3314
- const dir = path12.dirname(QUEUE_PATH);
3998
+ const dir = path13.dirname(QUEUE_PATH);
3315
3999
  if (!existsSync11(dir)) mkdirSync5(dir, { recursive: true });
3316
4000
  }
3317
4001
  function readQueue() {
@@ -3417,10 +4101,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
3417
4101
  var init_intercom_queue = __esm({
3418
4102
  "src/lib/intercom-queue.ts"() {
3419
4103
  "use strict";
3420
- QUEUE_PATH = path12.join(os7.homedir(), ".exe-os", "intercom-queue.json");
4104
+ QUEUE_PATH = path13.join(os8.homedir(), ".exe-os", "intercom-queue.json");
3421
4105
  MAX_RETRIES2 = 5;
3422
4106
  TTL_MS = 60 * 60 * 1e3;
3423
- INTERCOM_LOG = path12.join(os7.homedir(), ".exe-os", "intercom.log");
4107
+ INTERCOM_LOG = path13.join(os8.homedir(), ".exe-os", "intercom.log");
3424
4108
  }
3425
4109
  });
3426
4110
 
@@ -3769,12 +4453,12 @@ __export(tmux_routing_exports, {
3769
4453
  });
3770
4454
  import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
3771
4455
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
3772
- import path13 from "path";
3773
- import os8 from "os";
4456
+ import path14 from "path";
4457
+ import os9 from "os";
3774
4458
  import { fileURLToPath as fileURLToPath2 } from "url";
3775
4459
  import { unlinkSync as unlinkSync4 } from "fs";
3776
4460
  function spawnLockPath(sessionName) {
3777
- return path13.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4461
+ return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3778
4462
  }
3779
4463
  function isProcessAlive(pid) {
3780
4464
  try {
@@ -3811,8 +4495,8 @@ function releaseSpawnLock2(sessionName) {
3811
4495
  function resolveBehaviorsExporterScript() {
3812
4496
  try {
3813
4497
  const thisFile = fileURLToPath2(import.meta.url);
3814
- const scriptPath = path13.join(
3815
- path13.dirname(thisFile),
4498
+ const scriptPath = path14.join(
4499
+ path14.dirname(thisFile),
3816
4500
  "..",
3817
4501
  "bin",
3818
4502
  "exe-export-behaviors.js"
@@ -3887,7 +4571,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3887
4571
  mkdirSync6(SESSION_CACHE, { recursive: true });
3888
4572
  }
3889
4573
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3890
- const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4574
+ const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3891
4575
  writeFileSync6(filePath, JSON.stringify({
3892
4576
  parentExe: rootExe,
3893
4577
  dispatchedBy: dispatchedBy || rootExe,
@@ -3896,7 +4580,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3896
4580
  }
3897
4581
  function getParentExe(sessionKey) {
3898
4582
  try {
3899
- const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4583
+ const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3900
4584
  return data.parentExe || null;
3901
4585
  } catch {
3902
4586
  return null;
@@ -3905,7 +4589,7 @@ function getParentExe(sessionKey) {
3905
4589
  function getDispatchedBy(sessionKey) {
3906
4590
  try {
3907
4591
  const data = JSON.parse(readFileSync10(
3908
- path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4592
+ path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3909
4593
  "utf8"
3910
4594
  ));
3911
4595
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -3916,15 +4600,24 @@ function getDispatchedBy(sessionKey) {
3916
4600
  function resolveExeSession() {
3917
4601
  const mySession = getMySession();
3918
4602
  if (!mySession) return null;
4603
+ const fromSessionName = extractRootExe(mySession);
3919
4604
  try {
3920
4605
  const key = getSessionKey();
3921
4606
  const parentExe = getParentExe(key);
3922
4607
  if (parentExe) {
3923
- return extractRootExe(parentExe) ?? parentExe;
4608
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
4609
+ if (fromSessionName && fromCache !== fromSessionName) {
4610
+ process.stderr.write(
4611
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
4612
+ `
4613
+ );
4614
+ return fromSessionName;
4615
+ }
4616
+ return fromCache;
3924
4617
  }
3925
4618
  } catch {
3926
4619
  }
3927
- return extractRootExe(mySession) ?? mySession;
4620
+ return fromSessionName ?? mySession;
3928
4621
  }
3929
4622
  function isEmployeeAlive(sessionName) {
3930
4623
  return getTransport().isAlive(sessionName);
@@ -4082,7 +4775,7 @@ function sendIntercom(targetSession) {
4082
4775
  try {
4083
4776
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4084
4777
  const agent = baseAgentName(rawAgent);
4085
- const markerPath = path13.join(SESSION_CACHE, `current-task-${agent}.json`);
4778
+ const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
4086
4779
  if (existsSync12(markerPath)) {
4087
4780
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
4088
4781
  return "debounced";
@@ -4092,7 +4785,7 @@ function sendIntercom(targetSession) {
4092
4785
  try {
4093
4786
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4094
4787
  const agent = baseAgentName(rawAgent);
4095
- const taskDir = path13.join(process.cwd(), "exe", agent);
4788
+ const taskDir = path14.join(process.cwd(), "exe", agent);
4096
4789
  if (existsSync12(taskDir)) {
4097
4790
  const files = readdirSync3(taskDir).filter(
4098
4791
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -4226,8 +4919,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4226
4919
  const transport = getTransport();
4227
4920
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4228
4921
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4229
- const logDir = path13.join(os8.homedir(), ".exe-os", "session-logs");
4230
- const logFile = path13.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4922
+ const logDir = path14.join(os9.homedir(), ".exe-os", "session-logs");
4923
+ const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4231
4924
  if (!existsSync12(logDir)) {
4232
4925
  mkdirSync6(logDir, { recursive: true });
4233
4926
  }
@@ -4235,14 +4928,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4235
4928
  let cleanupSuffix = "";
4236
4929
  try {
4237
4930
  const thisFile = fileURLToPath2(import.meta.url);
4238
- const cleanupScript = path13.join(path13.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4931
+ const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4239
4932
  if (existsSync12(cleanupScript)) {
4240
4933
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4241
4934
  }
4242
4935
  } catch {
4243
4936
  }
4244
4937
  try {
4245
- const claudeJsonPath = path13.join(os8.homedir(), ".claude.json");
4938
+ const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
4246
4939
  let claudeJson = {};
4247
4940
  try {
4248
4941
  claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
@@ -4257,10 +4950,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4257
4950
  } catch {
4258
4951
  }
4259
4952
  try {
4260
- const settingsDir = path13.join(os8.homedir(), ".claude", "projects");
4953
+ const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
4261
4954
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4262
- const projSettingsDir = path13.join(settingsDir, normalizedKey);
4263
- const settingsPath = path13.join(projSettingsDir, "settings.json");
4955
+ const projSettingsDir = path14.join(settingsDir, normalizedKey);
4956
+ const settingsPath = path14.join(projSettingsDir, "settings.json");
4264
4957
  let settings = {};
4265
4958
  try {
4266
4959
  settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
@@ -4307,8 +5000,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4307
5000
  let behaviorsFlag = "";
4308
5001
  let legacyFallbackWarned = false;
4309
5002
  if (!useExeAgent && !useBinSymlink) {
4310
- const identityPath = path13.join(
4311
- os8.homedir(),
5003
+ const identityPath = path14.join(
5004
+ os9.homedir(),
4312
5005
  ".exe-os",
4313
5006
  "identity",
4314
5007
  `${employeeName}.md`
@@ -4323,7 +5016,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4323
5016
  }
4324
5017
  const behaviorsFile = exportBehaviorsSync(
4325
5018
  employeeName,
4326
- path13.basename(spawnCwd),
5019
+ path14.basename(spawnCwd),
4327
5020
  sessionName
4328
5021
  );
4329
5022
  if (behaviorsFile) {
@@ -4338,9 +5031,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4338
5031
  }
4339
5032
  let sessionContextFlag = "";
4340
5033
  try {
4341
- const ctxDir = path13.join(os8.homedir(), ".exe-os", "session-cache");
5034
+ const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
4342
5035
  mkdirSync6(ctxDir, { recursive: true });
4343
- const ctxFile = path13.join(ctxDir, `session-context-${sessionName}.md`);
5036
+ const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
4344
5037
  const ctxContent = [
4345
5038
  `## Session Context`,
4346
5039
  `You are running in tmux session: ${sessionName}.`,
@@ -4424,7 +5117,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4424
5117
  transport.pipeLog(sessionName, logFile);
4425
5118
  try {
4426
5119
  const mySession = getMySession();
4427
- const dispatchInfo = path13.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5120
+ const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4428
5121
  writeFileSync6(dispatchInfo, JSON.stringify({
4429
5122
  dispatchedBy: mySession,
4430
5123
  rootExe: exeSession,
@@ -4499,15 +5192,15 @@ var init_tmux_routing = __esm({
4499
5192
  init_intercom_queue();
4500
5193
  init_plan_limits();
4501
5194
  init_employees();
4502
- SPAWN_LOCK_DIR = path13.join(os8.homedir(), ".exe-os", "spawn-locks");
4503
- SESSION_CACHE = path13.join(os8.homedir(), ".exe-os", "session-cache");
5195
+ SPAWN_LOCK_DIR = path14.join(os9.homedir(), ".exe-os", "spawn-locks");
5196
+ SESSION_CACHE = path14.join(os9.homedir(), ".exe-os", "session-cache");
4504
5197
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4505
5198
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4506
5199
  VERIFY_PANE_LINES = 200;
4507
5200
  INTERCOM_DEBOUNCE_MS = 3e4;
4508
5201
  CODEX_DEBOUNCE_MS = 12e4;
4509
- INTERCOM_LOG2 = path13.join(os8.homedir(), ".exe-os", "intercom.log");
4510
- DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
5202
+ INTERCOM_LOG2 = path14.join(os9.homedir(), ".exe-os", "intercom.log");
5203
+ DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4511
5204
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4512
5205
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4513
5206
  }
@@ -4539,8 +5232,8 @@ var init_task_scope = __esm({
4539
5232
 
4540
5233
  // src/lib/tasks-crud.ts
4541
5234
  import crypto4 from "crypto";
4542
- import path14 from "path";
4543
- import os9 from "os";
5235
+ import path15 from "path";
5236
+ import os10 from "os";
4544
5237
  import { execSync as execSync6 } from "child_process";
4545
5238
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
4546
5239
  import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
@@ -4718,8 +5411,8 @@ ${laneWarning}` : laneWarning;
4718
5411
  }
4719
5412
  if (input2.baseDir) {
4720
5413
  try {
4721
- await mkdir4(path14.join(input2.baseDir, "exe", "output"), { recursive: true });
4722
- await mkdir4(path14.join(input2.baseDir, "exe", "research"), { recursive: true });
5414
+ await mkdir4(path15.join(input2.baseDir, "exe", "output"), { recursive: true });
5415
+ await mkdir4(path15.join(input2.baseDir, "exe", "research"), { recursive: true });
4723
5416
  await ensureArchitectureDoc(input2.baseDir, input2.projectName);
4724
5417
  await ensureGitignoreExe(input2.baseDir);
4725
5418
  } catch {
@@ -4755,9 +5448,9 @@ ${laneWarning}` : laneWarning;
4755
5448
  });
4756
5449
  if (input2.baseDir) {
4757
5450
  try {
4758
- const EXE_OS_DIR = path14.join(os9.homedir(), ".exe-os");
4759
- const mdPath = path14.join(EXE_OS_DIR, taskFile);
4760
- const mdDir = path14.dirname(mdPath);
5451
+ const EXE_OS_DIR = path15.join(os10.homedir(), ".exe-os");
5452
+ const mdPath = path15.join(EXE_OS_DIR, taskFile);
5453
+ const mdDir = path15.dirname(mdPath);
4761
5454
  if (!existsSync13(mdDir)) await mkdir4(mdDir, { recursive: true });
4762
5455
  const reviewer = input2.reviewer ?? input2.assignedBy;
4763
5456
  const mdContent = `# ${input2.title}
@@ -5058,7 +5751,7 @@ async function deleteTaskCore(taskId, _baseDir) {
5058
5751
  return { taskFile, assignedTo, assignedBy, taskSlug };
5059
5752
  }
5060
5753
  async function ensureArchitectureDoc(baseDir, projectName) {
5061
- const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
5754
+ const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
5062
5755
  try {
5063
5756
  if (existsSync13(archPath)) return;
5064
5757
  const template = [
@@ -5093,7 +5786,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
5093
5786
  }
5094
5787
  }
5095
5788
  async function ensureGitignoreExe(baseDir) {
5096
- const gitignorePath = path14.join(baseDir, ".gitignore");
5789
+ const gitignorePath = path15.join(baseDir, ".gitignore");
5097
5790
  try {
5098
5791
  if (existsSync13(gitignorePath)) {
5099
5792
  const content = readFileSync11(gitignorePath, "utf-8");
@@ -5127,13 +5820,13 @@ var init_tasks_crud = __esm({
5127
5820
  });
5128
5821
 
5129
5822
  // src/lib/tasks-review.ts
5130
- import path15 from "path";
5823
+ import path16 from "path";
5131
5824
  import { existsSync as existsSync14, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
5132
5825
  async function countPendingReviews(sessionScope) {
5133
5826
  const client = getClient();
5134
5827
  if (sessionScope) {
5135
5828
  const result2 = await client.execute({
5136
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
5829
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
5137
5830
  args: [sessionScope]
5138
5831
  });
5139
5832
  return Number(result2.rows[0]?.cnt) || 0;
@@ -5309,11 +6002,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
5309
6002
  );
5310
6003
  }
5311
6004
  try {
5312
- const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
6005
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5313
6006
  if (existsSync14(cacheDir)) {
5314
6007
  for (const f of readdirSync4(cacheDir)) {
5315
6008
  if (f.startsWith("review-notified-")) {
5316
- unlinkSync5(path15.join(cacheDir, f));
6009
+ unlinkSync5(path16.join(cacheDir, f));
5317
6010
  }
5318
6011
  }
5319
6012
  }
@@ -5334,7 +6027,7 @@ var init_tasks_review = __esm({
5334
6027
  });
5335
6028
 
5336
6029
  // src/lib/tasks-chain.ts
5337
- import path16 from "path";
6030
+ import path17 from "path";
5338
6031
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
5339
6032
  async function cascadeUnblock(taskId, baseDir, now) {
5340
6033
  const client = getClient();
@@ -5351,7 +6044,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
5351
6044
  });
5352
6045
  for (const ur of unblockedRows.rows) {
5353
6046
  try {
5354
- const ubFile = path16.join(baseDir, String(ur.task_file));
6047
+ const ubFile = path17.join(baseDir, String(ur.task_file));
5355
6048
  let ubContent = await readFile4(ubFile, "utf-8");
5356
6049
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
5357
6050
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -5877,7 +6570,7 @@ __export(tasks_exports, {
5877
6570
  updateTaskStatus: () => updateTaskStatus,
5878
6571
  writeCheckpoint: () => writeCheckpoint
5879
6572
  });
5880
- import path17 from "path";
6573
+ import path18 from "path";
5881
6574
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
5882
6575
  async function createTask(input2) {
5883
6576
  const result = await createTaskCore(input2);
@@ -5897,8 +6590,8 @@ async function updateTask(input2) {
5897
6590
  const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
5898
6591
  try {
5899
6592
  const agent = String(row.assigned_to);
5900
- const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5901
- const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
6593
+ const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
6594
+ const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
5902
6595
  if (input2.status === "in_progress") {
5903
6596
  mkdirSync7(cacheDir, { recursive: true });
5904
6597
  writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -6069,12 +6762,12 @@ __export(worker_gate_exports, {
6069
6762
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
6070
6763
  });
6071
6764
  import { readdirSync as readdirSync5, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
6072
- import path18 from "path";
6765
+ import path19 from "path";
6073
6766
  function tryAcquireWorkerSlot() {
6074
6767
  try {
6075
6768
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
6076
6769
  const reservationId = `res-${process.pid}-${Date.now()}`;
6077
- const reservationPath = path18.join(WORKER_PID_DIR, `${reservationId}.pid`);
6770
+ const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
6078
6771
  writeFileSync8(reservationPath, String(process.pid));
6079
6772
  const files = readdirSync5(WORKER_PID_DIR);
6080
6773
  let alive = 0;
@@ -6092,7 +6785,7 @@ function tryAcquireWorkerSlot() {
6092
6785
  alive++;
6093
6786
  } catch {
6094
6787
  try {
6095
- unlinkSync7(path18.join(WORKER_PID_DIR, f));
6788
+ unlinkSync7(path19.join(WORKER_PID_DIR, f));
6096
6789
  } catch {
6097
6790
  }
6098
6791
  }
@@ -6116,13 +6809,13 @@ function tryAcquireWorkerSlot() {
6116
6809
  function registerWorkerPid(pid) {
6117
6810
  try {
6118
6811
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
6119
- writeFileSync8(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6812
+ writeFileSync8(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6120
6813
  } catch {
6121
6814
  }
6122
6815
  }
6123
6816
  function cleanupWorkerPid() {
6124
6817
  try {
6125
- unlinkSync7(path18.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6818
+ unlinkSync7(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6126
6819
  } catch {
6127
6820
  }
6128
6821
  }
@@ -6162,9 +6855,9 @@ var init_worker_gate = __esm({
6162
6855
  "src/lib/worker-gate.ts"() {
6163
6856
  "use strict";
6164
6857
  init_config();
6165
- WORKER_PID_DIR = path18.join(EXE_AI_DIR, "worker-pids");
6858
+ WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
6166
6859
  MAX_CONCURRENT_WORKERS = 3;
6167
- BACKFILL_LOCK = path18.join(WORKER_PID_DIR, "backfill.lock");
6860
+ BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
6168
6861
  }
6169
6862
  });
6170
6863
 
@@ -6172,7 +6865,7 @@ var init_worker_gate = __esm({
6172
6865
  import crypto7 from "crypto";
6173
6866
  import { execSync as execSync7 } from "child_process";
6174
6867
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
6175
- import path19 from "path";
6868
+ import path20 from "path";
6176
6869
 
6177
6870
  // src/lib/error-detector.ts
6178
6871
  init_mcp_prefix();
@@ -6283,15 +6976,15 @@ import { createHash } from "crypto";
6283
6976
  // src/lib/keychain.ts
6284
6977
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
6285
6978
  import { existsSync as existsSync4 } from "fs";
6286
- import path5 from "path";
6287
- import os4 from "os";
6979
+ import path6 from "path";
6980
+ import os5 from "os";
6288
6981
  var SERVICE = "exe-mem";
6289
6982
  var ACCOUNT = "master-key";
6290
6983
  function getKeyDir() {
6291
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
6984
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
6292
6985
  }
6293
6986
  function getKeyPath() {
6294
- return path5.join(getKeyDir(), "master.key");
6987
+ return path6.join(getKeyDir(), "master.key");
6295
6988
  }
6296
6989
  async function tryKeytar() {
6297
6990
  try {
@@ -6314,7 +7007,7 @@ async function getMasterKey() {
6314
7007
  const keyPath = getKeyPath();
6315
7008
  if (!existsSync4(keyPath)) {
6316
7009
  process.stderr.write(
6317
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
7010
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6318
7011
  `
6319
7012
  );
6320
7013
  return null;
@@ -6787,9 +7480,9 @@ function extractBash(input2, response) {
6787
7480
  }
6788
7481
  function extractGrep(input2, response) {
6789
7482
  const pattern = String(input2.pattern ?? "");
6790
- const path20 = input2.path ? String(input2.path) : "";
7483
+ const path21 = input2.path ? String(input2.path) : "";
6791
7484
  const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
6792
- return `Searched for "${pattern}"${path20 ? ` in ${path20}` : ""}
7485
+ return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
6793
7486
  ${output.slice(0, MAX_OUTPUT)}`;
6794
7487
  }
6795
7488
  function extractGlob(input2, response) {
@@ -6932,7 +7625,7 @@ process.stdin.on("end", async () => {
6932
7625
  try {
6933
7626
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
6934
7627
  const agentId2 = process.env.AGENT_ID;
6935
- const cachePath = path19.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
7628
+ const cachePath = path20.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
6936
7629
  const { readFileSync: rf } = await import("fs");
6937
7630
  const cached = JSON.parse(rf(cachePath, "utf8"));
6938
7631
  taskId = cached.taskId ?? null;
@@ -6971,7 +7664,7 @@ process.stdin.on("end", async () => {
6971
7664
  if (needsBackfill) {
6972
7665
  try {
6973
7666
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
6974
- const flagPath = path19.join(exeDir, "session-cache", "needs-backfill");
7667
+ const flagPath = path20.join(exeDir, "session-cache", "needs-backfill");
6975
7668
  writeFileSync9(flagPath, "1");
6976
7669
  } catch (err) {
6977
7670
  process.stderr.write(`[ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
@@ -7083,8 +7776,8 @@ process.stdin.on("end", async () => {
7083
7776
  }
7084
7777
  const cwd = data.cwd ?? process.cwd();
7085
7778
  try {
7086
- mkdirSync9(path19.join(cwd, "exe/output"), { recursive: true });
7087
- mkdirSync9(path19.join(cwd, "exe/research"), { recursive: true });
7779
+ mkdirSync9(path20.join(cwd, "exe/output"), { recursive: true });
7780
+ mkdirSync9(path20.join(cwd, "exe/research"), { recursive: true });
7088
7781
  const { ensureGitignoreExe: ensureGitignoreExe2 } = await Promise.resolve().then(() => (init_tasks(), tasks_exports));
7089
7782
  await ensureGitignoreExe2(cwd);
7090
7783
  } catch (err) {