@askexenow/exe-os 0.8.83 → 0.8.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +154 -21
  5. package/dist/bin/cli.js +14678 -12676
  6. package/dist/bin/exe-agent-config.js +242 -0
  7. package/dist/bin/exe-agent.js +100 -91
  8. package/dist/bin/exe-assign.js +1003 -854
  9. package/dist/bin/exe-boot.js +1420 -485
  10. package/dist/bin/exe-call.js +10 -0
  11. package/dist/bin/exe-cloud.js +29 -6
  12. package/dist/bin/exe-dispatch.js +572 -271
  13. package/dist/bin/exe-doctor.js +403 -6
  14. package/dist/bin/exe-export-behaviors.js +175 -72
  15. package/dist/bin/exe-forget.js +102 -3
  16. package/dist/bin/exe-gateway.js +796 -292
  17. package/dist/bin/exe-healthcheck.js +134 -1
  18. package/dist/bin/exe-heartbeat.js +172 -36
  19. package/dist/bin/exe-kill.js +175 -72
  20. package/dist/bin/exe-launch-agent.js +189 -76
  21. package/dist/bin/exe-link.js +927 -82
  22. package/dist/bin/exe-new-employee.js +60 -8
  23. package/dist/bin/exe-pending-messages.js +151 -19
  24. package/dist/bin/exe-pending-notifications.js +97 -2
  25. package/dist/bin/exe-pending-reviews.js +155 -22
  26. package/dist/bin/exe-rename.js +564 -23
  27. package/dist/bin/exe-review.js +231 -73
  28. package/dist/bin/exe-search.js +995 -228
  29. package/dist/bin/exe-session-cleanup.js +4930 -1664
  30. package/dist/bin/exe-settings.js +20 -5
  31. package/dist/bin/exe-start-codex.js +2598 -0
  32. package/dist/bin/exe-start.sh +15 -3
  33. package/dist/bin/exe-status.js +154 -21
  34. package/dist/bin/exe-team.js +97 -2
  35. package/dist/bin/git-sweep.js +1180 -363
  36. package/dist/bin/graph-backfill.js +175 -72
  37. package/dist/bin/graph-export.js +175 -72
  38. package/dist/bin/install.js +60 -7
  39. package/dist/bin/list-providers.js +1 -0
  40. package/dist/bin/scan-tasks.js +1185 -367
  41. package/dist/bin/setup.js +914 -270
  42. package/dist/bin/shard-migrate.js +175 -72
  43. package/dist/bin/update.js +1 -0
  44. package/dist/bin/wiki-sync.js +175 -72
  45. package/dist/gateway/index.js +792 -285
  46. package/dist/hooks/bug-report-worker.js +445 -135
  47. package/dist/hooks/commit-complete.js +1178 -361
  48. package/dist/hooks/error-recall.js +994 -228
  49. package/dist/hooks/ingest-worker.js +1799 -1234
  50. package/dist/hooks/ingest.js +3 -0
  51. package/dist/hooks/instructions-loaded.js +707 -97
  52. package/dist/hooks/notification.js +699 -89
  53. package/dist/hooks/post-compact.js +757 -109
  54. package/dist/hooks/pre-compact.js +1061 -244
  55. package/dist/hooks/pre-tool-use.js +787 -130
  56. package/dist/hooks/prompt-ingest-worker.js +242 -101
  57. package/dist/hooks/prompt-submit.js +1121 -299
  58. package/dist/hooks/response-ingest-worker.js +242 -101
  59. package/dist/hooks/session-end.js +4063 -397
  60. package/dist/hooks/session-start.js +1071 -254
  61. package/dist/hooks/stop.js +768 -120
  62. package/dist/hooks/subagent-stop.js +757 -109
  63. package/dist/hooks/summary-worker.js +1706 -1011
  64. package/dist/index.js +1821 -1098
  65. package/dist/lib/agent-config.js +167 -0
  66. package/dist/lib/cloud-sync.js +932 -88
  67. package/dist/lib/consolidation.js +2 -1
  68. package/dist/lib/database.js +642 -87
  69. package/dist/lib/db-daemon-client.js +503 -0
  70. package/dist/lib/device-registry.js +547 -7
  71. package/dist/lib/embedder.js +14 -28
  72. package/dist/lib/employee-templates.js +84 -74
  73. package/dist/lib/employees.js +9 -0
  74. package/dist/lib/exe-daemon-client.js +16 -29
  75. package/dist/lib/exe-daemon.js +2733 -1575
  76. package/dist/lib/hybrid-search.js +995 -228
  77. package/dist/lib/identity.js +87 -67
  78. package/dist/lib/keychain.js +9 -1
  79. package/dist/lib/messaging.js +103 -40
  80. package/dist/lib/reminders.js +91 -74
  81. package/dist/lib/runtime-table.js +16 -0
  82. package/dist/lib/schedules.js +96 -2
  83. package/dist/lib/session-wrappers.js +22 -0
  84. package/dist/lib/skill-learning.js +103 -85
  85. package/dist/lib/store.js +234 -73
  86. package/dist/lib/tasks.js +348 -134
  87. package/dist/lib/tmux-routing.js +422 -208
  88. package/dist/lib/token-spend.js +273 -0
  89. package/dist/lib/ws-client.js +11 -0
  90. package/dist/mcp/server.js +5742 -696
  91. package/dist/mcp/tools/complete-reminder.js +94 -77
  92. package/dist/mcp/tools/create-reminder.js +94 -77
  93. package/dist/mcp/tools/create-task.js +375 -152
  94. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  95. package/dist/mcp/tools/list-reminders.js +94 -77
  96. package/dist/mcp/tools/list-tasks.js +99 -31
  97. package/dist/mcp/tools/send-message.js +108 -45
  98. package/dist/mcp/tools/update-task.js +162 -77
  99. package/dist/runtime/index.js +1075 -258
  100. package/dist/tui/App.js +1333 -506
  101. package/package.json +6 -1
  102. package/src/commands/exe/agent-config.md +27 -0
  103. package/src/commands/exe/cc-doctor.md +10 -0
@@ -244,6 +244,7 @@ __export(employees_exports, {
244
244
  DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
245
245
  EMPLOYEES_PATH: () => EMPLOYEES_PATH,
246
246
  addEmployee: () => addEmployee,
247
+ baseAgentName: () => baseAgentName,
247
248
  canCoordinate: () => canCoordinate,
248
249
  getCoordinatorEmployee: () => getCoordinatorEmployee,
249
250
  getCoordinatorName: () => getCoordinatorName,
@@ -340,6 +341,14 @@ function hasRole(agentName, role) {
340
341
  const emp = getEmployee(employees, agentName);
341
342
  return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
342
343
  }
344
+ function baseAgentName(name, employees) {
345
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
346
+ if (!match) return name;
347
+ const base = match[1];
348
+ const roster = employees ?? loadEmployeesSync();
349
+ if (getEmployee(roster, base)) return base;
350
+ return name;
351
+ }
343
352
  function isMultiInstance(agentName, employees) {
344
353
  const roster = employees ?? loadEmployeesSync();
345
354
  const emp = getEmployee(roster, agentName);
@@ -582,17 +591,53 @@ var init_provider_table = __esm({
582
591
  }
583
592
  });
584
593
 
585
- // src/lib/intercom-queue.ts
586
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
594
+ // src/lib/runtime-table.ts
595
+ var RUNTIME_TABLE;
596
+ var init_runtime_table = __esm({
597
+ "src/lib/runtime-table.ts"() {
598
+ "use strict";
599
+ RUNTIME_TABLE = {
600
+ codex: {
601
+ binary: "codex",
602
+ launchMode: "exec",
603
+ autoApproveFlag: "--full-auto",
604
+ inlineFlag: "--no-alt-screen",
605
+ apiKeyEnv: "OPENAI_API_KEY",
606
+ defaultModel: "gpt-5.4"
607
+ }
608
+ };
609
+ }
610
+ });
611
+
612
+ // src/lib/agent-config.ts
613
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
587
614
  import path5 from "path";
615
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
616
+ var init_agent_config = __esm({
617
+ "src/lib/agent-config.ts"() {
618
+ "use strict";
619
+ init_config();
620
+ init_runtime_table();
621
+ AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
622
+ DEFAULT_MODELS = {
623
+ claude: "claude-opus-4",
624
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
625
+ opencode: "minimax-m2.7"
626
+ };
627
+ }
628
+ });
629
+
630
+ // src/lib/intercom-queue.ts
631
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
632
+ import path6 from "path";
588
633
  import os4 from "os";
589
634
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
590
635
  var init_intercom_queue = __esm({
591
636
  "src/lib/intercom-queue.ts"() {
592
637
  "use strict";
593
- QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
638
+ QUEUE_PATH = path6.join(os4.homedir(), ".exe-os", "intercom-queue.json");
594
639
  TTL_MS = 60 * 60 * 1e3;
595
- INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
640
+ INTERCOM_LOG = path6.join(os4.homedir(), ".exe-os", "intercom.log");
596
641
  }
597
642
  });
598
643
 
@@ -651,6 +696,443 @@ var init_db_retry = __esm({
651
696
  }
652
697
  });
653
698
 
699
+ // src/lib/exe-daemon-client.ts
700
+ import net from "net";
701
+ import { spawn } from "child_process";
702
+ import { randomUUID } from "crypto";
703
+ import { existsSync as existsSync5, unlinkSync as unlinkSync3, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
704
+ import path7 from "path";
705
+ import { fileURLToPath } from "url";
706
+ function handleData(chunk) {
707
+ _buffer += chunk.toString();
708
+ if (_buffer.length > MAX_BUFFER) {
709
+ _buffer = "";
710
+ return;
711
+ }
712
+ let newlineIdx;
713
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
714
+ const line = _buffer.slice(0, newlineIdx).trim();
715
+ _buffer = _buffer.slice(newlineIdx + 1);
716
+ if (!line) continue;
717
+ try {
718
+ const response = JSON.parse(line);
719
+ const id = response.id;
720
+ if (!id) continue;
721
+ const entry = _pending.get(id);
722
+ if (entry) {
723
+ clearTimeout(entry.timer);
724
+ _pending.delete(id);
725
+ entry.resolve(response);
726
+ }
727
+ } catch {
728
+ }
729
+ }
730
+ }
731
+ function cleanupStaleFiles() {
732
+ if (existsSync5(PID_PATH)) {
733
+ try {
734
+ const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
735
+ if (pid > 0) {
736
+ try {
737
+ process.kill(pid, 0);
738
+ return;
739
+ } catch {
740
+ }
741
+ }
742
+ } catch {
743
+ }
744
+ try {
745
+ unlinkSync3(PID_PATH);
746
+ } catch {
747
+ }
748
+ try {
749
+ unlinkSync3(SOCKET_PATH);
750
+ } catch {
751
+ }
752
+ }
753
+ }
754
+ function findPackageRoot() {
755
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
756
+ const { root } = path7.parse(dir);
757
+ while (dir !== root) {
758
+ if (existsSync5(path7.join(dir, "package.json"))) return dir;
759
+ dir = path7.dirname(dir);
760
+ }
761
+ return null;
762
+ }
763
+ function spawnDaemon() {
764
+ const pkgRoot = findPackageRoot();
765
+ if (!pkgRoot) {
766
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
767
+ return;
768
+ }
769
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
770
+ if (!existsSync5(daemonPath)) {
771
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
772
+ `);
773
+ return;
774
+ }
775
+ const resolvedPath = daemonPath;
776
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
777
+ `);
778
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
779
+ let stderrFd = "ignore";
780
+ try {
781
+ stderrFd = openSync(logPath, "a");
782
+ } catch {
783
+ }
784
+ const child = spawn(process.execPath, [resolvedPath], {
785
+ detached: true,
786
+ stdio: ["ignore", "ignore", stderrFd],
787
+ env: {
788
+ ...process.env,
789
+ TMUX: void 0,
790
+ // Daemon is global — must not inherit session scope
791
+ TMUX_PANE: void 0,
792
+ // Prevents resolveExeSession() from scoping to one session
793
+ EXE_DAEMON_SOCK: SOCKET_PATH,
794
+ EXE_DAEMON_PID: PID_PATH
795
+ }
796
+ });
797
+ child.unref();
798
+ if (typeof stderrFd === "number") {
799
+ try {
800
+ closeSync(stderrFd);
801
+ } catch {
802
+ }
803
+ }
804
+ }
805
+ function acquireSpawnLock() {
806
+ try {
807
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
808
+ closeSync(fd);
809
+ return true;
810
+ } catch {
811
+ try {
812
+ const stat = statSync(SPAWN_LOCK_PATH);
813
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
814
+ try {
815
+ unlinkSync3(SPAWN_LOCK_PATH);
816
+ } catch {
817
+ }
818
+ try {
819
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
820
+ closeSync(fd);
821
+ return true;
822
+ } catch {
823
+ }
824
+ }
825
+ } catch {
826
+ }
827
+ return false;
828
+ }
829
+ }
830
+ function releaseSpawnLock() {
831
+ try {
832
+ unlinkSync3(SPAWN_LOCK_PATH);
833
+ } catch {
834
+ }
835
+ }
836
+ function connectToSocket() {
837
+ return new Promise((resolve) => {
838
+ if (_socket && _connected) {
839
+ resolve(true);
840
+ return;
841
+ }
842
+ const socket = net.createConnection({ path: SOCKET_PATH });
843
+ const connectTimeout = setTimeout(() => {
844
+ socket.destroy();
845
+ resolve(false);
846
+ }, 2e3);
847
+ socket.on("connect", () => {
848
+ clearTimeout(connectTimeout);
849
+ _socket = socket;
850
+ _connected = true;
851
+ _buffer = "";
852
+ socket.on("data", handleData);
853
+ socket.on("close", () => {
854
+ _connected = false;
855
+ _socket = null;
856
+ for (const [id, entry] of _pending) {
857
+ clearTimeout(entry.timer);
858
+ _pending.delete(id);
859
+ entry.resolve({ error: "Connection closed" });
860
+ }
861
+ });
862
+ socket.on("error", () => {
863
+ _connected = false;
864
+ _socket = null;
865
+ });
866
+ resolve(true);
867
+ });
868
+ socket.on("error", () => {
869
+ clearTimeout(connectTimeout);
870
+ resolve(false);
871
+ });
872
+ });
873
+ }
874
+ async function connectEmbedDaemon() {
875
+ if (_socket && _connected) return true;
876
+ if (await connectToSocket()) return true;
877
+ if (acquireSpawnLock()) {
878
+ try {
879
+ cleanupStaleFiles();
880
+ spawnDaemon();
881
+ } finally {
882
+ releaseSpawnLock();
883
+ }
884
+ }
885
+ const start = Date.now();
886
+ let delay2 = 100;
887
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
888
+ await new Promise((r) => setTimeout(r, delay2));
889
+ if (await connectToSocket()) return true;
890
+ delay2 = Math.min(delay2 * 2, 3e3);
891
+ }
892
+ return false;
893
+ }
894
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
895
+ return new Promise((resolve) => {
896
+ if (!_socket || !_connected) {
897
+ resolve({ error: "Not connected" });
898
+ return;
899
+ }
900
+ const id = randomUUID();
901
+ const timer = setTimeout(() => {
902
+ _pending.delete(id);
903
+ resolve({ error: "Request timeout" });
904
+ }, timeoutMs);
905
+ _pending.set(id, { resolve, timer });
906
+ try {
907
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
908
+ } catch {
909
+ clearTimeout(timer);
910
+ _pending.delete(id);
911
+ resolve({ error: "Write failed" });
912
+ }
913
+ });
914
+ }
915
+ function isClientConnected() {
916
+ return _connected;
917
+ }
918
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
919
+ var init_exe_daemon_client = __esm({
920
+ "src/lib/exe-daemon-client.ts"() {
921
+ "use strict";
922
+ init_config();
923
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
924
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
925
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
926
+ SPAWN_LOCK_STALE_MS = 3e4;
927
+ CONNECT_TIMEOUT_MS = 15e3;
928
+ REQUEST_TIMEOUT_MS = 3e4;
929
+ _socket = null;
930
+ _connected = false;
931
+ _buffer = "";
932
+ _pending = /* @__PURE__ */ new Map();
933
+ MAX_BUFFER = 1e7;
934
+ }
935
+ });
936
+
937
+ // src/lib/daemon-protocol.ts
938
+ function serializeValue(v) {
939
+ if (v === null || v === void 0) return null;
940
+ if (typeof v === "bigint") return Number(v);
941
+ if (typeof v === "boolean") return v ? 1 : 0;
942
+ if (v instanceof Uint8Array) {
943
+ return { __blob: Buffer.from(v).toString("base64") };
944
+ }
945
+ if (ArrayBuffer.isView(v)) {
946
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
947
+ }
948
+ if (v instanceof ArrayBuffer) {
949
+ return { __blob: Buffer.from(v).toString("base64") };
950
+ }
951
+ if (typeof v === "string" || typeof v === "number") return v;
952
+ return String(v);
953
+ }
954
+ function deserializeValue(v) {
955
+ if (v === null) return null;
956
+ if (typeof v === "object" && v !== null && "__blob" in v) {
957
+ const buf = Buffer.from(v.__blob, "base64");
958
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
959
+ }
960
+ return v;
961
+ }
962
+ function deserializeResultSet(srs) {
963
+ const rows = srs.rows.map((obj) => {
964
+ const values = srs.columns.map(
965
+ (col) => deserializeValue(obj[col] ?? null)
966
+ );
967
+ const row = values;
968
+ for (let i = 0; i < srs.columns.length; i++) {
969
+ const col = srs.columns[i];
970
+ if (col !== void 0) {
971
+ row[col] = values[i] ?? null;
972
+ }
973
+ }
974
+ Object.defineProperty(row, "length", {
975
+ value: values.length,
976
+ enumerable: false
977
+ });
978
+ return row;
979
+ });
980
+ return {
981
+ columns: srs.columns,
982
+ columnTypes: srs.columnTypes ?? [],
983
+ rows,
984
+ rowsAffected: srs.rowsAffected,
985
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
986
+ toJSON: () => ({
987
+ columns: srs.columns,
988
+ columnTypes: srs.columnTypes ?? [],
989
+ rows: srs.rows,
990
+ rowsAffected: srs.rowsAffected,
991
+ lastInsertRowid: srs.lastInsertRowid
992
+ })
993
+ };
994
+ }
995
+ var init_daemon_protocol = __esm({
996
+ "src/lib/daemon-protocol.ts"() {
997
+ "use strict";
998
+ }
999
+ });
1000
+
1001
+ // src/lib/db-daemon-client.ts
1002
+ var db_daemon_client_exports = {};
1003
+ __export(db_daemon_client_exports, {
1004
+ createDaemonDbClient: () => createDaemonDbClient,
1005
+ initDaemonDbClient: () => initDaemonDbClient
1006
+ });
1007
+ function normalizeStatement(stmt) {
1008
+ if (typeof stmt === "string") {
1009
+ return { sql: stmt, args: [] };
1010
+ }
1011
+ const sql = stmt.sql;
1012
+ let args = [];
1013
+ if (Array.isArray(stmt.args)) {
1014
+ args = stmt.args.map((v) => serializeValue(v));
1015
+ } else if (stmt.args && typeof stmt.args === "object") {
1016
+ const named = {};
1017
+ for (const [key, val] of Object.entries(stmt.args)) {
1018
+ named[key] = serializeValue(val);
1019
+ }
1020
+ return { sql, args: named };
1021
+ }
1022
+ return { sql, args };
1023
+ }
1024
+ function createDaemonDbClient(fallbackClient) {
1025
+ let _useDaemon = false;
1026
+ const client = {
1027
+ async execute(stmt) {
1028
+ if (!_useDaemon || !isClientConnected()) {
1029
+ return fallbackClient.execute(stmt);
1030
+ }
1031
+ const { sql, args } = normalizeStatement(stmt);
1032
+ const response = await sendDaemonRequest({
1033
+ type: "db-execute",
1034
+ sql,
1035
+ args
1036
+ });
1037
+ if (response.error) {
1038
+ const errMsg = String(response.error);
1039
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1040
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1041
+ `);
1042
+ return fallbackClient.execute(stmt);
1043
+ }
1044
+ throw new Error(errMsg);
1045
+ }
1046
+ if (response.db) {
1047
+ return deserializeResultSet(response.db);
1048
+ }
1049
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
1050
+ return fallbackClient.execute(stmt);
1051
+ },
1052
+ async batch(stmts, mode) {
1053
+ if (!_useDaemon || !isClientConnected()) {
1054
+ return fallbackClient.batch(stmts, mode);
1055
+ }
1056
+ const statements = stmts.map(normalizeStatement);
1057
+ const response = await sendDaemonRequest({
1058
+ type: "db-batch",
1059
+ statements,
1060
+ mode: mode ?? "deferred"
1061
+ });
1062
+ if (response.error) {
1063
+ const errMsg = String(response.error);
1064
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
1065
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1066
+ `);
1067
+ return fallbackClient.batch(stmts, mode);
1068
+ }
1069
+ throw new Error(errMsg);
1070
+ }
1071
+ const batchResults = response["db-batch"];
1072
+ if (batchResults) {
1073
+ return batchResults.map(deserializeResultSet);
1074
+ }
1075
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
1076
+ return fallbackClient.batch(stmts, mode);
1077
+ },
1078
+ // Transaction support — delegate to fallback (transactions need direct connection)
1079
+ async transaction(mode) {
1080
+ return fallbackClient.transaction(mode);
1081
+ },
1082
+ // executeMultiple — delegate to fallback (used only for schema migrations)
1083
+ async executeMultiple(sql) {
1084
+ return fallbackClient.executeMultiple(sql);
1085
+ },
1086
+ // migrate — delegate to fallback
1087
+ async migrate(stmts) {
1088
+ return fallbackClient.migrate(stmts);
1089
+ },
1090
+ // Sync mode — delegate to fallback
1091
+ sync() {
1092
+ return fallbackClient.sync();
1093
+ },
1094
+ close() {
1095
+ _useDaemon = false;
1096
+ },
1097
+ get closed() {
1098
+ return fallbackClient.closed;
1099
+ },
1100
+ get protocol() {
1101
+ return fallbackClient.protocol;
1102
+ }
1103
+ };
1104
+ return {
1105
+ ...client,
1106
+ /** Enable daemon routing (call after confirming daemon is connected) */
1107
+ _enableDaemon() {
1108
+ _useDaemon = true;
1109
+ },
1110
+ /** Check if daemon routing is active */
1111
+ _isDaemonActive() {
1112
+ return _useDaemon && isClientConnected();
1113
+ }
1114
+ };
1115
+ }
1116
+ async function initDaemonDbClient(fallbackClient) {
1117
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1118
+ const connected = await connectEmbedDaemon();
1119
+ if (!connected) {
1120
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1121
+ return null;
1122
+ }
1123
+ const client = createDaemonDbClient(fallbackClient);
1124
+ client._enableDaemon();
1125
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1126
+ return client;
1127
+ }
1128
+ var init_db_daemon_client = __esm({
1129
+ "src/lib/db-daemon-client.ts"() {
1130
+ "use strict";
1131
+ init_exe_daemon_client();
1132
+ init_daemon_protocol();
1133
+ }
1134
+ });
1135
+
654
1136
  // src/lib/database.ts
655
1137
  var database_exports = {};
656
1138
  __export(database_exports, {
@@ -659,6 +1141,7 @@ __export(database_exports, {
659
1141
  ensureSchema: () => ensureSchema,
660
1142
  getClient: () => getClient,
661
1143
  getRawClient: () => getRawClient,
1144
+ initDaemonClient: () => initDaemonClient,
662
1145
  initDatabase: () => initDatabase,
663
1146
  initTurso: () => initTurso,
664
1147
  isInitialized: () => isInitialized
@@ -686,8 +1169,27 @@ function getClient() {
686
1169
  if (!_resilientClient) {
687
1170
  throw new Error("Database client not initialized. Call initDatabase() first.");
688
1171
  }
1172
+ if (process.env.EXE_IS_DAEMON === "1") {
1173
+ return _resilientClient;
1174
+ }
1175
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
1176
+ return _daemonClient;
1177
+ }
689
1178
  return _resilientClient;
690
1179
  }
1180
+ async function initDaemonClient() {
1181
+ if (process.env.EXE_IS_DAEMON === "1") return;
1182
+ if (!_resilientClient) return;
1183
+ try {
1184
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1185
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
1186
+ } catch (err) {
1187
+ process.stderr.write(
1188
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
1189
+ `
1190
+ );
1191
+ }
1192
+ }
691
1193
  function getRawClient() {
692
1194
  if (!_client) {
693
1195
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1174,6 +1676,12 @@ async function ensureSchema() {
1174
1676
  } catch {
1175
1677
  }
1176
1678
  }
1679
+ try {
1680
+ await client.execute(
1681
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1682
+ );
1683
+ } catch {
1684
+ }
1177
1685
  await client.executeMultiple(`
1178
1686
  CREATE TABLE IF NOT EXISTS entities (
1179
1687
  id TEXT PRIMARY KEY,
@@ -1226,7 +1734,30 @@ async function ensureSchema() {
1226
1734
  entity_id TEXT NOT NULL,
1227
1735
  PRIMARY KEY (hyperedge_id, entity_id)
1228
1736
  );
1737
+
1738
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1739
+ name,
1740
+ content=entities,
1741
+ content_rowid=rowid
1742
+ );
1743
+
1744
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1745
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1746
+ END;
1747
+
1748
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1749
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1750
+ END;
1751
+
1752
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1753
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1754
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1755
+ END;
1229
1756
  `);
1757
+ try {
1758
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1759
+ } catch {
1760
+ }
1230
1761
  await client.executeMultiple(`
1231
1762
  CREATE TABLE IF NOT EXISTS entity_aliases (
1232
1763
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1407,6 +1938,33 @@ async function ensureSchema() {
1407
1938
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1408
1939
  ON conversations(channel_id);
1409
1940
  `);
1941
+ await client.executeMultiple(`
1942
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1943
+ session_uuid TEXT PRIMARY KEY,
1944
+ agent_id TEXT NOT NULL,
1945
+ session_name TEXT,
1946
+ task_id TEXT,
1947
+ project_name TEXT,
1948
+ started_at TEXT NOT NULL
1949
+ );
1950
+
1951
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1952
+ ON session_agent_map(agent_id);
1953
+ `);
1954
+ try {
1955
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1956
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1957
+ await client.execute({
1958
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1959
+ SELECT session_id, agent_id, '', MIN(timestamp)
1960
+ FROM memories
1961
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1962
+ GROUP BY session_id, agent_id`,
1963
+ args: []
1964
+ });
1965
+ }
1966
+ } catch {
1967
+ }
1410
1968
  try {
1411
1969
  await client.execute({
1412
1970
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1540,15 +2098,41 @@ async function ensureSchema() {
1540
2098
  });
1541
2099
  } catch {
1542
2100
  }
2101
+ for (const col of [
2102
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2103
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2104
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2105
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2106
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2107
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2108
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2109
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2110
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2111
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2112
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2113
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2114
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2115
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2116
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2117
+ ]) {
2118
+ try {
2119
+ await client.execute(col);
2120
+ } catch {
2121
+ }
2122
+ }
1543
2123
  }
1544
2124
  async function disposeDatabase() {
2125
+ if (_daemonClient) {
2126
+ _daemonClient.close();
2127
+ _daemonClient = null;
2128
+ }
1545
2129
  if (_client) {
1546
2130
  _client.close();
1547
2131
  _client = null;
1548
2132
  _resilientClient = null;
1549
2133
  }
1550
2134
  }
1551
- var _client, _resilientClient, initTurso, disposeTurso;
2135
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1552
2136
  var init_database = __esm({
1553
2137
  "src/lib/database.ts"() {
1554
2138
  "use strict";
@@ -1556,30 +2140,31 @@ var init_database = __esm({
1556
2140
  init_employees();
1557
2141
  _client = null;
1558
2142
  _resilientClient = null;
2143
+ _daemonClient = null;
1559
2144
  initTurso = initDatabase;
1560
2145
  disposeTurso = disposeDatabase;
1561
2146
  }
1562
2147
  });
1563
2148
 
1564
2149
  // src/lib/license.ts
1565
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
1566
- import { randomUUID } from "crypto";
1567
- import path6 from "path";
2150
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2151
+ import { randomUUID as randomUUID2 } from "crypto";
2152
+ import path8 from "path";
1568
2153
  import { jwtVerify, importSPKI } from "jose";
1569
2154
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
1570
2155
  var init_license = __esm({
1571
2156
  "src/lib/license.ts"() {
1572
2157
  "use strict";
1573
2158
  init_config();
1574
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1575
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1576
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2159
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2160
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2161
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
1577
2162
  }
1578
2163
  });
1579
2164
 
1580
2165
  // src/lib/plan-limits.ts
1581
- import { readFileSync as readFileSync6, existsSync as existsSync5 } from "fs";
1582
- import path7 from "path";
2166
+ import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
2167
+ import path9 from "path";
1583
2168
  var CACHE_PATH2;
1584
2169
  var init_plan_limits = __esm({
1585
2170
  "src/lib/plan-limits.ts"() {
@@ -1588,15 +2173,15 @@ var init_plan_limits = __esm({
1588
2173
  init_employees();
1589
2174
  init_license();
1590
2175
  init_config();
1591
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2176
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
1592
2177
  }
1593
2178
  });
1594
2179
 
1595
2180
  // src/lib/tmux-routing.ts
1596
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync6, appendFileSync } from "fs";
1597
- import path8 from "path";
2181
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync8, appendFileSync } from "fs";
2182
+ import path10 from "path";
1598
2183
  import os5 from "os";
1599
- import { fileURLToPath } from "url";
2184
+ import { fileURLToPath as fileURLToPath2 } from "url";
1600
2185
  function getMySession() {
1601
2186
  return getTransport().getMySession();
1602
2187
  }
@@ -1608,7 +2193,7 @@ function extractRootExe(name) {
1608
2193
  }
1609
2194
  function getParentExe(sessionKey) {
1610
2195
  try {
1611
- const data = JSON.parse(readFileSync7(path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2196
+ const data = JSON.parse(readFileSync9(path10.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
1612
2197
  return data.parentExe || null;
1613
2198
  } catch {
1614
2199
  return null;
@@ -1637,13 +2222,15 @@ var init_tmux_routing = __esm({
1637
2222
  init_cc_agent_support();
1638
2223
  init_mcp_prefix();
1639
2224
  init_provider_table();
2225
+ init_agent_config();
2226
+ init_runtime_table();
1640
2227
  init_intercom_queue();
1641
2228
  init_plan_limits();
1642
2229
  init_employees();
1643
- SPAWN_LOCK_DIR = path8.join(os5.homedir(), ".exe-os", "spawn-locks");
1644
- SESSION_CACHE = path8.join(os5.homedir(), ".exe-os", "session-cache");
1645
- INTERCOM_LOG2 = path8.join(os5.homedir(), ".exe-os", "intercom.log");
1646
- DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
2230
+ SPAWN_LOCK_DIR = path10.join(os5.homedir(), ".exe-os", "spawn-locks");
2231
+ SESSION_CACHE = path10.join(os5.homedir(), ".exe-os", "session-cache");
2232
+ INTERCOM_LOG2 = path10.join(os5.homedir(), ".exe-os", "intercom.log");
2233
+ DEBOUNCE_FILE = path10.join(SESSION_CACHE, "intercom-debounce.json");
1647
2234
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1648
2235
  }
1649
2236
  });
@@ -1686,8 +2273,8 @@ __export(cto_delegation_gate_exports, {
1686
2273
  scratchpadPath: () => scratchpadPath
1687
2274
  });
1688
2275
  import os6 from "os";
1689
- import path9 from "path";
1690
- import { existsSync as existsSync7, readFileSync as readFileSync8, statSync } from "fs";
2276
+ import path11 from "path";
2277
+ import { existsSync as existsSync9, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
1691
2278
  function resolveGatedAgent() {
1692
2279
  try {
1693
2280
  const employees = loadEmployeesSync();
@@ -1701,12 +2288,12 @@ function getGatedAgent() {
1701
2288
  return resolveGatedAgent() || GATED_AGENT;
1702
2289
  }
1703
2290
  function toWorkspaceRelative(filePath, cwd = process.cwd()) {
1704
- const normalized = path9.normalize(filePath);
1705
- if (normalized.startsWith(cwd + path9.sep)) {
2291
+ const normalized = path11.normalize(filePath);
2292
+ if (normalized.startsWith(cwd + path11.sep)) {
1706
2293
  return normalized.slice(cwd.length + 1);
1707
2294
  }
1708
- if (path9.isAbsolute(normalized)) {
1709
- return path9.basename(normalized);
2295
+ if (path11.isAbsolute(normalized)) {
2296
+ return path11.basename(normalized);
1710
2297
  }
1711
2298
  return normalized;
1712
2299
  }
@@ -1715,8 +2302,8 @@ function isDockerfile(basename) {
1715
2302
  }
1716
2303
  function classifyPath(filePath, cwd) {
1717
2304
  const rel = toWorkspaceRelative(filePath, cwd);
1718
- const basename = path9.basename(rel);
1719
- const ext = path9.extname(rel).toLowerCase();
2305
+ const basename = path11.basename(rel);
2306
+ const ext = path11.extname(rel).toLowerCase();
1720
2307
  if (ext === ".md") {
1721
2308
  for (const prefix of EXEMPT_MD_DIR_PREFIXES) {
1722
2309
  if (rel.startsWith(prefix)) return "exempt";
@@ -1759,7 +2346,7 @@ async function hasRecentEngineerDispatch(now = Date.now()) {
1759
2346
  }
1760
2347
  }
1761
2348
  function scratchpadPath(sessionId, homeDir = os6.homedir()) {
1762
- return path9.join(
2349
+ return path11.join(
1763
2350
  homeDir,
1764
2351
  ".exe-os",
1765
2352
  "session-cache",
@@ -1769,11 +2356,11 @@ function scratchpadPath(sessionId, homeDir = os6.homedir()) {
1769
2356
  function hasValidScratchpadEscape(sessionId, now = Date.now(), homeDir = os6.homedir()) {
1770
2357
  const paths = [scratchpadPath(sessionId, homeDir)];
1771
2358
  for (const filePath of paths) {
1772
- if (!existsSync7(filePath)) continue;
2359
+ if (!existsSync9(filePath)) continue;
1773
2360
  try {
1774
- const stat = statSync(filePath);
2361
+ const stat = statSync2(filePath);
1775
2362
  if (now - stat.mtimeMs > SCRATCHPAD_MAX_AGE_MS) continue;
1776
- const body = readFileSync8(filePath, "utf-8");
2363
+ const body = readFileSync10(filePath, "utf-8");
1777
2364
  if (SCRATCHPAD_ESCAPE_PATTERN.test(body)) return true;
1778
2365
  } catch {
1779
2366
  }
@@ -1864,14 +2451,14 @@ var init_memory = __esm({
1864
2451
 
1865
2452
  // src/lib/keychain.ts
1866
2453
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1867
- import { existsSync as existsSync8 } from "fs";
1868
- import path10 from "path";
2454
+ import { existsSync as existsSync10 } from "fs";
2455
+ import path12 from "path";
1869
2456
  import os7 from "os";
1870
2457
  function getKeyDir() {
1871
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path10.join(os7.homedir(), ".exe-os");
2458
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path12.join(os7.homedir(), ".exe-os");
1872
2459
  }
1873
2460
  function getKeyPath() {
1874
- return path10.join(getKeyDir(), "master.key");
2461
+ return path12.join(getKeyDir(), "master.key");
1875
2462
  }
1876
2463
  async function tryKeytar() {
1877
2464
  try {
@@ -1892,13 +2479,21 @@ async function getMasterKey() {
1892
2479
  }
1893
2480
  }
1894
2481
  const keyPath = getKeyPath();
1895
- if (!existsSync8(keyPath)) {
2482
+ if (!existsSync10(keyPath)) {
2483
+ process.stderr.write(
2484
+ `[keychain] Key not found at ${keyPath} (HOME=${os7.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2485
+ `
2486
+ );
1896
2487
  return null;
1897
2488
  }
1898
2489
  try {
1899
2490
  const content = await readFile3(keyPath, "utf-8");
1900
2491
  return Buffer.from(content.trim(), "base64");
1901
- } catch {
2492
+ } catch (err) {
2493
+ process.stderr.write(
2494
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
2495
+ `
2496
+ );
1902
2497
  return null;
1903
2498
  }
1904
2499
  }
@@ -1979,13 +2574,13 @@ __export(shard_manager_exports, {
1979
2574
  listShards: () => listShards,
1980
2575
  shardExists: () => shardExists
1981
2576
  });
1982
- import path11 from "path";
1983
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
2577
+ import path13 from "path";
2578
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync2 } from "fs";
1984
2579
  import { createClient as createClient2 } from "@libsql/client";
1985
2580
  function initShardManager(encryptionKey) {
1986
2581
  _encryptionKey = encryptionKey;
1987
- if (!existsSync9(SHARDS_DIR)) {
1988
- mkdirSync5(SHARDS_DIR, { recursive: true });
2582
+ if (!existsSync11(SHARDS_DIR)) {
2583
+ mkdirSync6(SHARDS_DIR, { recursive: true });
1989
2584
  }
1990
2585
  _shardingEnabled = true;
1991
2586
  }
@@ -2005,7 +2600,7 @@ function getShardClient(projectName) {
2005
2600
  }
2006
2601
  const cached = _shards.get(safeName);
2007
2602
  if (cached) return cached;
2008
- const dbPath = path11.join(SHARDS_DIR, `${safeName}.db`);
2603
+ const dbPath = path13.join(SHARDS_DIR, `${safeName}.db`);
2009
2604
  const client = createClient2({
2010
2605
  url: `file:${dbPath}`,
2011
2606
  encryptionKey: _encryptionKey
@@ -2015,10 +2610,10 @@ function getShardClient(projectName) {
2015
2610
  }
2016
2611
  function shardExists(projectName) {
2017
2612
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2018
- return existsSync9(path11.join(SHARDS_DIR, `${safeName}.db`));
2613
+ return existsSync11(path13.join(SHARDS_DIR, `${safeName}.db`));
2019
2614
  }
2020
2615
  function listShards() {
2021
- if (!existsSync9(SHARDS_DIR)) return [];
2616
+ if (!existsSync11(SHARDS_DIR)) return [];
2022
2617
  return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2023
2618
  }
2024
2619
  async function ensureShardSchema(client) {
@@ -2204,7 +2799,7 @@ var init_shard_manager = __esm({
2204
2799
  "src/lib/shard-manager.ts"() {
2205
2800
  "use strict";
2206
2801
  init_config();
2207
- SHARDS_DIR = path11.join(EXE_AI_DIR, "shards");
2802
+ SHARDS_DIR = path13.join(EXE_AI_DIR, "shards");
2208
2803
  _shards = /* @__PURE__ */ new Map();
2209
2804
  _encryptionKey = null;
2210
2805
  _shardingEnabled = false;
@@ -2329,7 +2924,7 @@ __export(global_procedures_exports, {
2329
2924
  loadGlobalProcedures: () => loadGlobalProcedures,
2330
2925
  storeGlobalProcedure: () => storeGlobalProcedure
2331
2926
  });
2332
- import { randomUUID as randomUUID2 } from "crypto";
2927
+ import { randomUUID as randomUUID3 } from "crypto";
2333
2928
  async function loadGlobalProcedures() {
2334
2929
  const client = getClient();
2335
2930
  const result = await client.execute({
@@ -2358,7 +2953,7 @@ ${sections.join("\n\n")}
2358
2953
  `;
2359
2954
  }
2360
2955
  async function storeGlobalProcedure(input2) {
2361
- const id = randomUUID2();
2956
+ const id = randomUUID3();
2362
2957
  const now = (/* @__PURE__ */ new Date()).toISOString();
2363
2958
  const client = getClient();
2364
2959
  await client.execute({
@@ -2409,6 +3004,7 @@ __export(store_exports, {
2409
3004
  vectorToBlob: () => vectorToBlob,
2410
3005
  writeMemory: () => writeMemory
2411
3006
  });
3007
+ import { createHash } from "crypto";
2412
3008
  function isBusyError2(err) {
2413
3009
  if (err instanceof Error) {
2414
3010
  const msg = err.message.toLowerCase();
@@ -2482,12 +3078,52 @@ function classifyTier(record) {
2482
3078
  if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
2483
3079
  return 3;
2484
3080
  }
3081
+ function inferFilePaths(record) {
3082
+ if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
3083
+ const firstLine = record.raw_text.split("\n")[0] ?? "";
3084
+ const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
3085
+ return match ? JSON.stringify([match[1]]) : null;
3086
+ }
3087
+ function inferCommitHash(record) {
3088
+ if (record.tool_name !== "Bash") return null;
3089
+ const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
3090
+ return match ? match[1] : null;
3091
+ }
3092
+ function inferLanguageType(record) {
3093
+ const text = record.raw_text;
3094
+ if (!text || text.length < 10) return null;
3095
+ const trimmed = text.trimStart();
3096
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
3097
+ if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
3098
+ if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
3099
+ if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
3100
+ return "mixed";
3101
+ }
3102
+ function inferDomain(record) {
3103
+ const proj = (record.project_name ?? "").toLowerCase();
3104
+ if (proj.includes("marketing") || proj.includes("content")) return "marketing";
3105
+ if (proj.includes("crm") || proj.includes("customer")) return "customer";
3106
+ return null;
3107
+ }
2485
3108
  async function writeMemory(record) {
2486
3109
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
2487
3110
  throw new Error(
2488
3111
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
2489
3112
  );
2490
3113
  }
3114
+ const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3115
+ if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3116
+ return;
3117
+ }
3118
+ try {
3119
+ const client = getClient();
3120
+ const existing = await client.execute({
3121
+ sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3122
+ args: [contentHash, record.agent_id]
3123
+ });
3124
+ if (existing.rows.length > 0) return;
3125
+ } catch {
3126
+ }
2491
3127
  const dbRow = {
2492
3128
  id: record.id,
2493
3129
  agent_id: record.agent_id,
@@ -2517,7 +3153,23 @@ async function writeMemory(record) {
2517
3153
  supersedes_id: record.supersedes_id ?? null,
2518
3154
  draft: record.draft ? 1 : 0,
2519
3155
  memory_type: record.memory_type ?? "raw",
2520
- trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
3156
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3157
+ content_hash: contentHash,
3158
+ intent: record.intent ?? null,
3159
+ outcome: record.outcome ?? null,
3160
+ domain: record.domain ?? inferDomain(record),
3161
+ referenced_entities: record.referenced_entities ?? null,
3162
+ retrieval_count: record.retrieval_count ?? 0,
3163
+ chain_position: record.chain_position ?? null,
3164
+ review_status: record.review_status ?? null,
3165
+ context_window_pct: record.context_window_pct ?? null,
3166
+ file_paths: record.file_paths ?? inferFilePaths(record),
3167
+ commit_hash: record.commit_hash ?? inferCommitHash(record),
3168
+ duration_ms: record.duration_ms ?? null,
3169
+ token_cost: record.token_cost ?? null,
3170
+ audience: record.audience ?? null,
3171
+ language_type: record.language_type ?? inferLanguageType(record),
3172
+ parent_memory_id: record.parent_memory_id ?? null
2521
3173
  };
2522
3174
  _pendingRecords.push(dbRow);
2523
3175
  orgBus.emit({
@@ -2575,80 +3227,85 @@ async function flushBatch() {
2575
3227
  const draft = row.draft ? 1 : 0;
2576
3228
  const memoryType = row.memory_type ?? "raw";
2577
3229
  const trajectory = row.trajectory ?? null;
2578
- return {
2579
- sql: hasVector ? `INSERT OR IGNORE INTO memories
2580
- (id, agent_id, agent_role, session_id, timestamp,
3230
+ const contentHash = row.content_hash ?? null;
3231
+ const intent = row.intent ?? null;
3232
+ const outcome = row.outcome ?? null;
3233
+ const domain = row.domain ?? null;
3234
+ const referencedEntities = row.referenced_entities ?? null;
3235
+ const retrievalCount = row.retrieval_count ?? 0;
3236
+ const chainPosition = row.chain_position ?? null;
3237
+ const reviewStatus = row.review_status ?? null;
3238
+ const contextWindowPct = row.context_window_pct ?? null;
3239
+ const filePaths = row.file_paths ?? null;
3240
+ const commitHash = row.commit_hash ?? null;
3241
+ const durationMs = row.duration_ms ?? null;
3242
+ const tokenCost = row.token_cost ?? null;
3243
+ const audience = row.audience ?? null;
3244
+ const languageType = row.language_type ?? null;
3245
+ const parentMemoryId = row.parent_memory_id ?? null;
3246
+ const cols = `id, agent_id, agent_role, session_id, timestamp,
2581
3247
  tool_name, project_name,
2582
3248
  has_error, raw_text, vector, version, task_id, importance, status,
2583
3249
  confidence, last_accessed,
2584
3250
  workspace_id, document_id, user_id, char_offset, page_number,
2585
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
2586
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
2587
- (id, agent_id, agent_role, session_id, timestamp,
2588
- tool_name, project_name,
2589
- has_error, raw_text, vector, version, task_id, importance, status,
2590
- confidence, last_accessed,
2591
- workspace_id, document_id, user_id, char_offset, page_number,
2592
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
2593
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2594
- args: hasVector ? [
2595
- row.id,
2596
- row.agent_id,
2597
- row.agent_role,
2598
- row.session_id,
2599
- row.timestamp,
2600
- row.tool_name,
2601
- row.project_name,
2602
- row.has_error,
2603
- row.raw_text,
2604
- vectorToBlob(row.vector),
2605
- row.version,
2606
- taskId,
2607
- importance,
2608
- status,
2609
- confidence,
2610
- lastAccessed,
2611
- workspaceId,
2612
- documentId,
2613
- userId,
2614
- charOffset,
2615
- pageNumber,
2616
- sourcePath,
2617
- sourceType,
2618
- tier,
2619
- supersedesId,
2620
- draft,
2621
- memoryType,
2622
- trajectory
2623
- ] : [
2624
- row.id,
2625
- row.agent_id,
2626
- row.agent_role,
2627
- row.session_id,
2628
- row.timestamp,
2629
- row.tool_name,
2630
- row.project_name,
2631
- row.has_error,
2632
- row.raw_text,
2633
- row.version,
2634
- taskId,
2635
- importance,
2636
- status,
2637
- confidence,
2638
- lastAccessed,
2639
- workspaceId,
2640
- documentId,
2641
- userId,
2642
- charOffset,
2643
- pageNumber,
2644
- sourcePath,
2645
- sourceType,
2646
- tier,
2647
- supersedesId,
2648
- draft,
2649
- memoryType,
2650
- trajectory
2651
- ]
3251
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
3252
+ intent, outcome, domain, referenced_entities, retrieval_count,
3253
+ chain_position, review_status, context_window_pct, file_paths, commit_hash,
3254
+ duration_ms, token_cost, audience, language_type, parent_memory_id`;
3255
+ const metaArgs = [
3256
+ intent,
3257
+ outcome,
3258
+ domain,
3259
+ referencedEntities,
3260
+ retrievalCount,
3261
+ chainPosition,
3262
+ reviewStatus,
3263
+ contextWindowPct,
3264
+ filePaths,
3265
+ commitHash,
3266
+ durationMs,
3267
+ tokenCost,
3268
+ audience,
3269
+ languageType,
3270
+ parentMemoryId
3271
+ ];
3272
+ const baseArgs = [
3273
+ row.id,
3274
+ row.agent_id,
3275
+ row.agent_role,
3276
+ row.session_id,
3277
+ row.timestamp,
3278
+ row.tool_name,
3279
+ row.project_name,
3280
+ row.has_error,
3281
+ row.raw_text
3282
+ ];
3283
+ const sharedArgs = [
3284
+ row.version,
3285
+ taskId,
3286
+ importance,
3287
+ status,
3288
+ confidence,
3289
+ lastAccessed,
3290
+ workspaceId,
3291
+ documentId,
3292
+ userId,
3293
+ charOffset,
3294
+ pageNumber,
3295
+ sourcePath,
3296
+ sourceType,
3297
+ tier,
3298
+ supersedesId,
3299
+ draft,
3300
+ memoryType,
3301
+ trajectory,
3302
+ contentHash
3303
+ ];
3304
+ return {
3305
+ sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
3306
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
3307
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
3308
+ args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
2652
3309
  };
2653
3310
  };
2654
3311
  const globalClient = getClient();
@@ -2908,7 +3565,7 @@ __export(review_gate_exports, {
2908
3565
  runReviewGate: () => runReviewGate
2909
3566
  });
2910
3567
  import { execSync as execSync5 } from "child_process";
2911
- import { existsSync as existsSync10 } from "fs";
3568
+ import { existsSync as existsSync12 } from "fs";
2912
3569
  function checkCommitsExist(taskCreatedAt) {
2913
3570
  try {
2914
3571
  const since = new Date(taskCreatedAt).toISOString();
@@ -2971,7 +3628,7 @@ function checkTestCoverage(taskCreatedAt) {
2971
3628
  const testPath = file.replace(/^src\//, "tests/").replace(/\.ts$/, ".test.ts");
2972
3629
  const baseName = file.split("/").pop()?.replace(/\.ts$/, "") ?? "";
2973
3630
  const testDir = testPath.substring(0, testPath.lastIndexOf("/"));
2974
- const hasDirectTest = existsSync10(testPath);
3631
+ const hasDirectTest = existsSync12(testPath);
2975
3632
  let hasRelatedTest = false;
2976
3633
  try {
2977
3634
  const related = execSync5(
@@ -3025,9 +3682,9 @@ var init_review_gate = __esm({
3025
3682
  });
3026
3683
 
3027
3684
  // src/adapters/claude/hooks/pre-tool-use.ts
3028
- import { existsSync as existsSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
3685
+ import { existsSync as existsSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
3029
3686
  import { execSync as execSync6 } from "child_process";
3030
- import path12 from "path";
3687
+ import path14 from "path";
3031
3688
 
3032
3689
  // src/adapters/claude/active-agent.ts
3033
3690
  init_config();
@@ -3140,25 +3797,25 @@ if (!process.env.AGENT_ID) {
3140
3797
  }
3141
3798
  var DELEGATION_TASK_THRESHOLD = 3;
3142
3799
  var CTO_ROLES = ["CTO", "executive"];
3143
- var CACHE_DIR2 = path12.join(EXE_AI_DIR, "session-cache");
3800
+ var CACHE_DIR2 = path14.join(EXE_AI_DIR, "session-cache");
3144
3801
  var timeout = setTimeout(() => {
3145
3802
  process.exit(0);
3146
3803
  }, 5e3);
3147
3804
  timeout.unref();
3148
3805
  function getDelegationFlagPath() {
3149
- return path12.join(CACHE_DIR2, `delegation-checkpoint-${getSessionKey()}.json`);
3806
+ return path14.join(CACHE_DIR2, `delegation-checkpoint-${getSessionKey()}.json`);
3150
3807
  }
3151
3808
  function hasDelegationFired() {
3152
3809
  try {
3153
- return existsSync11(getDelegationFlagPath());
3810
+ return existsSync13(getDelegationFlagPath());
3154
3811
  } catch {
3155
3812
  return false;
3156
3813
  }
3157
3814
  }
3158
3815
  function markDelegationFired() {
3159
3816
  try {
3160
- mkdirSync6(CACHE_DIR2, { recursive: true });
3161
- writeFileSync6(getDelegationFlagPath(), JSON.stringify({ fired: true }));
3817
+ mkdirSync7(CACHE_DIR2, { recursive: true });
3818
+ writeFileSync7(getDelegationFlagPath(), JSON.stringify({ fired: true }));
3162
3819
  } catch {
3163
3820
  }
3164
3821
  }