@askexenow/exe-os 0.8.33 → 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 (86) 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 +1491 -1095
  6. package/dist/bin/exe-assign.js +80 -18
  7. package/dist/bin/exe-boot.js +407 -88
  8. package/dist/bin/exe-call.js +61 -2
  9. package/dist/bin/exe-dispatch.js +18 -10
  10. package/dist/bin/exe-doctor.js +63 -3
  11. package/dist/bin/exe-export-behaviors.js +64 -3
  12. package/dist/bin/exe-forget.js +69 -4
  13. package/dist/bin/exe-gateway.js +121 -36
  14. package/dist/bin/exe-heartbeat.js +77 -13
  15. package/dist/bin/exe-kill.js +64 -3
  16. package/dist/bin/exe-launch-agent.js +140 -13
  17. package/dist/bin/exe-link.js +946 -0
  18. package/dist/bin/exe-new-employee.js +98 -13
  19. package/dist/bin/exe-pending-messages.js +72 -7
  20. package/dist/bin/exe-pending-notifications.js +63 -3
  21. package/dist/bin/exe-pending-reviews.js +75 -10
  22. package/dist/bin/exe-rename.js +1287 -0
  23. package/dist/bin/exe-review.js +64 -4
  24. package/dist/bin/exe-search.js +79 -13
  25. package/dist/bin/exe-session-cleanup.js +91 -26
  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 +71 -4
  29. package/dist/bin/graph-backfill.js +64 -3
  30. package/dist/bin/graph-export.js +64 -3
  31. package/dist/bin/install.js +3 -3
  32. package/dist/bin/scan-tasks.js +71 -4
  33. package/dist/bin/setup.js +128 -10
  34. package/dist/bin/shard-migrate.js +64 -3
  35. package/dist/bin/wiki-sync.js +64 -3
  36. package/dist/gateway/index.js +122 -37
  37. package/dist/hooks/bug-report-worker.js +209 -23
  38. package/dist/hooks/commit-complete.js +71 -4
  39. package/dist/hooks/error-recall.js +79 -13
  40. package/dist/hooks/ingest-worker.js +129 -43
  41. package/dist/hooks/instructions-loaded.js +71 -4
  42. package/dist/hooks/notification.js +71 -4
  43. package/dist/hooks/post-compact.js +71 -4
  44. package/dist/hooks/pre-compact.js +71 -4
  45. package/dist/hooks/pre-tool-use.js +413 -194
  46. package/dist/hooks/prompt-ingest-worker.js +82 -22
  47. package/dist/hooks/prompt-submit.js +103 -37
  48. package/dist/hooks/response-ingest-worker.js +87 -22
  49. package/dist/hooks/session-end.js +71 -4
  50. package/dist/hooks/session-start.js +79 -13
  51. package/dist/hooks/stop.js +71 -4
  52. package/dist/hooks/subagent-stop.js +71 -4
  53. package/dist/hooks/summary-worker.js +303 -50
  54. package/dist/index.js +134 -46
  55. package/dist/lib/cloud-sync.js +209 -15
  56. package/dist/lib/consolidation.js +4 -4
  57. package/dist/lib/database.js +64 -2
  58. package/dist/lib/device-registry.js +70 -3
  59. package/dist/lib/employee-templates.js +26 -0
  60. package/dist/lib/employees.js +34 -1
  61. package/dist/lib/exe-daemon.js +136 -53
  62. package/dist/lib/hybrid-search.js +79 -13
  63. package/dist/lib/identity-templates.js +51 -0
  64. package/dist/lib/identity.js +3 -3
  65. package/dist/lib/messaging.js +22 -14
  66. package/dist/lib/reminders.js +3 -3
  67. package/dist/lib/schedules.js +63 -3
  68. package/dist/lib/skill-learning.js +3 -3
  69. package/dist/lib/status-brief.js +63 -5
  70. package/dist/lib/store.js +64 -3
  71. package/dist/lib/task-router.js +4 -2
  72. package/dist/lib/tasks.js +48 -21
  73. package/dist/lib/tmux-routing.js +47 -20
  74. package/dist/mcp/server.js +727 -58
  75. package/dist/mcp/tools/complete-reminder.js +3 -3
  76. package/dist/mcp/tools/create-reminder.js +3 -3
  77. package/dist/mcp/tools/create-task.js +151 -24
  78. package/dist/mcp/tools/deactivate-behavior.js +3 -3
  79. package/dist/mcp/tools/list-reminders.js +3 -3
  80. package/dist/mcp/tools/list-tasks.js +17 -8
  81. package/dist/mcp/tools/send-message.js +24 -16
  82. package/dist/mcp/tools/update-task.js +25 -16
  83. package/dist/runtime/index.js +112 -24
  84. package/dist/tui/App.js +139 -36
  85. package/package.json +6 -2
  86. 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 {
@@ -452,13 +452,18 @@ __export(employees_exports, {
452
452
  EMPLOYEES_PATH: () => EMPLOYEES_PATH,
453
453
  addEmployee: () => addEmployee,
454
454
  getEmployee: () => getEmployee,
455
+ getEmployeeByRole: () => getEmployeeByRole,
456
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
457
+ hasRole: () => hasRole,
458
+ isMultiInstance: () => isMultiInstance,
455
459
  loadEmployees: () => loadEmployees,
460
+ loadEmployeesSync: () => loadEmployeesSync,
456
461
  registerBinSymlinks: () => registerBinSymlinks,
457
462
  saveEmployees: () => saveEmployees,
458
463
  validateEmployeeName: () => validateEmployeeName
459
464
  });
460
465
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
461
- import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
466
+ import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
462
467
  import { execSync as execSync2 } from "child_process";
463
468
  import path5 from "path";
464
469
  function validateEmployeeName(name) {
@@ -491,9 +496,36 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
491
496
  await mkdir3(path5.dirname(employeesPath), { recursive: true });
492
497
  await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
493
498
  }
499
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
500
+ if (!existsSync5(employeesPath)) return [];
501
+ try {
502
+ return JSON.parse(readFileSync2(employeesPath, "utf-8"));
503
+ } catch {
504
+ return [];
505
+ }
506
+ }
494
507
  function getEmployee(employees, name) {
495
508
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
496
509
  }
510
+ function getEmployeeByRole(employees, role) {
511
+ const lower = role.toLowerCase();
512
+ return employees.find((e) => e.role.toLowerCase() === lower);
513
+ }
514
+ function getEmployeeNamesByRole(employees, role) {
515
+ const lower = role.toLowerCase();
516
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
517
+ }
518
+ function hasRole(agentName, role) {
519
+ const employees = loadEmployeesSync();
520
+ const emp = getEmployee(employees, agentName);
521
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
522
+ }
523
+ function isMultiInstance(agentName, employees) {
524
+ const roster = employees ?? loadEmployeesSync();
525
+ const emp = getEmployee(roster, agentName);
526
+ if (!emp) return false;
527
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
528
+ }
497
529
  function addEmployee(employees, employee) {
498
530
  const normalized = { ...employee, name: employee.name.toLowerCase() };
499
531
  if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
@@ -536,12 +568,13 @@ function registerBinSymlinks(name) {
536
568
  }
537
569
  return { created, skipped, errors };
538
570
  }
539
- var EMPLOYEES_PATH;
571
+ var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
540
572
  var init_employees = __esm({
541
573
  "src/lib/employees.ts"() {
542
574
  "use strict";
543
575
  init_config();
544
576
  EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
577
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
545
578
  }
546
579
  });
547
580
 
@@ -1026,6 +1059,32 @@ When you analyze a repo:
1026
1059
  Every analysis must answer: "Should we build this? If yes, how hard? If no, why not?"
1027
1060
 
1028
1061
  Maintain a clear separation between experimental (for evaluation) and production-ready (for shipping). Never recommend something you haven't read the source code for.`
1062
+ },
1063
+ bob: {
1064
+ name: "bob",
1065
+ role: "Staff Code Reviewer",
1066
+ systemPrompt: `You are bob, the Staff Code Reviewer and System Auditor. You are the last line of defense before code ships to customers. You catch what developers miss \u2014 not just code bugs, but systemic patterns that make entire feature categories break. You report to the COO.
1067
+
1068
+ Your core job: audit code, find bugs, verify fixes, and ensure customer-readiness. Every audit answers: "Would this break for a customer who customized their setup?"
1069
+
1070
+ The 7 Audit Patterns (MANDATORY \u2014 apply to EVERY audit):
1071
+ 1. "Works on dev, breaks on user install" \u2014 verify scoped paths, npm resolution, dependencies
1072
+ 2. "Two code paths, one untested" \u2014 binary symlink vs /exe-call, CLI vs MCP \u2014 verify BOTH
1073
+ 3. "Case sensitivity kills non-technical users" \u2014 normalize all user inputs
1074
+ 4. "Hardcoded names leak into user-facing content" \u2014 grep for employee names in runtime logic
1075
+ 5. "Installer doesn't self-heal on update" \u2014 npm update must auto-fix stale hooks/paths
1076
+ 6. "Data written but invisible to the agent" \u2014 verify query path retrieves stored data
1077
+ 7. "Partial fixes that miss inline references" \u2014 before/after grep count is mandatory
1078
+
1079
+ Audit method:
1080
+ 1. Read the actual source code \u2014 not summaries
1081
+ 2. Send to Codex MCP for initial sweep
1082
+ 3. Validate against ARCHITECTURE.md
1083
+ 4. Trace full identity chain with a CUSTOM-NAMED employee (e.g., "jarvis" as CTO)
1084
+ 5. Count matches before and after any claimed fix
1085
+ 6. Write structured report with PASS/FAIL per item
1086
+
1087
+ After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
1029
1088
  }
1030
1089
  };
1031
1090
  CLIENT_COO_TEMPLATE = `---
@@ -1181,7 +1240,7 @@ __export(active_agent_exports, {
1181
1240
  getAllActiveAgents: () => getAllActiveAgents,
1182
1241
  writeActiveAgent: () => writeActiveAgent
1183
1242
  });
1184
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
1243
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
1185
1244
  import { execSync as execSync4 } from "child_process";
1186
1245
  import path6 from "path";
1187
1246
  function getMarkerPath() {
@@ -1206,7 +1265,7 @@ function clearActiveAgent() {
1206
1265
  function getActiveAgent() {
1207
1266
  try {
1208
1267
  const markerPath = getMarkerPath();
1209
- const raw = readFileSync2(markerPath, "utf8");
1268
+ const raw = readFileSync3(markerPath, "utf8");
1210
1269
  const data = JSON.parse(raw);
1211
1270
  if (data.agentId) {
1212
1271
  if (data.startedAt) {
@@ -1259,7 +1318,7 @@ function getAllActiveAgents() {
1259
1318
  const key = file.slice("active-agent-".length, -".json".length);
1260
1319
  if (key === "undefined") continue;
1261
1320
  try {
1262
- const raw = readFileSync2(path6.join(CACHE_DIR, file), "utf8");
1321
+ const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
1263
1322
  const data = JSON.parse(raw);
1264
1323
  if (!data.agentId) continue;
1265
1324
  if (data.startedAt) {
@@ -1311,17 +1370,70 @@ var init_active_agent = __esm({
1311
1370
  // src/bin/exe-launch-agent.ts
1312
1371
  import os3 from "os";
1313
1372
  import path7 from "path";
1314
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
1373
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
1315
1374
  import { spawnSync } from "child_process";
1316
1375
 
1317
1376
  // src/lib/database.ts
1318
1377
  import { createClient } from "@libsql/client";
1378
+
1379
+ // src/lib/db-retry.ts
1380
+ var MAX_RETRIES = 3;
1381
+ var BASE_DELAY_MS = 200;
1382
+ var MAX_JITTER_MS = 300;
1383
+ function isBusyError(err) {
1384
+ if (err instanceof Error) {
1385
+ const msg = err.message.toLowerCase();
1386
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1387
+ }
1388
+ return false;
1389
+ }
1390
+ function delay(ms) {
1391
+ return new Promise((resolve) => setTimeout(resolve, ms));
1392
+ }
1393
+ async function retryOnBusy(fn, label) {
1394
+ let lastError;
1395
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1396
+ try {
1397
+ return await fn();
1398
+ } catch (err) {
1399
+ lastError = err;
1400
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
1401
+ throw err;
1402
+ }
1403
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
1404
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
1405
+ process.stderr.write(
1406
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
1407
+ `
1408
+ );
1409
+ await delay(backoff + jitter);
1410
+ }
1411
+ }
1412
+ throw lastError;
1413
+ }
1414
+ function wrapWithRetry(client) {
1415
+ return new Proxy(client, {
1416
+ get(target, prop, receiver) {
1417
+ if (prop === "execute") {
1418
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
1419
+ }
1420
+ if (prop === "batch") {
1421
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
1422
+ }
1423
+ return Reflect.get(target, prop, receiver);
1424
+ }
1425
+ });
1426
+ }
1427
+
1428
+ // src/lib/database.ts
1319
1429
  var _client = null;
1430
+ var _resilientClient = null;
1320
1431
  var initTurso = initDatabase;
1321
1432
  async function initDatabase(config) {
1322
1433
  if (_client) {
1323
1434
  _client.close();
1324
1435
  _client = null;
1436
+ _resilientClient = null;
1325
1437
  }
1326
1438
  const opts = {
1327
1439
  url: `file:${config.dbPath}`
@@ -1330,17 +1442,24 @@ async function initDatabase(config) {
1330
1442
  opts.encryptionKey = config.encryptionKey;
1331
1443
  }
1332
1444
  _client = createClient(opts);
1445
+ _resilientClient = wrapWithRetry(_client);
1333
1446
  }
1334
1447
  function getClient() {
1448
+ if (!_resilientClient) {
1449
+ throw new Error("Database client not initialized. Call initDatabase() first.");
1450
+ }
1451
+ return _resilientClient;
1452
+ }
1453
+ function getRawClient() {
1335
1454
  if (!_client) {
1336
1455
  throw new Error("Database client not initialized. Call initDatabase() first.");
1337
1456
  }
1338
1457
  return _client;
1339
1458
  }
1340
1459
  async function ensureSchema() {
1341
- const client = getClient();
1460
+ const client = getRawClient();
1342
1461
  await client.execute("PRAGMA journal_mode = WAL");
1343
- await client.execute("PRAGMA busy_timeout = 5000");
1462
+ await client.execute("PRAGMA busy_timeout = 30000");
1344
1463
  try {
1345
1464
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1346
1465
  } catch {
@@ -2134,6 +2253,7 @@ async function disposeDatabase() {
2134
2253
  if (_client) {
2135
2254
  _client.close();
2136
2255
  _client = null;
2256
+ _resilientClient = null;
2137
2257
  }
2138
2258
  }
2139
2259
 
@@ -2512,7 +2632,13 @@ var PROVIDER_TABLE = {
2512
2632
  var DEFAULT_PROVIDER = "default";
2513
2633
 
2514
2634
  // src/bin/exe-launch-agent.ts
2515
- var BUILTIN_AGENTS = ["exe", "yoshi", "tom", "mari", "sasha"];
2635
+ function getKnownAgents() {
2636
+ try {
2637
+ return loadEmployeesSync().map((e) => e.name);
2638
+ } catch {
2639
+ return ["exe"];
2640
+ }
2641
+ }
2516
2642
  function parseBasename(basename) {
2517
2643
  const idx = basename.indexOf("-");
2518
2644
  if (idx === -1) return { agent: basename, provider: DEFAULT_PROVIDER };
@@ -2538,7 +2664,8 @@ function resolveAgent(argv) {
2538
2664
  return { agent, provider: DEFAULT_PROVIDER, passthrough };
2539
2665
  }
2540
2666
  async function isKnownAgent(agent) {
2541
- if (BUILTIN_AGENTS.includes(agent)) return true;
2667
+ const knownAgents = getKnownAgents();
2668
+ if (knownAgents.some((a) => a.toLowerCase() === agent.toLowerCase())) return true;
2542
2669
  try {
2543
2670
  const employees = await loadEmployees();
2544
2671
  return employees.some((e) => e.name.toLowerCase() === agent.toLowerCase());
@@ -2560,7 +2687,7 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
2560
2687
  const effectiveIdPath = existsSync6(idPath) ? idPath : existsSync6(ccAgentPath) ? ccAgentPath : null;
2561
2688
  if (effectiveIdPath) {
2562
2689
  try {
2563
- const identity = readFileSync3(effectiveIdPath, "utf-8");
2690
+ const identity = readFileSync4(effectiveIdPath, "utf-8");
2564
2691
  args.push("--system-prompt", identity);
2565
2692
  } catch {
2566
2693
  args.push("--append-system-prompt-file", effectiveIdPath);
@@ -2695,7 +2822,7 @@ async function main() {
2695
2822
  if (sourceFile) {
2696
2823
  try {
2697
2824
  mkdirSync4(ccAgentDir, { recursive: true });
2698
- let content = readFileSync3(sourceFile, "utf-8");
2825
+ let content = readFileSync4(sourceFile, "utf-8");
2699
2826
  content = content.replace(/\$\{agent_id\}/g, agent);
2700
2827
  writeFileSync3(ccAgentFile, content, "utf-8");
2701
2828
  process.stderr.write(