@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
@@ -262,7 +262,7 @@ function listShards() {
262
262
  }
263
263
  async function ensureShardSchema(client) {
264
264
  await client.execute("PRAGMA journal_mode = WAL");
265
- await client.execute("PRAGMA busy_timeout = 5000");
265
+ await client.execute("PRAGMA busy_timeout = 30000");
266
266
  try {
267
267
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
268
268
  } catch {
@@ -449,7 +449,7 @@ var init_shard_manager = __esm({
449
449
  // src/lib/employees.ts
450
450
  init_config();
451
451
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
452
- import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
452
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
453
453
  import { execSync } from "child_process";
454
454
  import path2 from "path";
455
455
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
@@ -479,12 +479,14 @@ var DEFAULT_BLOOM_CONFIG = {
479
479
  },
480
480
  tierRules: {
481
481
  junior: {
482
- eligible: ["tom"],
482
+ eligible: [],
483
+ // resolved dynamically from roster (Principal Engineer role)
483
484
  reviewRequired: false,
484
485
  manualOnly: false
485
486
  },
486
487
  standard: {
487
- eligible: ["tom"],
488
+ eligible: [],
489
+ // resolved dynamically from roster (Principal Engineer role)
488
490
  reviewRequired: false,
489
491
  manualOnly: false
490
492
  },
@@ -597,7 +599,7 @@ init_config();
597
599
  import net from "net";
598
600
  import { spawn } from "child_process";
599
601
  import { randomUUID as randomUUID2 } from "crypto";
600
- import { existsSync as existsSync3, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
602
+ import { existsSync as existsSync3, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
601
603
  import path3 from "path";
602
604
  import { fileURLToPath } from "url";
603
605
  var SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
@@ -634,7 +636,7 @@ function handleData(chunk) {
634
636
  function cleanupStaleFiles() {
635
637
  if (existsSync3(PID_PATH)) {
636
638
  try {
637
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
639
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
638
640
  if (pid > 0) {
639
641
  try {
640
642
  process.kill(pid, 0);
@@ -782,11 +784,11 @@ async function connectEmbedDaemon() {
782
784
  }
783
785
  }
784
786
  const start = Date.now();
785
- let delay = 100;
787
+ let delay2 = 100;
786
788
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
787
- await new Promise((r) => setTimeout(r, delay));
789
+ await new Promise((r) => setTimeout(r, delay2));
788
790
  if (await connectToSocket()) return true;
789
- delay = Math.min(delay * 2, 3e3);
791
+ delay2 = Math.min(delay2 * 2, 3e3);
790
792
  }
791
793
  return false;
792
794
  }
@@ -842,7 +844,7 @@ function killAndRespawnDaemon() {
842
844
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
843
845
  if (existsSync3(PID_PATH)) {
844
846
  try {
845
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
847
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
846
848
  if (pid > 0) {
847
849
  try {
848
850
  process.kill(pid, "SIGKILL");
@@ -878,11 +880,11 @@ async function embedViaClient(text, priority = "high") {
878
880
  `);
879
881
  killAndRespawnDaemon();
880
882
  const start = Date.now();
881
- let delay = 200;
883
+ let delay2 = 200;
882
884
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
883
- await new Promise((r) => setTimeout(r, delay));
885
+ await new Promise((r) => setTimeout(r, delay2));
884
886
  if (await connectToSocket()) break;
885
- delay = Math.min(delay * 2, 3e3);
887
+ delay2 = Math.min(delay2 * 2, 3e3);
886
888
  }
887
889
  if (!_connected) return null;
888
890
  }
@@ -894,11 +896,11 @@ async function embedViaClient(text, priority = "high") {
894
896
  `);
895
897
  killAndRespawnDaemon();
896
898
  const start = Date.now();
897
- let delay = 200;
899
+ let delay2 = 200;
898
900
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
899
- await new Promise((r) => setTimeout(r, delay));
901
+ await new Promise((r) => setTimeout(r, delay2));
900
902
  if (await connectToSocket()) break;
901
- delay = Math.min(delay * 2, 3e3);
903
+ delay2 = Math.min(delay2 * 2, 3e3);
902
904
  }
903
905
  if (!_connected) return null;
904
906
  const retry = await sendRequest([text], priority);
@@ -928,12 +930,65 @@ async function embed(text) {
928
930
 
929
931
  // src/lib/database.ts
930
932
  import { createClient } from "@libsql/client";
933
+
934
+ // src/lib/db-retry.ts
935
+ var MAX_RETRIES = 3;
936
+ var BASE_DELAY_MS = 200;
937
+ var MAX_JITTER_MS = 300;
938
+ function isBusyError(err) {
939
+ if (err instanceof Error) {
940
+ const msg = err.message.toLowerCase();
941
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
942
+ }
943
+ return false;
944
+ }
945
+ function delay(ms) {
946
+ return new Promise((resolve) => setTimeout(resolve, ms));
947
+ }
948
+ async function retryOnBusy(fn, label) {
949
+ let lastError;
950
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
951
+ try {
952
+ return await fn();
953
+ } catch (err) {
954
+ lastError = err;
955
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
956
+ throw err;
957
+ }
958
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
959
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
960
+ process.stderr.write(
961
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
962
+ `
963
+ );
964
+ await delay(backoff + jitter);
965
+ }
966
+ }
967
+ throw lastError;
968
+ }
969
+ function wrapWithRetry(client) {
970
+ return new Proxy(client, {
971
+ get(target, prop, receiver) {
972
+ if (prop === "execute") {
973
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
974
+ }
975
+ if (prop === "batch") {
976
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
977
+ }
978
+ return Reflect.get(target, prop, receiver);
979
+ }
980
+ });
981
+ }
982
+
983
+ // src/lib/database.ts
931
984
  var _client = null;
985
+ var _resilientClient = null;
932
986
  var initTurso = initDatabase;
933
987
  async function initDatabase(config) {
934
988
  if (_client) {
935
989
  _client.close();
936
990
  _client = null;
991
+ _resilientClient = null;
937
992
  }
938
993
  const opts = {
939
994
  url: `file:${config.dbPath}`
@@ -942,17 +997,24 @@ async function initDatabase(config) {
942
997
  opts.encryptionKey = config.encryptionKey;
943
998
  }
944
999
  _client = createClient(opts);
1000
+ _resilientClient = wrapWithRetry(_client);
945
1001
  }
946
1002
  function getClient() {
1003
+ if (!_resilientClient) {
1004
+ throw new Error("Database client not initialized. Call initDatabase() first.");
1005
+ }
1006
+ return _resilientClient;
1007
+ }
1008
+ function getRawClient() {
947
1009
  if (!_client) {
948
1010
  throw new Error("Database client not initialized. Call initDatabase() first.");
949
1011
  }
950
1012
  return _client;
951
1013
  }
952
1014
  async function ensureSchema() {
953
- const client = getClient();
1015
+ const client = getRawClient();
954
1016
  await client.execute("PRAGMA journal_mode = WAL");
955
- await client.execute("PRAGMA busy_timeout = 5000");
1017
+ await client.execute("PRAGMA busy_timeout = 30000");
956
1018
  try {
957
1019
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
958
1020
  } catch {
@@ -1853,7 +1915,8 @@ async function writeMemory(record) {
1853
1915
  has_error: record.has_error ? 1 : 0,
1854
1916
  raw_text: record.raw_text,
1855
1917
  vector: record.vector,
1856
- version: _nextVersion++,
1918
+ version: 0,
1919
+ // Placeholder — assigned atomically at flush time
1857
1920
  task_id: record.task_id ?? null,
1858
1921
  importance: record.importance ?? 5,
1859
1922
  status: record.status ?? "active",
@@ -1887,6 +1950,13 @@ async function flushBatch() {
1887
1950
  _flushing = true;
1888
1951
  try {
1889
1952
  const batch = _pendingRecords.slice(0);
1953
+ const client = getClient();
1954
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1955
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1956
+ for (const row of batch) {
1957
+ row.version = baseVersion++;
1958
+ }
1959
+ _nextVersion = baseVersion;
1890
1960
  const buildStmt = (row) => {
1891
1961
  const hasVector = row.vector !== null;
1892
1962
  const taskId = row.task_id ?? null;