@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
@@ -567,7 +567,7 @@ function registerBinSymlinks(name) {
567
567
  }
568
568
  return { created, skipped, errors };
569
569
  }
570
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
570
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
571
571
  var init_employees = __esm({
572
572
  "src/lib/employees.ts"() {
573
573
  "use strict";
@@ -575,16 +575,601 @@ var init_employees = __esm({
575
575
  EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
576
576
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
577
577
  COORDINATOR_ROLE = "COO";
578
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
579
+ }
580
+ });
581
+
582
+ // src/lib/database-adapter.ts
583
+ import os4 from "os";
584
+ import path4 from "path";
585
+ import { createRequire } from "module";
586
+ import { pathToFileURL } from "url";
587
+ function quotedIdentifier(identifier) {
588
+ return `"${identifier.replace(/"/g, '""')}"`;
589
+ }
590
+ function unqualifiedTableName(name) {
591
+ const raw = name.trim().replace(/^"|"$/g, "");
592
+ const parts = raw.split(".");
593
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
594
+ }
595
+ function stripTrailingSemicolon(sql) {
596
+ return sql.trim().replace(/;+\s*$/u, "");
597
+ }
598
+ function appendClause(sql, clause) {
599
+ const trimmed = stripTrailingSemicolon(sql);
600
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
601
+ if (!returningMatch) {
602
+ return `${trimmed}${clause}`;
603
+ }
604
+ const idx = returningMatch.index;
605
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
606
+ }
607
+ function normalizeStatement(stmt) {
608
+ if (typeof stmt === "string") {
609
+ return { kind: "positional", sql: stmt, args: [] };
610
+ }
611
+ const sql = stmt.sql;
612
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
613
+ return { kind: "positional", sql, args: stmt.args ?? [] };
614
+ }
615
+ return { kind: "named", sql, args: stmt.args };
616
+ }
617
+ function rewriteBooleanLiterals(sql) {
618
+ let out = sql;
619
+ for (const column of BOOLEAN_COLUMN_NAMES) {
620
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
621
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
622
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
623
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
624
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
625
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
626
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
627
+ }
628
+ return out;
629
+ }
630
+ function rewriteInsertOrIgnore(sql) {
631
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
632
+ return sql;
633
+ }
634
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
635
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
636
+ }
637
+ function rewriteInsertOrReplace(sql) {
638
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
639
+ if (!match) {
640
+ return sql;
641
+ }
642
+ const rawTable = match[1];
643
+ const rawColumns = match[2];
644
+ const remainder = match[3];
645
+ const tableName = unqualifiedTableName(rawTable);
646
+ const conflictKeys = UPSERT_KEYS[tableName];
647
+ if (!conflictKeys?.length) {
648
+ return sql;
649
+ }
650
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
651
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
652
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
653
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
654
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
655
+ }
656
+ function rewriteSql(sql) {
657
+ let out = sql;
658
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
659
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
660
+ out = rewriteBooleanLiterals(out);
661
+ out = rewriteInsertOrReplace(out);
662
+ out = rewriteInsertOrIgnore(out);
663
+ return stripTrailingSemicolon(out);
664
+ }
665
+ function toBoolean(value) {
666
+ if (value === null || value === void 0) return value;
667
+ if (typeof value === "boolean") return value;
668
+ if (typeof value === "number") return value !== 0;
669
+ if (typeof value === "bigint") return value !== 0n;
670
+ if (typeof value === "string") {
671
+ const normalized = value.trim().toLowerCase();
672
+ if (normalized === "0" || normalized === "false") return false;
673
+ if (normalized === "1" || normalized === "true") return true;
674
+ }
675
+ return Boolean(value);
676
+ }
677
+ function countQuestionMarks(sql, end) {
678
+ let count = 0;
679
+ let inSingle = false;
680
+ let inDouble = false;
681
+ let inLineComment = false;
682
+ let inBlockComment = false;
683
+ for (let i = 0; i < end; i++) {
684
+ const ch = sql[i];
685
+ const next = sql[i + 1];
686
+ if (inLineComment) {
687
+ if (ch === "\n") inLineComment = false;
688
+ continue;
689
+ }
690
+ if (inBlockComment) {
691
+ if (ch === "*" && next === "/") {
692
+ inBlockComment = false;
693
+ i += 1;
694
+ }
695
+ continue;
696
+ }
697
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
698
+ inLineComment = true;
699
+ i += 1;
700
+ continue;
701
+ }
702
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
703
+ inBlockComment = true;
704
+ i += 1;
705
+ continue;
706
+ }
707
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
708
+ inSingle = !inSingle;
709
+ continue;
710
+ }
711
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
712
+ inDouble = !inDouble;
713
+ continue;
714
+ }
715
+ if (!inSingle && !inDouble && ch === "?") {
716
+ count += 1;
717
+ }
718
+ }
719
+ return count;
720
+ }
721
+ function findBooleanPlaceholderIndexes(sql) {
722
+ const indexes = /* @__PURE__ */ new Set();
723
+ for (const column of BOOLEAN_COLUMN_NAMES) {
724
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
725
+ for (const match of sql.matchAll(pattern)) {
726
+ const matchText = match[0];
727
+ const qIndex = match.index + matchText.lastIndexOf("?");
728
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
729
+ }
730
+ }
731
+ return indexes;
732
+ }
733
+ function coerceInsertBooleanArgs(sql, args) {
734
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
735
+ if (!match) return;
736
+ const rawTable = match[1];
737
+ const rawColumns = match[2];
738
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
739
+ if (!boolColumns?.size) return;
740
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
741
+ for (const [index, column] of columns.entries()) {
742
+ if (boolColumns.has(column) && index < args.length) {
743
+ args[index] = toBoolean(args[index]);
744
+ }
745
+ }
746
+ }
747
+ function coerceUpdateBooleanArgs(sql, args) {
748
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
749
+ if (!match) return;
750
+ const rawTable = match[1];
751
+ const setClause = match[2];
752
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
753
+ if (!boolColumns?.size) return;
754
+ const assignments = setClause.split(",");
755
+ let placeholderIndex = 0;
756
+ for (const assignment of assignments) {
757
+ if (!assignment.includes("?")) continue;
758
+ placeholderIndex += 1;
759
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
760
+ if (colMatch && boolColumns.has(colMatch[1])) {
761
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
762
+ }
763
+ }
764
+ }
765
+ function coerceBooleanArgs(sql, args) {
766
+ const nextArgs = [...args];
767
+ coerceInsertBooleanArgs(sql, nextArgs);
768
+ coerceUpdateBooleanArgs(sql, nextArgs);
769
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
770
+ for (const index of placeholderIndexes) {
771
+ if (index > 0 && index <= nextArgs.length) {
772
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
773
+ }
774
+ }
775
+ return nextArgs;
776
+ }
777
+ function convertQuestionMarksToDollarParams(sql) {
778
+ let out = "";
779
+ let placeholder = 0;
780
+ let inSingle = false;
781
+ let inDouble = false;
782
+ let inLineComment = false;
783
+ let inBlockComment = false;
784
+ for (let i = 0; i < sql.length; i++) {
785
+ const ch = sql[i];
786
+ const next = sql[i + 1];
787
+ if (inLineComment) {
788
+ out += ch;
789
+ if (ch === "\n") inLineComment = false;
790
+ continue;
791
+ }
792
+ if (inBlockComment) {
793
+ out += ch;
794
+ if (ch === "*" && next === "/") {
795
+ out += next;
796
+ inBlockComment = false;
797
+ i += 1;
798
+ }
799
+ continue;
800
+ }
801
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
802
+ out += ch + next;
803
+ inLineComment = true;
804
+ i += 1;
805
+ continue;
806
+ }
807
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
808
+ out += ch + next;
809
+ inBlockComment = true;
810
+ i += 1;
811
+ continue;
812
+ }
813
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
814
+ inSingle = !inSingle;
815
+ out += ch;
816
+ continue;
817
+ }
818
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
819
+ inDouble = !inDouble;
820
+ out += ch;
821
+ continue;
822
+ }
823
+ if (!inSingle && !inDouble && ch === "?") {
824
+ placeholder += 1;
825
+ out += `$${placeholder}`;
826
+ continue;
827
+ }
828
+ out += ch;
829
+ }
830
+ return out;
831
+ }
832
+ function translateStatementForPostgres(stmt) {
833
+ const normalized = normalizeStatement(stmt);
834
+ if (normalized.kind === "named") {
835
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
836
+ }
837
+ const rewrittenSql = rewriteSql(normalized.sql);
838
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
839
+ return {
840
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
841
+ args: coercedArgs
842
+ };
843
+ }
844
+ function shouldBypassPostgres(stmt) {
845
+ const normalized = normalizeStatement(stmt);
846
+ if (normalized.kind === "named") {
847
+ return true;
848
+ }
849
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
850
+ }
851
+ function shouldFallbackOnError(error) {
852
+ const message = error instanceof Error ? error.message : String(error);
853
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
854
+ }
855
+ function isReadQuery(sql) {
856
+ const trimmed = sql.trimStart();
857
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
858
+ }
859
+ function buildRow(row, columns) {
860
+ const values = columns.map((column) => row[column]);
861
+ return Object.assign(values, row);
862
+ }
863
+ function buildResultSet(rows, rowsAffected = 0) {
864
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
865
+ const resultRows = rows.map((row) => buildRow(row, columns));
866
+ return {
867
+ columns,
868
+ columnTypes: columns.map(() => ""),
869
+ rows: resultRows,
870
+ rowsAffected,
871
+ lastInsertRowid: void 0,
872
+ toJSON() {
873
+ return {
874
+ columns,
875
+ columnTypes: columns.map(() => ""),
876
+ rows,
877
+ rowsAffected,
878
+ lastInsertRowid: void 0
879
+ };
880
+ }
881
+ };
882
+ }
883
+ async function loadPrismaClient() {
884
+ if (!prismaClientPromise) {
885
+ prismaClientPromise = (async () => {
886
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
887
+ if (explicitPath) {
888
+ const module2 = await import(pathToFileURL(explicitPath).href);
889
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
890
+ if (!PrismaClient2) {
891
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
892
+ }
893
+ return new PrismaClient2();
894
+ }
895
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os4.homedir(), "exe-db");
896
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
897
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
898
+ const module = await import(pathToFileURL(prismaEntry).href);
899
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
900
+ if (!PrismaClient) {
901
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
902
+ }
903
+ return new PrismaClient();
904
+ })();
905
+ }
906
+ return prismaClientPromise;
907
+ }
908
+ async function ensureCompatibilityViews(prisma) {
909
+ if (!compatibilityBootstrapPromise) {
910
+ compatibilityBootstrapPromise = (async () => {
911
+ for (const mapping of VIEW_MAPPINGS) {
912
+ const relation = mapping.source.replace(/"/g, "");
913
+ const rows = await prisma.$queryRawUnsafe(
914
+ "SELECT to_regclass($1) AS regclass",
915
+ relation
916
+ );
917
+ if (!rows[0]?.regclass) {
918
+ continue;
919
+ }
920
+ await prisma.$executeRawUnsafe(
921
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
922
+ );
923
+ }
924
+ })();
925
+ }
926
+ return compatibilityBootstrapPromise;
927
+ }
928
+ async function executeOnPrisma(executor, stmt) {
929
+ const translated = translateStatementForPostgres(stmt);
930
+ if (isReadQuery(translated.sql)) {
931
+ const rows = await executor.$queryRawUnsafe(
932
+ translated.sql,
933
+ ...translated.args
934
+ );
935
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
936
+ }
937
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
938
+ return buildResultSet([], rowsAffected);
939
+ }
940
+ function splitSqlStatements(sql) {
941
+ const parts = [];
942
+ let current = "";
943
+ let inSingle = false;
944
+ let inDouble = false;
945
+ let inLineComment = false;
946
+ let inBlockComment = false;
947
+ for (let i = 0; i < sql.length; i++) {
948
+ const ch = sql[i];
949
+ const next = sql[i + 1];
950
+ if (inLineComment) {
951
+ current += ch;
952
+ if (ch === "\n") inLineComment = false;
953
+ continue;
954
+ }
955
+ if (inBlockComment) {
956
+ current += ch;
957
+ if (ch === "*" && next === "/") {
958
+ current += next;
959
+ inBlockComment = false;
960
+ i += 1;
961
+ }
962
+ continue;
963
+ }
964
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
965
+ current += ch + next;
966
+ inLineComment = true;
967
+ i += 1;
968
+ continue;
969
+ }
970
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
971
+ current += ch + next;
972
+ inBlockComment = true;
973
+ i += 1;
974
+ continue;
975
+ }
976
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
977
+ inSingle = !inSingle;
978
+ current += ch;
979
+ continue;
980
+ }
981
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
982
+ inDouble = !inDouble;
983
+ current += ch;
984
+ continue;
985
+ }
986
+ if (!inSingle && !inDouble && ch === ";") {
987
+ if (current.trim()) {
988
+ parts.push(current.trim());
989
+ }
990
+ current = "";
991
+ continue;
992
+ }
993
+ current += ch;
994
+ }
995
+ if (current.trim()) {
996
+ parts.push(current.trim());
997
+ }
998
+ return parts;
999
+ }
1000
+ async function createPrismaDbAdapter(fallbackClient) {
1001
+ const prisma = await loadPrismaClient();
1002
+ await ensureCompatibilityViews(prisma);
1003
+ let closed = false;
1004
+ let adapter;
1005
+ const fallbackExecute = async (stmt, error) => {
1006
+ if (!fallbackClient) {
1007
+ if (error) throw error;
1008
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1009
+ }
1010
+ if (error) {
1011
+ process.stderr.write(
1012
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1013
+ `
1014
+ );
1015
+ }
1016
+ return fallbackClient.execute(stmt);
1017
+ };
1018
+ adapter = {
1019
+ async execute(stmt) {
1020
+ if (shouldBypassPostgres(stmt)) {
1021
+ return fallbackExecute(stmt);
1022
+ }
1023
+ try {
1024
+ return await executeOnPrisma(prisma, stmt);
1025
+ } catch (error) {
1026
+ if (shouldFallbackOnError(error)) {
1027
+ return fallbackExecute(stmt, error);
1028
+ }
1029
+ throw error;
1030
+ }
1031
+ },
1032
+ async batch(stmts, mode) {
1033
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1034
+ if (!fallbackClient) {
1035
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1036
+ }
1037
+ return fallbackClient.batch(stmts, mode);
1038
+ }
1039
+ try {
1040
+ if (prisma.$transaction) {
1041
+ return await prisma.$transaction(async (tx) => {
1042
+ const results2 = [];
1043
+ for (const stmt of stmts) {
1044
+ results2.push(await executeOnPrisma(tx, stmt));
1045
+ }
1046
+ return results2;
1047
+ });
1048
+ }
1049
+ const results = [];
1050
+ for (const stmt of stmts) {
1051
+ results.push(await executeOnPrisma(prisma, stmt));
1052
+ }
1053
+ return results;
1054
+ } catch (error) {
1055
+ if (fallbackClient && shouldFallbackOnError(error)) {
1056
+ process.stderr.write(
1057
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1058
+ `
1059
+ );
1060
+ return fallbackClient.batch(stmts, mode);
1061
+ }
1062
+ throw error;
1063
+ }
1064
+ },
1065
+ async migrate(stmts) {
1066
+ if (fallbackClient) {
1067
+ return fallbackClient.migrate(stmts);
1068
+ }
1069
+ return adapter.batch(stmts, "deferred");
1070
+ },
1071
+ async transaction(mode) {
1072
+ if (!fallbackClient) {
1073
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1074
+ }
1075
+ return fallbackClient.transaction(mode);
1076
+ },
1077
+ async executeMultiple(sql) {
1078
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1079
+ return fallbackClient.executeMultiple(sql);
1080
+ }
1081
+ for (const statement of splitSqlStatements(sql)) {
1082
+ await adapter.execute(statement);
1083
+ }
1084
+ },
1085
+ async sync() {
1086
+ if (fallbackClient) {
1087
+ return fallbackClient.sync();
1088
+ }
1089
+ return { frame_no: 0, frames_synced: 0 };
1090
+ },
1091
+ close() {
1092
+ closed = true;
1093
+ prismaClientPromise = null;
1094
+ compatibilityBootstrapPromise = null;
1095
+ void prisma.$disconnect?.();
1096
+ },
1097
+ get closed() {
1098
+ return closed;
1099
+ },
1100
+ get protocol() {
1101
+ return "prisma-postgres";
1102
+ }
1103
+ };
1104
+ return adapter;
1105
+ }
1106
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1107
+ var init_database_adapter = __esm({
1108
+ "src/lib/database-adapter.ts"() {
1109
+ "use strict";
1110
+ VIEW_MAPPINGS = [
1111
+ { view: "memories", source: "memory.memory_records" },
1112
+ { view: "tasks", source: "memory.tasks" },
1113
+ { view: "behaviors", source: "memory.behaviors" },
1114
+ { view: "entities", source: "memory.entities" },
1115
+ { view: "relationships", source: "memory.relationships" },
1116
+ { view: "entity_memories", source: "memory.entity_memories" },
1117
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1118
+ { view: "notifications", source: "memory.notifications" },
1119
+ { view: "messages", source: "memory.messages" },
1120
+ { view: "users", source: "wiki.users" },
1121
+ { view: "workspaces", source: "wiki.workspaces" },
1122
+ { view: "workspace_users", source: "wiki.workspace_users" },
1123
+ { view: "documents", source: "wiki.workspace_documents" },
1124
+ { view: "chats", source: "wiki.workspace_chats" }
1125
+ ];
1126
+ UPSERT_KEYS = {
1127
+ memories: ["id"],
1128
+ tasks: ["id"],
1129
+ behaviors: ["id"],
1130
+ entities: ["id"],
1131
+ relationships: ["id"],
1132
+ entity_aliases: ["alias"],
1133
+ notifications: ["id"],
1134
+ messages: ["id"],
1135
+ users: ["id"],
1136
+ workspaces: ["id"],
1137
+ workspace_users: ["id"],
1138
+ documents: ["id"],
1139
+ chats: ["id"]
1140
+ };
1141
+ BOOLEAN_COLUMNS_BY_TABLE = {
1142
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1143
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1144
+ notifications: /* @__PURE__ */ new Set(["read"]),
1145
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1146
+ };
1147
+ BOOLEAN_COLUMN_NAMES = new Set(
1148
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1149
+ );
1150
+ IMMEDIATE_FALLBACK_PATTERNS = [
1151
+ /\bPRAGMA\b/i,
1152
+ /\bsqlite_master\b/i,
1153
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1154
+ /\bMATCH\b/i,
1155
+ /\bvector_distance_cos\s*\(/i,
1156
+ /\bjson_extract\s*\(/i,
1157
+ /\bjulianday\s*\(/i,
1158
+ /\bstrftime\s*\(/i,
1159
+ /\blast_insert_rowid\s*\(/i
1160
+ ];
1161
+ prismaClientPromise = null;
1162
+ compatibilityBootstrapPromise = null;
578
1163
  }
579
1164
  });
580
1165
 
581
1166
  // src/lib/exe-daemon-client.ts
582
1167
  import net from "net";
583
- import os4 from "os";
1168
+ import os5 from "os";
584
1169
  import { spawn } from "child_process";
585
1170
  import { randomUUID } from "crypto";
586
1171
  import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
587
- import path4 from "path";
1172
+ import path5 from "path";
588
1173
  import { fileURLToPath as fileURLToPath2 } from "url";
589
1174
  function handleData(chunk) {
590
1175
  _buffer += chunk.toString();
@@ -635,17 +1220,17 @@ function cleanupStaleFiles() {
635
1220
  }
636
1221
  }
637
1222
  function findPackageRoot() {
638
- let dir = path4.dirname(fileURLToPath2(import.meta.url));
639
- const { root } = path4.parse(dir);
1223
+ let dir = path5.dirname(fileURLToPath2(import.meta.url));
1224
+ const { root } = path5.parse(dir);
640
1225
  while (dir !== root) {
641
- if (existsSync4(path4.join(dir, "package.json"))) return dir;
642
- dir = path4.dirname(dir);
1226
+ if (existsSync4(path5.join(dir, "package.json"))) return dir;
1227
+ dir = path5.dirname(dir);
643
1228
  }
644
1229
  return null;
645
1230
  }
646
1231
  function spawnDaemon() {
647
- const freeGB = os4.freemem() / (1024 * 1024 * 1024);
648
- const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1232
+ const freeGB = os5.freemem() / (1024 * 1024 * 1024);
1233
+ const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
649
1234
  if (totalGB <= 8) {
650
1235
  process.stderr.write(
651
1236
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -665,7 +1250,7 @@ function spawnDaemon() {
665
1250
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
666
1251
  return;
667
1252
  }
668
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1253
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
669
1254
  if (!existsSync4(daemonPath)) {
670
1255
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
671
1256
  `);
@@ -674,7 +1259,7 @@ function spawnDaemon() {
674
1259
  const resolvedPath = daemonPath;
675
1260
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
676
1261
  `);
677
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
1262
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
678
1263
  let stderrFd = "ignore";
679
1264
  try {
680
1265
  stderrFd = openSync(logPath, "a");
@@ -821,9 +1406,9 @@ var init_exe_daemon_client = __esm({
821
1406
  "src/lib/exe-daemon-client.ts"() {
822
1407
  "use strict";
823
1408
  init_config();
824
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
825
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
826
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
1409
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1410
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1411
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
827
1412
  SPAWN_LOCK_STALE_MS = 3e4;
828
1413
  CONNECT_TIMEOUT_MS = 15e3;
829
1414
  REQUEST_TIMEOUT_MS = 3e4;
@@ -905,7 +1490,7 @@ __export(db_daemon_client_exports, {
905
1490
  createDaemonDbClient: () => createDaemonDbClient,
906
1491
  initDaemonDbClient: () => initDaemonDbClient
907
1492
  });
908
- function normalizeStatement(stmt) {
1493
+ function normalizeStatement2(stmt) {
909
1494
  if (typeof stmt === "string") {
910
1495
  return { sql: stmt, args: [] };
911
1496
  }
@@ -929,7 +1514,7 @@ function createDaemonDbClient(fallbackClient) {
929
1514
  if (!_useDaemon || !isClientConnected()) {
930
1515
  return fallbackClient.execute(stmt);
931
1516
  }
932
- const { sql, args } = normalizeStatement(stmt);
1517
+ const { sql, args } = normalizeStatement2(stmt);
933
1518
  const response = await sendDaemonRequest({
934
1519
  type: "db-execute",
935
1520
  sql,
@@ -954,7 +1539,7 @@ function createDaemonDbClient(fallbackClient) {
954
1539
  if (!_useDaemon || !isClientConnected()) {
955
1540
  return fallbackClient.batch(stmts, mode);
956
1541
  }
957
- const statements = stmts.map(normalizeStatement);
1542
+ const statements = stmts.map(normalizeStatement2);
958
1543
  const response = await sendDaemonRequest({
959
1544
  type: "db-batch",
960
1545
  statements,
@@ -1049,6 +1634,18 @@ __export(database_exports, {
1049
1634
  });
1050
1635
  import { createClient } from "@libsql/client";
1051
1636
  async function initDatabase(config) {
1637
+ if (_walCheckpointTimer) {
1638
+ clearInterval(_walCheckpointTimer);
1639
+ _walCheckpointTimer = null;
1640
+ }
1641
+ if (_daemonClient) {
1642
+ _daemonClient.close();
1643
+ _daemonClient = null;
1644
+ }
1645
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1646
+ _adapterClient.close();
1647
+ }
1648
+ _adapterClient = null;
1052
1649
  if (_client) {
1053
1650
  _client.close();
1054
1651
  _client = null;
@@ -1062,6 +1659,7 @@ async function initDatabase(config) {
1062
1659
  }
1063
1660
  _client = createClient(opts);
1064
1661
  _resilientClient = wrapWithRetry(_client);
1662
+ _adapterClient = _resilientClient;
1065
1663
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1066
1664
  });
1067
1665
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1072,14 +1670,20 @@ async function initDatabase(config) {
1072
1670
  });
1073
1671
  }, 3e4);
1074
1672
  _walCheckpointTimer.unref();
1673
+ if (process.env.DATABASE_URL) {
1674
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1675
+ }
1075
1676
  }
1076
1677
  function isInitialized() {
1077
- return _client !== null;
1678
+ return _adapterClient !== null || _client !== null;
1078
1679
  }
1079
1680
  function getClient() {
1080
- if (!_resilientClient) {
1681
+ if (!_adapterClient) {
1081
1682
  throw new Error("Database client not initialized. Call initDatabase() first.");
1082
1683
  }
1684
+ if (process.env.DATABASE_URL) {
1685
+ return _adapterClient;
1686
+ }
1083
1687
  if (process.env.EXE_IS_DAEMON === "1") {
1084
1688
  return _resilientClient;
1085
1689
  }
@@ -1089,6 +1693,7 @@ function getClient() {
1089
1693
  return _resilientClient;
1090
1694
  }
1091
1695
  async function initDaemonClient() {
1696
+ if (process.env.DATABASE_URL) return;
1092
1697
  if (process.env.EXE_IS_DAEMON === "1") return;
1093
1698
  if (!_resilientClient) return;
1094
1699
  try {
@@ -2033,26 +2638,36 @@ async function ensureSchema() {
2033
2638
  }
2034
2639
  }
2035
2640
  async function disposeDatabase() {
2641
+ if (_walCheckpointTimer) {
2642
+ clearInterval(_walCheckpointTimer);
2643
+ _walCheckpointTimer = null;
2644
+ }
2036
2645
  if (_daemonClient) {
2037
2646
  _daemonClient.close();
2038
2647
  _daemonClient = null;
2039
2648
  }
2649
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2650
+ _adapterClient.close();
2651
+ }
2652
+ _adapterClient = null;
2040
2653
  if (_client) {
2041
2654
  _client.close();
2042
2655
  _client = null;
2043
2656
  _resilientClient = null;
2044
2657
  }
2045
2658
  }
2046
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2659
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2047
2660
  var init_database = __esm({
2048
2661
  "src/lib/database.ts"() {
2049
2662
  "use strict";
2050
2663
  init_db_retry();
2051
2664
  init_employees();
2665
+ init_database_adapter();
2052
2666
  _client = null;
2053
2667
  _resilientClient = null;
2054
2668
  _walCheckpointTimer = null;
2055
2669
  _daemonClient = null;
2670
+ _adapterClient = null;
2056
2671
  initTurso = initDatabase;
2057
2672
  disposeTurso = disposeDatabase;
2058
2673
  }
@@ -2081,10 +2696,10 @@ var init_compress = __esm({
2081
2696
  // src/lib/license.ts
2082
2697
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync } from "fs";
2083
2698
  import { randomUUID as randomUUID2 } from "crypto";
2084
- import path5 from "path";
2699
+ import path6 from "path";
2085
2700
  import { jwtVerify, importSPKI } from "jose";
2086
2701
  function loadDeviceId() {
2087
- const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
2702
+ const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
2088
2703
  try {
2089
2704
  if (existsSync5(deviceJsonPath)) {
2090
2705
  const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
@@ -2109,9 +2724,9 @@ var init_license = __esm({
2109
2724
  "src/lib/license.ts"() {
2110
2725
  "use strict";
2111
2726
  init_config();
2112
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
2113
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
2114
- DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
2727
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
2728
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
2729
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2115
2730
  }
2116
2731
  });
2117
2732
 
@@ -2135,7 +2750,7 @@ __export(crdt_sync_exports, {
2135
2750
  });
2136
2751
  import * as Y from "yjs";
2137
2752
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
2138
- import path6 from "path";
2753
+ import path7 from "path";
2139
2754
  import { homedir } from "os";
2140
2755
  function getStatePath() {
2141
2756
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -2291,7 +2906,7 @@ function persistState() {
2291
2906
  if (!doc) return;
2292
2907
  try {
2293
2908
  const sp = getStatePath();
2294
- const dir = path6.dirname(sp);
2909
+ const dir = path7.dirname(sp);
2295
2910
  if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
2296
2911
  const state = Y.encodeStateAsUpdate(doc);
2297
2912
  writeFileSync3(sp, Buffer.from(state));
@@ -2335,7 +2950,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
2335
2950
  var init_crdt_sync = __esm({
2336
2951
  "src/lib/crdt-sync.ts"() {
2337
2952
  "use strict";
2338
- DEFAULT_STATE_PATH = path6.join(homedir(), ".exe-os", "crdt-state.bin");
2953
+ DEFAULT_STATE_PATH = path7.join(homedir(), ".exe-os", "crdt-state.bin");
2339
2954
  _statePathOverride = null;
2340
2955
  doc = null;
2341
2956
  }
@@ -2371,14 +2986,14 @@ __export(cloud_sync_exports, {
2371
2986
  });
2372
2987
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
2373
2988
  import crypto2 from "crypto";
2374
- import path7 from "path";
2989
+ import path8 from "path";
2375
2990
  import { homedir as homedir2 } from "os";
2376
2991
  function sqlSafe(v) {
2377
2992
  return v === void 0 ? null : v;
2378
2993
  }
2379
2994
  function logError(msg) {
2380
2995
  try {
2381
- const logPath = path7.join(homedir2(), ".exe-os", "workers.log");
2996
+ const logPath = path8.join(homedir2(), ".exe-os", "workers.log");
2382
2997
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
2383
2998
  `);
2384
2999
  } catch {
@@ -2773,7 +3388,7 @@ async function cloudSync(config) {
2773
3388
  try {
2774
3389
  const employees = await loadEmployees();
2775
3390
  rosterResult.employees = employees.length;
2776
- const idDir = path7.join(EXE_AI_DIR, "identity");
3391
+ const idDir = path8.join(EXE_AI_DIR, "identity");
2777
3392
  if (existsSync7(idDir)) {
2778
3393
  rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
2779
3394
  }
@@ -2814,9 +3429,9 @@ function consumeRosterDeletions() {
2814
3429
  }
2815
3430
  }
2816
3431
  function buildRosterBlob(paths) {
2817
- const rosterPath = paths?.rosterPath ?? path7.join(EXE_AI_DIR, "exe-employees.json");
2818
- const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
2819
- const configPath = paths?.configPath ?? path7.join(EXE_AI_DIR, "config.json");
3432
+ const rosterPath = paths?.rosterPath ?? path8.join(EXE_AI_DIR, "exe-employees.json");
3433
+ const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
3434
+ const configPath = paths?.configPath ?? path8.join(EXE_AI_DIR, "config.json");
2820
3435
  let roster = [];
2821
3436
  if (existsSync7(rosterPath)) {
2822
3437
  try {
@@ -2828,7 +3443,7 @@ function buildRosterBlob(paths) {
2828
3443
  if (existsSync7(identityDir)) {
2829
3444
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
2830
3445
  try {
2831
- identities[file] = readFileSync6(path7.join(identityDir, file), "utf-8");
3446
+ identities[file] = readFileSync6(path8.join(identityDir, file), "utf-8");
2832
3447
  } catch {
2833
3448
  }
2834
3449
  }
@@ -2841,7 +3456,7 @@ function buildRosterBlob(paths) {
2841
3456
  }
2842
3457
  }
2843
3458
  let agentConfig;
2844
- const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
3459
+ const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
2845
3460
  if (existsSync7(agentConfigPath)) {
2846
3461
  try {
2847
3462
  agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
@@ -2920,7 +3535,7 @@ async function cloudPullRoster(config) {
2920
3535
  }
2921
3536
  }
2922
3537
  function mergeConfig(remoteConfig, configPath) {
2923
- const cfgPath = configPath ?? path7.join(EXE_AI_DIR, "config.json");
3538
+ const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
2924
3539
  let local = {};
2925
3540
  if (existsSync7(cfgPath)) {
2926
3541
  try {
@@ -2929,14 +3544,14 @@ function mergeConfig(remoteConfig, configPath) {
2929
3544
  }
2930
3545
  }
2931
3546
  const merged = { ...remoteConfig, ...local };
2932
- const dir = path7.dirname(cfgPath);
3547
+ const dir = path8.dirname(cfgPath);
2933
3548
  if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2934
3549
  writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2935
3550
  }
2936
3551
  async function mergeRosterFromRemote(remote, paths) {
2937
3552
  return withRosterLock(async () => {
2938
3553
  const rosterPath = paths?.rosterPath ?? void 0;
2939
- const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
3554
+ const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
2940
3555
  const localEmployees = await loadEmployees(rosterPath);
2941
3556
  const localNames = new Set(localEmployees.map((e) => e.name));
2942
3557
  let added = 0;
@@ -2958,7 +3573,7 @@ async function mergeRosterFromRemote(remote, paths) {
2958
3573
  const remoteIdentity = remote.identities[matchedKey];
2959
3574
  if (remoteIdentity) {
2960
3575
  if (!existsSync7(identityDir)) mkdirSync3(identityDir, { recursive: true });
2961
- const idPath = path7.join(identityDir, `${remoteEmp.name}.md`);
3576
+ const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
2962
3577
  let localIdentity = null;
2963
3578
  try {
2964
3579
  localIdentity = existsSync7(idPath) ? readFileSync6(idPath, "utf-8") : null;
@@ -2991,7 +3606,7 @@ async function mergeRosterFromRemote(remote, paths) {
2991
3606
  }
2992
3607
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
2993
3608
  try {
2994
- const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
3609
+ const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
2995
3610
  let local = {};
2996
3611
  if (existsSync7(agentConfigPath)) {
2997
3612
  try {
@@ -3438,9 +4053,9 @@ var init_cloud_sync = __esm({
3438
4053
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
3439
4054
  FETCH_TIMEOUT_MS = 3e4;
3440
4055
  PUSH_BATCH_SIZE = 5e3;
3441
- ROSTER_LOCK_PATH = path7.join(EXE_AI_DIR, "roster-merge.lock");
4056
+ ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
3442
4057
  LOCK_STALE_MS = 3e4;
3443
- ROSTER_DELETIONS_PATH = path7.join(EXE_AI_DIR, "roster-deletions.json");
4058
+ ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
3444
4059
  }
3445
4060
  });
3446
4061