@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
package/dist/tui/App.js CHANGED
@@ -673,6 +673,7 @@ __export(employees_exports, {
673
673
  DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
674
674
  EMPLOYEES_PATH: () => EMPLOYEES_PATH,
675
675
  addEmployee: () => addEmployee,
676
+ baseAgentName: () => baseAgentName,
676
677
  canCoordinate: () => canCoordinate,
677
678
  getCoordinatorEmployee: () => getCoordinatorEmployee,
678
679
  getCoordinatorName: () => getCoordinatorName,
@@ -769,6 +770,14 @@ function hasRole(agentName, role) {
769
770
  const emp = getEmployee(employees, agentName);
770
771
  return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
771
772
  }
773
+ function baseAgentName(name, employees) {
774
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
775
+ if (!match) return name;
776
+ const base = match[1];
777
+ const roster = employees ?? loadEmployeesSync();
778
+ if (getEmployee(roster, base)) return base;
779
+ return name;
780
+ }
772
781
  function isMultiInstance(agentName, employees) {
773
782
  const roster = employees ?? loadEmployeesSync();
774
783
  const emp = getEmployee(roster, agentName);
@@ -864,6 +873,443 @@ var init_employees = __esm({
864
873
  }
865
874
  });
866
875
 
876
+ // src/lib/exe-daemon-client.ts
877
+ import net from "net";
878
+ import { spawn } from "child_process";
879
+ import { randomUUID } from "crypto";
880
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
881
+ import path5 from "path";
882
+ import { fileURLToPath } from "url";
883
+ function handleData(chunk) {
884
+ _buffer += chunk.toString();
885
+ if (_buffer.length > MAX_BUFFER) {
886
+ _buffer = "";
887
+ return;
888
+ }
889
+ let newlineIdx;
890
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
891
+ const line = _buffer.slice(0, newlineIdx).trim();
892
+ _buffer = _buffer.slice(newlineIdx + 1);
893
+ if (!line) continue;
894
+ try {
895
+ const response = JSON.parse(line);
896
+ const id = response.id;
897
+ if (!id) continue;
898
+ const entry = _pending.get(id);
899
+ if (entry) {
900
+ clearTimeout(entry.timer);
901
+ _pending.delete(id);
902
+ entry.resolve(response);
903
+ }
904
+ } catch {
905
+ }
906
+ }
907
+ }
908
+ function cleanupStaleFiles() {
909
+ if (existsSync6(PID_PATH)) {
910
+ try {
911
+ const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
912
+ if (pid > 0) {
913
+ try {
914
+ process.kill(pid, 0);
915
+ return;
916
+ } catch {
917
+ }
918
+ }
919
+ } catch {
920
+ }
921
+ try {
922
+ unlinkSync2(PID_PATH);
923
+ } catch {
924
+ }
925
+ try {
926
+ unlinkSync2(SOCKET_PATH);
927
+ } catch {
928
+ }
929
+ }
930
+ }
931
+ function findPackageRoot() {
932
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
933
+ const { root } = path5.parse(dir);
934
+ while (dir !== root) {
935
+ if (existsSync6(path5.join(dir, "package.json"))) return dir;
936
+ dir = path5.dirname(dir);
937
+ }
938
+ return null;
939
+ }
940
+ function spawnDaemon() {
941
+ const pkgRoot = findPackageRoot();
942
+ if (!pkgRoot) {
943
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
944
+ return;
945
+ }
946
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
947
+ if (!existsSync6(daemonPath)) {
948
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
949
+ `);
950
+ return;
951
+ }
952
+ const resolvedPath = daemonPath;
953
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
954
+ `);
955
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
956
+ let stderrFd = "ignore";
957
+ try {
958
+ stderrFd = openSync(logPath, "a");
959
+ } catch {
960
+ }
961
+ const child = spawn(process.execPath, [resolvedPath], {
962
+ detached: true,
963
+ stdio: ["ignore", "ignore", stderrFd],
964
+ env: {
965
+ ...process.env,
966
+ TMUX: void 0,
967
+ // Daemon is global — must not inherit session scope
968
+ TMUX_PANE: void 0,
969
+ // Prevents resolveExeSession() from scoping to one session
970
+ EXE_DAEMON_SOCK: SOCKET_PATH,
971
+ EXE_DAEMON_PID: PID_PATH
972
+ }
973
+ });
974
+ child.unref();
975
+ if (typeof stderrFd === "number") {
976
+ try {
977
+ closeSync(stderrFd);
978
+ } catch {
979
+ }
980
+ }
981
+ }
982
+ function acquireSpawnLock() {
983
+ try {
984
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
985
+ closeSync(fd);
986
+ return true;
987
+ } catch {
988
+ try {
989
+ const stat = statSync(SPAWN_LOCK_PATH);
990
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
991
+ try {
992
+ unlinkSync2(SPAWN_LOCK_PATH);
993
+ } catch {
994
+ }
995
+ try {
996
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
997
+ closeSync(fd);
998
+ return true;
999
+ } catch {
1000
+ }
1001
+ }
1002
+ } catch {
1003
+ }
1004
+ return false;
1005
+ }
1006
+ }
1007
+ function releaseSpawnLock() {
1008
+ try {
1009
+ unlinkSync2(SPAWN_LOCK_PATH);
1010
+ } catch {
1011
+ }
1012
+ }
1013
+ function connectToSocket() {
1014
+ return new Promise((resolve) => {
1015
+ if (_socket && _connected) {
1016
+ resolve(true);
1017
+ return;
1018
+ }
1019
+ const socket = net.createConnection({ path: SOCKET_PATH });
1020
+ const connectTimeout = setTimeout(() => {
1021
+ socket.destroy();
1022
+ resolve(false);
1023
+ }, 2e3);
1024
+ socket.on("connect", () => {
1025
+ clearTimeout(connectTimeout);
1026
+ _socket = socket;
1027
+ _connected = true;
1028
+ _buffer = "";
1029
+ socket.on("data", handleData);
1030
+ socket.on("close", () => {
1031
+ _connected = false;
1032
+ _socket = null;
1033
+ for (const [id, entry] of _pending) {
1034
+ clearTimeout(entry.timer);
1035
+ _pending.delete(id);
1036
+ entry.resolve({ error: "Connection closed" });
1037
+ }
1038
+ });
1039
+ socket.on("error", () => {
1040
+ _connected = false;
1041
+ _socket = null;
1042
+ });
1043
+ resolve(true);
1044
+ });
1045
+ socket.on("error", () => {
1046
+ clearTimeout(connectTimeout);
1047
+ resolve(false);
1048
+ });
1049
+ });
1050
+ }
1051
+ async function connectEmbedDaemon() {
1052
+ if (_socket && _connected) return true;
1053
+ if (await connectToSocket()) return true;
1054
+ if (acquireSpawnLock()) {
1055
+ try {
1056
+ cleanupStaleFiles();
1057
+ spawnDaemon();
1058
+ } finally {
1059
+ releaseSpawnLock();
1060
+ }
1061
+ }
1062
+ const start = Date.now();
1063
+ let delay2 = 100;
1064
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1065
+ await new Promise((r) => setTimeout(r, delay2));
1066
+ if (await connectToSocket()) return true;
1067
+ delay2 = Math.min(delay2 * 2, 3e3);
1068
+ }
1069
+ return false;
1070
+ }
1071
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1072
+ return new Promise((resolve) => {
1073
+ if (!_socket || !_connected) {
1074
+ resolve({ error: "Not connected" });
1075
+ return;
1076
+ }
1077
+ const id = randomUUID();
1078
+ const timer = setTimeout(() => {
1079
+ _pending.delete(id);
1080
+ resolve({ error: "Request timeout" });
1081
+ }, timeoutMs);
1082
+ _pending.set(id, { resolve, timer });
1083
+ try {
1084
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1085
+ } catch {
1086
+ clearTimeout(timer);
1087
+ _pending.delete(id);
1088
+ resolve({ error: "Write failed" });
1089
+ }
1090
+ });
1091
+ }
1092
+ function isClientConnected() {
1093
+ return _connected;
1094
+ }
1095
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1096
+ var init_exe_daemon_client = __esm({
1097
+ "src/lib/exe-daemon-client.ts"() {
1098
+ "use strict";
1099
+ init_config();
1100
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1101
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1102
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1103
+ SPAWN_LOCK_STALE_MS = 3e4;
1104
+ CONNECT_TIMEOUT_MS = 15e3;
1105
+ REQUEST_TIMEOUT_MS = 3e4;
1106
+ _socket = null;
1107
+ _connected = false;
1108
+ _buffer = "";
1109
+ _pending = /* @__PURE__ */ new Map();
1110
+ MAX_BUFFER = 1e7;
1111
+ }
1112
+ });
1113
+
1114
+ // src/lib/daemon-protocol.ts
1115
+ function serializeValue(v) {
1116
+ if (v === null || v === void 0) return null;
1117
+ if (typeof v === "bigint") return Number(v);
1118
+ if (typeof v === "boolean") return v ? 1 : 0;
1119
+ if (v instanceof Uint8Array) {
1120
+ return { __blob: Buffer.from(v).toString("base64") };
1121
+ }
1122
+ if (ArrayBuffer.isView(v)) {
1123
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
1124
+ }
1125
+ if (v instanceof ArrayBuffer) {
1126
+ return { __blob: Buffer.from(v).toString("base64") };
1127
+ }
1128
+ if (typeof v === "string" || typeof v === "number") return v;
1129
+ return String(v);
1130
+ }
1131
+ function deserializeValue(v) {
1132
+ if (v === null) return null;
1133
+ if (typeof v === "object" && v !== null && "__blob" in v) {
1134
+ const buf = Buffer.from(v.__blob, "base64");
1135
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
1136
+ }
1137
+ return v;
1138
+ }
1139
+ function deserializeResultSet(srs) {
1140
+ const rows = srs.rows.map((obj) => {
1141
+ const values = srs.columns.map(
1142
+ (col) => deserializeValue(obj[col] ?? null)
1143
+ );
1144
+ const row = values;
1145
+ for (let i = 0; i < srs.columns.length; i++) {
1146
+ const col = srs.columns[i];
1147
+ if (col !== void 0) {
1148
+ row[col] = values[i] ?? null;
1149
+ }
1150
+ }
1151
+ Object.defineProperty(row, "length", {
1152
+ value: values.length,
1153
+ enumerable: false
1154
+ });
1155
+ return row;
1156
+ });
1157
+ return {
1158
+ columns: srs.columns,
1159
+ columnTypes: srs.columnTypes ?? [],
1160
+ rows,
1161
+ rowsAffected: srs.rowsAffected,
1162
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
1163
+ toJSON: () => ({
1164
+ columns: srs.columns,
1165
+ columnTypes: srs.columnTypes ?? [],
1166
+ rows: srs.rows,
1167
+ rowsAffected: srs.rowsAffected,
1168
+ lastInsertRowid: srs.lastInsertRowid
1169
+ })
1170
+ };
1171
+ }
1172
+ var init_daemon_protocol = __esm({
1173
+ "src/lib/daemon-protocol.ts"() {
1174
+ "use strict";
1175
+ }
1176
+ });
1177
+
1178
+ // src/lib/db-daemon-client.ts
1179
+ var db_daemon_client_exports = {};
1180
+ __export(db_daemon_client_exports, {
1181
+ createDaemonDbClient: () => createDaemonDbClient,
1182
+ initDaemonDbClient: () => initDaemonDbClient
1183
+ });
1184
+ function normalizeStatement(stmt) {
1185
+ if (typeof stmt === "string") {
1186
+ return { sql: stmt, args: [] };
1187
+ }
1188
+ const sql = stmt.sql;
1189
+ let args = [];
1190
+ if (Array.isArray(stmt.args)) {
1191
+ args = stmt.args.map((v) => serializeValue(v));
1192
+ } else if (stmt.args && typeof stmt.args === "object") {
1193
+ const named = {};
1194
+ for (const [key, val] of Object.entries(stmt.args)) {
1195
+ named[key] = serializeValue(val);
1196
+ }
1197
+ return { sql, args: named };
1198
+ }
1199
+ return { sql, args };
1200
+ }
1201
+ function createDaemonDbClient(fallbackClient) {
1202
+ let _useDaemon = false;
1203
+ const client = {
1204
+ async execute(stmt) {
1205
+ if (!_useDaemon || !isClientConnected()) {
1206
+ return fallbackClient.execute(stmt);
1207
+ }
1208
+ const { sql, args } = normalizeStatement(stmt);
1209
+ const response = await sendDaemonRequest({
1210
+ type: "db-execute",
1211
+ sql,
1212
+ args
1213
+ });
1214
+ if (response.error) {
1215
+ const errMsg = String(response.error);
1216
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1217
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1218
+ `);
1219
+ return fallbackClient.execute(stmt);
1220
+ }
1221
+ throw new Error(errMsg);
1222
+ }
1223
+ if (response.db) {
1224
+ return deserializeResultSet(response.db);
1225
+ }
1226
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
1227
+ return fallbackClient.execute(stmt);
1228
+ },
1229
+ async batch(stmts, mode) {
1230
+ if (!_useDaemon || !isClientConnected()) {
1231
+ return fallbackClient.batch(stmts, mode);
1232
+ }
1233
+ const statements = stmts.map(normalizeStatement);
1234
+ const response = await sendDaemonRequest({
1235
+ type: "db-batch",
1236
+ statements,
1237
+ mode: mode ?? "deferred"
1238
+ });
1239
+ if (response.error) {
1240
+ const errMsg = String(response.error);
1241
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1242
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1243
+ `);
1244
+ return fallbackClient.batch(stmts, mode);
1245
+ }
1246
+ throw new Error(errMsg);
1247
+ }
1248
+ const batchResults = response["db-batch"];
1249
+ if (batchResults) {
1250
+ return batchResults.map(deserializeResultSet);
1251
+ }
1252
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
1253
+ return fallbackClient.batch(stmts, mode);
1254
+ },
1255
+ // Transaction support — delegate to fallback (transactions need direct connection)
1256
+ async transaction(mode) {
1257
+ return fallbackClient.transaction(mode);
1258
+ },
1259
+ // executeMultiple — delegate to fallback (used only for schema migrations)
1260
+ async executeMultiple(sql) {
1261
+ return fallbackClient.executeMultiple(sql);
1262
+ },
1263
+ // migrate — delegate to fallback
1264
+ async migrate(stmts) {
1265
+ return fallbackClient.migrate(stmts);
1266
+ },
1267
+ // Sync mode — delegate to fallback
1268
+ sync() {
1269
+ return fallbackClient.sync();
1270
+ },
1271
+ close() {
1272
+ _useDaemon = false;
1273
+ },
1274
+ get closed() {
1275
+ return fallbackClient.closed;
1276
+ },
1277
+ get protocol() {
1278
+ return fallbackClient.protocol;
1279
+ }
1280
+ };
1281
+ return {
1282
+ ...client,
1283
+ /** Enable daemon routing (call after confirming daemon is connected) */
1284
+ _enableDaemon() {
1285
+ _useDaemon = true;
1286
+ },
1287
+ /** Check if daemon routing is active */
1288
+ _isDaemonActive() {
1289
+ return _useDaemon && isClientConnected();
1290
+ }
1291
+ };
1292
+ }
1293
+ async function initDaemonDbClient(fallbackClient) {
1294
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1295
+ const connected = await connectEmbedDaemon();
1296
+ if (!connected) {
1297
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1298
+ return null;
1299
+ }
1300
+ const client = createDaemonDbClient(fallbackClient);
1301
+ client._enableDaemon();
1302
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1303
+ return client;
1304
+ }
1305
+ var init_db_daemon_client = __esm({
1306
+ "src/lib/db-daemon-client.ts"() {
1307
+ "use strict";
1308
+ init_exe_daemon_client();
1309
+ init_daemon_protocol();
1310
+ }
1311
+ });
1312
+
867
1313
  // src/lib/database.ts
868
1314
  var database_exports = {};
869
1315
  __export(database_exports, {
@@ -872,6 +1318,7 @@ __export(database_exports, {
872
1318
  ensureSchema: () => ensureSchema,
873
1319
  getClient: () => getClient,
874
1320
  getRawClient: () => getRawClient,
1321
+ initDaemonClient: () => initDaemonClient,
875
1322
  initDatabase: () => initDatabase,
876
1323
  initTurso: () => initTurso,
877
1324
  isInitialized: () => isInitialized
@@ -899,8 +1346,27 @@ function getClient() {
899
1346
  if (!_resilientClient) {
900
1347
  throw new Error("Database client not initialized. Call initDatabase() first.");
901
1348
  }
1349
+ if (process.env.EXE_IS_DAEMON === "1") {
1350
+ return _resilientClient;
1351
+ }
1352
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
1353
+ return _daemonClient;
1354
+ }
902
1355
  return _resilientClient;
903
1356
  }
1357
+ async function initDaemonClient() {
1358
+ if (process.env.EXE_IS_DAEMON === "1") return;
1359
+ if (!_resilientClient) return;
1360
+ try {
1361
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1362
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
1363
+ } catch (err) {
1364
+ process.stderr.write(
1365
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
1366
+ `
1367
+ );
1368
+ }
1369
+ }
904
1370
  function getRawClient() {
905
1371
  if (!_client) {
906
1372
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1387,6 +1853,12 @@ async function ensureSchema() {
1387
1853
  } catch {
1388
1854
  }
1389
1855
  }
1856
+ try {
1857
+ await client.execute(
1858
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1859
+ );
1860
+ } catch {
1861
+ }
1390
1862
  await client.executeMultiple(`
1391
1863
  CREATE TABLE IF NOT EXISTS entities (
1392
1864
  id TEXT PRIMARY KEY,
@@ -1439,7 +1911,30 @@ async function ensureSchema() {
1439
1911
  entity_id TEXT NOT NULL,
1440
1912
  PRIMARY KEY (hyperedge_id, entity_id)
1441
1913
  );
1914
+
1915
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1916
+ name,
1917
+ content=entities,
1918
+ content_rowid=rowid
1919
+ );
1920
+
1921
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1922
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1923
+ END;
1924
+
1925
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1926
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1927
+ END;
1928
+
1929
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1930
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1931
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1932
+ END;
1442
1933
  `);
1934
+ try {
1935
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1936
+ } catch {
1937
+ }
1443
1938
  await client.executeMultiple(`
1444
1939
  CREATE TABLE IF NOT EXISTS entity_aliases (
1445
1940
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1620,6 +2115,33 @@ async function ensureSchema() {
1620
2115
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1621
2116
  ON conversations(channel_id);
1622
2117
  `);
2118
+ await client.executeMultiple(`
2119
+ CREATE TABLE IF NOT EXISTS session_agent_map (
2120
+ session_uuid TEXT PRIMARY KEY,
2121
+ agent_id TEXT NOT NULL,
2122
+ session_name TEXT,
2123
+ task_id TEXT,
2124
+ project_name TEXT,
2125
+ started_at TEXT NOT NULL
2126
+ );
2127
+
2128
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2129
+ ON session_agent_map(agent_id);
2130
+ `);
2131
+ try {
2132
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2133
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
2134
+ await client.execute({
2135
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
2136
+ SELECT session_id, agent_id, '', MIN(timestamp)
2137
+ FROM memories
2138
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
2139
+ GROUP BY session_id, agent_id`,
2140
+ args: []
2141
+ });
2142
+ }
2143
+ } catch {
2144
+ }
1623
2145
  try {
1624
2146
  await client.execute({
1625
2147
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1753,15 +2275,41 @@ async function ensureSchema() {
1753
2275
  });
1754
2276
  } catch {
1755
2277
  }
2278
+ for (const col of [
2279
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2280
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2281
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2282
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2283
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2284
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2285
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2286
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2287
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2288
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2289
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2290
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2291
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2292
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2293
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2294
+ ]) {
2295
+ try {
2296
+ await client.execute(col);
2297
+ } catch {
2298
+ }
2299
+ }
1756
2300
  }
1757
2301
  async function disposeDatabase() {
2302
+ if (_daemonClient) {
2303
+ _daemonClient.close();
2304
+ _daemonClient = null;
2305
+ }
1758
2306
  if (_client) {
1759
2307
  _client.close();
1760
2308
  _client = null;
1761
2309
  _resilientClient = null;
1762
2310
  }
1763
2311
  }
1764
- var _client, _resilientClient, initTurso, disposeTurso;
2312
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1765
2313
  var init_database = __esm({
1766
2314
  "src/lib/database.ts"() {
1767
2315
  "use strict";
@@ -1769,6 +2317,7 @@ var init_database = __esm({
1769
2317
  init_employees();
1770
2318
  _client = null;
1771
2319
  _resilientClient = null;
2320
+ _daemonClient = null;
1772
2321
  initTurso = initDatabase;
1773
2322
  disposeTurso = disposeDatabase;
1774
2323
  }
@@ -1791,9 +2340,9 @@ __export(license_exports, {
1791
2340
  stopLicenseRevalidation: () => stopLicenseRevalidation,
1792
2341
  validateLicense: () => validateLicense
1793
2342
  });
1794
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1795
- import { randomUUID } from "crypto";
1796
- import path5 from "path";
2343
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
2344
+ import { randomUUID as randomUUID2 } from "crypto";
2345
+ import path6 from "path";
1797
2346
  import { jwtVerify, importSPKI } from "jose";
1798
2347
  async function fetchRetry(url, init) {
1799
2348
  try {
@@ -1804,30 +2353,30 @@ async function fetchRetry(url, init) {
1804
2353
  }
1805
2354
  }
1806
2355
  function loadDeviceId() {
1807
- const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
2356
+ const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
1808
2357
  try {
1809
- if (existsSync6(deviceJsonPath)) {
1810
- const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
2358
+ if (existsSync7(deviceJsonPath)) {
2359
+ const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
1811
2360
  if (data.deviceId) return data.deviceId;
1812
2361
  }
1813
2362
  } catch {
1814
2363
  }
1815
2364
  try {
1816
- if (existsSync6(DEVICE_ID_PATH)) {
1817
- const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
2365
+ if (existsSync7(DEVICE_ID_PATH)) {
2366
+ const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
1818
2367
  if (id2) return id2;
1819
2368
  }
1820
2369
  } catch {
1821
2370
  }
1822
- const id = randomUUID();
2371
+ const id = randomUUID2();
1823
2372
  mkdirSync3(EXE_AI_DIR, { recursive: true });
1824
2373
  writeFileSync4(DEVICE_ID_PATH, id, "utf8");
1825
2374
  return id;
1826
2375
  }
1827
2376
  function loadLicense() {
1828
2377
  try {
1829
- if (!existsSync6(LICENSE_PATH)) return null;
1830
- return readFileSync6(LICENSE_PATH, "utf8").trim();
2378
+ if (!existsSync7(LICENSE_PATH)) return null;
2379
+ return readFileSync7(LICENSE_PATH, "utf8").trim();
1831
2380
  } catch {
1832
2381
  return null;
1833
2382
  }
@@ -1860,8 +2409,8 @@ async function verifyLicenseJwt(token) {
1860
2409
  }
1861
2410
  async function getCachedLicense() {
1862
2411
  try {
1863
- if (!existsSync6(CACHE_PATH)) return null;
1864
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2412
+ if (!existsSync7(CACHE_PATH)) return null;
2413
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
1865
2414
  if (!raw.token || typeof raw.token !== "string") return null;
1866
2415
  return await verifyLicenseJwt(raw.token);
1867
2416
  } catch {
@@ -1870,8 +2419,8 @@ async function getCachedLicense() {
1870
2419
  }
1871
2420
  function readCachedToken() {
1872
2421
  try {
1873
- if (!existsSync6(CACHE_PATH)) return null;
1874
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2422
+ if (!existsSync7(CACHE_PATH)) return null;
2423
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
1875
2424
  return typeof raw.token === "string" ? raw.token : null;
1876
2425
  } catch {
1877
2426
  return null;
@@ -1958,8 +2507,8 @@ async function validateLicense(apiKey, deviceId) {
1958
2507
  }
1959
2508
  function getCacheAgeMs() {
1960
2509
  try {
1961
- const { statSync } = __require("fs");
1962
- const s = statSync(CACHE_PATH);
2510
+ const { statSync: statSync2 } = __require("fs");
2511
+ const s = statSync2(CACHE_PATH);
1963
2512
  return Date.now() - s.mtimeMs;
1964
2513
  } catch {
1965
2514
  return Infinity;
@@ -1969,9 +2518,9 @@ async function checkLicense() {
1969
2518
  let key = loadLicense();
1970
2519
  if (!key) {
1971
2520
  try {
1972
- const configPath = path5.join(EXE_AI_DIR, "config.json");
1973
- if (existsSync6(configPath)) {
1974
- const raw = JSON.parse(readFileSync6(configPath, "utf8"));
2521
+ const configPath = path6.join(EXE_AI_DIR, "config.json");
2522
+ if (existsSync7(configPath)) {
2523
+ const raw = JSON.parse(readFileSync7(configPath, "utf8"));
1975
2524
  const cloud = raw.cloud;
1976
2525
  if (cloud?.apiKey) {
1977
2526
  key = cloud.apiKey;
@@ -2130,9 +2679,9 @@ var init_license = __esm({
2130
2679
  "src/lib/license.ts"() {
2131
2680
  "use strict";
2132
2681
  init_config();
2133
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
2134
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
2135
- DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
2682
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
2683
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
2684
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2136
2685
  API_BASE = "https://askexe.com/cloud";
2137
2686
  RETRY_DELAY_MS = 500;
2138
2687
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2162,12 +2711,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2162
2711
  });
2163
2712
 
2164
2713
  // src/lib/plan-limits.ts
2165
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
2166
- import path6 from "path";
2714
+ import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
2715
+ import path7 from "path";
2167
2716
  function getLicenseSync() {
2168
2717
  try {
2169
- if (!existsSync7(CACHE_PATH2)) return freeLicense();
2170
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
2718
+ if (!existsSync8(CACHE_PATH2)) return freeLicense();
2719
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
2171
2720
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2172
2721
  const parts = raw.token.split(".");
2173
2722
  if (parts.length !== 3) return freeLicense();
@@ -2205,8 +2754,8 @@ function assertEmployeeLimitSync(rosterPath) {
2205
2754
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2206
2755
  let count = 0;
2207
2756
  try {
2208
- if (existsSync7(filePath)) {
2209
- const raw = readFileSync7(filePath, "utf8");
2757
+ if (existsSync8(filePath)) {
2758
+ const raw = readFileSync8(filePath, "utf8");
2210
2759
  const employees = JSON.parse(raw);
2211
2760
  count = Array.isArray(employees) ? employees.length : 0;
2212
2761
  }
@@ -2235,19 +2784,19 @@ var init_plan_limits = __esm({
2235
2784
  this.name = "PlanLimitError";
2236
2785
  }
2237
2786
  };
2238
- CACHE_PATH2 = path6.join(EXE_AI_DIR, "license-cache.json");
2787
+ CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2239
2788
  }
2240
2789
  });
2241
2790
 
2242
2791
  // src/lib/notifications.ts
2243
2792
  import crypto from "crypto";
2244
- import path7 from "path";
2793
+ import path8 from "path";
2245
2794
  import os5 from "os";
2246
2795
  import {
2247
- readFileSync as readFileSync8,
2796
+ readFileSync as readFileSync9,
2248
2797
  readdirSync,
2249
- unlinkSync as unlinkSync2,
2250
- existsSync as existsSync8,
2798
+ unlinkSync as unlinkSync3,
2799
+ existsSync as existsSync9,
2251
2800
  rmdirSync
2252
2801
  } from "fs";
2253
2802
  async function writeNotification(notification) {
@@ -2398,10 +2947,11 @@ __export(tasks_crud_exports, {
2398
2947
  writeCheckpoint: () => writeCheckpoint
2399
2948
  });
2400
2949
  import crypto3 from "crypto";
2401
- import path8 from "path";
2950
+ import path9 from "path";
2951
+ import os6 from "os";
2402
2952
  import { execSync as execSync5 } from "child_process";
2403
2953
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2404
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
2954
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
2405
2955
  async function writeCheckpoint(input) {
2406
2956
  const client = getClient();
2407
2957
  const row = await resolveTask(client, input.taskId);
@@ -2442,6 +2992,35 @@ function extractParentFromContext(contextBody) {
2442
2992
  function slugify(title) {
2443
2993
  return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2444
2994
  }
2995
+ function buildKeywordIndex() {
2996
+ const idx = /* @__PURE__ */ new Map();
2997
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
2998
+ for (const kw of keywords) {
2999
+ const existing = idx.get(kw) ?? [];
3000
+ existing.push(role);
3001
+ idx.set(kw, existing);
3002
+ }
3003
+ }
3004
+ return idx;
3005
+ }
3006
+ function checkLaneAffinity(title, context, assigneeName) {
3007
+ const employees = loadEmployeesSync();
3008
+ const employee = employees.find((e) => e.name === assigneeName);
3009
+ if (!employee) return void 0;
3010
+ const assigneeRole = employee.role;
3011
+ const text = `${title} ${context}`.toLowerCase();
3012
+ const matchedRoles = /* @__PURE__ */ new Set();
3013
+ for (const [keyword, roles] of KEYWORD_INDEX) {
3014
+ if (text.includes(keyword)) {
3015
+ for (const role of roles) matchedRoles.add(role);
3016
+ }
3017
+ }
3018
+ if (matchedRoles.size === 0) return void 0;
3019
+ if (matchedRoles.has(assigneeRole)) return void 0;
3020
+ if (assigneeRole === "COO") return void 0;
3021
+ const expectedRoles = Array.from(matchedRoles).join(" or ");
3022
+ return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
3023
+ }
2445
3024
  async function resolveTask(client, identifier, scopeSession) {
2446
3025
  const scope = sessionScopeFilter(scopeSession);
2447
3026
  let result = await client.execute({
@@ -2491,7 +3070,14 @@ async function createTaskCore(input) {
2491
3070
  const id = crypto3.randomUUID();
2492
3071
  const now = (/* @__PURE__ */ new Date()).toISOString();
2493
3072
  const slug = slugify(input.title);
2494
- const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
3073
+ let earlySessionScope = null;
3074
+ try {
3075
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3076
+ earlySessionScope = resolveExeSession2();
3077
+ } catch {
3078
+ }
3079
+ const scope = earlySessionScope ?? "default";
3080
+ const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
2495
3081
  let blockedById = null;
2496
3082
  const initialStatus = input.blockedBy ? "blocked" : "open";
2497
3083
  if (input.blockedBy) {
@@ -2531,22 +3117,24 @@ async function createTaskCore(input) {
2531
3117
  if (dupCheck.rows.length > 0) {
2532
3118
  warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
2533
3119
  }
3120
+ if (!process.env.DISABLE_LANE_AFFINITY) {
3121
+ const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
3122
+ if (laneWarning) {
3123
+ warning = warning ? `${warning}
3124
+ ${laneWarning}` : laneWarning;
3125
+ }
3126
+ }
2534
3127
  if (input.baseDir) {
2535
3128
  try {
2536
- await mkdir3(path8.join(input.baseDir, "exe", "output"), { recursive: true });
2537
- await mkdir3(path8.join(input.baseDir, "exe", "research"), { recursive: true });
3129
+ await mkdir3(path9.join(input.baseDir, "exe", "output"), { recursive: true });
3130
+ await mkdir3(path9.join(input.baseDir, "exe", "research"), { recursive: true });
2538
3131
  await ensureArchitectureDoc(input.baseDir, input.projectName);
2539
3132
  await ensureGitignoreExe(input.baseDir);
2540
3133
  } catch {
2541
3134
  }
2542
3135
  }
2543
3136
  const complexity = input.complexity ?? "standard";
2544
- let sessionScope = null;
2545
- try {
2546
- const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
2547
- sessionScope = resolveExeSession2();
2548
- } catch {
2549
- }
3137
+ const sessionScope = earlySessionScope;
2550
3138
  await client.execute({
2551
3139
  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)
2552
3140
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -2573,6 +3161,39 @@ async function createTaskCore(input) {
2573
3161
  now
2574
3162
  ]
2575
3163
  });
3164
+ if (input.baseDir) {
3165
+ try {
3166
+ const EXE_OS_DIR = path9.join(os6.homedir(), ".exe-os");
3167
+ const mdPath = path9.join(EXE_OS_DIR, taskFile);
3168
+ const mdDir = path9.dirname(mdPath);
3169
+ if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3170
+ const reviewer = input.reviewer ?? input.assignedBy;
3171
+ const mdContent = `# ${input.title}
3172
+
3173
+ **ID:** ${id}
3174
+ **Status:** ${initialStatus}
3175
+ **Priority:** ${input.priority}
3176
+ **Assigned by:** ${input.assignedBy}
3177
+ **Assigned to:** ${input.assignedTo}
3178
+ **Project:** ${input.projectName}
3179
+ **Created:** ${now.split("T")[0]}${parentTaskId ? `
3180
+ **Parent task:** ${parentTaskId}` : ""}
3181
+ **Reviewer:** ${reviewer}
3182
+
3183
+ ## Context
3184
+
3185
+ ${input.context}
3186
+
3187
+ ## MANDATORY: When done
3188
+
3189
+ You MUST call update_task with status "done" and a result summary when finished.
3190
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3191
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
3192
+ `;
3193
+ await writeFile3(mdPath, mdContent, "utf-8");
3194
+ } catch {
3195
+ }
3196
+ }
2576
3197
  return {
2577
3198
  id,
2578
3199
  title: input.title,
@@ -2765,7 +3386,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
2765
3386
  return { row, taskFile, now, taskId };
2766
3387
  }
2767
3388
  }
2768
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
3389
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
2769
3390
  process.stderr.write(
2770
3391
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
2771
3392
  `
@@ -2830,9 +3451,9 @@ async function deleteTaskCore(taskId, _baseDir) {
2830
3451
  return { taskFile, assignedTo, assignedBy, taskSlug };
2831
3452
  }
2832
3453
  async function ensureArchitectureDoc(baseDir, projectName) {
2833
- const archPath = path8.join(baseDir, "exe", "ARCHITECTURE.md");
3454
+ const archPath = path9.join(baseDir, "exe", "ARCHITECTURE.md");
2834
3455
  try {
2835
- if (existsSync9(archPath)) return;
3456
+ if (existsSync10(archPath)) return;
2836
3457
  const template = [
2837
3458
  `# ${projectName} \u2014 System Architecture`,
2838
3459
  "",
@@ -2865,10 +3486,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
2865
3486
  }
2866
3487
  }
2867
3488
  async function ensureGitignoreExe(baseDir) {
2868
- const gitignorePath = path8.join(baseDir, ".gitignore");
3489
+ const gitignorePath = path9.join(baseDir, ".gitignore");
2869
3490
  try {
2870
- if (existsSync9(gitignorePath)) {
2871
- const content = readFileSync9(gitignorePath, "utf-8");
3491
+ if (existsSync10(gitignorePath)) {
3492
+ const content = readFileSync10(gitignorePath, "utf-8");
2872
3493
  if (/^\/?exe\/?$/m.test(content)) return;
2873
3494
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
2874
3495
  } else {
@@ -2877,20 +3498,30 @@ async function ensureGitignoreExe(baseDir) {
2877
3498
  } catch {
2878
3499
  }
2879
3500
  }
2880
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
3501
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
2881
3502
  var init_tasks_crud = __esm({
2882
3503
  "src/lib/tasks-crud.ts"() {
2883
3504
  "use strict";
2884
3505
  init_database();
2885
3506
  init_task_scope();
3507
+ init_employees();
3508
+ LANE_KEYWORDS = {
3509
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
3510
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
3511
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
3512
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
3513
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
3514
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
3515
+ };
3516
+ KEYWORD_INDEX = buildKeywordIndex();
2886
3517
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
2887
3518
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
2888
3519
  }
2889
3520
  });
2890
3521
 
2891
3522
  // src/lib/tasks-review.ts
2892
- import path9 from "path";
2893
- import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
3523
+ import path10 from "path";
3524
+ import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
2894
3525
  async function countPendingReviews(sessionScope) {
2895
3526
  const client = getClient();
2896
3527
  if (sessionScope) {
@@ -2912,7 +3543,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
2912
3543
  const result2 = await client.execute({
2913
3544
  sql: `SELECT COUNT(*) as cnt FROM tasks
2914
3545
  WHERE status = 'needs_review' AND updated_at > ?
2915
- AND (session_scope = ? OR session_scope IS NULL)`,
3546
+ AND session_scope = ?`,
2916
3547
  args: [sinceIso, sessionScope]
2917
3548
  });
2918
3549
  return Number(result2.rows[0]?.cnt) || 0;
@@ -2930,7 +3561,7 @@ async function listPendingReviews(limit, sessionScope) {
2930
3561
  const result2 = await client.execute({
2931
3562
  sql: `SELECT title, assigned_to, project_name FROM tasks
2932
3563
  WHERE status = 'needs_review'
2933
- AND (session_scope = ? OR session_scope IS NULL)
3564
+ AND session_scope = ?
2934
3565
  ORDER BY priority ASC, created_at DESC LIMIT ?`,
2935
3566
  args: [sessionScope, limit]
2936
3567
  });
@@ -3051,14 +3682,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3051
3682
  if (parts.length >= 3 && parts[0] === "review") {
3052
3683
  const agent = parts[1];
3053
3684
  const slug = parts.slice(2).join("-");
3054
- const originalTaskFile = `exe/${agent}/${slug}.md`;
3685
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
3055
3686
  const result = await client.execute({
3056
- sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
3057
- args: [now, originalTaskFile]
3687
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
3688
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
3058
3689
  });
3059
3690
  if (result.rowsAffected > 0) {
3060
3691
  process.stderr.write(
3061
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
3692
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
3062
3693
  `
3063
3694
  );
3064
3695
  }
@@ -3071,11 +3702,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3071
3702
  );
3072
3703
  }
3073
3704
  try {
3074
- const cacheDir = path9.join(EXE_AI_DIR, "session-cache");
3075
- if (existsSync10(cacheDir)) {
3705
+ const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
3706
+ if (existsSync11(cacheDir)) {
3076
3707
  for (const f of readdirSync2(cacheDir)) {
3077
3708
  if (f.startsWith("review-notified-")) {
3078
- unlinkSync3(path9.join(cacheDir, f));
3709
+ unlinkSync4(path10.join(cacheDir, f));
3079
3710
  }
3080
3711
  }
3081
3712
  }
@@ -3096,7 +3727,7 @@ var init_tasks_review = __esm({
3096
3727
  });
3097
3728
 
3098
3729
  // src/lib/tasks-chain.ts
3099
- import path10 from "path";
3730
+ import path11 from "path";
3100
3731
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3101
3732
  async function cascadeUnblock(taskId, baseDir, now) {
3102
3733
  const client = getClient();
@@ -3113,7 +3744,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3113
3744
  });
3114
3745
  for (const ur of unblockedRows.rows) {
3115
3746
  try {
3116
- const ubFile = path10.join(baseDir, String(ur.task_file));
3747
+ const ubFile = path11.join(baseDir, String(ur.task_file));
3117
3748
  let ubContent = await readFile3(ubFile, "utf-8");
3118
3749
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3119
3750
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3182,7 +3813,7 @@ var init_tasks_chain = __esm({
3182
3813
 
3183
3814
  // src/lib/project-name.ts
3184
3815
  import { execSync as execSync6 } from "child_process";
3185
- import path11 from "path";
3816
+ import path12 from "path";
3186
3817
  function getProjectName(cwd2) {
3187
3818
  const dir = cwd2 ?? process.cwd();
3188
3819
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3195,7 +3826,7 @@ function getProjectName(cwd2) {
3195
3826
  timeout: 2e3,
3196
3827
  stdio: ["pipe", "pipe", "pipe"]
3197
3828
  }).trim();
3198
- repoRoot = path11.dirname(gitCommonDir);
3829
+ repoRoot = path12.dirname(gitCommonDir);
3199
3830
  } catch {
3200
3831
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3201
3832
  cwd: dir,
@@ -3204,11 +3835,11 @@ function getProjectName(cwd2) {
3204
3835
  stdio: ["pipe", "pipe", "pipe"]
3205
3836
  }).trim();
3206
3837
  }
3207
- _cached2 = path11.basename(repoRoot);
3838
+ _cached2 = path12.basename(repoRoot);
3208
3839
  _cachedCwd = dir;
3209
3840
  return _cached2;
3210
3841
  } catch {
3211
- _cached2 = path11.basename(dir);
3842
+ _cached2 = path12.basename(dir);
3212
3843
  _cachedCwd = dir;
3213
3844
  return _cached2;
3214
3845
  }
@@ -3240,7 +3871,7 @@ function findSessionForProject(projectName) {
3240
3871
  const sessions = listSessions();
3241
3872
  for (const s of sessions) {
3242
3873
  const proj = s.projectDir.split("/").filter(Boolean).pop();
3243
- if (proj === projectName && (s.agentId === "exe" || isCoordinatorName(s.agentId))) return s;
3874
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
3244
3875
  }
3245
3876
  return null;
3246
3877
  }
@@ -3286,7 +3917,7 @@ var init_session_scope = __esm({
3286
3917
 
3287
3918
  // src/lib/tasks-notify.ts
3288
3919
  async function dispatchTaskToEmployee(input) {
3289
- if (input.assignedTo === "exe" || isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
3920
+ if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
3290
3921
  let crossProject = false;
3291
3922
  if (input.projectName) {
3292
3923
  try {
@@ -3681,8 +4312,8 @@ __export(tasks_exports, {
3681
4312
  updateTaskStatus: () => updateTaskStatus,
3682
4313
  writeCheckpoint: () => writeCheckpoint
3683
4314
  });
3684
- import path12 from "path";
3685
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
4315
+ import path13 from "path";
4316
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync5 } from "fs";
3686
4317
  async function createTask(input) {
3687
4318
  const result = await createTaskCore(input);
3688
4319
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -3701,14 +4332,14 @@ async function updateTask(input) {
3701
4332
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
3702
4333
  try {
3703
4334
  const agent = String(row.assigned_to);
3704
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3705
- const cachePath = path12.join(cacheDir, `current-task-${agent}.json`);
4335
+ const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
4336
+ const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
3706
4337
  if (input.status === "in_progress") {
3707
4338
  mkdirSync4(cacheDir, { recursive: true });
3708
4339
  writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
3709
4340
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
3710
4341
  try {
3711
- unlinkSync4(cachePath);
4342
+ unlinkSync5(cachePath);
3712
4343
  } catch {
3713
4344
  }
3714
4345
  }
@@ -3765,7 +4396,7 @@ async function updateTask(input) {
3765
4396
  }
3766
4397
  const isTerminal = input.status === "done" || input.status === "needs_review";
3767
4398
  if (isTerminal) {
3768
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
4399
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
3769
4400
  if (!isCoordinator) {
3770
4401
  notifyTaskDone();
3771
4402
  }
@@ -3790,7 +4421,7 @@ async function updateTask(input) {
3790
4421
  }
3791
4422
  }
3792
4423
  }
3793
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4424
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
3794
4425
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
3795
4426
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
3796
4427
  taskId,
@@ -3806,7 +4437,7 @@ async function updateTask(input) {
3806
4437
  });
3807
4438
  }
3808
4439
  let nextTask;
3809
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
4440
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
3810
4441
  try {
3811
4442
  nextTask = await findNextTask(String(row.assigned_to));
3812
4443
  } catch {
@@ -4150,7 +4781,7 @@ var init_capacity_monitor = __esm({
4150
4781
  // src/lib/tmux-routing.ts
4151
4782
  var tmux_routing_exports = {};
4152
4783
  __export(tmux_routing_exports, {
4153
- acquireSpawnLock: () => acquireSpawnLock,
4784
+ acquireSpawnLock: () => acquireSpawnLock2,
4154
4785
  employeeSessionName: () => employeeSessionName,
4155
4786
  ensureEmployee: () => ensureEmployee,
4156
4787
  extractRootExe: () => extractRootExe,
@@ -4165,20 +4796,20 @@ __export(tmux_routing_exports, {
4165
4796
  notifyParentExe: () => notifyParentExe,
4166
4797
  parseParentExe: () => parseParentExe,
4167
4798
  registerParentExe: () => registerParentExe,
4168
- releaseSpawnLock: () => releaseSpawnLock,
4799
+ releaseSpawnLock: () => releaseSpawnLock2,
4169
4800
  resolveExeSession: () => resolveExeSession,
4170
4801
  sendIntercom: () => sendIntercom,
4171
4802
  spawnEmployee: () => spawnEmployee,
4172
4803
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4173
4804
  });
4174
4805
  import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
4175
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync11, appendFileSync } from "fs";
4176
- import path13 from "path";
4177
- import os6 from "os";
4178
- import { fileURLToPath } from "url";
4179
- import { unlinkSync as unlinkSync5 } from "fs";
4806
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync12, appendFileSync } from "fs";
4807
+ import path14 from "path";
4808
+ import os7 from "os";
4809
+ import { fileURLToPath as fileURLToPath2 } from "url";
4810
+ import { unlinkSync as unlinkSync6 } from "fs";
4180
4811
  function spawnLockPath(sessionName) {
4181
- return path13.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4812
+ return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4182
4813
  }
4183
4814
  function isProcessAlive(pid) {
4184
4815
  try {
@@ -4188,14 +4819,14 @@ function isProcessAlive(pid) {
4188
4819
  return false;
4189
4820
  }
4190
4821
  }
4191
- function acquireSpawnLock(sessionName) {
4192
- if (!existsSync11(SPAWN_LOCK_DIR)) {
4822
+ function acquireSpawnLock2(sessionName) {
4823
+ if (!existsSync12(SPAWN_LOCK_DIR)) {
4193
4824
  mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
4194
4825
  }
4195
4826
  const lockFile = spawnLockPath(sessionName);
4196
- if (existsSync11(lockFile)) {
4827
+ if (existsSync12(lockFile)) {
4197
4828
  try {
4198
- const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
4829
+ const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
4199
4830
  const age = Date.now() - lock.timestamp;
4200
4831
  if (isProcessAlive(lock.pid) && age < 6e4) {
4201
4832
  return false;
@@ -4206,22 +4837,22 @@ function acquireSpawnLock(sessionName) {
4206
4837
  writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4207
4838
  return true;
4208
4839
  }
4209
- function releaseSpawnLock(sessionName) {
4840
+ function releaseSpawnLock2(sessionName) {
4210
4841
  try {
4211
- unlinkSync5(spawnLockPath(sessionName));
4842
+ unlinkSync6(spawnLockPath(sessionName));
4212
4843
  } catch {
4213
4844
  }
4214
4845
  }
4215
4846
  function resolveBehaviorsExporterScript() {
4216
4847
  try {
4217
- const thisFile = fileURLToPath(import.meta.url);
4218
- const scriptPath = path13.join(
4219
- path13.dirname(thisFile),
4848
+ const thisFile = fileURLToPath2(import.meta.url);
4849
+ const scriptPath = path14.join(
4850
+ path14.dirname(thisFile),
4220
4851
  "..",
4221
4852
  "bin",
4222
4853
  "exe-export-behaviors.js"
4223
4854
  );
4224
- return existsSync11(scriptPath) ? scriptPath : null;
4855
+ return existsSync12(scriptPath) ? scriptPath : null;
4225
4856
  } catch {
4226
4857
  return null;
4227
4858
  }
@@ -4287,11 +4918,11 @@ function extractRootExe(name) {
4287
4918
  return parts.length > 0 ? parts[parts.length - 1] : null;
4288
4919
  }
4289
4920
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4290
- if (!existsSync11(SESSION_CACHE)) {
4921
+ if (!existsSync12(SESSION_CACHE)) {
4291
4922
  mkdirSync5(SESSION_CACHE, { recursive: true });
4292
4923
  }
4293
4924
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4294
- const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4925
+ const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4295
4926
  writeFileSync6(filePath, JSON.stringify({
4296
4927
  parentExe: rootExe,
4297
4928
  dispatchedBy: dispatchedBy || rootExe,
@@ -4300,7 +4931,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4300
4931
  }
4301
4932
  function getParentExe(sessionKey) {
4302
4933
  try {
4303
- const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4934
+ const data = JSON.parse(readFileSync11(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4304
4935
  return data.parentExe || null;
4305
4936
  } catch {
4306
4937
  return null;
@@ -4308,8 +4939,8 @@ function getParentExe(sessionKey) {
4308
4939
  }
4309
4940
  function getDispatchedBy(sessionKey) {
4310
4941
  try {
4311
- const data = JSON.parse(readFileSync10(
4312
- path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4942
+ const data = JSON.parse(readFileSync11(
4943
+ path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4313
4944
  "utf8"
4314
4945
  ));
4315
4946
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4335,10 +4966,10 @@ function isEmployeeAlive(sessionName) {
4335
4966
  }
4336
4967
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
4337
4968
  const base = employeeSessionName(employeeName, exeSession);
4338
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
4969
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
4339
4970
  for (let i = 2; i <= maxInstances; i++) {
4340
4971
  const candidate = employeeSessionName(employeeName, exeSession, i);
4341
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
4972
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
4342
4973
  }
4343
4974
  return null;
4344
4975
  }
@@ -4370,15 +5001,15 @@ async function verifyPaneAtCapacity(sessionName) {
4370
5001
  }
4371
5002
  function readDebounceState() {
4372
5003
  try {
4373
- if (!existsSync11(DEBOUNCE_FILE)) return {};
4374
- return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
5004
+ if (!existsSync12(DEBOUNCE_FILE)) return {};
5005
+ return JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
4375
5006
  } catch {
4376
5007
  return {};
4377
5008
  }
4378
5009
  }
4379
5010
  function writeDebounceState(state) {
4380
5011
  try {
4381
- if (!existsSync11(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
5012
+ if (!existsSync12(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
4382
5013
  writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
4383
5014
  } catch {
4384
5015
  }
@@ -4498,7 +5129,7 @@ function notifyParentExe(sessionKey) {
4498
5129
  return true;
4499
5130
  }
4500
5131
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
4501
- if (employeeName === "exe" || isCoordinatorName(employeeName)) {
5132
+ if (isCoordinatorName(employeeName)) {
4502
5133
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
4503
5134
  }
4504
5135
  try {
@@ -4570,26 +5201,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4570
5201
  const transport = getTransport();
4571
5202
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4572
5203
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4573
- const logDir = path13.join(os6.homedir(), ".exe-os", "session-logs");
4574
- const logFile = path13.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4575
- if (!existsSync11(logDir)) {
5204
+ const logDir = path14.join(os7.homedir(), ".exe-os", "session-logs");
5205
+ const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5206
+ if (!existsSync12(logDir)) {
4576
5207
  mkdirSync5(logDir, { recursive: true });
4577
5208
  }
4578
5209
  transport.kill(sessionName);
4579
5210
  let cleanupSuffix = "";
4580
5211
  try {
4581
- const thisFile = fileURLToPath(import.meta.url);
4582
- const cleanupScript = path13.join(path13.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4583
- if (existsSync11(cleanupScript)) {
5212
+ const thisFile = fileURLToPath2(import.meta.url);
5213
+ const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5214
+ if (existsSync12(cleanupScript)) {
4584
5215
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4585
5216
  }
4586
5217
  } catch {
4587
5218
  }
4588
5219
  try {
4589
- const claudeJsonPath = path13.join(os6.homedir(), ".claude.json");
5220
+ const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
4590
5221
  let claudeJson = {};
4591
5222
  try {
4592
- claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
5223
+ claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
4593
5224
  } catch {
4594
5225
  }
4595
5226
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4601,13 +5232,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4601
5232
  } catch {
4602
5233
  }
4603
5234
  try {
4604
- const settingsDir = path13.join(os6.homedir(), ".claude", "projects");
5235
+ const settingsDir = path14.join(os7.homedir(), ".claude", "projects");
4605
5236
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4606
- const projSettingsDir = path13.join(settingsDir, normalizedKey);
4607
- const settingsPath = path13.join(projSettingsDir, "settings.json");
5237
+ const projSettingsDir = path14.join(settingsDir, normalizedKey);
5238
+ const settingsPath = path14.join(projSettingsDir, "settings.json");
4608
5239
  let settings = {};
4609
5240
  try {
4610
- settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
5241
+ settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
4611
5242
  } catch {
4612
5243
  }
4613
5244
  const perms = settings.permissions ?? {};
@@ -4648,8 +5279,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4648
5279
  let behaviorsFlag = "";
4649
5280
  let legacyFallbackWarned = false;
4650
5281
  if (!useExeAgent && !useBinSymlink) {
4651
- const identityPath = path13.join(
4652
- os6.homedir(),
5282
+ const identityPath = path14.join(
5283
+ os7.homedir(),
4653
5284
  ".exe-os",
4654
5285
  "identity",
4655
5286
  `${employeeName}.md`
@@ -4658,13 +5289,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4658
5289
  const hasAgentFlag = claudeSupportsAgentFlag();
4659
5290
  if (hasAgentFlag) {
4660
5291
  identityFlag = ` --agent ${employeeName}`;
4661
- } else if (existsSync11(identityPath)) {
5292
+ } else if (existsSync12(identityPath)) {
4662
5293
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
4663
5294
  legacyFallbackWarned = true;
4664
5295
  }
4665
5296
  const behaviorsFile = exportBehaviorsSync(
4666
5297
  employeeName,
4667
- path13.basename(spawnCwd),
5298
+ path14.basename(spawnCwd),
4668
5299
  sessionName
4669
5300
  );
4670
5301
  if (behaviorsFile) {
@@ -4679,9 +5310,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4679
5310
  }
4680
5311
  let sessionContextFlag = "";
4681
5312
  try {
4682
- const ctxDir = path13.join(os6.homedir(), ".exe-os", "session-cache");
5313
+ const ctxDir = path14.join(os7.homedir(), ".exe-os", "session-cache");
4683
5314
  mkdirSync5(ctxDir, { recursive: true });
4684
- const ctxFile = path13.join(ctxDir, `session-context-${sessionName}.md`);
5315
+ const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
4685
5316
  const ctxContent = [
4686
5317
  `## Session Context`,
4687
5318
  `You are running in tmux session: ${sessionName}.`,
@@ -4720,13 +5351,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4720
5351
  command: spawnCommand
4721
5352
  });
4722
5353
  if (spawnResult.error) {
4723
- releaseSpawnLock(sessionName);
5354
+ releaseSpawnLock2(sessionName);
4724
5355
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
4725
5356
  }
4726
5357
  transport.pipeLog(sessionName, logFile);
4727
5358
  try {
4728
5359
  const mySession = getMySession();
4729
- const dispatchInfo = path13.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5360
+ const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4730
5361
  writeFileSync6(dispatchInfo, JSON.stringify({
4731
5362
  dispatchedBy: mySession,
4732
5363
  rootExe: exeSession,
@@ -4758,7 +5389,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4758
5389
  }
4759
5390
  }
4760
5391
  if (!booted) {
4761
- releaseSpawnLock(sessionName);
5392
+ releaseSpawnLock2(sessionName);
4762
5393
  return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
4763
5394
  }
4764
5395
  if (!useExeAgent) {
@@ -4775,7 +5406,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4775
5406
  pid: 0,
4776
5407
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
4777
5408
  });
4778
- releaseSpawnLock(sessionName);
5409
+ releaseSpawnLock2(sessionName);
4779
5410
  return { sessionName };
4780
5411
  }
4781
5412
  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;
@@ -4791,14 +5422,14 @@ var init_tmux_routing = __esm({
4791
5422
  init_intercom_queue();
4792
5423
  init_plan_limits();
4793
5424
  init_employees();
4794
- SPAWN_LOCK_DIR = path13.join(os6.homedir(), ".exe-os", "spawn-locks");
4795
- SESSION_CACHE = path13.join(os6.homedir(), ".exe-os", "session-cache");
5425
+ SPAWN_LOCK_DIR = path14.join(os7.homedir(), ".exe-os", "spawn-locks");
5426
+ SESSION_CACHE = path14.join(os7.homedir(), ".exe-os", "session-cache");
4796
5427
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4797
5428
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4798
5429
  VERIFY_PANE_LINES = 200;
4799
5430
  INTERCOM_DEBOUNCE_MS = 3e4;
4800
- INTERCOM_LOG2 = path13.join(os6.homedir(), ".exe-os", "intercom.log");
4801
- DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
5431
+ INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
5432
+ DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4802
5433
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4803
5434
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
4804
5435
  }
@@ -6191,7 +6822,7 @@ __export(global_procedures_exports, {
6191
6822
  loadGlobalProcedures: () => loadGlobalProcedures,
6192
6823
  storeGlobalProcedure: () => storeGlobalProcedure
6193
6824
  });
6194
- import { randomUUID as randomUUID2 } from "crypto";
6825
+ import { randomUUID as randomUUID3 } from "crypto";
6195
6826
  async function loadGlobalProcedures() {
6196
6827
  const client = getClient();
6197
6828
  const result = await client.execute({
@@ -6220,7 +6851,7 @@ ${sections.join("\n\n")}
6220
6851
  `;
6221
6852
  }
6222
6853
  async function storeGlobalProcedure(input) {
6223
- const id = randomUUID2();
6854
+ const id = randomUUID3();
6224
6855
  const now = (/* @__PURE__ */ new Date()).toISOString();
6225
6856
  const client = getClient();
6226
6857
  await client.execute({
@@ -6358,7 +6989,7 @@ var init_anthropic = __esm({
6358
6989
 
6359
6990
  // src/gateway/providers/openai-compat.ts
6360
6991
  import OpenAI from "openai";
6361
- import { randomUUID as randomUUID3 } from "crypto";
6992
+ import { randomUUID as randomUUID4 } from "crypto";
6362
6993
  var OpenAICompatProvider;
6363
6994
  var init_openai_compat = __esm({
6364
6995
  "src/gateway/providers/openai-compat.ts"() {
@@ -6475,7 +7106,7 @@ var init_openai_compat = __esm({
6475
7106
  }
6476
7107
  content.push({
6477
7108
  type: "tool_use",
6478
- id: call.id ?? randomUUID3(),
7109
+ id: call.id ?? randomUUID4(),
6479
7110
  name: fn.name,
6480
7111
  input
6481
7112
  });
@@ -6667,10 +7298,10 @@ var init_hooks = __esm({
6667
7298
  });
6668
7299
 
6669
7300
  // src/runtime/safety-checks.ts
6670
- import path14 from "path";
6671
- import os7 from "os";
7301
+ import path15 from "path";
7302
+ import os8 from "os";
6672
7303
  function checkPathSafety(filePath) {
6673
- const resolved = path14.resolve(filePath);
7304
+ const resolved = path15.resolve(filePath);
6674
7305
  for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
6675
7306
  const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
6676
7307
  if (matches) {
@@ -6680,7 +7311,7 @@ function checkPathSafety(filePath) {
6680
7311
  return { safe: true, bypassImmune: true };
6681
7312
  }
6682
7313
  function checkReadPathSafety(filePath) {
6683
- const resolved = path14.resolve(filePath);
7314
+ const resolved = path15.resolve(filePath);
6684
7315
  const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
6685
7316
  (p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
6686
7317
  );
@@ -6695,7 +7326,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
6695
7326
  var init_safety_checks = __esm({
6696
7327
  "src/runtime/safety-checks.ts"() {
6697
7328
  "use strict";
6698
- HOME = os7.homedir();
7329
+ HOME = os8.homedir();
6699
7330
  BYPASS_IMMUNE_PATTERNS = [
6700
7331
  {
6701
7332
  pattern: /\/\.git\/hooks\//,
@@ -6706,11 +7337,11 @@ var init_safety_checks = __esm({
6706
7337
  reason: "Git config can set hooks and command execution"
6707
7338
  },
6708
7339
  {
6709
- pattern: (p) => p.startsWith(path14.join(HOME, ".claude")),
7340
+ pattern: (p) => p.startsWith(path15.join(HOME, ".claude")),
6710
7341
  reason: "Claude configuration files are protected"
6711
7342
  },
6712
7343
  {
6713
- pattern: (p) => p.startsWith(path14.join(HOME, ".exe-os")),
7344
+ pattern: (p) => p.startsWith(path15.join(HOME, ".exe-os")),
6714
7345
  reason: "exe-os configuration files are protected"
6715
7346
  },
6716
7347
  {
@@ -6727,7 +7358,7 @@ var init_safety_checks = __esm({
6727
7358
  },
6728
7359
  {
6729
7360
  pattern: (p) => {
6730
- const name = path14.basename(p);
7361
+ const name = path15.basename(p);
6731
7362
  return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
6732
7363
  },
6733
7364
  reason: "Shell configuration files can execute arbitrary code on login"
@@ -6754,7 +7385,7 @@ __export(file_read_exports, {
6754
7385
  FileReadTool: () => FileReadTool
6755
7386
  });
6756
7387
  import fs3 from "fs/promises";
6757
- import path15 from "path";
7388
+ import path16 from "path";
6758
7389
  import { z } from "zod";
6759
7390
  function isBinary(buf) {
6760
7391
  for (let i = 0; i < buf.length; i++) {
@@ -6790,7 +7421,7 @@ var init_file_read = __esm({
6790
7421
  return { behavior: "allow" };
6791
7422
  },
6792
7423
  async call(input, context) {
6793
- const filePath = path15.isAbsolute(input.file_path) ? input.file_path : path15.resolve(context.cwd, input.file_path);
7424
+ const filePath = path16.isAbsolute(input.file_path) ? input.file_path : path16.resolve(context.cwd, input.file_path);
6794
7425
  let stat;
6795
7426
  try {
6796
7427
  stat = await fs3.stat(filePath);
@@ -6830,7 +7461,7 @@ __export(glob_exports, {
6830
7461
  GlobTool: () => GlobTool
6831
7462
  });
6832
7463
  import fs4 from "fs/promises";
6833
- import path16 from "path";
7464
+ import path17 from "path";
6834
7465
  import { z as z2 } from "zod";
6835
7466
  async function walkDir(dir, maxDepth = 10) {
6836
7467
  const results = [];
@@ -6846,7 +7477,7 @@ async function walkDir(dir, maxDepth = 10) {
6846
7477
  if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
6847
7478
  continue;
6848
7479
  }
6849
- const fullPath = path16.join(current, entry.name);
7480
+ const fullPath = path17.join(current, entry.name);
6850
7481
  if (entry.isDirectory()) {
6851
7482
  await walk(fullPath, depth + 1);
6852
7483
  } else {
@@ -6880,11 +7511,11 @@ var init_glob = __esm({
6880
7511
  inputSchema: inputSchema2,
6881
7512
  isReadOnly: true,
6882
7513
  async call(input, context) {
6883
- const baseDir = input.path ? path16.isAbsolute(input.path) ? input.path : path16.resolve(context.cwd, input.path) : context.cwd;
7514
+ const baseDir = input.path ? path17.isAbsolute(input.path) ? input.path : path17.resolve(context.cwd, input.path) : context.cwd;
6884
7515
  try {
6885
7516
  const entries = await walkDir(baseDir);
6886
7517
  const matched = entries.filter(
6887
- (e) => simpleGlobMatch(path16.relative(baseDir, e.path), input.pattern)
7518
+ (e) => simpleGlobMatch(path17.relative(baseDir, e.path), input.pattern)
6888
7519
  );
6889
7520
  matched.sort((a, b) => b.mtime - a.mtime);
6890
7521
  if (matched.length === 0) {
@@ -6908,9 +7539,9 @@ var grep_exports = {};
6908
7539
  __export(grep_exports, {
6909
7540
  GrepTool: () => GrepTool
6910
7541
  });
6911
- import { spawn } from "child_process";
7542
+ import { spawn as spawn2 } from "child_process";
6912
7543
  import fs5 from "fs/promises";
6913
- import path17 from "path";
7544
+ import path18 from "path";
6914
7545
  import { z as z3 } from "zod";
6915
7546
  function runRipgrep(input, searchPath, context) {
6916
7547
  return new Promise((resolve, reject) => {
@@ -6922,7 +7553,7 @@ function runRipgrep(input, searchPath, context) {
6922
7553
  args.push("--glob", input.include);
6923
7554
  }
6924
7555
  args.push(input.pattern, searchPath);
6925
- const child = spawn("rg", args, {
7556
+ const child = spawn2("rg", args, {
6926
7557
  cwd: searchPath,
6927
7558
  timeout: 3e4,
6928
7559
  stdio: ["ignore", "pipe", "pipe"]
@@ -6964,7 +7595,7 @@ async function nodeGrep(input, searchPath) {
6964
7595
  }
6965
7596
  for (const entry of entries) {
6966
7597
  if (entry.name === "node_modules" || entry.name === ".git") continue;
6967
- const fullPath = path17.join(dir, entry.name);
7598
+ const fullPath = path18.join(dir, entry.name);
6968
7599
  if (entry.isDirectory()) {
6969
7600
  await walk(fullPath);
6970
7601
  } else {
@@ -7010,7 +7641,7 @@ var init_grep = __esm({
7010
7641
  inputSchema: inputSchema3,
7011
7642
  isReadOnly: true,
7012
7643
  async call(input, context) {
7013
- const searchPath = input.path ? path17.isAbsolute(input.path) ? input.path : path17.resolve(context.cwd, input.path) : context.cwd;
7644
+ const searchPath = input.path ? path18.isAbsolute(input.path) ? input.path : path18.resolve(context.cwd, input.path) : context.cwd;
7014
7645
  try {
7015
7646
  const result = await runRipgrep(input, searchPath, context);
7016
7647
  return result;
@@ -7035,7 +7666,7 @@ __export(file_write_exports, {
7035
7666
  FileWriteTool: () => FileWriteTool
7036
7667
  });
7037
7668
  import fs6 from "fs/promises";
7038
- import path18 from "path";
7669
+ import path19 from "path";
7039
7670
  import { z as z4 } from "zod";
7040
7671
  var inputSchema4, FileWriteTool;
7041
7672
  var init_file_write = __esm({
@@ -7063,8 +7694,8 @@ var init_file_write = __esm({
7063
7694
  return { behavior: "allow" };
7064
7695
  },
7065
7696
  async call(input, context) {
7066
- const filePath = path18.isAbsolute(input.file_path) ? input.file_path : path18.resolve(context.cwd, input.file_path);
7067
- const dir = path18.dirname(filePath);
7697
+ const filePath = path19.isAbsolute(input.file_path) ? input.file_path : path19.resolve(context.cwd, input.file_path);
7698
+ const dir = path19.dirname(filePath);
7068
7699
  await fs6.mkdir(dir, { recursive: true });
7069
7700
  await fs6.writeFile(filePath, input.content, "utf-8");
7070
7701
  return {
@@ -7082,7 +7713,7 @@ __export(file_edit_exports, {
7082
7713
  FileEditTool: () => FileEditTool
7083
7714
  });
7084
7715
  import fs7 from "fs/promises";
7085
- import path19 from "path";
7716
+ import path20 from "path";
7086
7717
  import { z as z5 } from "zod";
7087
7718
  function countOccurrences(haystack, needle) {
7088
7719
  let count = 0;
@@ -7123,7 +7754,7 @@ var init_file_edit = __esm({
7123
7754
  return { behavior: "allow" };
7124
7755
  },
7125
7756
  async call(input, context) {
7126
- const filePath = path19.isAbsolute(input.file_path) ? input.file_path : path19.resolve(context.cwd, input.file_path);
7757
+ const filePath = path20.isAbsolute(input.file_path) ? input.file_path : path20.resolve(context.cwd, input.file_path);
7127
7758
  let content;
7128
7759
  try {
7129
7760
  content = await fs7.readFile(filePath, "utf-8");
@@ -7280,7 +7911,7 @@ var bash_exports = {};
7280
7911
  __export(bash_exports, {
7281
7912
  BashTool: () => BashTool
7282
7913
  });
7283
- import { spawn as spawn2 } from "child_process";
7914
+ import { spawn as spawn3 } from "child_process";
7284
7915
  import { z as z6 } from "zod";
7285
7916
  var DEFAULT_TIMEOUT_MS, inputSchema6, BashTool;
7286
7917
  var init_bash = __esm({
@@ -7311,7 +7942,7 @@ var init_bash = __esm({
7311
7942
  async call(input, context) {
7312
7943
  const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS;
7313
7944
  return new Promise((resolve) => {
7314
- const child = spawn2("bash", ["-c", input.command], {
7945
+ const child = spawn3("bash", ["-c", input.command], {
7315
7946
  cwd: context.cwd,
7316
7947
  timeout,
7317
7948
  stdio: ["ignore", "pipe", "pipe"],
@@ -7363,7 +7994,7 @@ var init_bash = __esm({
7363
7994
  });
7364
7995
 
7365
7996
  // src/lib/task-router.ts
7366
- import { randomUUID as randomUUID4 } from "crypto";
7997
+ import { randomUUID as randomUUID5 } from "crypto";
7367
7998
  function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
7368
7999
  const tier = config.complexityToTier[complexity];
7369
8000
  const rule = config.tierRules[tier];
@@ -7788,14 +8419,14 @@ __export(keychain_exports, {
7788
8419
  setMasterKey: () => setMasterKey
7789
8420
  });
7790
8421
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
7791
- import { existsSync as existsSync12 } from "fs";
7792
- import path22 from "path";
7793
- import os8 from "os";
8422
+ import { existsSync as existsSync13 } from "fs";
8423
+ import path23 from "path";
8424
+ import os9 from "os";
7794
8425
  function getKeyDir() {
7795
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os8.homedir(), ".exe-os");
8426
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path23.join(os9.homedir(), ".exe-os");
7796
8427
  }
7797
8428
  function getKeyPath() {
7798
- return path22.join(getKeyDir(), "master.key");
8429
+ return path23.join(getKeyDir(), "master.key");
7799
8430
  }
7800
8431
  async function tryKeytar() {
7801
8432
  try {
@@ -7816,13 +8447,21 @@ async function getMasterKey() {
7816
8447
  }
7817
8448
  }
7818
8449
  const keyPath = getKeyPath();
7819
- if (!existsSync12(keyPath)) {
8450
+ if (!existsSync13(keyPath)) {
8451
+ process.stderr.write(
8452
+ `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
8453
+ `
8454
+ );
7820
8455
  return null;
7821
8456
  }
7822
8457
  try {
7823
8458
  const content = await readFile4(keyPath, "utf-8");
7824
8459
  return Buffer.from(content.trim(), "base64");
7825
- } catch {
8460
+ } catch (err) {
8461
+ process.stderr.write(
8462
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
8463
+ `
8464
+ );
7826
8465
  return null;
7827
8466
  }
7828
8467
  }
@@ -7851,7 +8490,7 @@ async function deleteMasterKey() {
7851
8490
  }
7852
8491
  }
7853
8492
  const keyPath = getKeyPath();
7854
- if (existsSync12(keyPath)) {
8493
+ if (existsSync13(keyPath)) {
7855
8494
  await unlink(keyPath);
7856
8495
  }
7857
8496
  }
@@ -8151,14 +8790,14 @@ __export(wiki_client_exports, {
8151
8790
  listDocuments: () => listDocuments,
8152
8791
  listWorkspaces: () => listWorkspaces
8153
8792
  });
8154
- async function wikiFetch(config, path24, method = "GET", body) {
8155
- const url = `${config.baseUrl}/api/v1${path24}`;
8793
+ async function wikiFetch(config, path25, method = "GET", body) {
8794
+ const url = `${config.baseUrl}/api/v1${path25}`;
8156
8795
  const headers = {
8157
8796
  Authorization: `Bearer ${config.apiKey}`,
8158
8797
  "Content-Type": "application/json"
8159
8798
  };
8160
8799
  const controller = new AbortController();
8161
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
8800
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
8162
8801
  try {
8163
8802
  let response;
8164
8803
  try {
@@ -8171,7 +8810,7 @@ async function wikiFetch(config, path24, method = "GET", body) {
8171
8810
  } catch {
8172
8811
  clearTimeout(timeout);
8173
8812
  const retryController = new AbortController();
8174
- const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS);
8813
+ const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
8175
8814
  try {
8176
8815
  await new Promise((r) => setTimeout(r, 500));
8177
8816
  response = await fetch(url, {
@@ -8185,7 +8824,7 @@ async function wikiFetch(config, path24, method = "GET", body) {
8185
8824
  }
8186
8825
  }
8187
8826
  if (!response.ok) {
8188
- throw new Error(`Wiki API ${method} ${path24}: ${response.status} ${response.statusText}`);
8827
+ throw new Error(`Wiki API ${method} ${path25}: ${response.status} ${response.statusText}`);
8189
8828
  }
8190
8829
  return response.json();
8191
8830
  } finally {
@@ -8275,12 +8914,12 @@ async function getChatHistory(client, workspaceSlug, limit = 50) {
8275
8914
  sentAt: h.sentAt ?? 0
8276
8915
  }));
8277
8916
  }
8278
- var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS;
8917
+ var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS2;
8279
8918
  var init_wiki_client = __esm({
8280
8919
  "src/lib/wiki-client.ts"() {
8281
8920
  "use strict";
8282
- LOCAL_WIKI_URL = "http://localhost:3001";
8283
- REQUEST_TIMEOUT_MS = 8e3;
8921
+ LOCAL_WIKI_URL = process.env.EXE_WIKI_URL || "http://localhost:3001";
8922
+ REQUEST_TIMEOUT_MS2 = 8e3;
8284
8923
  }
8285
8924
  });
8286
8925
 
@@ -8306,12 +8945,12 @@ __export(shard_manager_exports, {
8306
8945
  listShards: () => listShards,
8307
8946
  shardExists: () => shardExists
8308
8947
  });
8309
- import path23 from "path";
8310
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
8948
+ import path24 from "path";
8949
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
8311
8950
  import { createClient as createClient2 } from "@libsql/client";
8312
8951
  function initShardManager(encryptionKey) {
8313
8952
  _encryptionKey = encryptionKey;
8314
- if (!existsSync13(SHARDS_DIR)) {
8953
+ if (!existsSync14(SHARDS_DIR)) {
8315
8954
  mkdirSync6(SHARDS_DIR, { recursive: true });
8316
8955
  }
8317
8956
  _shardingEnabled = true;
@@ -8332,7 +8971,7 @@ function getShardClient(projectName) {
8332
8971
  }
8333
8972
  const cached = _shards.get(safeName);
8334
8973
  if (cached) return cached;
8335
- const dbPath = path23.join(SHARDS_DIR, `${safeName}.db`);
8974
+ const dbPath = path24.join(SHARDS_DIR, `${safeName}.db`);
8336
8975
  const client = createClient2({
8337
8976
  url: `file:${dbPath}`,
8338
8977
  encryptionKey: _encryptionKey
@@ -8342,10 +8981,10 @@ function getShardClient(projectName) {
8342
8981
  }
8343
8982
  function shardExists(projectName) {
8344
8983
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
8345
- return existsSync13(path23.join(SHARDS_DIR, `${safeName}.db`));
8984
+ return existsSync14(path24.join(SHARDS_DIR, `${safeName}.db`));
8346
8985
  }
8347
8986
  function listShards() {
8348
- if (!existsSync13(SHARDS_DIR)) return [];
8987
+ if (!existsSync14(SHARDS_DIR)) return [];
8349
8988
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
8350
8989
  }
8351
8990
  async function ensureShardSchema(client) {
@@ -8531,7 +9170,7 @@ var init_shard_manager = __esm({
8531
9170
  "src/lib/shard-manager.ts"() {
8532
9171
  "use strict";
8533
9172
  init_config();
8534
- SHARDS_DIR = path23.join(EXE_AI_DIR, "shards");
9173
+ SHARDS_DIR = path24.join(EXE_AI_DIR, "shards");
8535
9174
  _shards = /* @__PURE__ */ new Map();
8536
9175
  _encryptionKey = null;
8537
9176
  _shardingEnabled = false;
@@ -8555,6 +9194,7 @@ __export(store_exports, {
8555
9194
  vectorToBlob: () => vectorToBlob,
8556
9195
  writeMemory: () => writeMemory
8557
9196
  });
9197
+ import { createHash } from "crypto";
8558
9198
  function isBusyError2(err) {
8559
9199
  if (err instanceof Error) {
8560
9200
  const msg = err.message.toLowerCase();
@@ -8628,12 +9268,52 @@ function classifyTier(record) {
8628
9268
  if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
8629
9269
  return 3;
8630
9270
  }
9271
+ function inferFilePaths(record) {
9272
+ if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
9273
+ const firstLine = record.raw_text.split("\n")[0] ?? "";
9274
+ const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
9275
+ return match ? JSON.stringify([match[1]]) : null;
9276
+ }
9277
+ function inferCommitHash(record) {
9278
+ if (record.tool_name !== "Bash") return null;
9279
+ const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
9280
+ return match ? match[1] : null;
9281
+ }
9282
+ function inferLanguageType(record) {
9283
+ const text = record.raw_text;
9284
+ if (!text || text.length < 10) return null;
9285
+ const trimmed = text.trimStart();
9286
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
9287
+ if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
9288
+ if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
9289
+ if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
9290
+ return "mixed";
9291
+ }
9292
+ function inferDomain(record) {
9293
+ const proj = (record.project_name ?? "").toLowerCase();
9294
+ if (proj.includes("marketing") || proj.includes("content")) return "marketing";
9295
+ if (proj.includes("crm") || proj.includes("customer")) return "customer";
9296
+ return null;
9297
+ }
8631
9298
  async function writeMemory(record) {
8632
9299
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
8633
9300
  throw new Error(
8634
9301
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
8635
9302
  );
8636
9303
  }
9304
+ const contentHash = createHash("md5").update(record.raw_text).digest("hex");
9305
+ if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
9306
+ return;
9307
+ }
9308
+ try {
9309
+ const client = getClient();
9310
+ const existing = await client.execute({
9311
+ sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
9312
+ args: [contentHash, record.agent_id]
9313
+ });
9314
+ if (existing.rows.length > 0) return;
9315
+ } catch {
9316
+ }
8637
9317
  const dbRow = {
8638
9318
  id: record.id,
8639
9319
  agent_id: record.agent_id,
@@ -8663,7 +9343,23 @@ async function writeMemory(record) {
8663
9343
  supersedes_id: record.supersedes_id ?? null,
8664
9344
  draft: record.draft ? 1 : 0,
8665
9345
  memory_type: record.memory_type ?? "raw",
8666
- trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
9346
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
9347
+ content_hash: contentHash,
9348
+ intent: record.intent ?? null,
9349
+ outcome: record.outcome ?? null,
9350
+ domain: record.domain ?? inferDomain(record),
9351
+ referenced_entities: record.referenced_entities ?? null,
9352
+ retrieval_count: record.retrieval_count ?? 0,
9353
+ chain_position: record.chain_position ?? null,
9354
+ review_status: record.review_status ?? null,
9355
+ context_window_pct: record.context_window_pct ?? null,
9356
+ file_paths: record.file_paths ?? inferFilePaths(record),
9357
+ commit_hash: record.commit_hash ?? inferCommitHash(record),
9358
+ duration_ms: record.duration_ms ?? null,
9359
+ token_cost: record.token_cost ?? null,
9360
+ audience: record.audience ?? null,
9361
+ language_type: record.language_type ?? inferLanguageType(record),
9362
+ parent_memory_id: record.parent_memory_id ?? null
8667
9363
  };
8668
9364
  _pendingRecords.push(dbRow);
8669
9365
  orgBus.emit({
@@ -8721,80 +9417,85 @@ async function flushBatch() {
8721
9417
  const draft = row.draft ? 1 : 0;
8722
9418
  const memoryType = row.memory_type ?? "raw";
8723
9419
  const trajectory = row.trajectory ?? null;
8724
- return {
8725
- sql: hasVector ? `INSERT OR IGNORE INTO memories
8726
- (id, agent_id, agent_role, session_id, timestamp,
9420
+ const contentHash = row.content_hash ?? null;
9421
+ const intent = row.intent ?? null;
9422
+ const outcome = row.outcome ?? null;
9423
+ const domain = row.domain ?? null;
9424
+ const referencedEntities = row.referenced_entities ?? null;
9425
+ const retrievalCount = row.retrieval_count ?? 0;
9426
+ const chainPosition = row.chain_position ?? null;
9427
+ const reviewStatus = row.review_status ?? null;
9428
+ const contextWindowPct = row.context_window_pct ?? null;
9429
+ const filePaths = row.file_paths ?? null;
9430
+ const commitHash = row.commit_hash ?? null;
9431
+ const durationMs = row.duration_ms ?? null;
9432
+ const tokenCost = row.token_cost ?? null;
9433
+ const audience = row.audience ?? null;
9434
+ const languageType = row.language_type ?? null;
9435
+ const parentMemoryId = row.parent_memory_id ?? null;
9436
+ const cols = `id, agent_id, agent_role, session_id, timestamp,
8727
9437
  tool_name, project_name,
8728
9438
  has_error, raw_text, vector, version, task_id, importance, status,
8729
9439
  confidence, last_accessed,
8730
9440
  workspace_id, document_id, user_id, char_offset, page_number,
8731
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
8732
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
8733
- (id, agent_id, agent_role, session_id, timestamp,
8734
- tool_name, project_name,
8735
- has_error, raw_text, vector, version, task_id, importance, status,
8736
- confidence, last_accessed,
8737
- workspace_id, document_id, user_id, char_offset, page_number,
8738
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
8739
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8740
- args: hasVector ? [
8741
- row.id,
8742
- row.agent_id,
8743
- row.agent_role,
8744
- row.session_id,
8745
- row.timestamp,
8746
- row.tool_name,
8747
- row.project_name,
8748
- row.has_error,
8749
- row.raw_text,
8750
- vectorToBlob(row.vector),
8751
- row.version,
8752
- taskId,
8753
- importance,
8754
- status,
8755
- confidence,
8756
- lastAccessed,
8757
- workspaceId,
8758
- documentId,
8759
- userId,
8760
- charOffset,
8761
- pageNumber,
8762
- sourcePath,
8763
- sourceType,
8764
- tier,
8765
- supersedesId,
8766
- draft,
8767
- memoryType,
8768
- trajectory
8769
- ] : [
8770
- row.id,
8771
- row.agent_id,
8772
- row.agent_role,
8773
- row.session_id,
8774
- row.timestamp,
8775
- row.tool_name,
8776
- row.project_name,
8777
- row.has_error,
8778
- row.raw_text,
8779
- row.version,
8780
- taskId,
8781
- importance,
8782
- status,
8783
- confidence,
8784
- lastAccessed,
8785
- workspaceId,
8786
- documentId,
8787
- userId,
8788
- charOffset,
8789
- pageNumber,
8790
- sourcePath,
8791
- sourceType,
8792
- tier,
8793
- supersedesId,
8794
- draft,
8795
- memoryType,
8796
- trajectory
8797
- ]
9441
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
9442
+ intent, outcome, domain, referenced_entities, retrieval_count,
9443
+ chain_position, review_status, context_window_pct, file_paths, commit_hash,
9444
+ duration_ms, token_cost, audience, language_type, parent_memory_id`;
9445
+ const metaArgs = [
9446
+ intent,
9447
+ outcome,
9448
+ domain,
9449
+ referencedEntities,
9450
+ retrievalCount,
9451
+ chainPosition,
9452
+ reviewStatus,
9453
+ contextWindowPct,
9454
+ filePaths,
9455
+ commitHash,
9456
+ durationMs,
9457
+ tokenCost,
9458
+ audience,
9459
+ languageType,
9460
+ parentMemoryId
9461
+ ];
9462
+ const baseArgs = [
9463
+ row.id,
9464
+ row.agent_id,
9465
+ row.agent_role,
9466
+ row.session_id,
9467
+ row.timestamp,
9468
+ row.tool_name,
9469
+ row.project_name,
9470
+ row.has_error,
9471
+ row.raw_text
9472
+ ];
9473
+ const sharedArgs = [
9474
+ row.version,
9475
+ taskId,
9476
+ importance,
9477
+ status,
9478
+ confidence,
9479
+ lastAccessed,
9480
+ workspaceId,
9481
+ documentId,
9482
+ userId,
9483
+ charOffset,
9484
+ pageNumber,
9485
+ sourcePath,
9486
+ sourceType,
9487
+ tier,
9488
+ supersedesId,
9489
+ draft,
9490
+ memoryType,
9491
+ trajectory,
9492
+ contentHash
9493
+ ];
9494
+ return {
9495
+ sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
9496
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
9497
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
9498
+ args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
8798
9499
  };
8799
9500
  };
8800
9501
  const globalClient = getClient();
@@ -13039,8 +13740,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
13039
13740
  }
13040
13741
 
13041
13742
  // src/tui/ink/components/ErrorOverview.js
13042
- var cleanupPath = (path24) => {
13043
- return path24?.replace(`file://${cwd()}/`, "");
13743
+ var cleanupPath = (path25) => {
13744
+ return path25?.replace(`file://${cwd()}/`, "");
13044
13745
  };
13045
13746
  var stackUtils = new StackUtils({
13046
13747
  cwd: cwd(),
@@ -14639,7 +15340,7 @@ var useInput = (inputHandler, options = {}) => {
14639
15340
  if (options.isActive === false) {
14640
15341
  return;
14641
15342
  }
14642
- const handleData = (data) => {
15343
+ const handleData2 = (data) => {
14643
15344
  const keypress = parse_keypress_default(data);
14644
15345
  const key = {
14645
15346
  upArrow: keypress.name === "up",
@@ -14698,9 +15399,9 @@ var useInput = (inputHandler, options = {}) => {
14698
15399
  });
14699
15400
  }
14700
15401
  };
14701
- internal_eventEmitter?.on("input", handleData);
15402
+ internal_eventEmitter?.on("input", handleData2);
14702
15403
  return () => {
14703
- internal_eventEmitter?.removeListener("input", handleData);
15404
+ internal_eventEmitter?.removeListener("input", handleData2);
14704
15405
  };
14705
15406
  }, [options.isActive, stdin, internal_exitOnCtrlC, inputHandler]);
14706
15407
  };
@@ -14972,11 +15673,11 @@ function Footer() {
14972
15673
  } catch {
14973
15674
  }
14974
15675
  try {
14975
- const { existsSync: existsSync14 } = await import("fs");
15676
+ const { existsSync: existsSync15 } = await import("fs");
14976
15677
  const { join } = await import("path");
14977
15678
  const home = process.env.HOME ?? "";
14978
15679
  const pidPath = join(home, ".exe-os", "exed.pid");
14979
- setDaemon(existsSync14(pidPath) ? "running" : "stopped");
15680
+ setDaemon(existsSync15(pidPath) ? "running" : "stopped");
14980
15681
  } catch {
14981
15682
  setDaemon("unknown");
14982
15683
  }
@@ -15058,7 +15759,7 @@ function Footer() {
15058
15759
  // src/tui/views/CommandCenter.tsx
15059
15760
  import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
15060
15761
  import TextInput from "ink-text-input";
15061
- import path20 from "path";
15762
+ import path21 from "path";
15062
15763
  import { homedir } from "os";
15063
15764
 
15064
15765
  // src/tui/components/StatusDot.tsx
@@ -15845,15 +16546,15 @@ function CommandCenterView({
15845
16546
  const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
15846
16547
  const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
15847
16548
  const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
15848
- const { readFileSync: readFileSync11, existsSync: existsSync14 } = await import("fs");
16549
+ const { readFileSync: readFileSync12, existsSync: existsSync15 } = await import("fs");
15849
16550
  const { join } = await import("path");
15850
16551
  const { homedir: homedir3 } = await import("os");
15851
16552
  const configPath = join(homedir3(), ".exe-os", "config.json");
15852
16553
  let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
15853
16554
  let providerConfigs = {};
15854
- if (existsSync14(configPath)) {
16555
+ if (existsSync15(configPath)) {
15855
16556
  try {
15856
- const raw = JSON.parse(readFileSync11(configPath, "utf8"));
16557
+ const raw = JSON.parse(readFileSync12(configPath, "utf8"));
15857
16558
  if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
15858
16559
  if (raw.providers && typeof raw.providers === "object") {
15859
16560
  providerConfigs = raw.providers;
@@ -15914,7 +16615,7 @@ function CommandCenterView({
15914
16615
  const markerDir = join(homedir3(), ".exe-os", "session-cache");
15915
16616
  const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
15916
16617
  for (const f of agentFiles) {
15917
- const data = JSON.parse(readFileSync11(join(markerDir, f), "utf8"));
16618
+ const data = JSON.parse(readFileSync12(join(markerDir, f), "utf8"));
15918
16619
  if (data.agentRole) {
15919
16620
  agentRole = data.agentRole;
15920
16621
  break;
@@ -16059,7 +16760,7 @@ function CommandCenterView({
16059
16760
  const demoEntries = DEMO_PROJECTS.map((p) => ({
16060
16761
  projectName: p.projectName,
16061
16762
  exeSession: p.exeSession,
16062
- projectDir: path20.join(homedir(), p.projectName),
16763
+ projectDir: path21.join(homedir(), p.projectName),
16063
16764
  employeeCount: p.employees.length,
16064
16765
  activeCount: p.employees.filter((e) => e.status === "active").length,
16065
16766
  memoryCount: p.employees.length * 4e3,
@@ -16097,7 +16798,7 @@ function CommandCenterView({
16097
16798
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
16098
16799
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
16099
16800
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
16100
- const { existsSync: existsSync14 } = await import("fs");
16801
+ const { existsSync: existsSync15 } = await import("fs");
16101
16802
  const { join } = await import("path");
16102
16803
  const client = getClient2();
16103
16804
  if (!client) {
@@ -16142,12 +16843,12 @@ function CommandCenterView({
16142
16843
  const registry = listSessions2();
16143
16844
  const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
16144
16845
  const roster = await loadEmployees2();
16145
- const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
16846
+ const { getCoordinatorName: getCoordinatorName2, isCoordinatorName: isCoordinatorName2, isCoordinatorRole: isCoordinatorRole2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
16146
16847
  const coordinatorName = getCoordinatorName2(roster);
16147
16848
  const employeeNames = roster.filter((e) => !isCoordinatorRole2(e.role)).map((e) => e.name);
16148
16849
  const projectSessions = /* @__PURE__ */ new Map();
16149
16850
  for (const entry of registry) {
16150
- if ((entry.agentId === coordinatorName || entry.agentId === "exe") && tmuxSessions.has(entry.windowName)) {
16851
+ if ((entry.agentId === coordinatorName || isCoordinatorName2(entry.agentId)) && tmuxSessions.has(entry.windowName)) {
16151
16852
  const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
16152
16853
  if (projName) {
16153
16854
  projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
@@ -16168,7 +16869,7 @@ function CommandCenterView({
16168
16869
  }
16169
16870
  const memoryCount = memoryCounts.get(name) ?? 0;
16170
16871
  const openTaskCount = openTaskCounts.get(name) ?? 0;
16171
- const hasGit = projectDir ? existsSync14(join(projectDir, ".git")) : false;
16872
+ const hasGit = projectDir ? existsSync15(join(projectDir, ".git")) : false;
16172
16873
  const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
16173
16874
  projectList.push({
16174
16875
  projectName: name,
@@ -16193,7 +16894,7 @@ function CommandCenterView({
16193
16894
  setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
16194
16895
  try {
16195
16896
  const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
16196
- setHealth((h) => ({ ...h, daemon: existsSync14(pidPath) ? "running" : "stopped" }));
16897
+ setHealth((h) => ({ ...h, daemon: existsSync15(pidPath) ? "running" : "stopped" }));
16197
16898
  } catch {
16198
16899
  }
16199
16900
  const activityResult = await client.execute(
@@ -16408,7 +17109,7 @@ function ChatMessageRow({ msg }) {
16408
17109
 
16409
17110
  // src/tui/views/Sessions.tsx
16410
17111
  import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
16411
- import path21 from "path";
17112
+ import path22 from "path";
16412
17113
  import { homedir as homedir2 } from "os";
16413
17114
 
16414
17115
  // src/tui/components/TmuxPane.tsx
@@ -16671,12 +17372,13 @@ function useOrchestrator(enabled = true) {
16671
17372
  }
16672
17373
 
16673
17374
  // src/tui/views/Sessions.tsx
17375
+ init_employees();
16674
17376
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
16675
17377
  var SESSION_REFRESH_MS = 5e3;
16676
17378
  var ACTIVE_PANE_PATTERN = /█|░|Calculating|tokens|writing|Reading/;
16677
17379
  var ACTIVITY_PREVIEW_MAX = 50;
16678
17380
  function isCoordinatorEntry(entry) {
16679
- return entry.role.toLowerCase() === "coo" || entry.name === "exe";
17381
+ return entry.role.toLowerCase() === "coo" || isCoordinatorName(entry.name);
16680
17382
  }
16681
17383
  function SessionsView({
16682
17384
  initialProject,
@@ -16710,7 +17412,7 @@ function SessionsView({
16710
17412
  if (demo) {
16711
17413
  setProjects(DEMO_PROJECTS.map((p) => ({
16712
17414
  ...p,
16713
- projectDir: path21.join(homedir2(), p.projectName),
17415
+ projectDir: path22.join(homedir2(), p.projectName),
16714
17416
  employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
16715
17417
  })));
16716
17418
  return;
@@ -16900,7 +17602,7 @@ function SessionsView({
16900
17602
  const coordinatorName = getCoordinatorName2(roster);
16901
17603
  const exeSessions = /* @__PURE__ */ new Map();
16902
17604
  for (const entry of registry) {
16903
- if ((entry.agentId === coordinatorName || entry.agentId === "exe") && tmuxSessions.has(entry.windowName)) {
17605
+ if ((entry.agentId === coordinatorName || isCoordinatorName(entry.agentId)) && tmuxSessions.has(entry.windowName)) {
16904
17606
  exeSessions.set(entry.windowName, entry.projectDir);
16905
17607
  }
16906
17608
  }
@@ -17607,12 +18309,12 @@ async function loadGatewayConfig() {
17607
18309
  state.running = false;
17608
18310
  }
17609
18311
  try {
17610
- const { existsSync: existsSync14, readFileSync: readFileSync11 } = await import("fs");
18312
+ const { existsSync: existsSync15, readFileSync: readFileSync12 } = await import("fs");
17611
18313
  const { join } = await import("path");
17612
18314
  const home = process.env.HOME ?? "";
17613
18315
  const configPath = join(home, ".exe-os", "gateway.json");
17614
- if (existsSync14(configPath)) {
17615
- const raw = JSON.parse(readFileSync11(configPath, "utf8"));
18316
+ if (existsSync15(configPath)) {
18317
+ const raw = JSON.parse(readFileSync12(configPath, "utf8"));
17616
18318
  state.port = raw.port ?? 3100;
17617
18319
  state.gatewayUrl = raw.gatewayUrl ?? "";
17618
18320
  if (raw.adapters) {
@@ -18138,12 +18840,12 @@ function TeamView({ onBack, onViewSessions }) {
18138
18840
  setMembers(teamData);
18139
18841
  setDbError(null);
18140
18842
  try {
18141
- const { existsSync: existsSync14, readFileSync: readFileSync11 } = await import("fs");
18843
+ const { existsSync: existsSync15, readFileSync: readFileSync12 } = await import("fs");
18142
18844
  const { join } = await import("path");
18143
18845
  const home = process.env.HOME ?? "";
18144
18846
  const gatewayConfig = join(home, ".exe-os", "gateway.json");
18145
- if (existsSync14(gatewayConfig)) {
18146
- const raw = JSON.parse(readFileSync11(gatewayConfig, "utf8"));
18847
+ if (existsSync15(gatewayConfig)) {
18848
+ const raw = JSON.parse(readFileSync12(gatewayConfig, "utf8"));
18147
18849
  if (raw.agents && raw.agents.length > 0) {
18148
18850
  setExternals(raw.agents.map((a) => ({
18149
18851
  name: a.name,
@@ -18807,12 +19509,12 @@ function SettingsView({ onBack }) {
18807
19509
  }
18808
19510
  setProviders(providerList);
18809
19511
  try {
18810
- const { existsSync: existsSync14 } = await import("fs");
19512
+ const { existsSync: existsSync15 } = await import("fs");
18811
19513
  const { join } = await import("path");
18812
19514
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
18813
19515
  const cfg = await loadConfig2();
18814
19516
  const home = process.env.HOME ?? "";
18815
- const hasKey = existsSync14(join(home, ".exe-os", "master.key"));
19517
+ const hasKey = existsSync15(join(home, ".exe-os", "master.key"));
18816
19518
  if (cfg.cloud) {
18817
19519
  setCloud({
18818
19520
  configured: true,
@@ -18825,22 +19527,22 @@ function SettingsView({ onBack }) {
18825
19527
  const pidPath = join(home, ".exe-os", "exed.pid");
18826
19528
  let daemon = "unknown";
18827
19529
  try {
18828
- daemon = existsSync14(pidPath) ? "running" : "stopped";
19530
+ daemon = existsSync15(pidPath) ? "running" : "stopped";
18829
19531
  } catch {
18830
19532
  }
18831
19533
  let version = "unknown";
18832
19534
  try {
18833
- const { readFileSync: readFileSync11 } = await import("fs");
19535
+ const { readFileSync: readFileSync12 } = await import("fs");
18834
19536
  const { createRequire } = await import("module");
18835
19537
  const require2 = createRequire(import.meta.url);
18836
19538
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
18837
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
19539
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
18838
19540
  version = pkg.version;
18839
19541
  } catch {
18840
19542
  try {
18841
- const { readFileSync: readFileSync11 } = await import("fs");
19543
+ const { readFileSync: readFileSync12 } = await import("fs");
18842
19544
  const { join: joinPath } = await import("path");
18843
- const pkg = JSON.parse(readFileSync11(joinPath(process.cwd(), "package.json"), "utf8"));
19545
+ const pkg = JSON.parse(readFileSync12(joinPath(process.cwd(), "package.json"), "utf8"));
18844
19546
  version = pkg.version;
18845
19547
  } catch {
18846
19548
  }