@askexenow/exe-os 0.8.83 → 0.8.85

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 +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 +97 -2
  5. package/dist/bin/cli.js +14350 -12518
  6. package/dist/bin/exe-agent.js +97 -88
  7. package/dist/bin/exe-assign.js +1003 -854
  8. package/dist/bin/exe-boot.js +1257 -320
  9. package/dist/bin/exe-call.js +10 -0
  10. package/dist/bin/exe-cloud.js +29 -6
  11. package/dist/bin/exe-dispatch.js +210 -34
  12. package/dist/bin/exe-doctor.js +403 -6
  13. package/dist/bin/exe-export-behaviors.js +175 -72
  14. package/dist/bin/exe-forget.js +97 -2
  15. package/dist/bin/exe-gateway.js +550 -171
  16. package/dist/bin/exe-healthcheck.js +1 -0
  17. package/dist/bin/exe-heartbeat.js +100 -5
  18. package/dist/bin/exe-kill.js +175 -72
  19. package/dist/bin/exe-launch-agent.js +189 -76
  20. package/dist/bin/exe-link.js +902 -80
  21. package/dist/bin/exe-new-employee.js +38 -8
  22. package/dist/bin/exe-pending-messages.js +96 -2
  23. package/dist/bin/exe-pending-notifications.js +97 -2
  24. package/dist/bin/exe-pending-reviews.js +98 -3
  25. package/dist/bin/exe-rename.js +564 -23
  26. package/dist/bin/exe-review.js +231 -73
  27. package/dist/bin/exe-search.js +989 -226
  28. package/dist/bin/exe-session-cleanup.js +4806 -1665
  29. package/dist/bin/exe-settings.js +20 -5
  30. package/dist/bin/exe-status.js +97 -2
  31. package/dist/bin/exe-team.js +97 -2
  32. package/dist/bin/git-sweep.js +899 -207
  33. package/dist/bin/graph-backfill.js +175 -72
  34. package/dist/bin/graph-export.js +175 -72
  35. package/dist/bin/install.js +38 -7
  36. package/dist/bin/list-providers.js +1 -0
  37. package/dist/bin/scan-tasks.js +904 -211
  38. package/dist/bin/setup.js +867 -268
  39. package/dist/bin/shard-migrate.js +175 -72
  40. package/dist/bin/update.js +1 -0
  41. package/dist/bin/wiki-sync.js +175 -72
  42. package/dist/gateway/index.js +548 -166
  43. package/dist/hooks/bug-report-worker.js +208 -23
  44. package/dist/hooks/commit-complete.js +897 -205
  45. package/dist/hooks/error-recall.js +988 -226
  46. package/dist/hooks/ingest-worker.js +1638 -1194
  47. package/dist/hooks/ingest.js +3 -0
  48. package/dist/hooks/instructions-loaded.js +707 -97
  49. package/dist/hooks/notification.js +699 -89
  50. package/dist/hooks/post-compact.js +714 -104
  51. package/dist/hooks/pre-compact.js +897 -205
  52. package/dist/hooks/pre-tool-use.js +742 -123
  53. package/dist/hooks/prompt-ingest-worker.js +242 -101
  54. package/dist/hooks/prompt-submit.js +995 -233
  55. package/dist/hooks/response-ingest-worker.js +242 -101
  56. package/dist/hooks/session-end.js +3941 -400
  57. package/dist/hooks/session-start.js +1001 -226
  58. package/dist/hooks/stop.js +725 -115
  59. package/dist/hooks/subagent-stop.js +714 -104
  60. package/dist/hooks/summary-worker.js +1964 -1330
  61. package/dist/index.js +1651 -1053
  62. package/dist/lib/cloud-sync.js +907 -86
  63. package/dist/lib/consolidation.js +2 -1
  64. package/dist/lib/database.js +642 -87
  65. package/dist/lib/db-daemon-client.js +503 -0
  66. package/dist/lib/device-registry.js +547 -7
  67. package/dist/lib/embedder.js +14 -28
  68. package/dist/lib/employee-templates.js +84 -74
  69. package/dist/lib/employees.js +9 -0
  70. package/dist/lib/exe-daemon-client.js +16 -29
  71. package/dist/lib/exe-daemon.js +1955 -922
  72. package/dist/lib/hybrid-search.js +988 -226
  73. package/dist/lib/identity.js +87 -67
  74. package/dist/lib/keychain.js +9 -1
  75. package/dist/lib/messaging.js +8 -1
  76. package/dist/lib/reminders.js +91 -74
  77. package/dist/lib/schedules.js +96 -2
  78. package/dist/lib/skill-learning.js +103 -85
  79. package/dist/lib/store.js +234 -73
  80. package/dist/lib/tasks.js +111 -22
  81. package/dist/lib/tmux-routing.js +120 -31
  82. package/dist/lib/token-spend.js +273 -0
  83. package/dist/lib/ws-client.js +11 -0
  84. package/dist/mcp/server.js +5222 -475
  85. package/dist/mcp/tools/complete-reminder.js +94 -77
  86. package/dist/mcp/tools/create-reminder.js +94 -77
  87. package/dist/mcp/tools/create-task.js +120 -22
  88. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  89. package/dist/mcp/tools/list-reminders.js +94 -77
  90. package/dist/mcp/tools/list-tasks.js +31 -1
  91. package/dist/mcp/tools/send-message.js +8 -1
  92. package/dist/mcp/tools/update-task.js +39 -10
  93. package/dist/runtime/index.js +911 -219
  94. package/dist/tui/App.js +997 -295
  95. package/package.json +6 -1
@@ -648,6 +648,443 @@ var init_db_retry = __esm({
648
648
  }
649
649
  });
650
650
 
651
+ // src/lib/exe-daemon-client.ts
652
+ import net from "net";
653
+ import { spawn } from "child_process";
654
+ import { randomUUID as randomUUID2 } from "crypto";
655
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
656
+ import path6 from "path";
657
+ import { fileURLToPath } from "url";
658
+ function handleData(chunk) {
659
+ _buffer += chunk.toString();
660
+ if (_buffer.length > MAX_BUFFER) {
661
+ _buffer = "";
662
+ return;
663
+ }
664
+ let newlineIdx;
665
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
666
+ const line = _buffer.slice(0, newlineIdx).trim();
667
+ _buffer = _buffer.slice(newlineIdx + 1);
668
+ if (!line) continue;
669
+ try {
670
+ const response = JSON.parse(line);
671
+ const id = response.id;
672
+ if (!id) continue;
673
+ const entry = _pending.get(id);
674
+ if (entry) {
675
+ clearTimeout(entry.timer);
676
+ _pending.delete(id);
677
+ entry.resolve(response);
678
+ }
679
+ } catch {
680
+ }
681
+ }
682
+ }
683
+ function cleanupStaleFiles() {
684
+ if (existsSync5(PID_PATH)) {
685
+ try {
686
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
687
+ if (pid > 0) {
688
+ try {
689
+ process.kill(pid, 0);
690
+ return;
691
+ } catch {
692
+ }
693
+ }
694
+ } catch {
695
+ }
696
+ try {
697
+ unlinkSync2(PID_PATH);
698
+ } catch {
699
+ }
700
+ try {
701
+ unlinkSync2(SOCKET_PATH);
702
+ } catch {
703
+ }
704
+ }
705
+ }
706
+ function findPackageRoot() {
707
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
708
+ const { root } = path6.parse(dir);
709
+ while (dir !== root) {
710
+ if (existsSync5(path6.join(dir, "package.json"))) return dir;
711
+ dir = path6.dirname(dir);
712
+ }
713
+ return null;
714
+ }
715
+ function spawnDaemon() {
716
+ const pkgRoot = findPackageRoot();
717
+ if (!pkgRoot) {
718
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
719
+ return;
720
+ }
721
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
722
+ if (!existsSync5(daemonPath)) {
723
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
724
+ `);
725
+ return;
726
+ }
727
+ const resolvedPath = daemonPath;
728
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
729
+ `);
730
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
731
+ let stderrFd = "ignore";
732
+ try {
733
+ stderrFd = openSync(logPath, "a");
734
+ } catch {
735
+ }
736
+ const child = spawn(process.execPath, [resolvedPath], {
737
+ detached: true,
738
+ stdio: ["ignore", "ignore", stderrFd],
739
+ env: {
740
+ ...process.env,
741
+ TMUX: void 0,
742
+ // Daemon is global — must not inherit session scope
743
+ TMUX_PANE: void 0,
744
+ // Prevents resolveExeSession() from scoping to one session
745
+ EXE_DAEMON_SOCK: SOCKET_PATH,
746
+ EXE_DAEMON_PID: PID_PATH
747
+ }
748
+ });
749
+ child.unref();
750
+ if (typeof stderrFd === "number") {
751
+ try {
752
+ closeSync(stderrFd);
753
+ } catch {
754
+ }
755
+ }
756
+ }
757
+ function acquireSpawnLock() {
758
+ try {
759
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
760
+ closeSync(fd);
761
+ return true;
762
+ } catch {
763
+ try {
764
+ const stat = statSync(SPAWN_LOCK_PATH);
765
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
766
+ try {
767
+ unlinkSync2(SPAWN_LOCK_PATH);
768
+ } catch {
769
+ }
770
+ try {
771
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
772
+ closeSync(fd);
773
+ return true;
774
+ } catch {
775
+ }
776
+ }
777
+ } catch {
778
+ }
779
+ return false;
780
+ }
781
+ }
782
+ function releaseSpawnLock() {
783
+ try {
784
+ unlinkSync2(SPAWN_LOCK_PATH);
785
+ } catch {
786
+ }
787
+ }
788
+ function connectToSocket() {
789
+ return new Promise((resolve) => {
790
+ if (_socket && _connected) {
791
+ resolve(true);
792
+ return;
793
+ }
794
+ const socket = net.createConnection({ path: SOCKET_PATH });
795
+ const connectTimeout = setTimeout(() => {
796
+ socket.destroy();
797
+ resolve(false);
798
+ }, 2e3);
799
+ socket.on("connect", () => {
800
+ clearTimeout(connectTimeout);
801
+ _socket = socket;
802
+ _connected = true;
803
+ _buffer = "";
804
+ socket.on("data", handleData);
805
+ socket.on("close", () => {
806
+ _connected = false;
807
+ _socket = null;
808
+ for (const [id, entry] of _pending) {
809
+ clearTimeout(entry.timer);
810
+ _pending.delete(id);
811
+ entry.resolve({ error: "Connection closed" });
812
+ }
813
+ });
814
+ socket.on("error", () => {
815
+ _connected = false;
816
+ _socket = null;
817
+ });
818
+ resolve(true);
819
+ });
820
+ socket.on("error", () => {
821
+ clearTimeout(connectTimeout);
822
+ resolve(false);
823
+ });
824
+ });
825
+ }
826
+ async function connectEmbedDaemon() {
827
+ if (_socket && _connected) return true;
828
+ if (await connectToSocket()) return true;
829
+ if (acquireSpawnLock()) {
830
+ try {
831
+ cleanupStaleFiles();
832
+ spawnDaemon();
833
+ } finally {
834
+ releaseSpawnLock();
835
+ }
836
+ }
837
+ const start = Date.now();
838
+ let delay2 = 100;
839
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
840
+ await new Promise((r) => setTimeout(r, delay2));
841
+ if (await connectToSocket()) return true;
842
+ delay2 = Math.min(delay2 * 2, 3e3);
843
+ }
844
+ return false;
845
+ }
846
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
847
+ return new Promise((resolve) => {
848
+ if (!_socket || !_connected) {
849
+ resolve({ error: "Not connected" });
850
+ return;
851
+ }
852
+ const id = randomUUID2();
853
+ const timer = setTimeout(() => {
854
+ _pending.delete(id);
855
+ resolve({ error: "Request timeout" });
856
+ }, timeoutMs);
857
+ _pending.set(id, { resolve, timer });
858
+ try {
859
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
860
+ } catch {
861
+ clearTimeout(timer);
862
+ _pending.delete(id);
863
+ resolve({ error: "Write failed" });
864
+ }
865
+ });
866
+ }
867
+ function isClientConnected() {
868
+ return _connected;
869
+ }
870
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
871
+ var init_exe_daemon_client = __esm({
872
+ "src/lib/exe-daemon-client.ts"() {
873
+ "use strict";
874
+ init_config();
875
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
876
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
877
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
878
+ SPAWN_LOCK_STALE_MS = 3e4;
879
+ CONNECT_TIMEOUT_MS = 15e3;
880
+ REQUEST_TIMEOUT_MS = 3e4;
881
+ _socket = null;
882
+ _connected = false;
883
+ _buffer = "";
884
+ _pending = /* @__PURE__ */ new Map();
885
+ MAX_BUFFER = 1e7;
886
+ }
887
+ });
888
+
889
+ // src/lib/daemon-protocol.ts
890
+ function serializeValue(v) {
891
+ if (v === null || v === void 0) return null;
892
+ if (typeof v === "bigint") return Number(v);
893
+ if (typeof v === "boolean") return v ? 1 : 0;
894
+ if (v instanceof Uint8Array) {
895
+ return { __blob: Buffer.from(v).toString("base64") };
896
+ }
897
+ if (ArrayBuffer.isView(v)) {
898
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
899
+ }
900
+ if (v instanceof ArrayBuffer) {
901
+ return { __blob: Buffer.from(v).toString("base64") };
902
+ }
903
+ if (typeof v === "string" || typeof v === "number") return v;
904
+ return String(v);
905
+ }
906
+ function deserializeValue(v) {
907
+ if (v === null) return null;
908
+ if (typeof v === "object" && v !== null && "__blob" in v) {
909
+ const buf = Buffer.from(v.__blob, "base64");
910
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
911
+ }
912
+ return v;
913
+ }
914
+ function deserializeResultSet(srs) {
915
+ const rows = srs.rows.map((obj) => {
916
+ const values = srs.columns.map(
917
+ (col) => deserializeValue(obj[col] ?? null)
918
+ );
919
+ const row = values;
920
+ for (let i = 0; i < srs.columns.length; i++) {
921
+ const col = srs.columns[i];
922
+ if (col !== void 0) {
923
+ row[col] = values[i] ?? null;
924
+ }
925
+ }
926
+ Object.defineProperty(row, "length", {
927
+ value: values.length,
928
+ enumerable: false
929
+ });
930
+ return row;
931
+ });
932
+ return {
933
+ columns: srs.columns,
934
+ columnTypes: srs.columnTypes ?? [],
935
+ rows,
936
+ rowsAffected: srs.rowsAffected,
937
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
938
+ toJSON: () => ({
939
+ columns: srs.columns,
940
+ columnTypes: srs.columnTypes ?? [],
941
+ rows: srs.rows,
942
+ rowsAffected: srs.rowsAffected,
943
+ lastInsertRowid: srs.lastInsertRowid
944
+ })
945
+ };
946
+ }
947
+ var init_daemon_protocol = __esm({
948
+ "src/lib/daemon-protocol.ts"() {
949
+ "use strict";
950
+ }
951
+ });
952
+
953
+ // src/lib/db-daemon-client.ts
954
+ var db_daemon_client_exports = {};
955
+ __export(db_daemon_client_exports, {
956
+ createDaemonDbClient: () => createDaemonDbClient,
957
+ initDaemonDbClient: () => initDaemonDbClient
958
+ });
959
+ function normalizeStatement(stmt) {
960
+ if (typeof stmt === "string") {
961
+ return { sql: stmt, args: [] };
962
+ }
963
+ const sql = stmt.sql;
964
+ let args = [];
965
+ if (Array.isArray(stmt.args)) {
966
+ args = stmt.args.map((v) => serializeValue(v));
967
+ } else if (stmt.args && typeof stmt.args === "object") {
968
+ const named = {};
969
+ for (const [key, val] of Object.entries(stmt.args)) {
970
+ named[key] = serializeValue(val);
971
+ }
972
+ return { sql, args: named };
973
+ }
974
+ return { sql, args };
975
+ }
976
+ function createDaemonDbClient(fallbackClient) {
977
+ let _useDaemon = false;
978
+ const client = {
979
+ async execute(stmt) {
980
+ if (!_useDaemon || !isClientConnected()) {
981
+ return fallbackClient.execute(stmt);
982
+ }
983
+ const { sql, args } = normalizeStatement(stmt);
984
+ const response = await sendDaemonRequest({
985
+ type: "db-execute",
986
+ sql,
987
+ args
988
+ });
989
+ if (response.error) {
990
+ const errMsg = String(response.error);
991
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
992
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
993
+ `);
994
+ return fallbackClient.execute(stmt);
995
+ }
996
+ throw new Error(errMsg);
997
+ }
998
+ if (response.db) {
999
+ return deserializeResultSet(response.db);
1000
+ }
1001
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
1002
+ return fallbackClient.execute(stmt);
1003
+ },
1004
+ async batch(stmts, mode) {
1005
+ if (!_useDaemon || !isClientConnected()) {
1006
+ return fallbackClient.batch(stmts, mode);
1007
+ }
1008
+ const statements = stmts.map(normalizeStatement);
1009
+ const response = await sendDaemonRequest({
1010
+ type: "db-batch",
1011
+ statements,
1012
+ mode: mode ?? "deferred"
1013
+ });
1014
+ if (response.error) {
1015
+ const errMsg = String(response.error);
1016
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1017
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1018
+ `);
1019
+ return fallbackClient.batch(stmts, mode);
1020
+ }
1021
+ throw new Error(errMsg);
1022
+ }
1023
+ const batchResults = response["db-batch"];
1024
+ if (batchResults) {
1025
+ return batchResults.map(deserializeResultSet);
1026
+ }
1027
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
1028
+ return fallbackClient.batch(stmts, mode);
1029
+ },
1030
+ // Transaction support — delegate to fallback (transactions need direct connection)
1031
+ async transaction(mode) {
1032
+ return fallbackClient.transaction(mode);
1033
+ },
1034
+ // executeMultiple — delegate to fallback (used only for schema migrations)
1035
+ async executeMultiple(sql) {
1036
+ return fallbackClient.executeMultiple(sql);
1037
+ },
1038
+ // migrate — delegate to fallback
1039
+ async migrate(stmts) {
1040
+ return fallbackClient.migrate(stmts);
1041
+ },
1042
+ // Sync mode — delegate to fallback
1043
+ sync() {
1044
+ return fallbackClient.sync();
1045
+ },
1046
+ close() {
1047
+ _useDaemon = false;
1048
+ },
1049
+ get closed() {
1050
+ return fallbackClient.closed;
1051
+ },
1052
+ get protocol() {
1053
+ return fallbackClient.protocol;
1054
+ }
1055
+ };
1056
+ return {
1057
+ ...client,
1058
+ /** Enable daemon routing (call after confirming daemon is connected) */
1059
+ _enableDaemon() {
1060
+ _useDaemon = true;
1061
+ },
1062
+ /** Check if daemon routing is active */
1063
+ _isDaemonActive() {
1064
+ return _useDaemon && isClientConnected();
1065
+ }
1066
+ };
1067
+ }
1068
+ async function initDaemonDbClient(fallbackClient) {
1069
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1070
+ const connected = await connectEmbedDaemon();
1071
+ if (!connected) {
1072
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1073
+ return null;
1074
+ }
1075
+ const client = createDaemonDbClient(fallbackClient);
1076
+ client._enableDaemon();
1077
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1078
+ return client;
1079
+ }
1080
+ var init_db_daemon_client = __esm({
1081
+ "src/lib/db-daemon-client.ts"() {
1082
+ "use strict";
1083
+ init_exe_daemon_client();
1084
+ init_daemon_protocol();
1085
+ }
1086
+ });
1087
+
651
1088
  // src/lib/database.ts
652
1089
  var database_exports = {};
653
1090
  __export(database_exports, {
@@ -656,6 +1093,7 @@ __export(database_exports, {
656
1093
  ensureSchema: () => ensureSchema,
657
1094
  getClient: () => getClient,
658
1095
  getRawClient: () => getRawClient,
1096
+ initDaemonClient: () => initDaemonClient,
659
1097
  initDatabase: () => initDatabase,
660
1098
  initTurso: () => initTurso,
661
1099
  isInitialized: () => isInitialized
@@ -683,8 +1121,27 @@ function getClient() {
683
1121
  if (!_resilientClient) {
684
1122
  throw new Error("Database client not initialized. Call initDatabase() first.");
685
1123
  }
1124
+ if (process.env.EXE_IS_DAEMON === "1") {
1125
+ return _resilientClient;
1126
+ }
1127
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
1128
+ return _daemonClient;
1129
+ }
686
1130
  return _resilientClient;
687
1131
  }
1132
+ async function initDaemonClient() {
1133
+ if (process.env.EXE_IS_DAEMON === "1") return;
1134
+ if (!_resilientClient) return;
1135
+ try {
1136
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1137
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
1138
+ } catch (err) {
1139
+ process.stderr.write(
1140
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
1141
+ `
1142
+ );
1143
+ }
1144
+ }
688
1145
  function getRawClient() {
689
1146
  if (!_client) {
690
1147
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1171,6 +1628,12 @@ async function ensureSchema() {
1171
1628
  } catch {
1172
1629
  }
1173
1630
  }
1631
+ try {
1632
+ await client.execute(
1633
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1634
+ );
1635
+ } catch {
1636
+ }
1174
1637
  await client.executeMultiple(`
1175
1638
  CREATE TABLE IF NOT EXISTS entities (
1176
1639
  id TEXT PRIMARY KEY,
@@ -1223,7 +1686,30 @@ async function ensureSchema() {
1223
1686
  entity_id TEXT NOT NULL,
1224
1687
  PRIMARY KEY (hyperedge_id, entity_id)
1225
1688
  );
1689
+
1690
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1691
+ name,
1692
+ content=entities,
1693
+ content_rowid=rowid
1694
+ );
1695
+
1696
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1697
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1698
+ END;
1699
+
1700
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1701
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1702
+ END;
1703
+
1704
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1705
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1706
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1707
+ END;
1226
1708
  `);
1709
+ try {
1710
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1711
+ } catch {
1712
+ }
1227
1713
  await client.executeMultiple(`
1228
1714
  CREATE TABLE IF NOT EXISTS entity_aliases (
1229
1715
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1404,6 +1890,33 @@ async function ensureSchema() {
1404
1890
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1405
1891
  ON conversations(channel_id);
1406
1892
  `);
1893
+ await client.executeMultiple(`
1894
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1895
+ session_uuid TEXT PRIMARY KEY,
1896
+ agent_id TEXT NOT NULL,
1897
+ session_name TEXT,
1898
+ task_id TEXT,
1899
+ project_name TEXT,
1900
+ started_at TEXT NOT NULL
1901
+ );
1902
+
1903
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1904
+ ON session_agent_map(agent_id);
1905
+ `);
1906
+ try {
1907
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1908
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1909
+ await client.execute({
1910
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1911
+ SELECT session_id, agent_id, '', MIN(timestamp)
1912
+ FROM memories
1913
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1914
+ GROUP BY session_id, agent_id`,
1915
+ args: []
1916
+ });
1917
+ }
1918
+ } catch {
1919
+ }
1407
1920
  try {
1408
1921
  await client.execute({
1409
1922
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1537,15 +2050,41 @@ async function ensureSchema() {
1537
2050
  });
1538
2051
  } catch {
1539
2052
  }
2053
+ for (const col of [
2054
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2055
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2056
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2057
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2058
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2059
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2060
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2061
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2062
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2063
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2064
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2065
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2066
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2067
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2068
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2069
+ ]) {
2070
+ try {
2071
+ await client.execute(col);
2072
+ } catch {
2073
+ }
2074
+ }
1540
2075
  }
1541
2076
  async function disposeDatabase() {
2077
+ if (_daemonClient) {
2078
+ _daemonClient.close();
2079
+ _daemonClient = null;
2080
+ }
1542
2081
  if (_client) {
1543
2082
  _client.close();
1544
2083
  _client = null;
1545
2084
  _resilientClient = null;
1546
2085
  }
1547
2086
  }
1548
- var _client, _resilientClient, initTurso, disposeTurso;
2087
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1549
2088
  var init_database = __esm({
1550
2089
  "src/lib/database.ts"() {
1551
2090
  "use strict";
@@ -1553,24 +2092,25 @@ var init_database = __esm({
1553
2092
  init_employees();
1554
2093
  _client = null;
1555
2094
  _resilientClient = null;
2095
+ _daemonClient = null;
1556
2096
  initTurso = initDatabase;
1557
2097
  disposeTurso = disposeDatabase;
1558
2098
  }
1559
2099
  });
1560
2100
 
1561
2101
  // src/lib/license.ts
1562
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
1563
- import { randomUUID as randomUUID2 } from "crypto";
1564
- import path6 from "path";
2102
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
2103
+ import { randomUUID as randomUUID3 } from "crypto";
2104
+ import path7 from "path";
1565
2105
  import { jwtVerify, importSPKI } from "jose";
1566
2106
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
1567
2107
  var init_license = __esm({
1568
2108
  "src/lib/license.ts"() {
1569
2109
  "use strict";
1570
2110
  init_config();
1571
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1572
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1573
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2111
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2112
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2113
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1574
2114
  PLAN_LIMITS = {
1575
2115
  free: { devices: 1, employees: 1, memories: 5e3 },
1576
2116
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -1582,12 +2122,12 @@ var init_license = __esm({
1582
2122
  });
1583
2123
 
1584
2124
  // src/lib/plan-limits.ts
1585
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
1586
- import path7 from "path";
2125
+ import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
2126
+ import path8 from "path";
1587
2127
  function getLicenseSync() {
1588
2128
  try {
1589
- if (!existsSync6(CACHE_PATH2)) return freeLicense();
1590
- const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
2129
+ if (!existsSync7(CACHE_PATH2)) return freeLicense();
2130
+ const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
1591
2131
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
1592
2132
  const parts = raw.token.split(".");
1593
2133
  if (parts.length !== 3) return freeLicense();
@@ -1625,8 +2165,8 @@ function assertEmployeeLimitSync(rosterPath) {
1625
2165
  const filePath = rosterPath ?? EMPLOYEES_PATH;
1626
2166
  let count = 0;
1627
2167
  try {
1628
- if (existsSync6(filePath)) {
1629
- const raw = readFileSync6(filePath, "utf8");
2168
+ if (existsSync7(filePath)) {
2169
+ const raw = readFileSync7(filePath, "utf8");
1630
2170
  const employees = JSON.parse(raw);
1631
2171
  count = Array.isArray(employees) ? employees.length : 0;
1632
2172
  }
@@ -1655,19 +2195,19 @@ var init_plan_limits = __esm({
1655
2195
  this.name = "PlanLimitError";
1656
2196
  }
1657
2197
  };
1658
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2198
+ CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
1659
2199
  }
1660
2200
  });
1661
2201
 
1662
2202
  // src/lib/notifications.ts
1663
2203
  import crypto from "crypto";
1664
- import path8 from "path";
2204
+ import path9 from "path";
1665
2205
  import os6 from "os";
1666
2206
  import {
1667
- readFileSync as readFileSync7,
2207
+ readFileSync as readFileSync8,
1668
2208
  readdirSync,
1669
- unlinkSync as unlinkSync2,
1670
- existsSync as existsSync7,
2209
+ unlinkSync as unlinkSync3,
2210
+ existsSync as existsSync8,
1671
2211
  rmdirSync
1672
2212
  } from "fs";
1673
2213
  async function writeNotification(notification) {
@@ -1831,10 +2371,11 @@ var init_state_bus = __esm({
1831
2371
 
1832
2372
  // src/lib/tasks-crud.ts
1833
2373
  import crypto3 from "crypto";
1834
- import path9 from "path";
2374
+ import path10 from "path";
2375
+ import os7 from "os";
1835
2376
  import { execSync as execSync5 } from "child_process";
1836
2377
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
1837
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
2378
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
1838
2379
  async function writeCheckpoint(input) {
1839
2380
  const client = getClient();
1840
2381
  const row = await resolveTask(client, input.taskId);
@@ -1875,6 +2416,35 @@ function extractParentFromContext(contextBody) {
1875
2416
  function slugify(title) {
1876
2417
  return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1877
2418
  }
2419
+ function buildKeywordIndex() {
2420
+ const idx = /* @__PURE__ */ new Map();
2421
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
2422
+ for (const kw of keywords) {
2423
+ const existing = idx.get(kw) ?? [];
2424
+ existing.push(role);
2425
+ idx.set(kw, existing);
2426
+ }
2427
+ }
2428
+ return idx;
2429
+ }
2430
+ function checkLaneAffinity(title, context, assigneeName) {
2431
+ const employees = loadEmployeesSync();
2432
+ const employee = employees.find((e) => e.name === assigneeName);
2433
+ if (!employee) return void 0;
2434
+ const assigneeRole = employee.role;
2435
+ const text = `${title} ${context}`.toLowerCase();
2436
+ const matchedRoles = /* @__PURE__ */ new Set();
2437
+ for (const [keyword, roles] of KEYWORD_INDEX) {
2438
+ if (text.includes(keyword)) {
2439
+ for (const role of roles) matchedRoles.add(role);
2440
+ }
2441
+ }
2442
+ if (matchedRoles.size === 0) return void 0;
2443
+ if (matchedRoles.has(assigneeRole)) return void 0;
2444
+ if (assigneeRole === "COO") return void 0;
2445
+ const expectedRoles = Array.from(matchedRoles).join(" or ");
2446
+ return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
2447
+ }
1878
2448
  async function resolveTask(client, identifier, scopeSession) {
1879
2449
  const scope = sessionScopeFilter(scopeSession);
1880
2450
  let result = await client.execute({
@@ -1924,7 +2494,14 @@ async function createTaskCore(input) {
1924
2494
  const id = crypto3.randomUUID();
1925
2495
  const now = (/* @__PURE__ */ new Date()).toISOString();
1926
2496
  const slug = slugify(input.title);
1927
- const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
2497
+ let earlySessionScope = null;
2498
+ try {
2499
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
2500
+ earlySessionScope = resolveExeSession2();
2501
+ } catch {
2502
+ }
2503
+ const scope = earlySessionScope ?? "default";
2504
+ const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
1928
2505
  let blockedById = null;
1929
2506
  const initialStatus = input.blockedBy ? "blocked" : "open";
1930
2507
  if (input.blockedBy) {
@@ -1964,22 +2541,24 @@ async function createTaskCore(input) {
1964
2541
  if (dupCheck.rows.length > 0) {
1965
2542
  warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
1966
2543
  }
2544
+ if (!process.env.DISABLE_LANE_AFFINITY) {
2545
+ const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
2546
+ if (laneWarning) {
2547
+ warning = warning ? `${warning}
2548
+ ${laneWarning}` : laneWarning;
2549
+ }
2550
+ }
1967
2551
  if (input.baseDir) {
1968
2552
  try {
1969
- await mkdir3(path9.join(input.baseDir, "exe", "output"), { recursive: true });
1970
- await mkdir3(path9.join(input.baseDir, "exe", "research"), { recursive: true });
2553
+ await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
2554
+ await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
1971
2555
  await ensureArchitectureDoc(input.baseDir, input.projectName);
1972
2556
  await ensureGitignoreExe(input.baseDir);
1973
2557
  } catch {
1974
2558
  }
1975
2559
  }
1976
2560
  const complexity = input.complexity ?? "standard";
1977
- let sessionScope = null;
1978
- try {
1979
- const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
1980
- sessionScope = resolveExeSession2();
1981
- } catch {
1982
- }
2561
+ const sessionScope = earlySessionScope;
1983
2562
  await client.execute({
1984
2563
  sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
1985
2564
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -2006,6 +2585,39 @@ async function createTaskCore(input) {
2006
2585
  now
2007
2586
  ]
2008
2587
  });
2588
+ if (input.baseDir) {
2589
+ try {
2590
+ const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
2591
+ const mdPath = path10.join(EXE_OS_DIR, taskFile);
2592
+ const mdDir = path10.dirname(mdPath);
2593
+ if (!existsSync9(mdDir)) await mkdir3(mdDir, { recursive: true });
2594
+ const reviewer = input.reviewer ?? input.assignedBy;
2595
+ const mdContent = `# ${input.title}
2596
+
2597
+ **ID:** ${id}
2598
+ **Status:** ${initialStatus}
2599
+ **Priority:** ${input.priority}
2600
+ **Assigned by:** ${input.assignedBy}
2601
+ **Assigned to:** ${input.assignedTo}
2602
+ **Project:** ${input.projectName}
2603
+ **Created:** ${now.split("T")[0]}${parentTaskId ? `
2604
+ **Parent task:** ${parentTaskId}` : ""}
2605
+ **Reviewer:** ${reviewer}
2606
+
2607
+ ## Context
2608
+
2609
+ ${input.context}
2610
+
2611
+ ## MANDATORY: When done
2612
+
2613
+ You MUST call update_task with status "done" and a result summary when finished.
2614
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
2615
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
2616
+ `;
2617
+ await writeFile3(mdPath, mdContent, "utf-8");
2618
+ } catch {
2619
+ }
2620
+ }
2009
2621
  return {
2010
2622
  id,
2011
2623
  title: input.title,
@@ -2198,7 +2810,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
2198
2810
  return { row, taskFile, now, taskId };
2199
2811
  }
2200
2812
  }
2201
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
2813
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
2202
2814
  process.stderr.write(
2203
2815
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
2204
2816
  `
@@ -2263,9 +2875,9 @@ async function deleteTaskCore(taskId, _baseDir) {
2263
2875
  return { taskFile, assignedTo, assignedBy, taskSlug };
2264
2876
  }
2265
2877
  async function ensureArchitectureDoc(baseDir, projectName) {
2266
- const archPath = path9.join(baseDir, "exe", "ARCHITECTURE.md");
2878
+ const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
2267
2879
  try {
2268
- if (existsSync8(archPath)) return;
2880
+ if (existsSync9(archPath)) return;
2269
2881
  const template = [
2270
2882
  `# ${projectName} \u2014 System Architecture`,
2271
2883
  "",
@@ -2298,10 +2910,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
2298
2910
  }
2299
2911
  }
2300
2912
  async function ensureGitignoreExe(baseDir) {
2301
- const gitignorePath = path9.join(baseDir, ".gitignore");
2913
+ const gitignorePath = path10.join(baseDir, ".gitignore");
2302
2914
  try {
2303
- if (existsSync8(gitignorePath)) {
2304
- const content = readFileSync8(gitignorePath, "utf-8");
2915
+ if (existsSync9(gitignorePath)) {
2916
+ const content = readFileSync9(gitignorePath, "utf-8");
2305
2917
  if (/^\/?exe\/?$/m.test(content)) return;
2306
2918
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
2307
2919
  } else {
@@ -2310,20 +2922,30 @@ async function ensureGitignoreExe(baseDir) {
2310
2922
  } catch {
2311
2923
  }
2312
2924
  }
2313
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
2925
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
2314
2926
  var init_tasks_crud = __esm({
2315
2927
  "src/lib/tasks-crud.ts"() {
2316
2928
  "use strict";
2317
2929
  init_database();
2318
2930
  init_task_scope();
2931
+ init_employees();
2932
+ LANE_KEYWORDS = {
2933
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
2934
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
2935
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
2936
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
2937
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
2938
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
2939
+ };
2940
+ KEYWORD_INDEX = buildKeywordIndex();
2319
2941
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
2320
2942
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
2321
2943
  }
2322
2944
  });
2323
2945
 
2324
2946
  // src/lib/tasks-review.ts
2325
- import path10 from "path";
2326
- import { existsSync as existsSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
2947
+ import path11 from "path";
2948
+ import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
2327
2949
  async function countPendingReviews(sessionScope) {
2328
2950
  const client = getClient();
2329
2951
  if (sessionScope) {
@@ -2345,7 +2967,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
2345
2967
  const result2 = await client.execute({
2346
2968
  sql: `SELECT COUNT(*) as cnt FROM tasks
2347
2969
  WHERE status = 'needs_review' AND updated_at > ?
2348
- AND (session_scope = ? OR session_scope IS NULL)`,
2970
+ AND session_scope = ?`,
2349
2971
  args: [sinceIso, sessionScope]
2350
2972
  });
2351
2973
  return Number(result2.rows[0]?.cnt) || 0;
@@ -2363,7 +2985,7 @@ async function listPendingReviews(limit, sessionScope) {
2363
2985
  const result2 = await client.execute({
2364
2986
  sql: `SELECT title, assigned_to, project_name FROM tasks
2365
2987
  WHERE status = 'needs_review'
2366
- AND (session_scope = ? OR session_scope IS NULL)
2988
+ AND session_scope = ?
2367
2989
  ORDER BY priority ASC, created_at DESC LIMIT ?`,
2368
2990
  args: [sessionScope, limit]
2369
2991
  });
@@ -2484,14 +3106,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
2484
3106
  if (parts.length >= 3 && parts[0] === "review") {
2485
3107
  const agent = parts[1];
2486
3108
  const slug = parts.slice(2).join("-");
2487
- const originalTaskFile = `exe/${agent}/${slug}.md`;
3109
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
2488
3110
  const result = await client.execute({
2489
- sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
2490
- args: [now, originalTaskFile]
3111
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
3112
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
2491
3113
  });
2492
3114
  if (result.rowsAffected > 0) {
2493
3115
  process.stderr.write(
2494
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
3116
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
2495
3117
  `
2496
3118
  );
2497
3119
  }
@@ -2504,11 +3126,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
2504
3126
  );
2505
3127
  }
2506
3128
  try {
2507
- const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
2508
- if (existsSync9(cacheDir)) {
3129
+ const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
3130
+ if (existsSync10(cacheDir)) {
2509
3131
  for (const f of readdirSync2(cacheDir)) {
2510
3132
  if (f.startsWith("review-notified-")) {
2511
- unlinkSync3(path10.join(cacheDir, f));
3133
+ unlinkSync4(path11.join(cacheDir, f));
2512
3134
  }
2513
3135
  }
2514
3136
  }
@@ -2529,7 +3151,7 @@ var init_tasks_review = __esm({
2529
3151
  });
2530
3152
 
2531
3153
  // src/lib/tasks-chain.ts
2532
- import path11 from "path";
3154
+ import path12 from "path";
2533
3155
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
2534
3156
  async function cascadeUnblock(taskId, baseDir, now) {
2535
3157
  const client = getClient();
@@ -2546,7 +3168,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
2546
3168
  });
2547
3169
  for (const ur of unblockedRows.rows) {
2548
3170
  try {
2549
- const ubFile = path11.join(baseDir, String(ur.task_file));
3171
+ const ubFile = path12.join(baseDir, String(ur.task_file));
2550
3172
  let ubContent = await readFile3(ubFile, "utf-8");
2551
3173
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
2552
3174
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -2615,7 +3237,7 @@ var init_tasks_chain = __esm({
2615
3237
 
2616
3238
  // src/lib/project-name.ts
2617
3239
  import { execSync as execSync6 } from "child_process";
2618
- import path12 from "path";
3240
+ import path13 from "path";
2619
3241
  function getProjectName(cwd) {
2620
3242
  const dir = cwd ?? process.cwd();
2621
3243
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -2628,7 +3250,7 @@ function getProjectName(cwd) {
2628
3250
  timeout: 2e3,
2629
3251
  stdio: ["pipe", "pipe", "pipe"]
2630
3252
  }).trim();
2631
- repoRoot = path12.dirname(gitCommonDir);
3253
+ repoRoot = path13.dirname(gitCommonDir);
2632
3254
  } catch {
2633
3255
  repoRoot = execSync6("git rev-parse --show-toplevel", {
2634
3256
  cwd: dir,
@@ -2637,11 +3259,11 @@ function getProjectName(cwd) {
2637
3259
  stdio: ["pipe", "pipe", "pipe"]
2638
3260
  }).trim();
2639
3261
  }
2640
- _cached2 = path12.basename(repoRoot);
3262
+ _cached2 = path13.basename(repoRoot);
2641
3263
  _cachedCwd = dir;
2642
3264
  return _cached2;
2643
3265
  } catch {
2644
- _cached2 = path12.basename(dir);
3266
+ _cached2 = path13.basename(dir);
2645
3267
  _cachedCwd = dir;
2646
3268
  return _cached2;
2647
3269
  }
@@ -2673,7 +3295,7 @@ function findSessionForProject(projectName) {
2673
3295
  const sessions = listSessions();
2674
3296
  for (const s of sessions) {
2675
3297
  const proj = s.projectDir.split("/").filter(Boolean).pop();
2676
- if (proj === projectName && (s.agentId === "exe" || isCoordinatorName(s.agentId))) return s;
3298
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
2677
3299
  }
2678
3300
  return null;
2679
3301
  }
@@ -2719,7 +3341,7 @@ var init_session_scope = __esm({
2719
3341
 
2720
3342
  // src/lib/tasks-notify.ts
2721
3343
  async function dispatchTaskToEmployee(input) {
2722
- if (input.assignedTo === "exe" || isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
3344
+ if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
2723
3345
  let crossProject = false;
2724
3346
  if (input.projectName) {
2725
3347
  try {
@@ -3176,8 +3798,8 @@ __export(tasks_exports, {
3176
3798
  updateTaskStatus: () => updateTaskStatus,
3177
3799
  writeCheckpoint: () => writeCheckpoint
3178
3800
  });
3179
- import path13 from "path";
3180
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
3801
+ import path14 from "path";
3802
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync5 } from "fs";
3181
3803
  async function createTask(input) {
3182
3804
  const result = await createTaskCore(input);
3183
3805
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -3196,14 +3818,14 @@ async function updateTask(input) {
3196
3818
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
3197
3819
  try {
3198
3820
  const agent = String(row.assigned_to);
3199
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
3200
- const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
3821
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
3822
+ const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
3201
3823
  if (input.status === "in_progress") {
3202
3824
  mkdirSync4(cacheDir, { recursive: true });
3203
3825
  writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
3204
3826
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
3205
3827
  try {
3206
- unlinkSync4(cachePath);
3828
+ unlinkSync5(cachePath);
3207
3829
  } catch {
3208
3830
  }
3209
3831
  }
@@ -3260,7 +3882,7 @@ async function updateTask(input) {
3260
3882
  }
3261
3883
  const isTerminal = input.status === "done" || input.status === "needs_review";
3262
3884
  if (isTerminal) {
3263
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
3885
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
3264
3886
  if (!isCoordinator) {
3265
3887
  notifyTaskDone();
3266
3888
  }
@@ -3285,7 +3907,7 @@ async function updateTask(input) {
3285
3907
  }
3286
3908
  }
3287
3909
  }
3288
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
3910
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
3289
3911
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
3290
3912
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
3291
3913
  taskId,
@@ -3301,7 +3923,7 @@ async function updateTask(input) {
3301
3923
  });
3302
3924
  }
3303
3925
  let nextTask;
3304
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
3926
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
3305
3927
  try {
3306
3928
  nextTask = await findNextTask(String(row.assigned_to));
3307
3929
  } catch {
@@ -3645,7 +4267,7 @@ var init_capacity_monitor = __esm({
3645
4267
  // src/lib/tmux-routing.ts
3646
4268
  var tmux_routing_exports = {};
3647
4269
  __export(tmux_routing_exports, {
3648
- acquireSpawnLock: () => acquireSpawnLock,
4270
+ acquireSpawnLock: () => acquireSpawnLock2,
3649
4271
  employeeSessionName: () => employeeSessionName,
3650
4272
  ensureEmployee: () => ensureEmployee,
3651
4273
  extractRootExe: () => extractRootExe,
@@ -3660,20 +4282,20 @@ __export(tmux_routing_exports, {
3660
4282
  notifyParentExe: () => notifyParentExe,
3661
4283
  parseParentExe: () => parseParentExe,
3662
4284
  registerParentExe: () => registerParentExe,
3663
- releaseSpawnLock: () => releaseSpawnLock,
4285
+ releaseSpawnLock: () => releaseSpawnLock2,
3664
4286
  resolveExeSession: () => resolveExeSession,
3665
4287
  sendIntercom: () => sendIntercom,
3666
4288
  spawnEmployee: () => spawnEmployee,
3667
4289
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
3668
4290
  });
3669
4291
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
3670
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync } from "fs";
3671
- import path14 from "path";
3672
- import os7 from "os";
3673
- import { fileURLToPath } from "url";
3674
- import { unlinkSync as unlinkSync5 } from "fs";
4292
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync11, appendFileSync } from "fs";
4293
+ import path15 from "path";
4294
+ import os8 from "os";
4295
+ import { fileURLToPath as fileURLToPath2 } from "url";
4296
+ import { unlinkSync as unlinkSync6 } from "fs";
3675
4297
  function spawnLockPath(sessionName) {
3676
- return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4298
+ return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3677
4299
  }
3678
4300
  function isProcessAlive(pid) {
3679
4301
  try {
@@ -3683,14 +4305,14 @@ function isProcessAlive(pid) {
3683
4305
  return false;
3684
4306
  }
3685
4307
  }
3686
- function acquireSpawnLock(sessionName) {
3687
- if (!existsSync10(SPAWN_LOCK_DIR)) {
4308
+ function acquireSpawnLock2(sessionName) {
4309
+ if (!existsSync11(SPAWN_LOCK_DIR)) {
3688
4310
  mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
3689
4311
  }
3690
4312
  const lockFile = spawnLockPath(sessionName);
3691
- if (existsSync10(lockFile)) {
4313
+ if (existsSync11(lockFile)) {
3692
4314
  try {
3693
- const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
4315
+ const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
3694
4316
  const age = Date.now() - lock.timestamp;
3695
4317
  if (isProcessAlive(lock.pid) && age < 6e4) {
3696
4318
  return false;
@@ -3701,22 +4323,22 @@ function acquireSpawnLock(sessionName) {
3701
4323
  writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3702
4324
  return true;
3703
4325
  }
3704
- function releaseSpawnLock(sessionName) {
4326
+ function releaseSpawnLock2(sessionName) {
3705
4327
  try {
3706
- unlinkSync5(spawnLockPath(sessionName));
4328
+ unlinkSync6(spawnLockPath(sessionName));
3707
4329
  } catch {
3708
4330
  }
3709
4331
  }
3710
4332
  function resolveBehaviorsExporterScript() {
3711
4333
  try {
3712
- const thisFile = fileURLToPath(import.meta.url);
3713
- const scriptPath = path14.join(
3714
- path14.dirname(thisFile),
4334
+ const thisFile = fileURLToPath2(import.meta.url);
4335
+ const scriptPath = path15.join(
4336
+ path15.dirname(thisFile),
3715
4337
  "..",
3716
4338
  "bin",
3717
4339
  "exe-export-behaviors.js"
3718
4340
  );
3719
- return existsSync10(scriptPath) ? scriptPath : null;
4341
+ return existsSync11(scriptPath) ? scriptPath : null;
3720
4342
  } catch {
3721
4343
  return null;
3722
4344
  }
@@ -3782,11 +4404,11 @@ function extractRootExe(name) {
3782
4404
  return parts.length > 0 ? parts[parts.length - 1] : null;
3783
4405
  }
3784
4406
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3785
- if (!existsSync10(SESSION_CACHE)) {
4407
+ if (!existsSync11(SESSION_CACHE)) {
3786
4408
  mkdirSync5(SESSION_CACHE, { recursive: true });
3787
4409
  }
3788
4410
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3789
- const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4411
+ const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3790
4412
  writeFileSync6(filePath, JSON.stringify({
3791
4413
  parentExe: rootExe,
3792
4414
  dispatchedBy: dispatchedBy || rootExe,
@@ -3795,7 +4417,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3795
4417
  }
3796
4418
  function getParentExe(sessionKey) {
3797
4419
  try {
3798
- const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4420
+ const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3799
4421
  return data.parentExe || null;
3800
4422
  } catch {
3801
4423
  return null;
@@ -3803,8 +4425,8 @@ function getParentExe(sessionKey) {
3803
4425
  }
3804
4426
  function getDispatchedBy(sessionKey) {
3805
4427
  try {
3806
- const data = JSON.parse(readFileSync9(
3807
- path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4428
+ const data = JSON.parse(readFileSync10(
4429
+ path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3808
4430
  "utf8"
3809
4431
  ));
3810
4432
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -3830,10 +4452,10 @@ function isEmployeeAlive(sessionName) {
3830
4452
  }
3831
4453
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
3832
4454
  const base = employeeSessionName(employeeName, exeSession);
3833
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
4455
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
3834
4456
  for (let i = 2; i <= maxInstances; i++) {
3835
4457
  const candidate = employeeSessionName(employeeName, exeSession, i);
3836
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
4458
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
3837
4459
  }
3838
4460
  return null;
3839
4461
  }
@@ -3865,15 +4487,15 @@ async function verifyPaneAtCapacity(sessionName) {
3865
4487
  }
3866
4488
  function readDebounceState() {
3867
4489
  try {
3868
- if (!existsSync10(DEBOUNCE_FILE)) return {};
3869
- return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
4490
+ if (!existsSync11(DEBOUNCE_FILE)) return {};
4491
+ return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
3870
4492
  } catch {
3871
4493
  return {};
3872
4494
  }
3873
4495
  }
3874
4496
  function writeDebounceState(state) {
3875
4497
  try {
3876
- if (!existsSync10(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
4498
+ if (!existsSync11(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
3877
4499
  writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
3878
4500
  } catch {
3879
4501
  }
@@ -3993,7 +4615,7 @@ function notifyParentExe(sessionKey) {
3993
4615
  return true;
3994
4616
  }
3995
4617
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
3996
- if (employeeName === "exe" || isCoordinatorName(employeeName)) {
4618
+ if (isCoordinatorName(employeeName)) {
3997
4619
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
3998
4620
  }
3999
4621
  try {
@@ -4065,26 +4687,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4065
4687
  const transport = getTransport();
4066
4688
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4067
4689
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4068
- const logDir = path14.join(os7.homedir(), ".exe-os", "session-logs");
4069
- const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4070
- if (!existsSync10(logDir)) {
4690
+ const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
4691
+ const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4692
+ if (!existsSync11(logDir)) {
4071
4693
  mkdirSync5(logDir, { recursive: true });
4072
4694
  }
4073
4695
  transport.kill(sessionName);
4074
4696
  let cleanupSuffix = "";
4075
4697
  try {
4076
- const thisFile = fileURLToPath(import.meta.url);
4077
- const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4078
- if (existsSync10(cleanupScript)) {
4698
+ const thisFile = fileURLToPath2(import.meta.url);
4699
+ const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4700
+ if (existsSync11(cleanupScript)) {
4079
4701
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4080
4702
  }
4081
4703
  } catch {
4082
4704
  }
4083
4705
  try {
4084
- const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
4706
+ const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
4085
4707
  let claudeJson = {};
4086
4708
  try {
4087
- claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
4709
+ claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
4088
4710
  } catch {
4089
4711
  }
4090
4712
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4096,13 +4718,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4096
4718
  } catch {
4097
4719
  }
4098
4720
  try {
4099
- const settingsDir = path14.join(os7.homedir(), ".claude", "projects");
4721
+ const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
4100
4722
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4101
- const projSettingsDir = path14.join(settingsDir, normalizedKey);
4102
- const settingsPath = path14.join(projSettingsDir, "settings.json");
4723
+ const projSettingsDir = path15.join(settingsDir, normalizedKey);
4724
+ const settingsPath = path15.join(projSettingsDir, "settings.json");
4103
4725
  let settings = {};
4104
4726
  try {
4105
- settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
4727
+ settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
4106
4728
  } catch {
4107
4729
  }
4108
4730
  const perms = settings.permissions ?? {};
@@ -4143,8 +4765,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4143
4765
  let behaviorsFlag = "";
4144
4766
  let legacyFallbackWarned = false;
4145
4767
  if (!useExeAgent && !useBinSymlink) {
4146
- const identityPath = path14.join(
4147
- os7.homedir(),
4768
+ const identityPath = path15.join(
4769
+ os8.homedir(),
4148
4770
  ".exe-os",
4149
4771
  "identity",
4150
4772
  `${employeeName}.md`
@@ -4153,13 +4775,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4153
4775
  const hasAgentFlag = claudeSupportsAgentFlag();
4154
4776
  if (hasAgentFlag) {
4155
4777
  identityFlag = ` --agent ${employeeName}`;
4156
- } else if (existsSync10(identityPath)) {
4778
+ } else if (existsSync11(identityPath)) {
4157
4779
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
4158
4780
  legacyFallbackWarned = true;
4159
4781
  }
4160
4782
  const behaviorsFile = exportBehaviorsSync(
4161
4783
  employeeName,
4162
- path14.basename(spawnCwd),
4784
+ path15.basename(spawnCwd),
4163
4785
  sessionName
4164
4786
  );
4165
4787
  if (behaviorsFile) {
@@ -4174,9 +4796,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4174
4796
  }
4175
4797
  let sessionContextFlag = "";
4176
4798
  try {
4177
- const ctxDir = path14.join(os7.homedir(), ".exe-os", "session-cache");
4799
+ const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
4178
4800
  mkdirSync5(ctxDir, { recursive: true });
4179
- const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
4801
+ const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
4180
4802
  const ctxContent = [
4181
4803
  `## Session Context`,
4182
4804
  `You are running in tmux session: ${sessionName}.`,
@@ -4215,13 +4837,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4215
4837
  command: spawnCommand
4216
4838
  });
4217
4839
  if (spawnResult.error) {
4218
- releaseSpawnLock(sessionName);
4840
+ releaseSpawnLock2(sessionName);
4219
4841
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
4220
4842
  }
4221
4843
  transport.pipeLog(sessionName, logFile);
4222
4844
  try {
4223
4845
  const mySession = getMySession();
4224
- const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4846
+ const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4225
4847
  writeFileSync6(dispatchInfo, JSON.stringify({
4226
4848
  dispatchedBy: mySession,
4227
4849
  rootExe: exeSession,
@@ -4253,7 +4875,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4253
4875
  }
4254
4876
  }
4255
4877
  if (!booted) {
4256
- releaseSpawnLock(sessionName);
4878
+ releaseSpawnLock2(sessionName);
4257
4879
  return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
4258
4880
  }
4259
4881
  if (!useExeAgent) {
@@ -4270,7 +4892,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4270
4892
  pid: 0,
4271
4893
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
4272
4894
  });
4273
- releaseSpawnLock(sessionName);
4895
+ releaseSpawnLock2(sessionName);
4274
4896
  return { sessionName };
4275
4897
  }
4276
4898
  var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
@@ -4286,14 +4908,14 @@ var init_tmux_routing = __esm({
4286
4908
  init_intercom_queue();
4287
4909
  init_plan_limits();
4288
4910
  init_employees();
4289
- SPAWN_LOCK_DIR = path14.join(os7.homedir(), ".exe-os", "spawn-locks");
4290
- SESSION_CACHE = path14.join(os7.homedir(), ".exe-os", "session-cache");
4911
+ SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
4912
+ SESSION_CACHE = path15.join(os8.homedir(), ".exe-os", "session-cache");
4291
4913
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4292
4914
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4293
4915
  VERIFY_PANE_LINES = 200;
4294
4916
  INTERCOM_DEBOUNCE_MS = 3e4;
4295
- INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
4296
- DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4917
+ INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
4918
+ DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
4297
4919
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4298
4920
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
4299
4921
  }
@@ -4310,14 +4932,14 @@ var init_memory = __esm({
4310
4932
 
4311
4933
  // src/lib/keychain.ts
4312
4934
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
4313
- import { existsSync as existsSync11 } from "fs";
4314
- import path15 from "path";
4315
- import os8 from "os";
4935
+ import { existsSync as existsSync12 } from "fs";
4936
+ import path16 from "path";
4937
+ import os9 from "os";
4316
4938
  function getKeyDir() {
4317
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os8.homedir(), ".exe-os");
4939
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os9.homedir(), ".exe-os");
4318
4940
  }
4319
4941
  function getKeyPath() {
4320
- return path15.join(getKeyDir(), "master.key");
4942
+ return path16.join(getKeyDir(), "master.key");
4321
4943
  }
4322
4944
  async function tryKeytar() {
4323
4945
  try {
@@ -4338,13 +4960,21 @@ async function getMasterKey() {
4338
4960
  }
4339
4961
  }
4340
4962
  const keyPath = getKeyPath();
4341
- if (!existsSync11(keyPath)) {
4963
+ if (!existsSync12(keyPath)) {
4964
+ process.stderr.write(
4965
+ `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
4966
+ `
4967
+ );
4342
4968
  return null;
4343
4969
  }
4344
4970
  try {
4345
4971
  const content = await readFile4(keyPath, "utf-8");
4346
4972
  return Buffer.from(content.trim(), "base64");
4347
- } catch {
4973
+ } catch (err) {
4974
+ process.stderr.write(
4975
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
4976
+ `
4977
+ );
4348
4978
  return null;
4349
4979
  }
4350
4980
  }
@@ -4370,12 +5000,12 @@ __export(shard_manager_exports, {
4370
5000
  listShards: () => listShards,
4371
5001
  shardExists: () => shardExists
4372
5002
  });
4373
- import path16 from "path";
4374
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
5003
+ import path17 from "path";
5004
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
4375
5005
  import { createClient as createClient2 } from "@libsql/client";
4376
5006
  function initShardManager(encryptionKey) {
4377
5007
  _encryptionKey = encryptionKey;
4378
- if (!existsSync12(SHARDS_DIR)) {
5008
+ if (!existsSync13(SHARDS_DIR)) {
4379
5009
  mkdirSync6(SHARDS_DIR, { recursive: true });
4380
5010
  }
4381
5011
  _shardingEnabled = true;
@@ -4396,7 +5026,7 @@ function getShardClient(projectName) {
4396
5026
  }
4397
5027
  const cached = _shards.get(safeName);
4398
5028
  if (cached) return cached;
4399
- const dbPath = path16.join(SHARDS_DIR, `${safeName}.db`);
5029
+ const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
4400
5030
  const client = createClient2({
4401
5031
  url: `file:${dbPath}`,
4402
5032
  encryptionKey: _encryptionKey
@@ -4406,10 +5036,10 @@ function getShardClient(projectName) {
4406
5036
  }
4407
5037
  function shardExists(projectName) {
4408
5038
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4409
- return existsSync12(path16.join(SHARDS_DIR, `${safeName}.db`));
5039
+ return existsSync13(path17.join(SHARDS_DIR, `${safeName}.db`));
4410
5040
  }
4411
5041
  function listShards() {
4412
- if (!existsSync12(SHARDS_DIR)) return [];
5042
+ if (!existsSync13(SHARDS_DIR)) return [];
4413
5043
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4414
5044
  }
4415
5045
  async function ensureShardSchema(client) {
@@ -4595,7 +5225,7 @@ var init_shard_manager = __esm({
4595
5225
  "src/lib/shard-manager.ts"() {
4596
5226
  "use strict";
4597
5227
  init_config();
4598
- SHARDS_DIR = path16.join(EXE_AI_DIR, "shards");
5228
+ SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
4599
5229
  _shards = /* @__PURE__ */ new Map();
4600
5230
  _encryptionKey = null;
4601
5231
  _shardingEnabled = false;
@@ -4720,7 +5350,7 @@ __export(global_procedures_exports, {
4720
5350
  loadGlobalProcedures: () => loadGlobalProcedures,
4721
5351
  storeGlobalProcedure: () => storeGlobalProcedure
4722
5352
  });
4723
- import { randomUUID as randomUUID3 } from "crypto";
5353
+ import { randomUUID as randomUUID4 } from "crypto";
4724
5354
  async function loadGlobalProcedures() {
4725
5355
  const client = getClient();
4726
5356
  const result = await client.execute({
@@ -4749,7 +5379,7 @@ ${sections.join("\n\n")}
4749
5379
  `;
4750
5380
  }
4751
5381
  async function storeGlobalProcedure(input) {
4752
- const id = randomUUID3();
5382
+ const id = randomUUID4();
4753
5383
  const now = (/* @__PURE__ */ new Date()).toISOString();
4754
5384
  const client = getClient();
4755
5385
  await client.execute({
@@ -4800,6 +5430,7 @@ __export(store_exports, {
4800
5430
  vectorToBlob: () => vectorToBlob,
4801
5431
  writeMemory: () => writeMemory
4802
5432
  });
5433
+ import { createHash } from "crypto";
4803
5434
  function isBusyError2(err) {
4804
5435
  if (err instanceof Error) {
4805
5436
  const msg = err.message.toLowerCase();
@@ -4873,12 +5504,52 @@ function classifyTier(record) {
4873
5504
  if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
4874
5505
  return 3;
4875
5506
  }
5507
+ function inferFilePaths(record) {
5508
+ if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
5509
+ const firstLine = record.raw_text.split("\n")[0] ?? "";
5510
+ const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
5511
+ return match ? JSON.stringify([match[1]]) : null;
5512
+ }
5513
+ function inferCommitHash(record) {
5514
+ if (record.tool_name !== "Bash") return null;
5515
+ const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
5516
+ return match ? match[1] : null;
5517
+ }
5518
+ function inferLanguageType(record) {
5519
+ const text = record.raw_text;
5520
+ if (!text || text.length < 10) return null;
5521
+ const trimmed = text.trimStart();
5522
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
5523
+ if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
5524
+ if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
5525
+ if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
5526
+ return "mixed";
5527
+ }
5528
+ function inferDomain(record) {
5529
+ const proj = (record.project_name ?? "").toLowerCase();
5530
+ if (proj.includes("marketing") || proj.includes("content")) return "marketing";
5531
+ if (proj.includes("crm") || proj.includes("customer")) return "customer";
5532
+ return null;
5533
+ }
4876
5534
  async function writeMemory(record) {
4877
5535
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
4878
5536
  throw new Error(
4879
5537
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
4880
5538
  );
4881
5539
  }
5540
+ const contentHash = createHash("md5").update(record.raw_text).digest("hex");
5541
+ if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
5542
+ return;
5543
+ }
5544
+ try {
5545
+ const client = getClient();
5546
+ const existing = await client.execute({
5547
+ sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
5548
+ args: [contentHash, record.agent_id]
5549
+ });
5550
+ if (existing.rows.length > 0) return;
5551
+ } catch {
5552
+ }
4882
5553
  const dbRow = {
4883
5554
  id: record.id,
4884
5555
  agent_id: record.agent_id,
@@ -4908,7 +5579,23 @@ async function writeMemory(record) {
4908
5579
  supersedes_id: record.supersedes_id ?? null,
4909
5580
  draft: record.draft ? 1 : 0,
4910
5581
  memory_type: record.memory_type ?? "raw",
4911
- trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
5582
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
5583
+ content_hash: contentHash,
5584
+ intent: record.intent ?? null,
5585
+ outcome: record.outcome ?? null,
5586
+ domain: record.domain ?? inferDomain(record),
5587
+ referenced_entities: record.referenced_entities ?? null,
5588
+ retrieval_count: record.retrieval_count ?? 0,
5589
+ chain_position: record.chain_position ?? null,
5590
+ review_status: record.review_status ?? null,
5591
+ context_window_pct: record.context_window_pct ?? null,
5592
+ file_paths: record.file_paths ?? inferFilePaths(record),
5593
+ commit_hash: record.commit_hash ?? inferCommitHash(record),
5594
+ duration_ms: record.duration_ms ?? null,
5595
+ token_cost: record.token_cost ?? null,
5596
+ audience: record.audience ?? null,
5597
+ language_type: record.language_type ?? inferLanguageType(record),
5598
+ parent_memory_id: record.parent_memory_id ?? null
4912
5599
  };
4913
5600
  _pendingRecords.push(dbRow);
4914
5601
  orgBus.emit({
@@ -4966,80 +5653,85 @@ async function flushBatch() {
4966
5653
  const draft = row.draft ? 1 : 0;
4967
5654
  const memoryType = row.memory_type ?? "raw";
4968
5655
  const trajectory = row.trajectory ?? null;
4969
- return {
4970
- sql: hasVector ? `INSERT OR IGNORE INTO memories
4971
- (id, agent_id, agent_role, session_id, timestamp,
4972
- tool_name, project_name,
4973
- has_error, raw_text, vector, version, task_id, importance, status,
4974
- confidence, last_accessed,
4975
- workspace_id, document_id, user_id, char_offset, page_number,
4976
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
4977
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
4978
- (id, agent_id, agent_role, session_id, timestamp,
5656
+ const contentHash = row.content_hash ?? null;
5657
+ const intent = row.intent ?? null;
5658
+ const outcome = row.outcome ?? null;
5659
+ const domain = row.domain ?? null;
5660
+ const referencedEntities = row.referenced_entities ?? null;
5661
+ const retrievalCount = row.retrieval_count ?? 0;
5662
+ const chainPosition = row.chain_position ?? null;
5663
+ const reviewStatus = row.review_status ?? null;
5664
+ const contextWindowPct = row.context_window_pct ?? null;
5665
+ const filePaths = row.file_paths ?? null;
5666
+ const commitHash = row.commit_hash ?? null;
5667
+ const durationMs = row.duration_ms ?? null;
5668
+ const tokenCost = row.token_cost ?? null;
5669
+ const audience = row.audience ?? null;
5670
+ const languageType = row.language_type ?? null;
5671
+ const parentMemoryId = row.parent_memory_id ?? null;
5672
+ const cols = `id, agent_id, agent_role, session_id, timestamp,
4979
5673
  tool_name, project_name,
4980
5674
  has_error, raw_text, vector, version, task_id, importance, status,
4981
5675
  confidence, last_accessed,
4982
5676
  workspace_id, document_id, user_id, char_offset, page_number,
4983
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
4984
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4985
- args: hasVector ? [
4986
- row.id,
4987
- row.agent_id,
4988
- row.agent_role,
4989
- row.session_id,
4990
- row.timestamp,
4991
- row.tool_name,
4992
- row.project_name,
4993
- row.has_error,
4994
- row.raw_text,
4995
- vectorToBlob(row.vector),
4996
- row.version,
4997
- taskId,
4998
- importance,
4999
- status,
5000
- confidence,
5001
- lastAccessed,
5002
- workspaceId,
5003
- documentId,
5004
- userId,
5005
- charOffset,
5006
- pageNumber,
5007
- sourcePath,
5008
- sourceType,
5009
- tier,
5010
- supersedesId,
5011
- draft,
5012
- memoryType,
5013
- trajectory
5014
- ] : [
5015
- row.id,
5016
- row.agent_id,
5017
- row.agent_role,
5018
- row.session_id,
5019
- row.timestamp,
5020
- row.tool_name,
5021
- row.project_name,
5022
- row.has_error,
5023
- row.raw_text,
5024
- row.version,
5025
- taskId,
5026
- importance,
5027
- status,
5028
- confidence,
5029
- lastAccessed,
5030
- workspaceId,
5031
- documentId,
5032
- userId,
5033
- charOffset,
5034
- pageNumber,
5035
- sourcePath,
5036
- sourceType,
5037
- tier,
5038
- supersedesId,
5039
- draft,
5040
- memoryType,
5041
- trajectory
5042
- ]
5677
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5678
+ intent, outcome, domain, referenced_entities, retrieval_count,
5679
+ chain_position, review_status, context_window_pct, file_paths, commit_hash,
5680
+ duration_ms, token_cost, audience, language_type, parent_memory_id`;
5681
+ const metaArgs = [
5682
+ intent,
5683
+ outcome,
5684
+ domain,
5685
+ referencedEntities,
5686
+ retrievalCount,
5687
+ chainPosition,
5688
+ reviewStatus,
5689
+ contextWindowPct,
5690
+ filePaths,
5691
+ commitHash,
5692
+ durationMs,
5693
+ tokenCost,
5694
+ audience,
5695
+ languageType,
5696
+ parentMemoryId
5697
+ ];
5698
+ const baseArgs = [
5699
+ row.id,
5700
+ row.agent_id,
5701
+ row.agent_role,
5702
+ row.session_id,
5703
+ row.timestamp,
5704
+ row.tool_name,
5705
+ row.project_name,
5706
+ row.has_error,
5707
+ row.raw_text
5708
+ ];
5709
+ const sharedArgs = [
5710
+ row.version,
5711
+ taskId,
5712
+ importance,
5713
+ status,
5714
+ confidence,
5715
+ lastAccessed,
5716
+ workspaceId,
5717
+ documentId,
5718
+ userId,
5719
+ charOffset,
5720
+ pageNumber,
5721
+ sourcePath,
5722
+ sourceType,
5723
+ tier,
5724
+ supersedesId,
5725
+ draft,
5726
+ memoryType,
5727
+ trajectory,
5728
+ contentHash
5729
+ ];
5730
+ return {
5731
+ sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5732
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5733
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5734
+ args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5043
5735
  };
5044
5736
  };
5045
5737
  const globalClient = getClient();
@@ -5322,8 +6014,8 @@ function findContainingChunk(filePath, snippet) {
5322
6014
  try {
5323
6015
  const ext = filePath.split(".").pop()?.toLowerCase();
5324
6016
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
5325
- const { readFileSync: readFileSync10 } = __require("fs");
5326
- const source = readFileSync10(filePath, "utf8");
6017
+ const { readFileSync: readFileSync11 } = __require("fs");
6018
+ const source = readFileSync11(filePath, "utf8");
5327
6019
  const lines = source.split("\n");
5328
6020
  const lowerSnippet = snippet.toLowerCase().slice(0, 80);
5329
6021
  let matchLine = -1;
@@ -5389,9 +6081,9 @@ function extractBash(input, response) {
5389
6081
  }
5390
6082
  function extractGrep(input, response) {
5391
6083
  const pattern = String(input.pattern ?? "");
5392
- const path17 = input.path ? String(input.path) : "";
6084
+ const path18 = input.path ? String(input.path) : "";
5393
6085
  const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
5394
- return `Searched for "${pattern}"${path17 ? ` in ${path17}` : ""}
6086
+ return `Searched for "${pattern}"${path18 ? ` in ${path18}` : ""}
5395
6087
  ${output.slice(0, MAX_OUTPUT)}`;
5396
6088
  }
5397
6089
  function extractGlob(input, response) {
@@ -7276,11 +7968,11 @@ function createQuietRenderer() {
7276
7968
  init_state_bus();
7277
7969
 
7278
7970
  // src/runtime/session-manager.ts
7279
- import { randomUUID as randomUUID5 } from "crypto";
7971
+ import { randomUUID as randomUUID6 } from "crypto";
7280
7972
  init_state_bus();
7281
7973
 
7282
7974
  // src/runtime/exe-hooks.ts
7283
- import { randomUUID as randomUUID4 } from "crypto";
7975
+ import { randomUUID as randomUUID5 } from "crypto";
7284
7976
  function createExeOSHooks(config) {
7285
7977
  let sessionRegistered = false;
7286
7978
  return {
@@ -7339,7 +8031,7 @@ function createExeOSHooks(config) {
7339
8031
  const toolResponse = result.isError ? { error: result.content } : { output: result.content };
7340
8032
  const rawText = extractSemanticText2(toolName, toolInput, toolResponse);
7341
8033
  await writeMemory2({
7342
- id: randomUUID4(),
8034
+ id: randomUUID5(),
7343
8035
  agent_id: config.agentId,
7344
8036
  agent_role: "employee",
7345
8037
  session_id: `api-${config.agentId}`,
@@ -7362,7 +8054,7 @@ function createExeOSHooks(config) {
7362
8054
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
7363
8055
  if (summary) {
7364
8056
  await writeMemory2({
7365
- id: randomUUID4(),
8057
+ id: randomUUID5(),
7366
8058
  agent_id: config.agentId,
7367
8059
  agent_role: "employee",
7368
8060
  session_id: `api-${config.agentId}`,
@@ -7435,7 +8127,7 @@ function createExeOSHooks(config) {
7435
8127
  await client.execute({
7436
8128
  sql: `INSERT OR IGNORE INTO notifications (id, type, source_agent, message, task_file, created_at, read)
7437
8129
  VALUES (?, 'system', ?, ?, NULL, ?, 0)`,
7438
- args: [randomUUID4(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
8130
+ args: [randomUUID5(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
7439
8131
  });
7440
8132
  } catch {
7441
8133
  }
@@ -7451,7 +8143,7 @@ function createExeOSHooks(config) {
7451
8143
  try {
7452
8144
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
7453
8145
  await writeMemory2({
7454
- id: randomUUID4(),
8146
+ id: randomUUID5(),
7455
8147
  agent_id: config.agentId,
7456
8148
  agent_role: "employee",
7457
8149
  session_id: `api-${config.agentId}`,
@@ -7473,7 +8165,7 @@ function createExeOSHooks(config) {
7473
8165
  try {
7474
8166
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
7475
8167
  await writeMemory2({
7476
- id: randomUUID4(),
8168
+ id: randomUUID5(),
7477
8169
  agent_id: config.agentId,
7478
8170
  agent_role: "employee",
7479
8171
  session_id: `api-${config.agentId}`,
@@ -7512,7 +8204,7 @@ function createExeOSHooks(config) {
7512
8204
  if (tasks.rows.length > 0) {
7513
8205
  const taskList = tasks.rows.map((r) => `- [${String(r.status)}] ${String(r.title)} (${String(r.task_file)})`).join("\n");
7514
8206
  await writeMemory2({
7515
- id: randomUUID4(),
8207
+ id: randomUUID5(),
7516
8208
  agent_id: config.agentId,
7517
8209
  agent_role: "employee",
7518
8210
  session_id: `api-${config.agentId}`,
@@ -7538,7 +8230,7 @@ ${taskList}`,
7538
8230
  try {
7539
8231
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
7540
8232
  await writeMemory2({
7541
- id: randomUUID4(),
8233
+ id: randomUUID5(),
7542
8234
  agent_id: config.agentId,
7543
8235
  agent_role: "employee",
7544
8236
  session_id: `api-${config.agentId}`,
@@ -7565,7 +8257,7 @@ var SessionManager = class {
7565
8257
  eventHandlers = /* @__PURE__ */ new Set();
7566
8258
  /** Start a new agent session for an employee */
7567
8259
  startSession(employeeId, config) {
7568
- const sessionId = randomUUID5();
8260
+ const sessionId = randomUUID6();
7569
8261
  const abortController = new AbortController();
7570
8262
  const session = {
7571
8263
  info: {