@askexenow/exe-os 0.8.33 → 0.8.37

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 (89) hide show
  1. package/dist/bin/backfill-conversations.js +341 -349
  2. package/dist/bin/backfill-responses.js +81 -13
  3. package/dist/bin/backfill-vectors.js +72 -12
  4. package/dist/bin/cleanup-stale-review-tasks.js +63 -3
  5. package/dist/bin/cli.js +1737 -1117
  6. package/dist/bin/exe-assign.js +89 -19
  7. package/dist/bin/exe-boot.js +951 -101
  8. package/dist/bin/exe-call.js +61 -2
  9. package/dist/bin/exe-dispatch.js +61 -13
  10. package/dist/bin/exe-doctor.js +63 -3
  11. package/dist/bin/exe-export-behaviors.js +71 -3
  12. package/dist/bin/exe-forget.js +69 -4
  13. package/dist/bin/exe-gateway.js +178 -45
  14. package/dist/bin/exe-heartbeat.js +79 -14
  15. package/dist/bin/exe-kill.js +71 -3
  16. package/dist/bin/exe-launch-agent.js +148 -14
  17. package/dist/bin/exe-link.js +1437 -0
  18. package/dist/bin/exe-new-employee.js +98 -13
  19. package/dist/bin/exe-pending-messages.js +74 -8
  20. package/dist/bin/exe-pending-notifications.js +63 -3
  21. package/dist/bin/exe-pending-reviews.js +77 -11
  22. package/dist/bin/exe-rename.js +1287 -0
  23. package/dist/bin/exe-review.js +73 -5
  24. package/dist/bin/exe-search.js +88 -14
  25. package/dist/bin/exe-session-cleanup.js +102 -28
  26. package/dist/bin/exe-status.js +64 -4
  27. package/dist/bin/exe-team.js +64 -4
  28. package/dist/bin/git-sweep.js +80 -5
  29. package/dist/bin/graph-backfill.js +71 -3
  30. package/dist/bin/graph-export.js +71 -3
  31. package/dist/bin/install.js +38 -8
  32. package/dist/bin/scan-tasks.js +80 -5
  33. package/dist/bin/setup.js +128 -10
  34. package/dist/bin/shard-migrate.js +71 -3
  35. package/dist/bin/wiki-sync.js +71 -3
  36. package/dist/gateway/index.js +179 -46
  37. package/dist/hooks/bug-report-worker.js +254 -28
  38. package/dist/hooks/commit-complete.js +80 -5
  39. package/dist/hooks/error-recall.js +89 -15
  40. package/dist/hooks/exe-heartbeat-hook.js +1 -1
  41. package/dist/hooks/ingest-worker.js +185 -51
  42. package/dist/hooks/ingest.js +1 -1
  43. package/dist/hooks/instructions-loaded.js +81 -6
  44. package/dist/hooks/notification.js +81 -6
  45. package/dist/hooks/post-compact.js +81 -6
  46. package/dist/hooks/pre-compact.js +81 -6
  47. package/dist/hooks/pre-tool-use.js +423 -196
  48. package/dist/hooks/prompt-ingest-worker.js +91 -23
  49. package/dist/hooks/prompt-submit.js +159 -45
  50. package/dist/hooks/response-ingest-worker.js +96 -23
  51. package/dist/hooks/session-end.js +81 -6
  52. package/dist/hooks/session-start.js +89 -15
  53. package/dist/hooks/stop.js +81 -6
  54. package/dist/hooks/subagent-stop.js +81 -6
  55. package/dist/hooks/summary-worker.js +807 -55
  56. package/dist/index.js +198 -60
  57. package/dist/lib/cloud-sync.js +703 -18
  58. package/dist/lib/consolidation.js +4 -4
  59. package/dist/lib/database.js +64 -2
  60. package/dist/lib/device-registry.js +70 -3
  61. package/dist/lib/employee-templates.js +26 -0
  62. package/dist/lib/employees.js +34 -1
  63. package/dist/lib/exe-daemon.js +207 -74
  64. package/dist/lib/hybrid-search.js +88 -14
  65. package/dist/lib/identity-templates.js +51 -0
  66. package/dist/lib/identity.js +3 -3
  67. package/dist/lib/messaging.js +65 -17
  68. package/dist/lib/reminders.js +3 -3
  69. package/dist/lib/schedules.js +63 -3
  70. package/dist/lib/skill-learning.js +3 -3
  71. package/dist/lib/status-brief.js +63 -5
  72. package/dist/lib/store.js +73 -4
  73. package/dist/lib/task-router.js +4 -2
  74. package/dist/lib/tasks.js +95 -28
  75. package/dist/lib/tmux-routing.js +92 -23
  76. package/dist/mcp/server.js +800 -74
  77. package/dist/mcp/tools/complete-reminder.js +3 -3
  78. package/dist/mcp/tools/create-reminder.js +3 -3
  79. package/dist/mcp/tools/create-task.js +198 -31
  80. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  81. package/dist/mcp/tools/list-reminders.js +3 -3
  82. package/dist/mcp/tools/list-tasks.js +19 -9
  83. package/dist/mcp/tools/send-message.js +69 -21
  84. package/dist/mcp/tools/update-task.js +28 -18
  85. package/dist/runtime/index.js +166 -28
  86. package/dist/tui/App.js +193 -40
  87. package/package.json +7 -3
  88. package/src/commands/exe/afk.md +116 -0
  89. package/src/commands/exe/rename.md +12 -0
@@ -324,7 +324,7 @@ function listShards() {
324
324
  }
325
325
  async function ensureShardSchema(client) {
326
326
  await client.execute("PRAGMA journal_mode = WAL");
327
- await client.execute("PRAGMA busy_timeout = 5000");
327
+ await client.execute("PRAGMA busy_timeout = 30000");
328
328
  try {
329
329
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
330
330
  } catch {
@@ -512,7 +512,7 @@ var init_shard_manager = __esm({
512
512
  import net from "net";
513
513
  import { spawn } from "child_process";
514
514
  import { randomUUID as randomUUID2 } from "crypto";
515
- import { existsSync as existsSync7, unlinkSync, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
515
+ import { existsSync as existsSync7, unlinkSync, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
516
516
  import path8 from "path";
517
517
  import { fileURLToPath } from "url";
518
518
  function handleData(chunk) {
@@ -537,7 +537,7 @@ function handleData(chunk) {
537
537
  function cleanupStaleFiles() {
538
538
  if (existsSync7(PID_PATH)) {
539
539
  try {
540
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
540
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
541
541
  if (pid > 0) {
542
542
  try {
543
543
  process.kill(pid, 0);
@@ -685,11 +685,11 @@ async function connectEmbedDaemon() {
685
685
  }
686
686
  }
687
687
  const start = Date.now();
688
- let delay = 100;
688
+ let delay2 = 100;
689
689
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
690
- await new Promise((r) => setTimeout(r, delay));
690
+ await new Promise((r) => setTimeout(r, delay2));
691
691
  if (await connectToSocket()) return true;
692
- delay = Math.min(delay * 2, 3e3);
692
+ delay2 = Math.min(delay2 * 2, 3e3);
693
693
  }
694
694
  return false;
695
695
  }
@@ -745,7 +745,7 @@ function killAndRespawnDaemon() {
745
745
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
746
746
  if (existsSync7(PID_PATH)) {
747
747
  try {
748
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
748
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
749
749
  if (pid > 0) {
750
750
  try {
751
751
  process.kill(pid, "SIGKILL");
@@ -781,11 +781,11 @@ async function embedViaClient(text, priority = "high") {
781
781
  `);
782
782
  killAndRespawnDaemon();
783
783
  const start = Date.now();
784
- let delay = 200;
784
+ let delay2 = 200;
785
785
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
786
- await new Promise((r) => setTimeout(r, delay));
786
+ await new Promise((r) => setTimeout(r, delay2));
787
787
  if (await connectToSocket()) break;
788
- delay = Math.min(delay * 2, 3e3);
788
+ delay2 = Math.min(delay2 * 2, 3e3);
789
789
  }
790
790
  if (!_connected) return null;
791
791
  }
@@ -797,11 +797,11 @@ async function embedViaClient(text, priority = "high") {
797
797
  `);
798
798
  killAndRespawnDaemon();
799
799
  const start = Date.now();
800
- let delay = 200;
800
+ let delay2 = 200;
801
801
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
802
- await new Promise((r) => setTimeout(r, delay));
802
+ await new Promise((r) => setTimeout(r, delay2));
803
803
  if (await connectToSocket()) break;
804
- delay = Math.min(delay * 2, 3e3);
804
+ delay2 = Math.min(delay2 * 2, 3e3);
805
805
  }
806
806
  if (!_connected) return null;
807
807
  const retry = await sendRequest([text], priority);
@@ -958,12 +958,65 @@ init_memory();
958
958
 
959
959
  // src/lib/database.ts
960
960
  import { createClient } from "@libsql/client";
961
+
962
+ // src/lib/db-retry.ts
963
+ var MAX_RETRIES = 3;
964
+ var BASE_DELAY_MS = 200;
965
+ var MAX_JITTER_MS = 300;
966
+ function isBusyError(err) {
967
+ if (err instanceof Error) {
968
+ const msg = err.message.toLowerCase();
969
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
970
+ }
971
+ return false;
972
+ }
973
+ function delay(ms) {
974
+ return new Promise((resolve) => setTimeout(resolve, ms));
975
+ }
976
+ async function retryOnBusy(fn, label) {
977
+ let lastError;
978
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
979
+ try {
980
+ return await fn();
981
+ } catch (err) {
982
+ lastError = err;
983
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
984
+ throw err;
985
+ }
986
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
987
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
988
+ process.stderr.write(
989
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
990
+ `
991
+ );
992
+ await delay(backoff + jitter);
993
+ }
994
+ }
995
+ throw lastError;
996
+ }
997
+ function wrapWithRetry(client) {
998
+ return new Proxy(client, {
999
+ get(target, prop, receiver) {
1000
+ if (prop === "execute") {
1001
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
1002
+ }
1003
+ if (prop === "batch") {
1004
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
1005
+ }
1006
+ return Reflect.get(target, prop, receiver);
1007
+ }
1008
+ });
1009
+ }
1010
+
1011
+ // src/lib/database.ts
961
1012
  var _client = null;
1013
+ var _resilientClient = null;
962
1014
  var initTurso = initDatabase;
963
1015
  async function initDatabase(config) {
964
1016
  if (_client) {
965
1017
  _client.close();
966
1018
  _client = null;
1019
+ _resilientClient = null;
967
1020
  }
968
1021
  const opts = {
969
1022
  url: `file:${config.dbPath}`
@@ -972,20 +1025,27 @@ async function initDatabase(config) {
972
1025
  opts.encryptionKey = config.encryptionKey;
973
1026
  }
974
1027
  _client = createClient(opts);
1028
+ _resilientClient = wrapWithRetry(_client);
975
1029
  }
976
1030
  function isInitialized() {
977
1031
  return _client !== null;
978
1032
  }
979
1033
  function getClient() {
1034
+ if (!_resilientClient) {
1035
+ throw new Error("Database client not initialized. Call initDatabase() first.");
1036
+ }
1037
+ return _resilientClient;
1038
+ }
1039
+ function getRawClient() {
980
1040
  if (!_client) {
981
1041
  throw new Error("Database client not initialized. Call initDatabase() first.");
982
1042
  }
983
1043
  return _client;
984
1044
  }
985
1045
  async function ensureSchema() {
986
- const client = getClient();
1046
+ const client = getRawClient();
987
1047
  await client.execute("PRAGMA journal_mode = WAL");
988
- await client.execute("PRAGMA busy_timeout = 5000");
1048
+ await client.execute("PRAGMA busy_timeout = 30000");
989
1049
  try {
990
1050
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
991
1051
  } catch {
@@ -1886,7 +1946,8 @@ async function writeMemory(record) {
1886
1946
  has_error: record.has_error ? 1 : 0,
1887
1947
  raw_text: record.raw_text,
1888
1948
  vector: record.vector,
1889
- version: _nextVersion++,
1949
+ version: 0,
1950
+ // Placeholder — assigned atomically at flush time
1890
1951
  task_id: record.task_id ?? null,
1891
1952
  importance: record.importance ?? 5,
1892
1953
  status: record.status ?? "active",
@@ -1920,6 +1981,13 @@ async function flushBatch() {
1920
1981
  _flushing = true;
1921
1982
  try {
1922
1983
  const batch = _pendingRecords.slice(0);
1984
+ const client = getClient();
1985
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1986
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1987
+ for (const row of batch) {
1988
+ row.version = baseVersion++;
1989
+ }
1990
+ _nextVersion = baseVersion;
1923
1991
  const buildStmt = (row) => {
1924
1992
  const hasVector = row.vector !== null;
1925
1993
  const taskId = row.task_id ?? null;
@@ -2045,20 +2113,20 @@ function vectorToBlob(vector) {
2045
2113
  }
2046
2114
 
2047
2115
  // src/lib/plan-limits.ts
2048
- import { readFileSync as readFileSync3, existsSync as existsSync6 } from "fs";
2116
+ import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
2049
2117
  import path7 from "path";
2050
2118
 
2051
2119
  // src/lib/employees.ts
2052
2120
  init_config();
2053
2121
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2054
- import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
2122
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
2055
2123
  import { execSync as execSync2 } from "child_process";
2056
2124
  import path5 from "path";
2057
2125
  var EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
2058
2126
 
2059
2127
  // src/lib/license.ts
2060
2128
  init_config();
2061
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2129
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2062
2130
  import { randomUUID } from "crypto";
2063
2131
  import path6 from "path";
2064
2132
  import { jwtVerify, importSPKI } from "jose";
@@ -2091,14 +2159,14 @@ function loadDeviceId() {
2091
2159
  const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
2092
2160
  try {
2093
2161
  if (existsSync5(deviceJsonPath)) {
2094
- const data = JSON.parse(readFileSync2(deviceJsonPath, "utf8"));
2162
+ const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
2095
2163
  if (data.deviceId) return data.deviceId;
2096
2164
  }
2097
2165
  } catch {
2098
2166
  }
2099
2167
  try {
2100
2168
  if (existsSync5(DEVICE_ID_PATH)) {
2101
- const id2 = readFileSync2(DEVICE_ID_PATH, "utf8").trim();
2169
+ const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
2102
2170
  if (id2) return id2;
2103
2171
  }
2104
2172
  } catch {
@@ -2111,7 +2179,7 @@ function loadDeviceId() {
2111
2179
  function loadLicense() {
2112
2180
  try {
2113
2181
  if (!existsSync5(LICENSE_PATH)) return null;
2114
- return readFileSync2(LICENSE_PATH, "utf8").trim();
2182
+ return readFileSync3(LICENSE_PATH, "utf8").trim();
2115
2183
  } catch {
2116
2184
  return null;
2117
2185
  }
@@ -2141,7 +2209,7 @@ async function verifyLicenseJwt(token) {
2141
2209
  async function getCachedLicense() {
2142
2210
  try {
2143
2211
  if (!existsSync5(CACHE_PATH)) return null;
2144
- const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
2212
+ const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
2145
2213
  if (!raw.token || typeof raw.token !== "string") return null;
2146
2214
  return await verifyLicenseJwt(raw.token);
2147
2215
  } catch {