@askexenow/exe-os 0.8.83 → 0.8.86

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 (103) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +154 -21
  5. package/dist/bin/cli.js +14678 -12676
  6. package/dist/bin/exe-agent-config.js +242 -0
  7. package/dist/bin/exe-agent.js +100 -91
  8. package/dist/bin/exe-assign.js +1003 -854
  9. package/dist/bin/exe-boot.js +1420 -485
  10. package/dist/bin/exe-call.js +10 -0
  11. package/dist/bin/exe-cloud.js +29 -6
  12. package/dist/bin/exe-dispatch.js +572 -271
  13. package/dist/bin/exe-doctor.js +403 -6
  14. package/dist/bin/exe-export-behaviors.js +175 -72
  15. package/dist/bin/exe-forget.js +102 -3
  16. package/dist/bin/exe-gateway.js +796 -292
  17. package/dist/bin/exe-healthcheck.js +134 -1
  18. package/dist/bin/exe-heartbeat.js +172 -36
  19. package/dist/bin/exe-kill.js +175 -72
  20. package/dist/bin/exe-launch-agent.js +189 -76
  21. package/dist/bin/exe-link.js +927 -82
  22. package/dist/bin/exe-new-employee.js +60 -8
  23. package/dist/bin/exe-pending-messages.js +151 -19
  24. package/dist/bin/exe-pending-notifications.js +97 -2
  25. package/dist/bin/exe-pending-reviews.js +155 -22
  26. package/dist/bin/exe-rename.js +564 -23
  27. package/dist/bin/exe-review.js +231 -73
  28. package/dist/bin/exe-search.js +995 -228
  29. package/dist/bin/exe-session-cleanup.js +4930 -1664
  30. package/dist/bin/exe-settings.js +20 -5
  31. package/dist/bin/exe-start-codex.js +2598 -0
  32. package/dist/bin/exe-start.sh +15 -3
  33. package/dist/bin/exe-status.js +154 -21
  34. package/dist/bin/exe-team.js +97 -2
  35. package/dist/bin/git-sweep.js +1180 -363
  36. package/dist/bin/graph-backfill.js +175 -72
  37. package/dist/bin/graph-export.js +175 -72
  38. package/dist/bin/install.js +60 -7
  39. package/dist/bin/list-providers.js +1 -0
  40. package/dist/bin/scan-tasks.js +1185 -367
  41. package/dist/bin/setup.js +914 -270
  42. package/dist/bin/shard-migrate.js +175 -72
  43. package/dist/bin/update.js +1 -0
  44. package/dist/bin/wiki-sync.js +175 -72
  45. package/dist/gateway/index.js +792 -285
  46. package/dist/hooks/bug-report-worker.js +445 -135
  47. package/dist/hooks/commit-complete.js +1178 -361
  48. package/dist/hooks/error-recall.js +994 -228
  49. package/dist/hooks/ingest-worker.js +1799 -1234
  50. package/dist/hooks/ingest.js +3 -0
  51. package/dist/hooks/instructions-loaded.js +707 -97
  52. package/dist/hooks/notification.js +699 -89
  53. package/dist/hooks/post-compact.js +757 -109
  54. package/dist/hooks/pre-compact.js +1061 -244
  55. package/dist/hooks/pre-tool-use.js +787 -130
  56. package/dist/hooks/prompt-ingest-worker.js +242 -101
  57. package/dist/hooks/prompt-submit.js +1121 -299
  58. package/dist/hooks/response-ingest-worker.js +242 -101
  59. package/dist/hooks/session-end.js +4063 -397
  60. package/dist/hooks/session-start.js +1071 -254
  61. package/dist/hooks/stop.js +768 -120
  62. package/dist/hooks/subagent-stop.js +757 -109
  63. package/dist/hooks/summary-worker.js +1706 -1011
  64. package/dist/index.js +1821 -1098
  65. package/dist/lib/agent-config.js +167 -0
  66. package/dist/lib/cloud-sync.js +932 -88
  67. package/dist/lib/consolidation.js +2 -1
  68. package/dist/lib/database.js +642 -87
  69. package/dist/lib/db-daemon-client.js +503 -0
  70. package/dist/lib/device-registry.js +547 -7
  71. package/dist/lib/embedder.js +14 -28
  72. package/dist/lib/employee-templates.js +84 -74
  73. package/dist/lib/employees.js +9 -0
  74. package/dist/lib/exe-daemon-client.js +16 -29
  75. package/dist/lib/exe-daemon.js +2733 -1575
  76. package/dist/lib/hybrid-search.js +995 -228
  77. package/dist/lib/identity.js +87 -67
  78. package/dist/lib/keychain.js +9 -1
  79. package/dist/lib/messaging.js +103 -40
  80. package/dist/lib/reminders.js +91 -74
  81. package/dist/lib/runtime-table.js +16 -0
  82. package/dist/lib/schedules.js +96 -2
  83. package/dist/lib/session-wrappers.js +22 -0
  84. package/dist/lib/skill-learning.js +103 -85
  85. package/dist/lib/store.js +234 -73
  86. package/dist/lib/tasks.js +348 -134
  87. package/dist/lib/tmux-routing.js +422 -208
  88. package/dist/lib/token-spend.js +273 -0
  89. package/dist/lib/ws-client.js +11 -0
  90. package/dist/mcp/server.js +5742 -696
  91. package/dist/mcp/tools/complete-reminder.js +94 -77
  92. package/dist/mcp/tools/create-reminder.js +94 -77
  93. package/dist/mcp/tools/create-task.js +375 -152
  94. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  95. package/dist/mcp/tools/list-reminders.js +94 -77
  96. package/dist/mcp/tools/list-tasks.js +99 -31
  97. package/dist/mcp/tools/send-message.js +108 -45
  98. package/dist/mcp/tools/update-task.js +162 -77
  99. package/dist/runtime/index.js +1075 -258
  100. package/dist/tui/App.js +1333 -506
  101. package/package.json +6 -1
  102. package/src/commands/exe/agent-config.md +27 -0
  103. package/src/commands/exe/cc-doctor.md +10 -0
@@ -48,12 +48,20 @@ async function getMasterKey() {
48
48
  }
49
49
  const keyPath = getKeyPath();
50
50
  if (!existsSync(keyPath)) {
51
+ process.stderr.write(
52
+ `[keychain] Key not found at ${keyPath} (HOME=${os.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
53
+ `
54
+ );
51
55
  return null;
52
56
  }
53
57
  try {
54
58
  const content = await readFile(keyPath, "utf-8");
55
59
  return Buffer.from(content.trim(), "base64");
56
- } catch {
60
+ } catch (err) {
61
+ process.stderr.write(
62
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
63
+ `
64
+ );
57
65
  return null;
58
66
  }
59
67
  }
@@ -570,6 +578,443 @@ var init_employees = __esm({
570
578
  }
571
579
  });
572
580
 
581
+ // src/lib/exe-daemon-client.ts
582
+ import net from "net";
583
+ import { spawn } from "child_process";
584
+ import { randomUUID } from "crypto";
585
+ import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
586
+ import path4 from "path";
587
+ import { fileURLToPath as fileURLToPath2 } from "url";
588
+ function handleData(chunk) {
589
+ _buffer += chunk.toString();
590
+ if (_buffer.length > MAX_BUFFER) {
591
+ _buffer = "";
592
+ return;
593
+ }
594
+ let newlineIdx;
595
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
596
+ const line = _buffer.slice(0, newlineIdx).trim();
597
+ _buffer = _buffer.slice(newlineIdx + 1);
598
+ if (!line) continue;
599
+ try {
600
+ const response = JSON.parse(line);
601
+ const id = response.id;
602
+ if (!id) continue;
603
+ const entry = _pending.get(id);
604
+ if (entry) {
605
+ clearTimeout(entry.timer);
606
+ _pending.delete(id);
607
+ entry.resolve(response);
608
+ }
609
+ } catch {
610
+ }
611
+ }
612
+ }
613
+ function cleanupStaleFiles() {
614
+ if (existsSync4(PID_PATH)) {
615
+ try {
616
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
617
+ if (pid > 0) {
618
+ try {
619
+ process.kill(pid, 0);
620
+ return;
621
+ } catch {
622
+ }
623
+ }
624
+ } catch {
625
+ }
626
+ try {
627
+ unlinkSync2(PID_PATH);
628
+ } catch {
629
+ }
630
+ try {
631
+ unlinkSync2(SOCKET_PATH);
632
+ } catch {
633
+ }
634
+ }
635
+ }
636
+ function findPackageRoot() {
637
+ let dir = path4.dirname(fileURLToPath2(import.meta.url));
638
+ const { root } = path4.parse(dir);
639
+ while (dir !== root) {
640
+ if (existsSync4(path4.join(dir, "package.json"))) return dir;
641
+ dir = path4.dirname(dir);
642
+ }
643
+ return null;
644
+ }
645
+ function spawnDaemon() {
646
+ const pkgRoot = findPackageRoot();
647
+ if (!pkgRoot) {
648
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
649
+ return;
650
+ }
651
+ const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
652
+ if (!existsSync4(daemonPath)) {
653
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
654
+ `);
655
+ return;
656
+ }
657
+ const resolvedPath = daemonPath;
658
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
659
+ `);
660
+ const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
661
+ let stderrFd = "ignore";
662
+ try {
663
+ stderrFd = openSync(logPath, "a");
664
+ } catch {
665
+ }
666
+ const child = spawn(process.execPath, [resolvedPath], {
667
+ detached: true,
668
+ stdio: ["ignore", "ignore", stderrFd],
669
+ env: {
670
+ ...process.env,
671
+ TMUX: void 0,
672
+ // Daemon is global — must not inherit session scope
673
+ TMUX_PANE: void 0,
674
+ // Prevents resolveExeSession() from scoping to one session
675
+ EXE_DAEMON_SOCK: SOCKET_PATH,
676
+ EXE_DAEMON_PID: PID_PATH
677
+ }
678
+ });
679
+ child.unref();
680
+ if (typeof stderrFd === "number") {
681
+ try {
682
+ closeSync(stderrFd);
683
+ } catch {
684
+ }
685
+ }
686
+ }
687
+ function acquireSpawnLock() {
688
+ try {
689
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
690
+ closeSync(fd);
691
+ return true;
692
+ } catch {
693
+ try {
694
+ const stat = statSync(SPAWN_LOCK_PATH);
695
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
696
+ try {
697
+ unlinkSync2(SPAWN_LOCK_PATH);
698
+ } catch {
699
+ }
700
+ try {
701
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
702
+ closeSync(fd);
703
+ return true;
704
+ } catch {
705
+ }
706
+ }
707
+ } catch {
708
+ }
709
+ return false;
710
+ }
711
+ }
712
+ function releaseSpawnLock() {
713
+ try {
714
+ unlinkSync2(SPAWN_LOCK_PATH);
715
+ } catch {
716
+ }
717
+ }
718
+ function connectToSocket() {
719
+ return new Promise((resolve) => {
720
+ if (_socket && _connected) {
721
+ resolve(true);
722
+ return;
723
+ }
724
+ const socket = net.createConnection({ path: SOCKET_PATH });
725
+ const connectTimeout = setTimeout(() => {
726
+ socket.destroy();
727
+ resolve(false);
728
+ }, 2e3);
729
+ socket.on("connect", () => {
730
+ clearTimeout(connectTimeout);
731
+ _socket = socket;
732
+ _connected = true;
733
+ _buffer = "";
734
+ socket.on("data", handleData);
735
+ socket.on("close", () => {
736
+ _connected = false;
737
+ _socket = null;
738
+ for (const [id, entry] of _pending) {
739
+ clearTimeout(entry.timer);
740
+ _pending.delete(id);
741
+ entry.resolve({ error: "Connection closed" });
742
+ }
743
+ });
744
+ socket.on("error", () => {
745
+ _connected = false;
746
+ _socket = null;
747
+ });
748
+ resolve(true);
749
+ });
750
+ socket.on("error", () => {
751
+ clearTimeout(connectTimeout);
752
+ resolve(false);
753
+ });
754
+ });
755
+ }
756
+ async function connectEmbedDaemon() {
757
+ if (_socket && _connected) return true;
758
+ if (await connectToSocket()) return true;
759
+ if (acquireSpawnLock()) {
760
+ try {
761
+ cleanupStaleFiles();
762
+ spawnDaemon();
763
+ } finally {
764
+ releaseSpawnLock();
765
+ }
766
+ }
767
+ const start = Date.now();
768
+ let delay2 = 100;
769
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
770
+ await new Promise((r) => setTimeout(r, delay2));
771
+ if (await connectToSocket()) return true;
772
+ delay2 = Math.min(delay2 * 2, 3e3);
773
+ }
774
+ return false;
775
+ }
776
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
777
+ return new Promise((resolve) => {
778
+ if (!_socket || !_connected) {
779
+ resolve({ error: "Not connected" });
780
+ return;
781
+ }
782
+ const id = randomUUID();
783
+ const timer = setTimeout(() => {
784
+ _pending.delete(id);
785
+ resolve({ error: "Request timeout" });
786
+ }, timeoutMs);
787
+ _pending.set(id, { resolve, timer });
788
+ try {
789
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
790
+ } catch {
791
+ clearTimeout(timer);
792
+ _pending.delete(id);
793
+ resolve({ error: "Write failed" });
794
+ }
795
+ });
796
+ }
797
+ function isClientConnected() {
798
+ return _connected;
799
+ }
800
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
801
+ var init_exe_daemon_client = __esm({
802
+ "src/lib/exe-daemon-client.ts"() {
803
+ "use strict";
804
+ init_config();
805
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
806
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
807
+ SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
808
+ SPAWN_LOCK_STALE_MS = 3e4;
809
+ CONNECT_TIMEOUT_MS = 15e3;
810
+ REQUEST_TIMEOUT_MS = 3e4;
811
+ _socket = null;
812
+ _connected = false;
813
+ _buffer = "";
814
+ _pending = /* @__PURE__ */ new Map();
815
+ MAX_BUFFER = 1e7;
816
+ }
817
+ });
818
+
819
+ // src/lib/daemon-protocol.ts
820
+ function serializeValue(v) {
821
+ if (v === null || v === void 0) return null;
822
+ if (typeof v === "bigint") return Number(v);
823
+ if (typeof v === "boolean") return v ? 1 : 0;
824
+ if (v instanceof Uint8Array) {
825
+ return { __blob: Buffer.from(v).toString("base64") };
826
+ }
827
+ if (ArrayBuffer.isView(v)) {
828
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
829
+ }
830
+ if (v instanceof ArrayBuffer) {
831
+ return { __blob: Buffer.from(v).toString("base64") };
832
+ }
833
+ if (typeof v === "string" || typeof v === "number") return v;
834
+ return String(v);
835
+ }
836
+ function deserializeValue(v) {
837
+ if (v === null) return null;
838
+ if (typeof v === "object" && v !== null && "__blob" in v) {
839
+ const buf = Buffer.from(v.__blob, "base64");
840
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
841
+ }
842
+ return v;
843
+ }
844
+ function deserializeResultSet(srs) {
845
+ const rows = srs.rows.map((obj) => {
846
+ const values = srs.columns.map(
847
+ (col) => deserializeValue(obj[col] ?? null)
848
+ );
849
+ const row = values;
850
+ for (let i = 0; i < srs.columns.length; i++) {
851
+ const col = srs.columns[i];
852
+ if (col !== void 0) {
853
+ row[col] = values[i] ?? null;
854
+ }
855
+ }
856
+ Object.defineProperty(row, "length", {
857
+ value: values.length,
858
+ enumerable: false
859
+ });
860
+ return row;
861
+ });
862
+ return {
863
+ columns: srs.columns,
864
+ columnTypes: srs.columnTypes ?? [],
865
+ rows,
866
+ rowsAffected: srs.rowsAffected,
867
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
868
+ toJSON: () => ({
869
+ columns: srs.columns,
870
+ columnTypes: srs.columnTypes ?? [],
871
+ rows: srs.rows,
872
+ rowsAffected: srs.rowsAffected,
873
+ lastInsertRowid: srs.lastInsertRowid
874
+ })
875
+ };
876
+ }
877
+ var init_daemon_protocol = __esm({
878
+ "src/lib/daemon-protocol.ts"() {
879
+ "use strict";
880
+ }
881
+ });
882
+
883
+ // src/lib/db-daemon-client.ts
884
+ var db_daemon_client_exports = {};
885
+ __export(db_daemon_client_exports, {
886
+ createDaemonDbClient: () => createDaemonDbClient,
887
+ initDaemonDbClient: () => initDaemonDbClient
888
+ });
889
+ function normalizeStatement(stmt) {
890
+ if (typeof stmt === "string") {
891
+ return { sql: stmt, args: [] };
892
+ }
893
+ const sql = stmt.sql;
894
+ let args = [];
895
+ if (Array.isArray(stmt.args)) {
896
+ args = stmt.args.map((v) => serializeValue(v));
897
+ } else if (stmt.args && typeof stmt.args === "object") {
898
+ const named = {};
899
+ for (const [key, val] of Object.entries(stmt.args)) {
900
+ named[key] = serializeValue(val);
901
+ }
902
+ return { sql, args: named };
903
+ }
904
+ return { sql, args };
905
+ }
906
+ function createDaemonDbClient(fallbackClient) {
907
+ let _useDaemon = false;
908
+ const client = {
909
+ async execute(stmt) {
910
+ if (!_useDaemon || !isClientConnected()) {
911
+ return fallbackClient.execute(stmt);
912
+ }
913
+ const { sql, args } = normalizeStatement(stmt);
914
+ const response = await sendDaemonRequest({
915
+ type: "db-execute",
916
+ sql,
917
+ args
918
+ });
919
+ if (response.error) {
920
+ const errMsg = String(response.error);
921
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
922
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
923
+ `);
924
+ return fallbackClient.execute(stmt);
925
+ }
926
+ throw new Error(errMsg);
927
+ }
928
+ if (response.db) {
929
+ return deserializeResultSet(response.db);
930
+ }
931
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
932
+ return fallbackClient.execute(stmt);
933
+ },
934
+ async batch(stmts, mode) {
935
+ if (!_useDaemon || !isClientConnected()) {
936
+ return fallbackClient.batch(stmts, mode);
937
+ }
938
+ const statements = stmts.map(normalizeStatement);
939
+ const response = await sendDaemonRequest({
940
+ type: "db-batch",
941
+ statements,
942
+ mode: mode ?? "deferred"
943
+ });
944
+ if (response.error) {
945
+ const errMsg = String(response.error);
946
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
947
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
948
+ `);
949
+ return fallbackClient.batch(stmts, mode);
950
+ }
951
+ throw new Error(errMsg);
952
+ }
953
+ const batchResults = response["db-batch"];
954
+ if (batchResults) {
955
+ return batchResults.map(deserializeResultSet);
956
+ }
957
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
958
+ return fallbackClient.batch(stmts, mode);
959
+ },
960
+ // Transaction support — delegate to fallback (transactions need direct connection)
961
+ async transaction(mode) {
962
+ return fallbackClient.transaction(mode);
963
+ },
964
+ // executeMultiple — delegate to fallback (used only for schema migrations)
965
+ async executeMultiple(sql) {
966
+ return fallbackClient.executeMultiple(sql);
967
+ },
968
+ // migrate — delegate to fallback
969
+ async migrate(stmts) {
970
+ return fallbackClient.migrate(stmts);
971
+ },
972
+ // Sync mode — delegate to fallback
973
+ sync() {
974
+ return fallbackClient.sync();
975
+ },
976
+ close() {
977
+ _useDaemon = false;
978
+ },
979
+ get closed() {
980
+ return fallbackClient.closed;
981
+ },
982
+ get protocol() {
983
+ return fallbackClient.protocol;
984
+ }
985
+ };
986
+ return {
987
+ ...client,
988
+ /** Enable daemon routing (call after confirming daemon is connected) */
989
+ _enableDaemon() {
990
+ _useDaemon = true;
991
+ },
992
+ /** Check if daemon routing is active */
993
+ _isDaemonActive() {
994
+ return _useDaemon && isClientConnected();
995
+ }
996
+ };
997
+ }
998
+ async function initDaemonDbClient(fallbackClient) {
999
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1000
+ const connected = await connectEmbedDaemon();
1001
+ if (!connected) {
1002
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1003
+ return null;
1004
+ }
1005
+ const client = createDaemonDbClient(fallbackClient);
1006
+ client._enableDaemon();
1007
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1008
+ return client;
1009
+ }
1010
+ var init_db_daemon_client = __esm({
1011
+ "src/lib/db-daemon-client.ts"() {
1012
+ "use strict";
1013
+ init_exe_daemon_client();
1014
+ init_daemon_protocol();
1015
+ }
1016
+ });
1017
+
573
1018
  // src/lib/database.ts
574
1019
  var database_exports = {};
575
1020
  __export(database_exports, {
@@ -578,6 +1023,7 @@ __export(database_exports, {
578
1023
  ensureSchema: () => ensureSchema,
579
1024
  getClient: () => getClient,
580
1025
  getRawClient: () => getRawClient,
1026
+ initDaemonClient: () => initDaemonClient,
581
1027
  initDatabase: () => initDatabase,
582
1028
  initTurso: () => initTurso,
583
1029
  isInitialized: () => isInitialized
@@ -605,8 +1051,27 @@ function getClient() {
605
1051
  if (!_resilientClient) {
606
1052
  throw new Error("Database client not initialized. Call initDatabase() first.");
607
1053
  }
1054
+ if (process.env.EXE_IS_DAEMON === "1") {
1055
+ return _resilientClient;
1056
+ }
1057
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
1058
+ return _daemonClient;
1059
+ }
608
1060
  return _resilientClient;
609
1061
  }
1062
+ async function initDaemonClient() {
1063
+ if (process.env.EXE_IS_DAEMON === "1") return;
1064
+ if (!_resilientClient) return;
1065
+ try {
1066
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1067
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
1068
+ } catch (err) {
1069
+ process.stderr.write(
1070
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
1071
+ `
1072
+ );
1073
+ }
1074
+ }
610
1075
  function getRawClient() {
611
1076
  if (!_client) {
612
1077
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1093,6 +1558,12 @@ async function ensureSchema() {
1093
1558
  } catch {
1094
1559
  }
1095
1560
  }
1561
+ try {
1562
+ await client.execute(
1563
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1564
+ );
1565
+ } catch {
1566
+ }
1096
1567
  await client.executeMultiple(`
1097
1568
  CREATE TABLE IF NOT EXISTS entities (
1098
1569
  id TEXT PRIMARY KEY,
@@ -1145,7 +1616,30 @@ async function ensureSchema() {
1145
1616
  entity_id TEXT NOT NULL,
1146
1617
  PRIMARY KEY (hyperedge_id, entity_id)
1147
1618
  );
1619
+
1620
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1621
+ name,
1622
+ content=entities,
1623
+ content_rowid=rowid
1624
+ );
1625
+
1626
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1627
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1628
+ END;
1629
+
1630
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1631
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1632
+ END;
1633
+
1634
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1635
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1636
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1637
+ END;
1148
1638
  `);
1639
+ try {
1640
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1641
+ } catch {
1642
+ }
1149
1643
  await client.executeMultiple(`
1150
1644
  CREATE TABLE IF NOT EXISTS entity_aliases (
1151
1645
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1326,6 +1820,33 @@ async function ensureSchema() {
1326
1820
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1327
1821
  ON conversations(channel_id);
1328
1822
  `);
1823
+ await client.executeMultiple(`
1824
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1825
+ session_uuid TEXT PRIMARY KEY,
1826
+ agent_id TEXT NOT NULL,
1827
+ session_name TEXT,
1828
+ task_id TEXT,
1829
+ project_name TEXT,
1830
+ started_at TEXT NOT NULL
1831
+ );
1832
+
1833
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1834
+ ON session_agent_map(agent_id);
1835
+ `);
1836
+ try {
1837
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1838
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1839
+ await client.execute({
1840
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1841
+ SELECT session_id, agent_id, '', MIN(timestamp)
1842
+ FROM memories
1843
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1844
+ GROUP BY session_id, agent_id`,
1845
+ args: []
1846
+ });
1847
+ }
1848
+ } catch {
1849
+ }
1329
1850
  try {
1330
1851
  await client.execute({
1331
1852
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1459,15 +1980,41 @@ async function ensureSchema() {
1459
1980
  });
1460
1981
  } catch {
1461
1982
  }
1983
+ for (const col of [
1984
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
1985
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
1986
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
1987
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
1988
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
1989
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
1990
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
1991
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
1992
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
1993
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
1994
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
1995
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
1996
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
1997
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
1998
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1999
+ ]) {
2000
+ try {
2001
+ await client.execute(col);
2002
+ } catch {
2003
+ }
2004
+ }
1462
2005
  }
1463
2006
  async function disposeDatabase() {
2007
+ if (_daemonClient) {
2008
+ _daemonClient.close();
2009
+ _daemonClient = null;
2010
+ }
1464
2011
  if (_client) {
1465
2012
  _client.close();
1466
2013
  _client = null;
1467
2014
  _resilientClient = null;
1468
2015
  }
1469
2016
  }
1470
- var _client, _resilientClient, initTurso, disposeTurso;
2017
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1471
2018
  var init_database = __esm({
1472
2019
  "src/lib/database.ts"() {
1473
2020
  "use strict";
@@ -1475,6 +2022,7 @@ var init_database = __esm({
1475
2022
  init_employees();
1476
2023
  _client = null;
1477
2024
  _resilientClient = null;
2025
+ _daemonClient = null;
1478
2026
  initTurso = initDatabase;
1479
2027
  disposeTurso = disposeDatabase;
1480
2028
  }
@@ -1501,27 +2049,27 @@ var init_compress = __esm({
1501
2049
  });
1502
2050
 
1503
2051
  // src/lib/license.ts
1504
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync } from "fs";
1505
- import { randomUUID } from "crypto";
1506
- import path4 from "path";
2052
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync } from "fs";
2053
+ import { randomUUID as randomUUID2 } from "crypto";
2054
+ import path5 from "path";
1507
2055
  import { jwtVerify, importSPKI } from "jose";
1508
2056
  function loadDeviceId() {
1509
- const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
2057
+ const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
1510
2058
  try {
1511
- if (existsSync4(deviceJsonPath)) {
1512
- const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
2059
+ if (existsSync5(deviceJsonPath)) {
2060
+ const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
1513
2061
  if (data.deviceId) return data.deviceId;
1514
2062
  }
1515
2063
  } catch {
1516
2064
  }
1517
2065
  try {
1518
- if (existsSync4(DEVICE_ID_PATH)) {
1519
- const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
2066
+ if (existsSync5(DEVICE_ID_PATH)) {
2067
+ const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
1520
2068
  if (id2) return id2;
1521
2069
  }
1522
2070
  } catch {
1523
2071
  }
1524
- const id = randomUUID();
2072
+ const id = randomUUID2();
1525
2073
  mkdirSync(EXE_AI_DIR, { recursive: true });
1526
2074
  writeFileSync2(DEVICE_ID_PATH, id, "utf8");
1527
2075
  return id;
@@ -1531,9 +2079,235 @@ var init_license = __esm({
1531
2079
  "src/lib/license.ts"() {
1532
2080
  "use strict";
1533
2081
  init_config();
1534
- LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
1535
- CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
1536
- DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
2082
+ LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
2083
+ CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
2084
+ DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
2085
+ }
2086
+ });
2087
+
2088
+ // src/lib/crdt-sync.ts
2089
+ var crdt_sync_exports = {};
2090
+ __export(crdt_sync_exports, {
2091
+ _setStatePath: () => _setStatePath,
2092
+ applyRemoteUpdate: () => applyRemoteUpdate,
2093
+ destroyCrdtDoc: () => destroyCrdtDoc,
2094
+ getDiffUpdate: () => getDiffUpdate,
2095
+ getFullState: () => getFullState,
2096
+ getStateVector: () => getStateVector,
2097
+ importExistingBehaviors: () => importExistingBehaviors,
2098
+ importExistingMemories: () => importExistingMemories,
2099
+ initCrdtDoc: () => initCrdtDoc,
2100
+ isCrdtSyncEnabled: () => isCrdtSyncEnabled,
2101
+ onUpdate: () => onUpdate,
2102
+ readAllBehaviors: () => readAllBehaviors,
2103
+ readAllMemories: () => readAllMemories,
2104
+ rebuildFromDb: () => rebuildFromDb
2105
+ });
2106
+ import * as Y from "yjs";
2107
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
2108
+ import path6 from "path";
2109
+ import { homedir } from "os";
2110
+ function getStatePath() {
2111
+ return _statePathOverride ?? DEFAULT_STATE_PATH;
2112
+ }
2113
+ function _setStatePath(p) {
2114
+ _statePathOverride = p;
2115
+ }
2116
+ function initCrdtDoc() {
2117
+ if (doc) return doc;
2118
+ doc = new Y.Doc();
2119
+ const sp = getStatePath();
2120
+ if (existsSync6(sp)) {
2121
+ try {
2122
+ const state = readFileSync5(sp);
2123
+ Y.applyUpdate(doc, new Uint8Array(state));
2124
+ } catch {
2125
+ console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
2126
+ try {
2127
+ unlinkSync3(sp);
2128
+ } catch {
2129
+ }
2130
+ rebuildFromDb().catch((err) => {
2131
+ console.warn("[crdt-sync] rebuild from DB failed:", err);
2132
+ });
2133
+ }
2134
+ }
2135
+ doc.on("update", () => {
2136
+ persistState();
2137
+ });
2138
+ return doc;
2139
+ }
2140
+ function getMemoriesMap() {
2141
+ const d = initCrdtDoc();
2142
+ return d.getMap("memories");
2143
+ }
2144
+ function getBehaviorsMap() {
2145
+ const d = initCrdtDoc();
2146
+ return d.getMap("behaviors");
2147
+ }
2148
+ function applyRemoteUpdate(update) {
2149
+ const d = initCrdtDoc();
2150
+ Y.applyUpdate(d, update);
2151
+ }
2152
+ function getFullState() {
2153
+ const d = initCrdtDoc();
2154
+ return Y.encodeStateAsUpdate(d);
2155
+ }
2156
+ function getDiffUpdate(remoteStateVector) {
2157
+ const d = initCrdtDoc();
2158
+ return Y.encodeStateAsUpdate(d, remoteStateVector);
2159
+ }
2160
+ function getStateVector() {
2161
+ const d = initCrdtDoc();
2162
+ return Y.encodeStateVector(d);
2163
+ }
2164
+ function importExistingMemories(memories) {
2165
+ const map = getMemoriesMap();
2166
+ const d = initCrdtDoc();
2167
+ let imported = 0;
2168
+ d.transact(() => {
2169
+ for (const mem of memories) {
2170
+ if (!mem.id) continue;
2171
+ if (map.has(mem.id)) continue;
2172
+ const entry = new Y.Map();
2173
+ entry.set("id", mem.id);
2174
+ entry.set("agent_id", mem.agent_id ?? null);
2175
+ entry.set("agent_role", mem.agent_role ?? null);
2176
+ entry.set("session_id", mem.session_id ?? null);
2177
+ entry.set("timestamp", mem.timestamp ?? null);
2178
+ entry.set("tool_name", mem.tool_name ?? null);
2179
+ entry.set("project_name", mem.project_name ?? null);
2180
+ entry.set("has_error", mem.has_error ?? 0);
2181
+ entry.set("raw_text", mem.raw_text ?? "");
2182
+ entry.set("version", mem.version ?? 0);
2183
+ entry.set("author_device_id", mem.author_device_id ?? null);
2184
+ entry.set("scope", mem.scope ?? "business");
2185
+ map.set(mem.id, entry);
2186
+ imported++;
2187
+ }
2188
+ });
2189
+ return imported;
2190
+ }
2191
+ function importExistingBehaviors(behaviors) {
2192
+ const map = getBehaviorsMap();
2193
+ const d = initCrdtDoc();
2194
+ let imported = 0;
2195
+ d.transact(() => {
2196
+ for (const beh of behaviors) {
2197
+ if (!beh.id) continue;
2198
+ if (map.has(beh.id)) continue;
2199
+ const entry = new Y.Map();
2200
+ entry.set("id", beh.id);
2201
+ entry.set("agent_id", beh.agent_id ?? null);
2202
+ entry.set("project_name", beh.project_name ?? null);
2203
+ entry.set("domain", beh.domain ?? null);
2204
+ entry.set("content", beh.content ?? null);
2205
+ entry.set("active", beh.active ?? 1);
2206
+ entry.set("priority", beh.priority ?? "p1");
2207
+ entry.set("created_at", beh.created_at ?? null);
2208
+ entry.set("updated_at", beh.updated_at ?? null);
2209
+ map.set(beh.id, entry);
2210
+ imported++;
2211
+ }
2212
+ });
2213
+ return imported;
2214
+ }
2215
+ function readAllMemories() {
2216
+ const map = getMemoriesMap();
2217
+ const records = [];
2218
+ map.forEach((entry, id) => {
2219
+ records.push({
2220
+ id,
2221
+ agent_id: entry.get("agent_id"),
2222
+ agent_role: entry.get("agent_role"),
2223
+ session_id: entry.get("session_id"),
2224
+ timestamp: entry.get("timestamp"),
2225
+ tool_name: entry.get("tool_name"),
2226
+ project_name: entry.get("project_name"),
2227
+ has_error: entry.get("has_error"),
2228
+ raw_text: entry.get("raw_text"),
2229
+ version: entry.get("version"),
2230
+ author_device_id: entry.get("author_device_id"),
2231
+ scope: entry.get("scope")
2232
+ });
2233
+ });
2234
+ return records;
2235
+ }
2236
+ function readAllBehaviors() {
2237
+ const map = getBehaviorsMap();
2238
+ const records = [];
2239
+ map.forEach((entry, id) => {
2240
+ records.push({
2241
+ id,
2242
+ agent_id: entry.get("agent_id"),
2243
+ project_name: entry.get("project_name"),
2244
+ domain: entry.get("domain"),
2245
+ content: entry.get("content"),
2246
+ active: entry.get("active"),
2247
+ priority: entry.get("priority"),
2248
+ created_at: entry.get("created_at"),
2249
+ updated_at: entry.get("updated_at")
2250
+ });
2251
+ });
2252
+ return records;
2253
+ }
2254
+ function onUpdate(callback) {
2255
+ const d = initCrdtDoc();
2256
+ const handler = (update) => callback(update);
2257
+ d.on("update", handler);
2258
+ return () => d.off("update", handler);
2259
+ }
2260
+ function persistState() {
2261
+ if (!doc) return;
2262
+ try {
2263
+ const sp = getStatePath();
2264
+ const dir = path6.dirname(sp);
2265
+ if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
2266
+ const state = Y.encodeStateAsUpdate(doc);
2267
+ writeFileSync3(sp, Buffer.from(state));
2268
+ } catch {
2269
+ }
2270
+ }
2271
+ function isCrdtSyncEnabled() {
2272
+ return process.env.EXE_CRDT_SYNC !== "0";
2273
+ }
2274
+ async function rebuildFromDb() {
2275
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
2276
+ const client = getClient2();
2277
+ const result = await client.execute(
2278
+ "SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
2279
+ );
2280
+ const memories = result.rows.map((row) => ({
2281
+ id: String(row.id),
2282
+ agent_id: row.agent_id,
2283
+ agent_role: row.agent_role,
2284
+ session_id: row.session_id,
2285
+ timestamp: row.timestamp,
2286
+ tool_name: row.tool_name,
2287
+ project_name: row.project_name,
2288
+ has_error: row.has_error,
2289
+ raw_text: row.raw_text,
2290
+ version: row.version,
2291
+ author_device_id: row.author_device_id,
2292
+ scope: row.scope
2293
+ }));
2294
+ const count = importExistingMemories(memories);
2295
+ persistState();
2296
+ return count;
2297
+ }
2298
+ function destroyCrdtDoc() {
2299
+ if (doc) {
2300
+ doc.destroy();
2301
+ doc = null;
2302
+ }
2303
+ }
2304
+ var DEFAULT_STATE_PATH, _statePathOverride, doc;
2305
+ var init_crdt_sync = __esm({
2306
+ "src/lib/crdt-sync.ts"() {
2307
+ "use strict";
2308
+ DEFAULT_STATE_PATH = path6.join(homedir(), ".exe-os", "crdt-state.bin");
2309
+ _statePathOverride = null;
2310
+ doc = null;
1537
2311
  }
1538
2312
  });
1539
2313
 
@@ -1565,16 +2339,16 @@ __export(cloud_sync_exports, {
1565
2339
  mergeRosterFromRemote: () => mergeRosterFromRemote,
1566
2340
  recordRosterDeletion: () => recordRosterDeletion
1567
2341
  });
1568
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync as unlinkSync2, openSync, closeSync } from "fs";
2342
+ 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";
1569
2343
  import crypto2 from "crypto";
1570
- import path5 from "path";
1571
- import { homedir } from "os";
2344
+ import path7 from "path";
2345
+ import { homedir as homedir2 } from "os";
1572
2346
  function sqlSafe(v) {
1573
2347
  return v === void 0 ? null : v;
1574
2348
  }
1575
2349
  function logError(msg) {
1576
2350
  try {
1577
- const logPath = path5.join(homedir(), ".exe-os", "workers.log");
2351
+ const logPath = path7.join(homedir2(), ".exe-os", "workers.log");
1578
2352
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
1579
2353
  `);
1580
2354
  } catch {
@@ -1582,20 +2356,20 @@ function logError(msg) {
1582
2356
  }
1583
2357
  async function withRosterLock(fn) {
1584
2358
  try {
1585
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
1586
- closeSync(fd);
1587
- writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
2359
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2360
+ closeSync2(fd);
2361
+ writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
1588
2362
  } catch (err) {
1589
2363
  if (err.code === "EEXIST") {
1590
2364
  try {
1591
- const ts = parseInt(readFileSync4(ROSTER_LOCK_PATH, "utf-8"), 10);
2365
+ const ts = parseInt(readFileSync6(ROSTER_LOCK_PATH, "utf-8"), 10);
1592
2366
  if (Date.now() - ts < LOCK_STALE_MS) {
1593
2367
  throw new Error("Roster merge already in progress \u2014 another sync is running");
1594
2368
  }
1595
- unlinkSync2(ROSTER_LOCK_PATH);
1596
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
1597
- closeSync(fd);
1598
- writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
2369
+ unlinkSync4(ROSTER_LOCK_PATH);
2370
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
2371
+ closeSync2(fd);
2372
+ writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
1599
2373
  } catch (retryErr) {
1600
2374
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
1601
2375
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -1608,7 +2382,7 @@ async function withRosterLock(fn) {
1608
2382
  return await fn();
1609
2383
  } finally {
1610
2384
  try {
1611
- unlinkSync2(ROSTER_LOCK_PATH);
2385
+ unlinkSync4(ROSTER_LOCK_PATH);
1612
2386
  } catch {
1613
2387
  }
1614
2388
  }
@@ -1753,29 +2527,75 @@ async function cloudSync(config) {
1753
2527
  const pullResult = await cloudPull(lastPullVersion, config);
1754
2528
  let pulled = 0;
1755
2529
  if (pullResult.records.length > 0) {
1756
- const stmts = pullResult.records.map((rec) => ({
1757
- sql: `INSERT OR REPLACE INTO memories
1758
- (id, agent_id, agent_role, session_id, timestamp,
1759
- tool_name, project_name, has_error, raw_text, version,
1760
- author_device_id, scope)
1761
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1762
- args: [
1763
- sqlSafe(rec.id),
1764
- sqlSafe(rec.agent_id),
1765
- sqlSafe(rec.agent_role),
1766
- sqlSafe(rec.session_id),
1767
- sqlSafe(rec.timestamp),
1768
- sqlSafe(rec.tool_name),
1769
- sqlSafe(rec.project_name),
1770
- sqlSafe(rec.has_error ?? 0),
1771
- sqlSafe(rec.raw_text ?? ""),
1772
- sqlSafe(rec.version ?? 0),
1773
- sqlSafe(rec.author_device_id),
1774
- sqlSafe(rec.scope ?? "business")
1775
- ]
1776
- }));
1777
- await client.batch(stmts, "write");
1778
- pulled = pullResult.records.length;
2530
+ if (isCrdtSyncEnabled()) {
2531
+ const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
2532
+ initCrdtDoc2();
2533
+ importExistingMemories2(
2534
+ pullResult.records.map((rec) => ({
2535
+ id: String(rec.id ?? ""),
2536
+ agent_id: rec.agent_id,
2537
+ agent_role: rec.agent_role,
2538
+ session_id: rec.session_id,
2539
+ timestamp: rec.timestamp,
2540
+ tool_name: rec.tool_name,
2541
+ project_name: rec.project_name,
2542
+ has_error: rec.has_error ?? 0,
2543
+ raw_text: rec.raw_text ?? "",
2544
+ version: rec.version ?? 0,
2545
+ author_device_id: rec.author_device_id,
2546
+ scope: rec.scope ?? "business"
2547
+ }))
2548
+ );
2549
+ const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
2550
+ const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
2551
+ const stmts = merged.map((rec) => ({
2552
+ sql: `INSERT OR REPLACE INTO memories
2553
+ (id, agent_id, agent_role, session_id, timestamp,
2554
+ tool_name, project_name, has_error, raw_text, version,
2555
+ author_device_id, scope)
2556
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2557
+ args: [
2558
+ sqlSafe(rec.id),
2559
+ sqlSafe(rec.agent_id),
2560
+ sqlSafe(rec.agent_role),
2561
+ sqlSafe(rec.session_id),
2562
+ sqlSafe(rec.timestamp),
2563
+ sqlSafe(rec.tool_name),
2564
+ sqlSafe(rec.project_name),
2565
+ sqlSafe(rec.has_error ?? 0),
2566
+ sqlSafe(rec.raw_text ?? ""),
2567
+ sqlSafe(rec.version ?? 0),
2568
+ sqlSafe(rec.author_device_id),
2569
+ sqlSafe(rec.scope ?? "business")
2570
+ ]
2571
+ }));
2572
+ if (stmts.length > 0) await client.batch(stmts, "write");
2573
+ pulled = pullResult.records.length;
2574
+ } else {
2575
+ const stmts = pullResult.records.map((rec) => ({
2576
+ sql: `INSERT OR REPLACE INTO memories
2577
+ (id, agent_id, agent_role, session_id, timestamp,
2578
+ tool_name, project_name, has_error, raw_text, version,
2579
+ author_device_id, scope)
2580
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2581
+ args: [
2582
+ sqlSafe(rec.id),
2583
+ sqlSafe(rec.agent_id),
2584
+ sqlSafe(rec.agent_role),
2585
+ sqlSafe(rec.session_id),
2586
+ sqlSafe(rec.timestamp),
2587
+ sqlSafe(rec.tool_name),
2588
+ sqlSafe(rec.project_name),
2589
+ sqlSafe(rec.has_error ?? 0),
2590
+ sqlSafe(rec.raw_text ?? ""),
2591
+ sqlSafe(rec.version ?? 0),
2592
+ sqlSafe(rec.author_device_id),
2593
+ sqlSafe(rec.scope ?? "business")
2594
+ ]
2595
+ }));
2596
+ await client.batch(stmts, "write");
2597
+ pulled = pullResult.records.length;
2598
+ }
1779
2599
  }
1780
2600
  if (pullResult.maxVersion > lastPullVersion) {
1781
2601
  await client.execute({
@@ -1923,8 +2743,8 @@ async function cloudSync(config) {
1923
2743
  try {
1924
2744
  const employees = await loadEmployees();
1925
2745
  rosterResult.employees = employees.length;
1926
- const idDir = path5.join(EXE_AI_DIR, "identity");
1927
- if (existsSync5(idDir)) {
2746
+ const idDir = path7.join(EXE_AI_DIR, "identity");
2747
+ if (existsSync7(idDir)) {
1928
2748
  rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
1929
2749
  }
1930
2750
  } catch {
@@ -1945,55 +2765,63 @@ async function cloudSync(config) {
1945
2765
  function recordRosterDeletion(name) {
1946
2766
  let deletions = [];
1947
2767
  try {
1948
- if (existsSync5(ROSTER_DELETIONS_PATH)) {
1949
- deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
2768
+ if (existsSync7(ROSTER_DELETIONS_PATH)) {
2769
+ deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
1950
2770
  }
1951
2771
  } catch {
1952
2772
  }
1953
2773
  if (!deletions.includes(name)) deletions.push(name);
1954
- writeFileSync3(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
2774
+ writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
1955
2775
  }
1956
2776
  function consumeRosterDeletions() {
1957
2777
  try {
1958
- if (!existsSync5(ROSTER_DELETIONS_PATH)) return [];
1959
- const deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
1960
- writeFileSync3(ROSTER_DELETIONS_PATH, "[]");
2778
+ if (!existsSync7(ROSTER_DELETIONS_PATH)) return [];
2779
+ const deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
2780
+ writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
1961
2781
  return deletions;
1962
2782
  } catch {
1963
2783
  return [];
1964
2784
  }
1965
2785
  }
1966
2786
  function buildRosterBlob(paths) {
1967
- const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
1968
- const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
1969
- const configPath = paths?.configPath ?? path5.join(EXE_AI_DIR, "config.json");
2787
+ const rosterPath = paths?.rosterPath ?? path7.join(EXE_AI_DIR, "exe-employees.json");
2788
+ const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
2789
+ const configPath = paths?.configPath ?? path7.join(EXE_AI_DIR, "config.json");
1970
2790
  let roster = [];
1971
- if (existsSync5(rosterPath)) {
2791
+ if (existsSync7(rosterPath)) {
1972
2792
  try {
1973
- roster = JSON.parse(readFileSync4(rosterPath, "utf-8"));
2793
+ roster = JSON.parse(readFileSync6(rosterPath, "utf-8"));
1974
2794
  } catch {
1975
2795
  }
1976
2796
  }
1977
2797
  const identities = {};
1978
- if (existsSync5(identityDir)) {
2798
+ if (existsSync7(identityDir)) {
1979
2799
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
1980
2800
  try {
1981
- identities[file] = readFileSync4(path5.join(identityDir, file), "utf-8");
2801
+ identities[file] = readFileSync6(path7.join(identityDir, file), "utf-8");
1982
2802
  } catch {
1983
2803
  }
1984
2804
  }
1985
2805
  }
1986
2806
  let config;
1987
- if (existsSync5(configPath)) {
2807
+ if (existsSync7(configPath)) {
2808
+ try {
2809
+ config = JSON.parse(readFileSync6(configPath, "utf-8"));
2810
+ } catch {
2811
+ }
2812
+ }
2813
+ let agentConfig;
2814
+ const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
2815
+ if (existsSync7(agentConfigPath)) {
1988
2816
  try {
1989
- config = JSON.parse(readFileSync4(configPath, "utf-8"));
2817
+ agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
1990
2818
  } catch {
1991
2819
  }
1992
2820
  }
1993
2821
  const deletedNames = consumeRosterDeletions();
1994
- const content = JSON.stringify({ roster, identities, config, deletedNames });
2822
+ const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
1995
2823
  const hash = crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
1996
- return { roster, identities, config, deletedNames, version: hash };
2824
+ return { roster, identities, config, agentConfig, deletedNames, version: hash };
1997
2825
  }
1998
2826
  async function cloudPushRoster(config) {
1999
2827
  assertSecureEndpoint(config.endpoint);
@@ -2062,23 +2890,23 @@ async function cloudPullRoster(config) {
2062
2890
  }
2063
2891
  }
2064
2892
  function mergeConfig(remoteConfig, configPath) {
2065
- const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
2893
+ const cfgPath = configPath ?? path7.join(EXE_AI_DIR, "config.json");
2066
2894
  let local = {};
2067
- if (existsSync5(cfgPath)) {
2895
+ if (existsSync7(cfgPath)) {
2068
2896
  try {
2069
- local = JSON.parse(readFileSync4(cfgPath, "utf-8"));
2897
+ local = JSON.parse(readFileSync6(cfgPath, "utf-8"));
2070
2898
  } catch {
2071
2899
  }
2072
2900
  }
2073
2901
  const merged = { ...remoteConfig, ...local };
2074
- const dir = path5.dirname(cfgPath);
2075
- if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
2076
- writeFileSync3(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2902
+ const dir = path7.dirname(cfgPath);
2903
+ if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2904
+ writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2077
2905
  }
2078
2906
  async function mergeRosterFromRemote(remote, paths) {
2079
2907
  return withRosterLock(async () => {
2080
2908
  const rosterPath = paths?.rosterPath ?? void 0;
2081
- const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
2909
+ const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
2082
2910
  const localEmployees = await loadEmployees(rosterPath);
2083
2911
  const localNames = new Set(localEmployees.map((e) => e.name));
2084
2912
  let added = 0;
@@ -2099,15 +2927,15 @@ async function mergeRosterFromRemote(remote, paths) {
2099
2927
  ) ?? lookupKey;
2100
2928
  const remoteIdentity = remote.identities[matchedKey];
2101
2929
  if (remoteIdentity) {
2102
- if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
2103
- const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
2930
+ if (!existsSync7(identityDir)) mkdirSync3(identityDir, { recursive: true });
2931
+ const idPath = path7.join(identityDir, `${remoteEmp.name}.md`);
2104
2932
  let localIdentity = null;
2105
2933
  try {
2106
- localIdentity = existsSync5(idPath) ? readFileSync4(idPath, "utf-8") : null;
2934
+ localIdentity = existsSync7(idPath) ? readFileSync6(idPath, "utf-8") : null;
2107
2935
  } catch {
2108
2936
  }
2109
2937
  if (localIdentity !== remoteIdentity) {
2110
- writeFileSync3(idPath, remoteIdentity, "utf-8");
2938
+ writeFileSync4(idPath, remoteIdentity, "utf-8");
2111
2939
  identitiesUpdated++;
2112
2940
  }
2113
2941
  }
@@ -2131,6 +2959,21 @@ async function mergeRosterFromRemote(remote, paths) {
2131
2959
  } catch {
2132
2960
  }
2133
2961
  }
2962
+ if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
2963
+ try {
2964
+ const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
2965
+ let local = {};
2966
+ if (existsSync7(agentConfigPath)) {
2967
+ try {
2968
+ local = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
2969
+ } catch {
2970
+ }
2971
+ }
2972
+ const merged = { ...remote.agentConfig, ...local };
2973
+ writeFileSync4(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
2974
+ } catch {
2975
+ }
2976
+ }
2134
2977
  return { added, identitiesUpdated };
2135
2978
  });
2136
2979
  }
@@ -2560,13 +3403,14 @@ var init_cloud_sync = __esm({
2560
3403
  init_compress();
2561
3404
  init_license();
2562
3405
  init_config();
3406
+ init_crdt_sync();
2563
3407
  init_employees();
2564
3408
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
2565
3409
  FETCH_TIMEOUT_MS = 3e4;
2566
3410
  PUSH_BATCH_SIZE = 5e3;
2567
- ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
3411
+ ROSTER_LOCK_PATH = path7.join(EXE_AI_DIR, "roster-merge.lock");
2568
3412
  LOCK_STALE_MS = 3e4;
2569
- ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
3413
+ ROSTER_DELETIONS_PATH = path7.join(EXE_AI_DIR, "roster-deletions.json");
2570
3414
  }
2571
3415
  });
2572
3416
 
@@ -2579,6 +3423,7 @@ import { realpathSync } from "fs";
2579
3423
  import { fileURLToPath } from "url";
2580
3424
  function isMainModule(importMetaUrl) {
2581
3425
  if (process.argv[1] == null) return false;
3426
+ if (process.argv[1].includes("mcp/server")) return false;
2582
3427
  try {
2583
3428
  const scriptPath = realpathSync(process.argv[1]);
2584
3429
  const modulePath = realpathSync(fileURLToPath(importMetaUrl));