@askexenow/exe-os 0.9.7 → 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 +657 -35
  5. package/dist/bin/cli.js +1388 -605
  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 +784 -153
  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 +692 -70
  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 +1053 -271
  17. package/dist/bin/exe-heartbeat.js +665 -43
  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 +834 -150
  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 +657 -35
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +720 -89
  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 +724 -93
  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 +1027 -245
  43. package/dist/hooks/bug-report-worker.js +891 -170
  44. package/dist/hooks/commit-complete.js +718 -87
  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 +840 -156
  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 +674 -43
  52. package/dist/hooks/pre-compact.js +718 -87
  53. package/dist/hooks/pre-tool-use.js +872 -125
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1060 -319
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +721 -90
  58. package/dist/hooks/session-start.js +1031 -207
  59. package/dist/hooks/stop.js +680 -49
  60. package/dist/hooks/subagent-stop.js +674 -43
  61. package/dist/hooks/summary-worker.js +816 -132
  62. package/dist/index.js +1015 -232
  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 +894 -162
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +55 -28
  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 +98 -71
  82. package/dist/lib/tmux-routing.js +87 -60
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1784 -458
  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 +290 -164
  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 +195 -38
  91. package/dist/mcp/tools/send-message.js +58 -31
  92. package/dist/mcp/tools/update-task.js +75 -48
  93. package/dist/runtime/index.js +720 -89
  94. package/dist/tui/App.js +853 -123
  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;
@@ -4091,7 +4775,7 @@ function sendIntercom(targetSession) {
4091
4775
  try {
4092
4776
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4093
4777
  const agent = baseAgentName(rawAgent);
4094
- const markerPath = path13.join(SESSION_CACHE, `current-task-${agent}.json`);
4778
+ const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
4095
4779
  if (existsSync12(markerPath)) {
4096
4780
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
4097
4781
  return "debounced";
@@ -4101,7 +4785,7 @@ function sendIntercom(targetSession) {
4101
4785
  try {
4102
4786
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4103
4787
  const agent = baseAgentName(rawAgent);
4104
- const taskDir = path13.join(process.cwd(), "exe", agent);
4788
+ const taskDir = path14.join(process.cwd(), "exe", agent);
4105
4789
  if (existsSync12(taskDir)) {
4106
4790
  const files = readdirSync3(taskDir).filter(
4107
4791
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -4235,8 +4919,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4235
4919
  const transport = getTransport();
4236
4920
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4237
4921
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4238
- const logDir = path13.join(os8.homedir(), ".exe-os", "session-logs");
4239
- 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`);
4240
4924
  if (!existsSync12(logDir)) {
4241
4925
  mkdirSync6(logDir, { recursive: true });
4242
4926
  }
@@ -4244,14 +4928,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4244
4928
  let cleanupSuffix = "";
4245
4929
  try {
4246
4930
  const thisFile = fileURLToPath2(import.meta.url);
4247
- 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");
4248
4932
  if (existsSync12(cleanupScript)) {
4249
4933
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4250
4934
  }
4251
4935
  } catch {
4252
4936
  }
4253
4937
  try {
4254
- const claudeJsonPath = path13.join(os8.homedir(), ".claude.json");
4938
+ const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
4255
4939
  let claudeJson = {};
4256
4940
  try {
4257
4941
  claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
@@ -4266,10 +4950,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4266
4950
  } catch {
4267
4951
  }
4268
4952
  try {
4269
- const settingsDir = path13.join(os8.homedir(), ".claude", "projects");
4953
+ const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
4270
4954
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4271
- const projSettingsDir = path13.join(settingsDir, normalizedKey);
4272
- const settingsPath = path13.join(projSettingsDir, "settings.json");
4955
+ const projSettingsDir = path14.join(settingsDir, normalizedKey);
4956
+ const settingsPath = path14.join(projSettingsDir, "settings.json");
4273
4957
  let settings = {};
4274
4958
  try {
4275
4959
  settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
@@ -4316,8 +5000,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4316
5000
  let behaviorsFlag = "";
4317
5001
  let legacyFallbackWarned = false;
4318
5002
  if (!useExeAgent && !useBinSymlink) {
4319
- const identityPath = path13.join(
4320
- os8.homedir(),
5003
+ const identityPath = path14.join(
5004
+ os9.homedir(),
4321
5005
  ".exe-os",
4322
5006
  "identity",
4323
5007
  `${employeeName}.md`
@@ -4332,7 +5016,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4332
5016
  }
4333
5017
  const behaviorsFile = exportBehaviorsSync(
4334
5018
  employeeName,
4335
- path13.basename(spawnCwd),
5019
+ path14.basename(spawnCwd),
4336
5020
  sessionName
4337
5021
  );
4338
5022
  if (behaviorsFile) {
@@ -4347,9 +5031,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4347
5031
  }
4348
5032
  let sessionContextFlag = "";
4349
5033
  try {
4350
- const ctxDir = path13.join(os8.homedir(), ".exe-os", "session-cache");
5034
+ const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
4351
5035
  mkdirSync6(ctxDir, { recursive: true });
4352
- const ctxFile = path13.join(ctxDir, `session-context-${sessionName}.md`);
5036
+ const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
4353
5037
  const ctxContent = [
4354
5038
  `## Session Context`,
4355
5039
  `You are running in tmux session: ${sessionName}.`,
@@ -4433,7 +5117,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4433
5117
  transport.pipeLog(sessionName, logFile);
4434
5118
  try {
4435
5119
  const mySession = getMySession();
4436
- const dispatchInfo = path13.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5120
+ const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4437
5121
  writeFileSync6(dispatchInfo, JSON.stringify({
4438
5122
  dispatchedBy: mySession,
4439
5123
  rootExe: exeSession,
@@ -4508,15 +5192,15 @@ var init_tmux_routing = __esm({
4508
5192
  init_intercom_queue();
4509
5193
  init_plan_limits();
4510
5194
  init_employees();
4511
- SPAWN_LOCK_DIR = path13.join(os8.homedir(), ".exe-os", "spawn-locks");
4512
- 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");
4513
5197
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4514
5198
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4515
5199
  VERIFY_PANE_LINES = 200;
4516
5200
  INTERCOM_DEBOUNCE_MS = 3e4;
4517
5201
  CODEX_DEBOUNCE_MS = 12e4;
4518
- INTERCOM_LOG2 = path13.join(os8.homedir(), ".exe-os", "intercom.log");
4519
- 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");
4520
5204
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4521
5205
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4522
5206
  }
@@ -4548,8 +5232,8 @@ var init_task_scope = __esm({
4548
5232
 
4549
5233
  // src/lib/tasks-crud.ts
4550
5234
  import crypto4 from "crypto";
4551
- import path14 from "path";
4552
- import os9 from "os";
5235
+ import path15 from "path";
5236
+ import os10 from "os";
4553
5237
  import { execSync as execSync6 } from "child_process";
4554
5238
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
4555
5239
  import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
@@ -4727,8 +5411,8 @@ ${laneWarning}` : laneWarning;
4727
5411
  }
4728
5412
  if (input2.baseDir) {
4729
5413
  try {
4730
- await mkdir4(path14.join(input2.baseDir, "exe", "output"), { recursive: true });
4731
- 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 });
4732
5416
  await ensureArchitectureDoc(input2.baseDir, input2.projectName);
4733
5417
  await ensureGitignoreExe(input2.baseDir);
4734
5418
  } catch {
@@ -4764,9 +5448,9 @@ ${laneWarning}` : laneWarning;
4764
5448
  });
4765
5449
  if (input2.baseDir) {
4766
5450
  try {
4767
- const EXE_OS_DIR = path14.join(os9.homedir(), ".exe-os");
4768
- const mdPath = path14.join(EXE_OS_DIR, taskFile);
4769
- 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);
4770
5454
  if (!existsSync13(mdDir)) await mkdir4(mdDir, { recursive: true });
4771
5455
  const reviewer = input2.reviewer ?? input2.assignedBy;
4772
5456
  const mdContent = `# ${input2.title}
@@ -5067,7 +5751,7 @@ async function deleteTaskCore(taskId, _baseDir) {
5067
5751
  return { taskFile, assignedTo, assignedBy, taskSlug };
5068
5752
  }
5069
5753
  async function ensureArchitectureDoc(baseDir, projectName) {
5070
- const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
5754
+ const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
5071
5755
  try {
5072
5756
  if (existsSync13(archPath)) return;
5073
5757
  const template = [
@@ -5102,7 +5786,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
5102
5786
  }
5103
5787
  }
5104
5788
  async function ensureGitignoreExe(baseDir) {
5105
- const gitignorePath = path14.join(baseDir, ".gitignore");
5789
+ const gitignorePath = path15.join(baseDir, ".gitignore");
5106
5790
  try {
5107
5791
  if (existsSync13(gitignorePath)) {
5108
5792
  const content = readFileSync11(gitignorePath, "utf-8");
@@ -5136,13 +5820,13 @@ var init_tasks_crud = __esm({
5136
5820
  });
5137
5821
 
5138
5822
  // src/lib/tasks-review.ts
5139
- import path15 from "path";
5823
+ import path16 from "path";
5140
5824
  import { existsSync as existsSync14, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
5141
5825
  async function countPendingReviews(sessionScope) {
5142
5826
  const client = getClient();
5143
5827
  if (sessionScope) {
5144
5828
  const result2 = await client.execute({
5145
- 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 = ?",
5146
5830
  args: [sessionScope]
5147
5831
  });
5148
5832
  return Number(result2.rows[0]?.cnt) || 0;
@@ -5318,11 +6002,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
5318
6002
  );
5319
6003
  }
5320
6004
  try {
5321
- const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
6005
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5322
6006
  if (existsSync14(cacheDir)) {
5323
6007
  for (const f of readdirSync4(cacheDir)) {
5324
6008
  if (f.startsWith("review-notified-")) {
5325
- unlinkSync5(path15.join(cacheDir, f));
6009
+ unlinkSync5(path16.join(cacheDir, f));
5326
6010
  }
5327
6011
  }
5328
6012
  }
@@ -5343,7 +6027,7 @@ var init_tasks_review = __esm({
5343
6027
  });
5344
6028
 
5345
6029
  // src/lib/tasks-chain.ts
5346
- import path16 from "path";
6030
+ import path17 from "path";
5347
6031
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
5348
6032
  async function cascadeUnblock(taskId, baseDir, now) {
5349
6033
  const client = getClient();
@@ -5360,7 +6044,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
5360
6044
  });
5361
6045
  for (const ur of unblockedRows.rows) {
5362
6046
  try {
5363
- const ubFile = path16.join(baseDir, String(ur.task_file));
6047
+ const ubFile = path17.join(baseDir, String(ur.task_file));
5364
6048
  let ubContent = await readFile4(ubFile, "utf-8");
5365
6049
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
5366
6050
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -5886,7 +6570,7 @@ __export(tasks_exports, {
5886
6570
  updateTaskStatus: () => updateTaskStatus,
5887
6571
  writeCheckpoint: () => writeCheckpoint
5888
6572
  });
5889
- import path17 from "path";
6573
+ import path18 from "path";
5890
6574
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
5891
6575
  async function createTask(input2) {
5892
6576
  const result = await createTaskCore(input2);
@@ -5906,8 +6590,8 @@ async function updateTask(input2) {
5906
6590
  const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
5907
6591
  try {
5908
6592
  const agent = String(row.assigned_to);
5909
- const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5910
- 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`);
5911
6595
  if (input2.status === "in_progress") {
5912
6596
  mkdirSync7(cacheDir, { recursive: true });
5913
6597
  writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -6078,12 +6762,12 @@ __export(worker_gate_exports, {
6078
6762
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
6079
6763
  });
6080
6764
  import { readdirSync as readdirSync5, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
6081
- import path18 from "path";
6765
+ import path19 from "path";
6082
6766
  function tryAcquireWorkerSlot() {
6083
6767
  try {
6084
6768
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
6085
6769
  const reservationId = `res-${process.pid}-${Date.now()}`;
6086
- const reservationPath = path18.join(WORKER_PID_DIR, `${reservationId}.pid`);
6770
+ const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
6087
6771
  writeFileSync8(reservationPath, String(process.pid));
6088
6772
  const files = readdirSync5(WORKER_PID_DIR);
6089
6773
  let alive = 0;
@@ -6101,7 +6785,7 @@ function tryAcquireWorkerSlot() {
6101
6785
  alive++;
6102
6786
  } catch {
6103
6787
  try {
6104
- unlinkSync7(path18.join(WORKER_PID_DIR, f));
6788
+ unlinkSync7(path19.join(WORKER_PID_DIR, f));
6105
6789
  } catch {
6106
6790
  }
6107
6791
  }
@@ -6125,13 +6809,13 @@ function tryAcquireWorkerSlot() {
6125
6809
  function registerWorkerPid(pid) {
6126
6810
  try {
6127
6811
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
6128
- writeFileSync8(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6812
+ writeFileSync8(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6129
6813
  } catch {
6130
6814
  }
6131
6815
  }
6132
6816
  function cleanupWorkerPid() {
6133
6817
  try {
6134
- unlinkSync7(path18.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6818
+ unlinkSync7(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6135
6819
  } catch {
6136
6820
  }
6137
6821
  }
@@ -6171,9 +6855,9 @@ var init_worker_gate = __esm({
6171
6855
  "src/lib/worker-gate.ts"() {
6172
6856
  "use strict";
6173
6857
  init_config();
6174
- WORKER_PID_DIR = path18.join(EXE_AI_DIR, "worker-pids");
6858
+ WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
6175
6859
  MAX_CONCURRENT_WORKERS = 3;
6176
- BACKFILL_LOCK = path18.join(WORKER_PID_DIR, "backfill.lock");
6860
+ BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
6177
6861
  }
6178
6862
  });
6179
6863
 
@@ -6181,7 +6865,7 @@ var init_worker_gate = __esm({
6181
6865
  import crypto7 from "crypto";
6182
6866
  import { execSync as execSync7 } from "child_process";
6183
6867
  import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
6184
- import path19 from "path";
6868
+ import path20 from "path";
6185
6869
 
6186
6870
  // src/lib/error-detector.ts
6187
6871
  init_mcp_prefix();
@@ -6292,15 +6976,15 @@ import { createHash } from "crypto";
6292
6976
  // src/lib/keychain.ts
6293
6977
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
6294
6978
  import { existsSync as existsSync4 } from "fs";
6295
- import path5 from "path";
6296
- import os4 from "os";
6979
+ import path6 from "path";
6980
+ import os5 from "os";
6297
6981
  var SERVICE = "exe-mem";
6298
6982
  var ACCOUNT = "master-key";
6299
6983
  function getKeyDir() {
6300
- 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");
6301
6985
  }
6302
6986
  function getKeyPath() {
6303
- return path5.join(getKeyDir(), "master.key");
6987
+ return path6.join(getKeyDir(), "master.key");
6304
6988
  }
6305
6989
  async function tryKeytar() {
6306
6990
  try {
@@ -6323,7 +7007,7 @@ async function getMasterKey() {
6323
7007
  const keyPath = getKeyPath();
6324
7008
  if (!existsSync4(keyPath)) {
6325
7009
  process.stderr.write(
6326
- `[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"})
6327
7011
  `
6328
7012
  );
6329
7013
  return null;
@@ -6796,9 +7480,9 @@ function extractBash(input2, response) {
6796
7480
  }
6797
7481
  function extractGrep(input2, response) {
6798
7482
  const pattern = String(input2.pattern ?? "");
6799
- const path20 = input2.path ? String(input2.path) : "";
7483
+ const path21 = input2.path ? String(input2.path) : "";
6800
7484
  const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
6801
- return `Searched for "${pattern}"${path20 ? ` in ${path20}` : ""}
7485
+ return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
6802
7486
  ${output.slice(0, MAX_OUTPUT)}`;
6803
7487
  }
6804
7488
  function extractGlob(input2, response) {
@@ -6941,7 +7625,7 @@ process.stdin.on("end", async () => {
6941
7625
  try {
6942
7626
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
6943
7627
  const agentId2 = process.env.AGENT_ID;
6944
- const cachePath = path19.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
7628
+ const cachePath = path20.join(exeDir, "session-cache", `current-task-${agentId2}.json`);
6945
7629
  const { readFileSync: rf } = await import("fs");
6946
7630
  const cached = JSON.parse(rf(cachePath, "utf8"));
6947
7631
  taskId = cached.taskId ?? null;
@@ -6980,7 +7664,7 @@ process.stdin.on("end", async () => {
6980
7664
  if (needsBackfill) {
6981
7665
  try {
6982
7666
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
6983
- const flagPath = path19.join(exeDir, "session-cache", "needs-backfill");
7667
+ const flagPath = path20.join(exeDir, "session-cache", "needs-backfill");
6984
7668
  writeFileSync9(flagPath, "1");
6985
7669
  } catch (err) {
6986
7670
  process.stderr.write(`[ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
@@ -7092,8 +7776,8 @@ process.stdin.on("end", async () => {
7092
7776
  }
7093
7777
  const cwd = data.cwd ?? process.cwd();
7094
7778
  try {
7095
- mkdirSync9(path19.join(cwd, "exe/output"), { recursive: true });
7096
- 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 });
7097
7781
  const { ensureGitignoreExe: ensureGitignoreExe2 } = await Promise.resolve().then(() => (init_tasks(), tasks_exports));
7098
7782
  await ensureGitignoreExe2(cwd);
7099
7783
  } catch (err) {