@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -64,6 +64,21 @@ var init_secure_files = __esm({
64
64
  });
65
65
 
66
66
  // src/lib/config.ts
67
+ var config_exports = {};
68
+ __export(config_exports, {
69
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
70
+ CONFIG_PATH: () => CONFIG_PATH,
71
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
72
+ DB_PATH: () => DB_PATH,
73
+ EXE_AI_DIR: () => EXE_AI_DIR,
74
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
75
+ MODELS_DIR: () => MODELS_DIR,
76
+ loadConfig: () => loadConfig,
77
+ loadConfigFrom: () => loadConfigFrom,
78
+ loadConfigSync: () => loadConfigSync,
79
+ migrateConfig: () => migrateConfig,
80
+ saveConfig: () => saveConfig
81
+ });
67
82
  import { readFile, writeFile } from "fs/promises";
68
83
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
69
84
  import path2 from "path";
@@ -167,6 +182,46 @@ async function loadConfig() {
167
182
  return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
168
183
  }
169
184
  }
185
+ function loadConfigSync() {
186
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
187
+ const configPath = path2.join(dir, "config.json");
188
+ if (!existsSync2(configPath)) {
189
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
190
+ }
191
+ try {
192
+ const raw = readFileSync(configPath, "utf-8");
193
+ let parsed = JSON.parse(raw);
194
+ parsed = migrateLegacyConfig(parsed);
195
+ const { config: migratedCfg } = migrateConfig(parsed);
196
+ normalizeScalingRoadmap(migratedCfg);
197
+ normalizeSessionLifecycle(migratedCfg);
198
+ normalizeAutoUpdate(migratedCfg);
199
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
200
+ } catch {
201
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
202
+ }
203
+ }
204
+ async function saveConfig(config) {
205
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
206
+ await ensurePrivateDir(dir);
207
+ const configPath = path2.join(dir, "config.json");
208
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
209
+ await enforcePrivateFile(configPath);
210
+ }
211
+ async function loadConfigFrom(configPath) {
212
+ const raw = await readFile(configPath, "utf-8");
213
+ try {
214
+ let parsed = JSON.parse(raw);
215
+ parsed = migrateLegacyConfig(parsed);
216
+ const { config: migratedCfg } = migrateConfig(parsed);
217
+ normalizeScalingRoadmap(migratedCfg);
218
+ normalizeSessionLifecycle(migratedCfg);
219
+ normalizeAutoUpdate(migratedCfg);
220
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
221
+ } catch {
222
+ return { ...DEFAULT_CONFIG };
223
+ }
224
+ }
170
225
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
171
226
  var init_config = __esm({
172
227
  "src/lib/config.ts"() {
@@ -324,6 +379,7 @@ var session_registry_exports = {};
324
379
  __export(session_registry_exports, {
325
380
  listSessions: () => listSessions,
326
381
  pruneStaleSessions: () => pruneStaleSessions,
382
+ refreshSessionProject: () => refreshSessionProject,
327
383
  registerSession: () => registerSession
328
384
  });
329
385
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
@@ -344,6 +400,16 @@ function registerSession(entry) {
344
400
  }
345
401
  writeFileSync2(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
346
402
  }
403
+ function refreshSessionProject(windowName, projectDir) {
404
+ const sessions = listSessions();
405
+ const entry = sessions.find((s) => s.windowName === windowName);
406
+ if (!entry || entry.projectDir === projectDir) return;
407
+ entry.projectDir = projectDir;
408
+ try {
409
+ writeFileSync2(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
410
+ } catch {
411
+ }
412
+ }
347
413
  function listSessions() {
348
414
  try {
349
415
  const raw = readFileSync3(REGISTRY_PATH, "utf8");
@@ -702,7 +768,7 @@ var init_agent_config = __esm({
702
768
  init_secure_files();
703
769
  AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
704
770
  DEFAULT_MODELS = {
705
- claude: "claude-opus-4",
771
+ claude: "claude-opus-4.6",
706
772
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
707
773
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
708
774
  };
@@ -884,9 +950,9 @@ var MAX_RETRIES2, BASE_DELAY_MS, MAX_JITTER_MS;
884
950
  var init_db_retry = __esm({
885
951
  "src/lib/db-retry.ts"() {
886
952
  "use strict";
887
- MAX_RETRIES2 = 3;
888
- BASE_DELAY_MS = 200;
889
- MAX_JITTER_MS = 300;
953
+ MAX_RETRIES2 = 5;
954
+ BASE_DELAY_MS = 250;
955
+ MAX_JITTER_MS = 400;
890
956
  }
891
957
  });
892
958
 
@@ -1474,6 +1540,15 @@ var init_database_adapter = __esm({
1474
1540
  }
1475
1541
  });
1476
1542
 
1543
+ // src/types/memory.ts
1544
+ var EMBEDDING_DIM;
1545
+ var init_memory = __esm({
1546
+ "src/types/memory.ts"() {
1547
+ "use strict";
1548
+ EMBEDDING_DIM = 1024;
1549
+ }
1550
+ });
1551
+
1477
1552
  // src/lib/daemon-auth.ts
1478
1553
  import crypto from "crypto";
1479
1554
  import path8 from "path";
@@ -1748,6 +1823,9 @@ async function connectEmbedDaemon() {
1748
1823
  }
1749
1824
  return false;
1750
1825
  }
1826
+ function sendRequest(texts, priority) {
1827
+ return sendDaemonRequest({ texts, priority });
1828
+ }
1751
1829
  function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1752
1830
  return new Promise((resolve) => {
1753
1831
  if (!_socket || !_connected) {
@@ -1770,10 +1848,150 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1770
1848
  }
1771
1849
  });
1772
1850
  }
1851
+ async function pingDaemon() {
1852
+ if (!_socket || !_connected) return null;
1853
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
1854
+ if (response.health) {
1855
+ return response.health;
1856
+ }
1857
+ return null;
1858
+ }
1859
+ function killAndRespawnDaemon() {
1860
+ if (!acquireSpawnLock()) {
1861
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1862
+ if (_socket) {
1863
+ _socket.destroy();
1864
+ _socket = null;
1865
+ }
1866
+ _connected = false;
1867
+ _buffer = "";
1868
+ return;
1869
+ }
1870
+ try {
1871
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1872
+ if (existsSync8(PID_PATH)) {
1873
+ try {
1874
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1875
+ if (pid > 0) {
1876
+ try {
1877
+ process.kill(pid, "SIGKILL");
1878
+ } catch {
1879
+ }
1880
+ }
1881
+ } catch {
1882
+ }
1883
+ }
1884
+ if (_socket) {
1885
+ _socket.destroy();
1886
+ _socket = null;
1887
+ }
1888
+ _connected = false;
1889
+ _buffer = "";
1890
+ try {
1891
+ unlinkSync2(PID_PATH);
1892
+ } catch {
1893
+ }
1894
+ try {
1895
+ unlinkSync2(SOCKET_PATH);
1896
+ } catch {
1897
+ }
1898
+ spawnDaemon();
1899
+ } finally {
1900
+ releaseSpawnLock();
1901
+ }
1902
+ }
1903
+ function isDaemonTooYoung() {
1904
+ try {
1905
+ const stat = statSync(PID_PATH);
1906
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1907
+ } catch {
1908
+ return false;
1909
+ }
1910
+ }
1911
+ async function retryThenRestart(doRequest, label) {
1912
+ const result = await doRequest();
1913
+ if (!result.error) {
1914
+ _consecutiveFailures = 0;
1915
+ return result;
1916
+ }
1917
+ _consecutiveFailures++;
1918
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1919
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1920
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1921
+ `);
1922
+ await new Promise((r) => setTimeout(r, delayMs));
1923
+ if (!_connected) {
1924
+ if (!await connectToSocket()) continue;
1925
+ }
1926
+ const retry = await doRequest();
1927
+ if (!retry.error) {
1928
+ _consecutiveFailures = 0;
1929
+ return retry;
1930
+ }
1931
+ _consecutiveFailures++;
1932
+ }
1933
+ if (isDaemonTooYoung()) {
1934
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1935
+ `);
1936
+ return { error: result.error };
1937
+ }
1938
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1939
+ `);
1940
+ killAndRespawnDaemon();
1941
+ const start = Date.now();
1942
+ let delay2 = 200;
1943
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1944
+ await new Promise((r) => setTimeout(r, delay2));
1945
+ if (await connectToSocket()) break;
1946
+ delay2 = Math.min(delay2 * 2, 3e3);
1947
+ }
1948
+ if (!_connected) return { error: "Daemon restart failed" };
1949
+ const final = await doRequest();
1950
+ if (!final.error) _consecutiveFailures = 0;
1951
+ return final;
1952
+ }
1953
+ async function embedViaClient(text, priority = "high") {
1954
+ if (!_connected && !await connectEmbedDaemon()) return null;
1955
+ _requestCount++;
1956
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1957
+ const health = await pingDaemon();
1958
+ if (!health && !isDaemonTooYoung()) {
1959
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1960
+ `);
1961
+ killAndRespawnDaemon();
1962
+ const start = Date.now();
1963
+ let d = 200;
1964
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1965
+ await new Promise((r) => setTimeout(r, d));
1966
+ if (await connectToSocket()) break;
1967
+ d = Math.min(d * 2, 3e3);
1968
+ }
1969
+ if (!_connected) return null;
1970
+ }
1971
+ }
1972
+ const result = await retryThenRestart(
1973
+ () => sendRequest([text], priority),
1974
+ "Embed"
1975
+ );
1976
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
1977
+ }
1978
+ function disconnectClient() {
1979
+ if (_socket) {
1980
+ _socket.destroy();
1981
+ _socket = null;
1982
+ }
1983
+ _connected = false;
1984
+ _buffer = "";
1985
+ for (const [id, entry] of _pending) {
1986
+ clearTimeout(entry.timer);
1987
+ _pending.delete(id);
1988
+ entry.resolve({ error: "Client disconnected" });
1989
+ }
1990
+ }
1773
1991
  function isClientConnected() {
1774
1992
  return _connected;
1775
1993
  }
1776
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1994
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
1777
1995
  var init_exe_daemon_client = __esm({
1778
1996
  "src/lib/exe-daemon-client.ts"() {
1779
1997
  "use strict";
@@ -1789,6 +2007,12 @@ var init_exe_daemon_client = __esm({
1789
2007
  _socket = null;
1790
2008
  _connected = false;
1791
2009
  _buffer = "";
2010
+ _requestCount = 0;
2011
+ _consecutiveFailures = 0;
2012
+ HEALTH_CHECK_INTERVAL = 100;
2013
+ MAX_RETRIES_BEFORE_RESTART = 3;
2014
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
2015
+ MIN_DAEMON_AGE_MS = 3e4;
1792
2016
  _pending = /* @__PURE__ */ new Map();
1793
2017
  MAX_BUFFER = 1e7;
1794
2018
  }
@@ -1896,7 +2120,7 @@ function createDaemonDbClient(fallbackClient) {
1896
2120
  });
1897
2121
  if (response.error) {
1898
2122
  const errMsg = String(response.error);
1899
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
2123
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1900
2124
  process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1901
2125
  `);
1902
2126
  return fallbackClient.execute(stmt);
@@ -1921,7 +2145,7 @@ function createDaemonDbClient(fallbackClient) {
1921
2145
  });
1922
2146
  if (response.error) {
1923
2147
  const errMsg = String(response.error);
1924
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
2148
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1925
2149
  process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1926
2150
  `);
1927
2151
  return fallbackClient.batch(stmts, mode);
@@ -2044,7 +2268,7 @@ async function initDatabase(config) {
2044
2268
  });
2045
2269
  }, 3e4);
2046
2270
  _walCheckpointTimer.unref();
2047
- if (process.env.DATABASE_URL) {
2271
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
2048
2272
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
2049
2273
  }
2050
2274
  }
@@ -2055,7 +2279,7 @@ function getClient() {
2055
2279
  if (!_adapterClient) {
2056
2280
  throw new Error("Database client not initialized. Call initDatabase() first.");
2057
2281
  }
2058
- if (process.env.DATABASE_URL) {
2282
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
2059
2283
  return _adapterClient;
2060
2284
  }
2061
2285
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -2067,9 +2291,11 @@ function getClient() {
2067
2291
  return _resilientClient;
2068
2292
  }
2069
2293
  async function initDaemonClient() {
2070
- if (process.env.DATABASE_URL) return;
2294
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
2071
2295
  if (process.env.EXE_IS_DAEMON === "1") return;
2296
+ if (process.env.VITEST) return;
2072
2297
  if (!_resilientClient) return;
2298
+ if (_daemonClient) return;
2073
2299
  try {
2074
2300
  const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
2075
2301
  _daemonClient = await initDaemonDbClient2(_resilientClient);
@@ -2218,6 +2444,13 @@ async function ensureSchema() {
2218
2444
  });
2219
2445
  } catch {
2220
2446
  }
2447
+ try {
2448
+ await client.execute({
2449
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
2450
+ args: []
2451
+ });
2452
+ } catch {
2453
+ }
2221
2454
  try {
2222
2455
  await client.execute({
2223
2456
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -3088,6 +3321,7 @@ var init_database = __esm({
3088
3321
  init_db_retry();
3089
3322
  init_employees();
3090
3323
  init_database_adapter();
3324
+ init_memory();
3091
3325
  _client = null;
3092
3326
  _resilientClient = null;
3093
3327
  _walCheckpointTimer = null;
@@ -3622,15 +3856,20 @@ async function createTaskCore(input) {
3622
3856
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3623
3857
  const resolved = resolveExeSession2();
3624
3858
  if (resolved && input.projectName) {
3625
- const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
3626
- const sessionProject = getSessionProject2(resolved);
3627
- if (sessionProject && sessionProject !== input.projectName) {
3628
- scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
3629
- process.stderr.write(`[create_task] ${scopeMismatchWarning}
3630
- `);
3631
- earlySessionScope = null;
3632
- } else {
3859
+ const isCoordinatorSession = !resolved.includes("-");
3860
+ if (isCoordinatorSession) {
3633
3861
  earlySessionScope = resolved;
3862
+ } else {
3863
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
3864
+ const sessionProject = getSessionProject2(resolved);
3865
+ if (sessionProject && sessionProject !== input.projectName) {
3866
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
3867
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
3868
+ `);
3869
+ earlySessionScope = null;
3870
+ } else {
3871
+ earlySessionScope = resolved;
3872
+ }
3634
3873
  }
3635
3874
  } else {
3636
3875
  earlySessionScope = resolved;
@@ -4515,6 +4754,10 @@ async function dispatchTaskToEmployee(input) {
4515
4754
  if (transport.isAlive(sessionName)) {
4516
4755
  const result = sendIntercom(sessionName);
4517
4756
  const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
4757
+ process.stderr.write(
4758
+ `[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
4759
+ `
4760
+ );
4518
4761
  return { dispatched, session: sessionName, crossProject };
4519
4762
  } else {
4520
4763
  const projectDir = input.projectDir ?? process.cwd();
@@ -4523,11 +4766,15 @@ async function dispatchTaskToEmployee(input) {
4523
4766
  });
4524
4767
  if (result.status === "failed") {
4525
4768
  process.stderr.write(
4526
- `[dispatch] Failed to spawn ${input.assignedTo}: ${result.error}
4769
+ `[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
4527
4770
  `
4528
4771
  );
4529
4772
  return { dispatched: "session_missing" };
4530
4773
  }
4774
+ process.stderr.write(
4775
+ `[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
4776
+ `
4777
+ );
4531
4778
  return { dispatched: "spawned", session: result.sessionName, crossProject };
4532
4779
  }
4533
4780
  } catch {
@@ -4537,7 +4784,13 @@ async function dispatchTaskToEmployee(input) {
4537
4784
  function notifyTaskDone() {
4538
4785
  try {
4539
4786
  const key = getSessionKey();
4540
- if (key && !process.env.VITEST) notifyParentExe(key);
4787
+ if (key && !process.env.VITEST) {
4788
+ notifyParentExe(key);
4789
+ process.stderr.write(
4790
+ `[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
4791
+ `
4792
+ );
4793
+ }
4541
4794
  } catch {
4542
4795
  }
4543
4796
  }
@@ -4558,6 +4811,74 @@ var init_tasks_notify = __esm({
4558
4811
  }
4559
4812
  });
4560
4813
 
4814
+ // src/lib/embedder.ts
4815
+ var embedder_exports = {};
4816
+ __export(embedder_exports, {
4817
+ disposeEmbedder: () => disposeEmbedder,
4818
+ embed: () => embed,
4819
+ embedDirect: () => embedDirect,
4820
+ getEmbedder: () => getEmbedder
4821
+ });
4822
+ async function getEmbedder() {
4823
+ const ok = await connectEmbedDaemon();
4824
+ if (!ok) {
4825
+ throw new Error(
4826
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
4827
+ );
4828
+ }
4829
+ }
4830
+ async function embed(text) {
4831
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
4832
+ const vector = await embedViaClient(text, priority);
4833
+ if (!vector) {
4834
+ throw new Error(
4835
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
4836
+ );
4837
+ }
4838
+ if (vector.length !== EMBEDDING_DIM) {
4839
+ throw new Error(
4840
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
4841
+ );
4842
+ }
4843
+ return vector;
4844
+ }
4845
+ async function disposeEmbedder() {
4846
+ disconnectClient();
4847
+ }
4848
+ async function embedDirect(text) {
4849
+ const llamaCpp = await import("node-llama-cpp");
4850
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4851
+ const { existsSync: existsSync17 } = await import("fs");
4852
+ const path21 = await import("path");
4853
+ const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4854
+ if (!existsSync17(modelPath)) {
4855
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4856
+ }
4857
+ const llama = await llamaCpp.getLlama();
4858
+ const model = await llama.loadModel({ modelPath });
4859
+ const context = await model.createEmbeddingContext();
4860
+ try {
4861
+ const embedding = await context.getEmbeddingFor(text);
4862
+ const vector = Array.from(embedding.vector);
4863
+ if (vector.length !== EMBEDDING_DIM) {
4864
+ throw new Error(
4865
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
4866
+ );
4867
+ }
4868
+ return vector;
4869
+ } finally {
4870
+ await context.dispose();
4871
+ await model.dispose();
4872
+ }
4873
+ }
4874
+ var init_embedder = __esm({
4875
+ "src/lib/embedder.ts"() {
4876
+ "use strict";
4877
+ init_memory();
4878
+ init_exe_daemon_client();
4879
+ }
4880
+ });
4881
+
4561
4882
  // src/lib/behaviors.ts
4562
4883
  var behaviors_exports = {};
4563
4884
  __export(behaviors_exports, {
@@ -4571,17 +4892,24 @@ async function storeBehavior(opts) {
4571
4892
  const client = getClient();
4572
4893
  const id = crypto5.randomUUID();
4573
4894
  const now = (/* @__PURE__ */ new Date()).toISOString();
4895
+ let vector = null;
4896
+ try {
4897
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
4898
+ const vec = await embed2(opts.content);
4899
+ vector = new Float32Array(vec);
4900
+ } catch {
4901
+ }
4574
4902
  await client.execute({
4575
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
4576
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
4577
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
4903
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
4904
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
4905
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
4578
4906
  });
4579
4907
  return id;
4580
4908
  }
4581
4909
  async function listBehaviors(agentId, projectName, limit = 30) {
4582
4910
  const client = getClient();
4583
4911
  const result = await client.execute({
4584
- sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at
4912
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
4585
4913
  FROM behaviors
4586
4914
  WHERE agent_id = ? AND active = 1
4587
4915
  AND (project_name IS NULL OR project_name = ?)
@@ -4602,13 +4930,14 @@ async function listBehaviors(agentId, projectName, limit = 30) {
4602
4930
  content: String(r.content),
4603
4931
  active: Number(r.active),
4604
4932
  created_at: String(r.created_at),
4605
- updated_at: String(r.updated_at)
4933
+ updated_at: String(r.updated_at),
4934
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
4606
4935
  }));
4607
4936
  }
4608
4937
  async function listBehaviorsByDomain(agentId, domain) {
4609
4938
  const client = getClient();
4610
4939
  const result = await client.execute({
4611
- sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at
4940
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
4612
4941
  FROM behaviors
4613
4942
  WHERE agent_id = ? AND domain = ? AND active = 1`,
4614
4943
  args: [agentId, domain]
@@ -4622,7 +4951,8 @@ async function listBehaviorsByDomain(agentId, domain) {
4622
4951
  content: String(r.content),
4623
4952
  active: Number(r.active),
4624
4953
  created_at: String(r.created_at),
4625
- updated_at: String(r.updated_at)
4954
+ updated_at: String(r.updated_at),
4955
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
4626
4956
  }));
4627
4957
  }
4628
4958
  async function deactivateBehavior(id) {
@@ -5732,7 +6062,7 @@ function isSessionBusy(sessionName) {
5732
6062
  function isExeSession(sessionName) {
5733
6063
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
5734
6064
  const coordinatorName = getCoordinatorName();
5735
- return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
6065
+ return matchesBaseWithInstance(coordinatorName);
5736
6066
  }
5737
6067
  function sendIntercom(targetSession) {
5738
6068
  const transport = getTransport();
@@ -6236,15 +6566,6 @@ var init_tmux_routing = __esm({
6236
6566
  }
6237
6567
  });
6238
6568
 
6239
- // src/types/memory.ts
6240
- var EMBEDDING_DIM;
6241
- var init_memory = __esm({
6242
- "src/types/memory.ts"() {
6243
- "use strict";
6244
- EMBEDDING_DIM = 1024;
6245
- }
6246
- });
6247
-
6248
6569
  // src/lib/keychain.ts
6249
6570
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
6250
6571
  import { existsSync as existsSync15 } from "fs";
@@ -6732,6 +7053,68 @@ var init_platform_procedures = __esm({
6732
7053
  domain: "architecture",
6733
7054
  priority: "p0",
6734
7055
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
7056
+ },
7057
+ // --- MCP is the ONLY data interface ---
7058
+ {
7059
+ title: "MCP disconnect \u2014 ask the user, never work around it",
7060
+ domain: "workflow",
7061
+ priority: "p0",
7062
+ content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
7063
+ },
7064
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
7065
+ {
7066
+ title: "MCP tools \u2014 memory and search",
7067
+ domain: "tool-use",
7068
+ priority: "p1",
7069
+ content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
7070
+ },
7071
+ {
7072
+ title: "MCP tools \u2014 task orchestration",
7073
+ domain: "tool-use",
7074
+ priority: "p1",
7075
+ content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
7076
+ },
7077
+ {
7078
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
7079
+ domain: "tool-use",
7080
+ priority: "p1",
7081
+ content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
7082
+ },
7083
+ {
7084
+ title: "MCP tools \u2014 identity, behavior, and decisions",
7085
+ domain: "tool-use",
7086
+ priority: "p1",
7087
+ content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
7088
+ },
7089
+ {
7090
+ title: "MCP tools \u2014 communication and messaging",
7091
+ domain: "tool-use",
7092
+ priority: "p1",
7093
+ content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
7094
+ },
7095
+ {
7096
+ title: "MCP tools \u2014 wiki, documents, and content",
7097
+ domain: "tool-use",
7098
+ priority: "p1",
7099
+ content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
7100
+ },
7101
+ {
7102
+ title: "MCP tools \u2014 system, operations, and admin",
7103
+ domain: "tool-use",
7104
+ priority: "p1",
7105
+ content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
7106
+ },
7107
+ {
7108
+ title: "MCP tools \u2014 config, licensing, and team",
7109
+ domain: "tool-use",
7110
+ priority: "p1",
7111
+ content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
7112
+ },
7113
+ {
7114
+ title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
7115
+ domain: "tool-use",
7116
+ priority: "p1",
7117
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
6735
7118
  }
6736
7119
  ];
6737
7120
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -6881,6 +7264,11 @@ async function initStore(options) {
6881
7264
  encryptionKey: hexKey
6882
7265
  });
6883
7266
  await retryOnBusy2(() => ensureSchema(), "ensureSchema");
7267
+ try {
7268
+ const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7269
+ await initDaemonClient2();
7270
+ } catch {
7271
+ }
6884
7272
  if (!options?.lightweight) {
6885
7273
  try {
6886
7274
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));