@askexenow/exe-os 0.8.32 → 0.8.36

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 (87) hide show
  1. package/dist/bin/backfill-conversations.js +332 -348
  2. package/dist/bin/backfill-responses.js +72 -12
  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 +1518 -1122
  6. package/dist/bin/exe-agent.js +4 -4
  7. package/dist/bin/exe-assign.js +80 -18
  8. package/dist/bin/exe-boot.js +408 -89
  9. package/dist/bin/exe-call.js +83 -24
  10. package/dist/bin/exe-dispatch.js +18 -10
  11. package/dist/bin/exe-doctor.js +63 -3
  12. package/dist/bin/exe-export-behaviors.js +64 -3
  13. package/dist/bin/exe-forget.js +69 -4
  14. package/dist/bin/exe-gateway.js +121 -36
  15. package/dist/bin/exe-heartbeat.js +77 -13
  16. package/dist/bin/exe-kill.js +64 -3
  17. package/dist/bin/exe-launch-agent.js +162 -35
  18. package/dist/bin/exe-link.js +946 -0
  19. package/dist/bin/exe-new-employee.js +121 -36
  20. package/dist/bin/exe-pending-messages.js +72 -7
  21. package/dist/bin/exe-pending-notifications.js +63 -3
  22. package/dist/bin/exe-pending-reviews.js +75 -10
  23. package/dist/bin/exe-rename.js +1287 -0
  24. package/dist/bin/exe-review.js +64 -4
  25. package/dist/bin/exe-search.js +79 -13
  26. package/dist/bin/exe-session-cleanup.js +91 -26
  27. package/dist/bin/exe-status.js +64 -4
  28. package/dist/bin/exe-team.js +64 -4
  29. package/dist/bin/git-sweep.js +71 -4
  30. package/dist/bin/graph-backfill.js +64 -3
  31. package/dist/bin/graph-export.js +64 -3
  32. package/dist/bin/install.js +3 -3
  33. package/dist/bin/scan-tasks.js +71 -4
  34. package/dist/bin/setup.js +156 -38
  35. package/dist/bin/shard-migrate.js +64 -3
  36. package/dist/bin/wiki-sync.js +64 -3
  37. package/dist/gateway/index.js +122 -37
  38. package/dist/hooks/bug-report-worker.js +209 -23
  39. package/dist/hooks/commit-complete.js +71 -4
  40. package/dist/hooks/error-recall.js +79 -13
  41. package/dist/hooks/ingest-worker.js +129 -43
  42. package/dist/hooks/instructions-loaded.js +71 -4
  43. package/dist/hooks/notification.js +71 -4
  44. package/dist/hooks/post-compact.js +71 -4
  45. package/dist/hooks/pre-compact.js +71 -4
  46. package/dist/hooks/pre-tool-use.js +413 -194
  47. package/dist/hooks/prompt-ingest-worker.js +82 -22
  48. package/dist/hooks/prompt-submit.js +103 -37
  49. package/dist/hooks/response-ingest-worker.js +87 -22
  50. package/dist/hooks/session-end.js +71 -4
  51. package/dist/hooks/session-start.js +79 -13
  52. package/dist/hooks/stop.js +71 -4
  53. package/dist/hooks/subagent-stop.js +71 -4
  54. package/dist/hooks/summary-worker.js +303 -50
  55. package/dist/index.js +134 -46
  56. package/dist/lib/cloud-sync.js +209 -15
  57. package/dist/lib/consolidation.js +4 -4
  58. package/dist/lib/database.js +64 -2
  59. package/dist/lib/device-registry.js +70 -3
  60. package/dist/lib/employee-templates.js +48 -22
  61. package/dist/lib/employees.js +34 -1
  62. package/dist/lib/exe-daemon.js +136 -53
  63. package/dist/lib/hybrid-search.js +79 -13
  64. package/dist/lib/identity-templates.js +57 -6
  65. package/dist/lib/identity.js +3 -3
  66. package/dist/lib/messaging.js +22 -14
  67. package/dist/lib/reminders.js +3 -3
  68. package/dist/lib/schedules.js +63 -3
  69. package/dist/lib/skill-learning.js +3 -3
  70. package/dist/lib/status-brief.js +63 -5
  71. package/dist/lib/store.js +64 -3
  72. package/dist/lib/task-router.js +4 -2
  73. package/dist/lib/tasks.js +48 -21
  74. package/dist/lib/tmux-routing.js +47 -20
  75. package/dist/mcp/server.js +727 -58
  76. package/dist/mcp/tools/complete-reminder.js +3 -3
  77. package/dist/mcp/tools/create-reminder.js +3 -3
  78. package/dist/mcp/tools/create-task.js +151 -24
  79. package/dist/mcp/tools/deactivate-behavior.js +3 -3
  80. package/dist/mcp/tools/list-reminders.js +3 -3
  81. package/dist/mcp/tools/list-tasks.js +17 -8
  82. package/dist/mcp/tools/send-message.js +24 -16
  83. package/dist/mcp/tools/update-task.js +25 -16
  84. package/dist/runtime/index.js +112 -24
  85. package/dist/tui/App.js +139 -36
  86. package/package.json +6 -2
  87. package/src/commands/exe/rename.md +12 -0
@@ -1176,12 +1176,12 @@ Always reference .planning/ARCHITECTURE.md and .planning/PROJECT.md as source of
1176
1176
 
1177
1177
  OPERATING PROCEDURES (mandatory for all employees):
1178
1178
 
1179
- You report to exe (COO). All work flows through exe. These procedures are non-negotiable.
1179
+ You report to the COO. All work flows through exe. These procedures are non-negotiable.
1180
1180
 
1181
1181
  1. BEFORE starting work:
1182
1182
  - Read exe/ARCHITECTURE.md (if it exists). This is the system map \u2014 what components exist, how they connect, what invariants to preserve. Understand the architecture before changing anything.
1183
1183
  - Check YOUR task folder ONLY: Read exe/<your-name>/ for assigned tasks
1184
- - NEVER read, write, or modify files in another employee's folder (e.g., exe/mari/, exe/yoshi/). Those are their tasks, not yours. Use ask_team_memory() if you need context from a colleague.
1184
+ - NEVER read, write, or modify files in another employee's folder. Those are their tasks, not yours. Use ask_team_memory() if you need context from a colleague.
1185
1185
  - If you have open tasks, work on the highest priority one first
1186
1186
  - Ensure exe/output/ exists (mkdir -p exe/output). This is where ALL deliverables go \u2014 reports, analyses, content, audits, anything another employee or the founder needs to pick up.
1187
1187
  - Update task status to "in_progress" when starting (use update_task MCP tool)
@@ -1248,7 +1248,7 @@ DO NOT keep working degraded. Instead:
1248
1248
  3. Stop working immediately. Do not attempt to continue with degraded context.
1249
1249
 
1250
1250
  COMMUNICATION CHAIN \u2014 who you talk to:
1251
- - You report to exe (COO). Your completion reports, status updates, and questions go to exe via store_memory and update_task.
1251
+ - You report to the COO. Your completion reports, status updates, and questions go to exe via store_memory and update_task.
1252
1252
  - Do NOT address the human user directly for decisions, permissions, or status updates. That's exe's job. The user talks to exe; exe talks to you.
1253
1253
  - Exception: if the user sends you a direct message in your tmux window, respond to them. But default to reporting through exe.
1254
1254
 
@@ -1267,7 +1267,7 @@ NEVER spawn sessions without a task assigned \u2014 idle sessions waste resource
1267
1267
  NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, it's your work.
1268
1268
 
1269
1269
  CREATING TASKS FOR OTHER EMPLOYEES:
1270
- When you need to assign work to another employee (e.g., yoshi assigns to tom):
1270
+ When you need to assign work to another employee (e.g., CTO assigns to an engineer):
1271
1271
  - ALWAYS use create_task MCP tool. NEVER write .md files directly to exe/{name}/.
1272
1272
  - Direct .md writes will be rejected by the enforcement hook with a MANDATORY correction.
1273
1273
  - create_task creates both the .md file AND the DB row atomically.
@@ -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 {