@askexenow/exe-os 0.8.41 → 0.8.43

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 (76) hide show
  1. package/dist/bin/backfill-conversations.js +805 -642
  2. package/dist/bin/backfill-responses.js +804 -641
  3. package/dist/bin/backfill-vectors.js +791 -634
  4. package/dist/bin/cleanup-stale-review-tasks.js +788 -631
  5. package/dist/bin/cli.js +1345 -660
  6. package/dist/bin/exe-agent.js +20 -1
  7. package/dist/bin/exe-assign.js +1503 -1343
  8. package/dist/bin/exe-boot.js +2518 -1798
  9. package/dist/bin/exe-call.js +39 -1
  10. package/dist/bin/exe-cloud.js +15 -1
  11. package/dist/bin/exe-dispatch.js +39 -2
  12. package/dist/bin/exe-doctor.js +790 -633
  13. package/dist/bin/exe-export-behaviors.js +792 -637
  14. package/dist/bin/exe-forget.js +145 -0
  15. package/dist/bin/exe-gateway.js +2500 -1877
  16. package/dist/bin/exe-heartbeat.js +147 -1
  17. package/dist/bin/exe-kill.js +795 -640
  18. package/dist/bin/exe-launch-agent.js +2168 -2008
  19. package/dist/bin/exe-link.js +28 -2
  20. package/dist/bin/exe-new-employee.js +25 -3
  21. package/dist/bin/exe-pending-messages.js +146 -1
  22. package/dist/bin/exe-pending-notifications.js +788 -631
  23. package/dist/bin/exe-pending-reviews.js +147 -1
  24. package/dist/bin/exe-rename.js +23 -0
  25. package/dist/bin/exe-review.js +490 -327
  26. package/dist/bin/exe-search.js +154 -3
  27. package/dist/bin/exe-session-cleanup.js +2466 -413
  28. package/dist/bin/exe-status.js +474 -317
  29. package/dist/bin/exe-team.js +474 -317
  30. package/dist/bin/git-sweep.js +2690 -150
  31. package/dist/bin/graph-backfill.js +794 -637
  32. package/dist/bin/graph-export.js +798 -641
  33. package/dist/bin/scan-tasks.js +2951 -44
  34. package/dist/bin/setup.js +62 -26
  35. package/dist/bin/shard-migrate.js +792 -637
  36. package/dist/bin/wiki-sync.js +794 -637
  37. package/dist/gateway/index.js +2504 -1895
  38. package/dist/hooks/bug-report-worker.js +2118 -576
  39. package/dist/hooks/commit-complete.js +2689 -149
  40. package/dist/hooks/error-recall.js +154 -3
  41. package/dist/hooks/ingest-worker.js +1439 -815
  42. package/dist/hooks/instructions-loaded.js +151 -0
  43. package/dist/hooks/notification.js +153 -2
  44. package/dist/hooks/post-compact.js +164 -0
  45. package/dist/hooks/pre-compact.js +3073 -101
  46. package/dist/hooks/pre-tool-use.js +151 -0
  47. package/dist/hooks/prompt-ingest-worker.js +1714 -1537
  48. package/dist/hooks/prompt-submit.js +2658 -1113
  49. package/dist/hooks/response-ingest-worker.js +170 -6
  50. package/dist/hooks/session-end.js +153 -2
  51. package/dist/hooks/session-start.js +154 -3
  52. package/dist/hooks/stop.js +151 -0
  53. package/dist/hooks/subagent-stop.js +151 -0
  54. package/dist/hooks/summary-worker.js +179 -7
  55. package/dist/index.js +278 -100
  56. package/dist/lib/cloud-sync.js +28 -2
  57. package/dist/lib/consolidation.js +69 -2
  58. package/dist/lib/database.js +19 -0
  59. package/dist/lib/device-registry.js +19 -0
  60. package/dist/lib/employee-templates.js +20 -1
  61. package/dist/lib/exe-daemon.js +236 -16
  62. package/dist/lib/hybrid-search.js +154 -3
  63. package/dist/lib/license.js +15 -1
  64. package/dist/lib/messaging.js +39 -2
  65. package/dist/lib/schedules.js +792 -637
  66. package/dist/lib/store.js +796 -636
  67. package/dist/lib/tasks.js +1614 -1091
  68. package/dist/lib/tmux-routing.js +149 -9
  69. package/dist/mcp/server.js +1825 -1138
  70. package/dist/mcp/tools/create-task.js +2280 -828
  71. package/dist/mcp/tools/list-tasks.js +2788 -159
  72. package/dist/mcp/tools/send-message.js +39 -2
  73. package/dist/mcp/tools/update-task.js +64 -0
  74. package/dist/runtime/index.js +235 -67
  75. package/dist/tui/App.js +1452 -644
  76. package/package.json +3 -2
@@ -310,6 +310,13 @@ async function ensureSchema() {
310
310
  });
311
311
  } catch {
312
312
  }
313
+ try {
314
+ await client.execute({
315
+ sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
316
+ args: []
317
+ });
318
+ } catch {
319
+ }
313
320
  try {
314
321
  await client.execute({
315
322
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -756,6 +763,18 @@ async function ensureSchema() {
756
763
  CREATE INDEX IF NOT EXISTS idx_session_kills_agent
757
764
  ON session_kills(agent_id);
758
765
  `);
766
+ await client.execute(`
767
+ CREATE TABLE IF NOT EXISTS global_procedures (
768
+ id TEXT PRIMARY KEY,
769
+ title TEXT NOT NULL,
770
+ content TEXT NOT NULL,
771
+ priority TEXT NOT NULL DEFAULT 'p0',
772
+ domain TEXT,
773
+ active INTEGER NOT NULL DEFAULT 1,
774
+ created_at TEXT NOT NULL,
775
+ updated_at TEXT NOT NULL
776
+ )
777
+ `);
759
778
  await client.executeMultiple(`
760
779
  CREATE TABLE IF NOT EXISTS conversations (
761
780
  id TEXT PRIMARY KEY,
@@ -1211,6 +1230,61 @@ var init_config = __esm({
1211
1230
  }
1212
1231
  });
1213
1232
 
1233
+ // src/lib/state-bus.ts
1234
+ var StateBus, orgBus;
1235
+ var init_state_bus = __esm({
1236
+ "src/lib/state-bus.ts"() {
1237
+ "use strict";
1238
+ StateBus = class {
1239
+ handlers = /* @__PURE__ */ new Map();
1240
+ globalHandlers = /* @__PURE__ */ new Set();
1241
+ /** Emit an event to all subscribers */
1242
+ emit(event) {
1243
+ const typeHandlers = this.handlers.get(event.type);
1244
+ if (typeHandlers) {
1245
+ for (const handler of typeHandlers) {
1246
+ try {
1247
+ handler(event);
1248
+ } catch {
1249
+ }
1250
+ }
1251
+ }
1252
+ for (const handler of this.globalHandlers) {
1253
+ try {
1254
+ handler(event);
1255
+ } catch {
1256
+ }
1257
+ }
1258
+ }
1259
+ /** Subscribe to a specific event type */
1260
+ on(type, handler) {
1261
+ if (!this.handlers.has(type)) {
1262
+ this.handlers.set(type, /* @__PURE__ */ new Set());
1263
+ }
1264
+ this.handlers.get(type).add(handler);
1265
+ }
1266
+ /** Subscribe to ALL events */
1267
+ onAny(handler) {
1268
+ this.globalHandlers.add(handler);
1269
+ }
1270
+ /** Unsubscribe from a specific event type */
1271
+ off(type, handler) {
1272
+ this.handlers.get(type)?.delete(handler);
1273
+ }
1274
+ /** Unsubscribe from ALL events */
1275
+ offAny(handler) {
1276
+ this.globalHandlers.delete(handler);
1277
+ }
1278
+ /** Remove all listeners */
1279
+ clear() {
1280
+ this.handlers.clear();
1281
+ this.globalHandlers.clear();
1282
+ }
1283
+ };
1284
+ orgBus = new StateBus();
1285
+ }
1286
+ });
1287
+
1214
1288
  // src/lib/shard-manager.ts
1215
1289
  var shard_manager_exports = {};
1216
1290
  __export(shard_manager_exports, {
@@ -1452,6 +1526,71 @@ var init_shard_manager = __esm({
1452
1526
  }
1453
1527
  });
1454
1528
 
1529
+ // src/lib/global-procedures.ts
1530
+ var global_procedures_exports = {};
1531
+ __export(global_procedures_exports, {
1532
+ deactivateGlobalProcedure: () => deactivateGlobalProcedure,
1533
+ getGlobalProceduresBlock: () => getGlobalProceduresBlock,
1534
+ loadGlobalProcedures: () => loadGlobalProcedures,
1535
+ storeGlobalProcedure: () => storeGlobalProcedure
1536
+ });
1537
+ import { randomUUID } from "crypto";
1538
+ async function loadGlobalProcedures() {
1539
+ const client = getClient();
1540
+ const result = await client.execute({
1541
+ sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
1542
+ args: []
1543
+ });
1544
+ const procedures = result.rows;
1545
+ if (procedures.length > 0) {
1546
+ _cache = procedures.map((p) => `### ${p.title}
1547
+ ${p.content}`).join("\n\n");
1548
+ } else {
1549
+ _cache = "";
1550
+ }
1551
+ _cacheLoaded = true;
1552
+ return procedures;
1553
+ }
1554
+ function getGlobalProceduresBlock() {
1555
+ if (!_cacheLoaded) return "";
1556
+ if (!_cache) return "";
1557
+ return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
1558
+
1559
+ ${_cache}
1560
+ `;
1561
+ }
1562
+ async function storeGlobalProcedure(input) {
1563
+ const id = randomUUID();
1564
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1565
+ const client = getClient();
1566
+ await client.execute({
1567
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
1568
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
1569
+ args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
1570
+ });
1571
+ await loadGlobalProcedures();
1572
+ return id;
1573
+ }
1574
+ async function deactivateGlobalProcedure(id) {
1575
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1576
+ const client = getClient();
1577
+ const result = await client.execute({
1578
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
1579
+ args: [now, id]
1580
+ });
1581
+ await loadGlobalProcedures();
1582
+ return result.rowsAffected > 0;
1583
+ }
1584
+ var _cache, _cacheLoaded;
1585
+ var init_global_procedures = __esm({
1586
+ "src/lib/global-procedures.ts"() {
1587
+ "use strict";
1588
+ init_database();
1589
+ _cache = "";
1590
+ _cacheLoaded = false;
1591
+ }
1592
+ });
1593
+
1455
1594
  // src/lib/store.ts
1456
1595
  var store_exports = {};
1457
1596
  __export(store_exports, {
@@ -1531,6 +1670,11 @@ async function initStore(options) {
1531
1670
  "version-query"
1532
1671
  );
1533
1672
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1673
+ try {
1674
+ const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
1675
+ await loadGlobalProcedures2();
1676
+ } catch {
1677
+ }
1534
1678
  }
1535
1679
  function classifyTier(record) {
1536
1680
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -1572,6 +1716,12 @@ async function writeMemory(record) {
1572
1716
  supersedes_id: record.supersedes_id ?? null
1573
1717
  };
1574
1718
  _pendingRecords.push(dbRow);
1719
+ orgBus.emit({
1720
+ type: "memory_stored",
1721
+ agentId: record.agent_id,
1722
+ project: record.project_name,
1723
+ timestamp: record.timestamp
1724
+ });
1575
1725
  const MAX_PENDING = 1e3;
1576
1726
  if (_pendingRecords.length > MAX_PENDING) {
1577
1727
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -1917,6 +2067,7 @@ var init_store = __esm({
1917
2067
  init_database();
1918
2068
  init_keychain();
1919
2069
  init_config();
2070
+ init_state_bus();
1920
2071
  INIT_MAX_RETRIES = 3;
1921
2072
  INIT_RETRY_DELAY_MS = 1e3;
1922
2073
  _pendingRecords = [];
@@ -2020,7 +2171,7 @@ var init_self_query_router = __esm({
2020
2171
  // src/lib/exe-daemon-client.ts
2021
2172
  import net from "net";
2022
2173
  import { spawn } from "child_process";
2023
- import { randomUUID } from "crypto";
2174
+ import { randomUUID as randomUUID2 } from "crypto";
2024
2175
  import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
2025
2176
  import path4 from "path";
2026
2177
  import { fileURLToPath } from "url";
@@ -2212,7 +2363,7 @@ function sendRequest(texts, priority) {
2212
2363
  resolve({ error: "Not connected" });
2213
2364
  return;
2214
2365
  }
2215
- const id = randomUUID();
2366
+ const id = randomUUID2();
2216
2367
  const timer = setTimeout(() => {
2217
2368
  _pending.delete(id);
2218
2369
  resolve({ error: "Request timeout" });
@@ -2230,7 +2381,7 @@ function sendRequest(texts, priority) {
2230
2381
  async function pingDaemon() {
2231
2382
  if (!_socket || !_connected) return null;
2232
2383
  return new Promise((resolve) => {
2233
- const id = randomUUID();
2384
+ const id = randomUUID2();
2234
2385
  const timer = setTimeout(() => {
2235
2386
  _pending.delete(id);
2236
2387
  resolve(null);
@@ -269,7 +269,21 @@ function getCacheAgeMs() {
269
269
  }
270
270
  }
271
271
  async function checkLicense() {
272
- const key = loadLicense();
272
+ let key = loadLicense();
273
+ if (!key) {
274
+ try {
275
+ const configPath = path2.join(EXE_AI_DIR, "config.json");
276
+ if (existsSync2(configPath)) {
277
+ const raw = JSON.parse(readFileSync2(configPath, "utf8"));
278
+ const cloud = raw.cloud;
279
+ if (cloud?.apiKey) {
280
+ key = cloud.apiKey;
281
+ saveLicense(key);
282
+ }
283
+ }
284
+ } catch {
285
+ }
286
+ }
273
287
  if (!key) return FREE_LICENSE;
274
288
  const cached = await getCachedLicense();
275
289
  if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
@@ -631,8 +631,28 @@ function getMySession() {
631
631
  return getTransport().getMySession();
632
632
  }
633
633
  function employeeSessionName(employee, exeSession, instance) {
634
+ if (!/^exe\d+$/.test(exeSession)) {
635
+ const root = extractRootExe(exeSession);
636
+ if (root) {
637
+ process.stderr.write(
638
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
639
+ `
640
+ );
641
+ exeSession = root;
642
+ } else {
643
+ throw new Error(
644
+ `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
645
+ );
646
+ }
647
+ }
634
648
  const suffix = instance != null && instance > 0 ? String(instance) : "";
635
- return `${employee}${suffix}-${exeSession}`;
649
+ const name = `${employee}${suffix}-${exeSession}`;
650
+ if (!VALID_SESSION_NAME.test(name)) {
651
+ throw new Error(
652
+ `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
653
+ );
654
+ }
655
+ return name;
636
656
  }
637
657
  function extractRootExe(name) {
638
658
  const match = name.match(/(exe\d+)$/);
@@ -791,6 +811,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
791
811
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
792
812
  };
793
813
  }
814
+ if (!/^exe\d+$/.test(exeSession)) {
815
+ const root = extractRootExe(exeSession);
816
+ if (root) {
817
+ process.stderr.write(
818
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
819
+ `
820
+ );
821
+ exeSession = root;
822
+ } else {
823
+ return {
824
+ status: "failed",
825
+ sessionName: "",
826
+ error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
827
+ };
828
+ }
829
+ }
794
830
  let effectiveInstance = opts?.instance;
795
831
  if (effectiveInstance === void 0 && opts?.autoInstance) {
796
832
  const free = findFreeInstance(
@@ -1037,7 +1073,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
1037
1073
  releaseSpawnLock(sessionName);
1038
1074
  return { sessionName };
1039
1075
  }
1040
- var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
1076
+ var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
1041
1077
  var init_tmux_routing = __esm({
1042
1078
  "src/lib/tmux-routing.ts"() {
1043
1079
  "use strict";
@@ -1052,6 +1088,7 @@ var init_tmux_routing = __esm({
1052
1088
  SPAWN_LOCK_DIR = path7.join(os4.homedir(), ".exe-os", "spawn-locks");
1053
1089
  SESSION_CACHE = path7.join(os4.homedir(), ".exe-os", "session-cache");
1054
1090
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
1091
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
1055
1092
  INTERCOM_DEBOUNCE_MS = 3e4;
1056
1093
  INTERCOM_LOG2 = path7.join(os4.homedir(), ".exe-os", "intercom.log");
1057
1094
  DEBOUNCE_FILE = path7.join(SESSION_CACHE, "intercom-debounce.json");