@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
package/dist/index.js CHANGED
@@ -666,6 +666,13 @@ async function ensureSchema() {
666
666
  });
667
667
  } catch {
668
668
  }
669
+ try {
670
+ await client.execute({
671
+ sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
672
+ args: []
673
+ });
674
+ } catch {
675
+ }
669
676
  try {
670
677
  await client.execute({
671
678
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -1112,6 +1119,18 @@ async function ensureSchema() {
1112
1119
  CREATE INDEX IF NOT EXISTS idx_session_kills_agent
1113
1120
  ON session_kills(agent_id);
1114
1121
  `);
1122
+ await client.execute(`
1123
+ CREATE TABLE IF NOT EXISTS global_procedures (
1124
+ id TEXT PRIMARY KEY,
1125
+ title TEXT NOT NULL,
1126
+ content TEXT NOT NULL,
1127
+ priority TEXT NOT NULL DEFAULT 'p0',
1128
+ domain TEXT,
1129
+ active INTEGER NOT NULL DEFAULT 1,
1130
+ created_at TEXT NOT NULL,
1131
+ updated_at TEXT NOT NULL
1132
+ )
1133
+ `);
1115
1134
  await client.executeMultiple(`
1116
1135
  CREATE TABLE IF NOT EXISTS conversations (
1117
1136
  id TEXT PRIMARY KEY,
@@ -1736,6 +1755,61 @@ var init_session_kill_telemetry = __esm({
1736
1755
  }
1737
1756
  });
1738
1757
 
1758
+ // src/lib/state-bus.ts
1759
+ var StateBus, orgBus;
1760
+ var init_state_bus = __esm({
1761
+ "src/lib/state-bus.ts"() {
1762
+ "use strict";
1763
+ StateBus = class {
1764
+ handlers = /* @__PURE__ */ new Map();
1765
+ globalHandlers = /* @__PURE__ */ new Set();
1766
+ /** Emit an event to all subscribers */
1767
+ emit(event) {
1768
+ const typeHandlers = this.handlers.get(event.type);
1769
+ if (typeHandlers) {
1770
+ for (const handler of typeHandlers) {
1771
+ try {
1772
+ handler(event);
1773
+ } catch {
1774
+ }
1775
+ }
1776
+ }
1777
+ for (const handler of this.globalHandlers) {
1778
+ try {
1779
+ handler(event);
1780
+ } catch {
1781
+ }
1782
+ }
1783
+ }
1784
+ /** Subscribe to a specific event type */
1785
+ on(type, handler) {
1786
+ if (!this.handlers.has(type)) {
1787
+ this.handlers.set(type, /* @__PURE__ */ new Set());
1788
+ }
1789
+ this.handlers.get(type).add(handler);
1790
+ }
1791
+ /** Subscribe to ALL events */
1792
+ onAny(handler) {
1793
+ this.globalHandlers.add(handler);
1794
+ }
1795
+ /** Unsubscribe from a specific event type */
1796
+ off(type, handler) {
1797
+ this.handlers.get(type)?.delete(handler);
1798
+ }
1799
+ /** Unsubscribe from ALL events */
1800
+ offAny(handler) {
1801
+ this.globalHandlers.delete(handler);
1802
+ }
1803
+ /** Remove all listeners */
1804
+ clear() {
1805
+ this.handlers.clear();
1806
+ this.globalHandlers.clear();
1807
+ }
1808
+ };
1809
+ orgBus = new StateBus();
1810
+ }
1811
+ });
1812
+
1739
1813
  // src/lib/tasks-crud.ts
1740
1814
  import crypto3 from "crypto";
1741
1815
  import path9 from "path";
@@ -1879,9 +1953,15 @@ async function createTaskCore(input) {
1879
1953
  }
1880
1954
  }
1881
1955
  const complexity = input.complexity ?? "standard";
1956
+ let sessionScope = null;
1957
+ try {
1958
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
1959
+ sessionScope = resolveExeSession2();
1960
+ } catch {
1961
+ }
1882
1962
  await client.execute({
1883
- sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
1884
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1963
+ sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
1964
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1885
1965
  args: [
1886
1966
  id,
1887
1967
  input.title,
@@ -1900,6 +1980,7 @@ async function createTaskCore(input) {
1900
1980
  input.budgetFallbackModel ?? null,
1901
1981
  0,
1902
1982
  null,
1983
+ sessionScope,
1903
1984
  now,
1904
1985
  now
1905
1986
  ]
@@ -1944,6 +2025,15 @@ async function listTasks(input) {
1944
2025
  conditions.push("priority = ?");
1945
2026
  args.push(input.priority);
1946
2027
  }
2028
+ try {
2029
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
2030
+ const session = resolveExeSession2();
2031
+ if (session) {
2032
+ conditions.push("(session_scope IS NULL OR session_scope = ?)");
2033
+ args.push(session);
2034
+ }
2035
+ } catch {
2036
+ }
1947
2037
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1948
2038
  const result = await client.execute({
1949
2039
  sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
@@ -2308,6 +2398,7 @@ var init_tasks_review = __esm({
2308
2398
  init_tasks_crud();
2309
2399
  init_tmux_routing();
2310
2400
  init_session_key();
2401
+ init_state_bus();
2311
2402
  }
2312
2403
  });
2313
2404
 
@@ -2472,13 +2563,12 @@ function assertSessionScope(actionType, targetProject) {
2472
2563
  };
2473
2564
  }
2474
2565
  process.stderr.write(
2475
- `[session-scope] Cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
2566
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
2476
2567
  `
2477
2568
  );
2478
2569
  return {
2479
- allowed: true,
2480
- // v1: warn-only, don't block
2481
- reason: "cross_session_granted",
2570
+ allowed: false,
2571
+ reason: "cross_session_denied",
2482
2572
  currentProject,
2483
2573
  targetProject,
2484
2574
  targetSession: findSessionForProject(targetProject)?.windowName
@@ -2504,8 +2594,9 @@ async function dispatchTaskToEmployee(input) {
2504
2594
  try {
2505
2595
  const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
2506
2596
  const check = assertSessionScope2("dispatch_task", input.projectName);
2507
- if (check.reason === "cross_session_granted") {
2597
+ if (check.reason === "cross_session_denied") {
2508
2598
  crossProject = true;
2599
+ return { dispatched: "skipped", crossProject: true };
2509
2600
  }
2510
2601
  } catch {
2511
2602
  }
@@ -3030,6 +3121,13 @@ async function updateTask(input) {
3030
3121
  await cascadeUnblock(taskId, input.baseDir, now);
3031
3122
  } catch {
3032
3123
  }
3124
+ orgBus.emit({
3125
+ type: "task_completed",
3126
+ taskId,
3127
+ employee: String(row.assigned_to),
3128
+ result: input.result ?? "",
3129
+ timestamp: now
3130
+ });
3033
3131
  if (row.parent_task_id) {
3034
3132
  try {
3035
3133
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -3097,6 +3195,7 @@ var init_tasks = __esm({
3097
3195
  init_database();
3098
3196
  init_config();
3099
3197
  init_notifications();
3198
+ init_state_bus();
3100
3199
  init_tasks_crud();
3101
3200
  init_tasks_review();
3102
3201
  init_tasks_crud();
@@ -3487,8 +3586,28 @@ function getMySession() {
3487
3586
  return getTransport().getMySession();
3488
3587
  }
3489
3588
  function employeeSessionName(employee, exeSession, instance) {
3589
+ if (!/^exe\d+$/.test(exeSession)) {
3590
+ const root = extractRootExe(exeSession);
3591
+ if (root) {
3592
+ process.stderr.write(
3593
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
3594
+ `
3595
+ );
3596
+ exeSession = root;
3597
+ } else {
3598
+ throw new Error(
3599
+ `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
3600
+ );
3601
+ }
3602
+ }
3490
3603
  const suffix = instance != null && instance > 0 ? String(instance) : "";
3491
- return `${employee}${suffix}-${exeSession}`;
3604
+ const name = `${employee}${suffix}-${exeSession}`;
3605
+ if (!VALID_SESSION_NAME.test(name)) {
3606
+ throw new Error(
3607
+ `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
3608
+ );
3609
+ }
3610
+ return name;
3492
3611
  }
3493
3612
  function parseParentExe(sessionName, agentId) {
3494
3613
  const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3728,6 +3847,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
3728
3847
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
3729
3848
  };
3730
3849
  }
3850
+ if (!/^exe\d+$/.test(exeSession)) {
3851
+ const root = extractRootExe(exeSession);
3852
+ if (root) {
3853
+ process.stderr.write(
3854
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
3855
+ `
3856
+ );
3857
+ exeSession = root;
3858
+ } else {
3859
+ return {
3860
+ status: "failed",
3861
+ sessionName: "",
3862
+ error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
3863
+ };
3864
+ }
3865
+ }
3731
3866
  let effectiveInstance = opts?.instance;
3732
3867
  if (effectiveInstance === void 0 && opts?.autoInstance) {
3733
3868
  const free = findFreeInstance(
@@ -3974,7 +4109,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3974
4109
  releaseSpawnLock(sessionName);
3975
4110
  return { sessionName };
3976
4111
  }
3977
- var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
4112
+ var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
3978
4113
  var init_tmux_routing = __esm({
3979
4114
  "src/lib/tmux-routing.ts"() {
3980
4115
  "use strict";
@@ -3989,6 +4124,7 @@ var init_tmux_routing = __esm({
3989
4124
  SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
3990
4125
  SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
3991
4126
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4127
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
3992
4128
  VERIFY_PANE_LINES = 200;
3993
4129
  INTERCOM_DEBOUNCE_MS = 3e4;
3994
4130
  INTERCOM_LOG2 = path14.join(os6.homedir(), ".exe-os", "intercom.log");
@@ -4298,6 +4434,71 @@ var init_shard_manager = __esm({
4298
4434
  }
4299
4435
  });
4300
4436
 
4437
+ // src/lib/global-procedures.ts
4438
+ var global_procedures_exports = {};
4439
+ __export(global_procedures_exports, {
4440
+ deactivateGlobalProcedure: () => deactivateGlobalProcedure,
4441
+ getGlobalProceduresBlock: () => getGlobalProceduresBlock,
4442
+ loadGlobalProcedures: () => loadGlobalProcedures,
4443
+ storeGlobalProcedure: () => storeGlobalProcedure
4444
+ });
4445
+ import { randomUUID as randomUUID3 } from "crypto";
4446
+ async function loadGlobalProcedures() {
4447
+ const client = getClient();
4448
+ const result = await client.execute({
4449
+ sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
4450
+ args: []
4451
+ });
4452
+ const procedures = result.rows;
4453
+ if (procedures.length > 0) {
4454
+ _cache = procedures.map((p) => `### ${p.title}
4455
+ ${p.content}`).join("\n\n");
4456
+ } else {
4457
+ _cache = "";
4458
+ }
4459
+ _cacheLoaded = true;
4460
+ return procedures;
4461
+ }
4462
+ function getGlobalProceduresBlock() {
4463
+ if (!_cacheLoaded) return "";
4464
+ if (!_cache) return "";
4465
+ return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
4466
+
4467
+ ${_cache}
4468
+ `;
4469
+ }
4470
+ async function storeGlobalProcedure(input) {
4471
+ const id = randomUUID3();
4472
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4473
+ const client = getClient();
4474
+ await client.execute({
4475
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
4476
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
4477
+ args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
4478
+ });
4479
+ await loadGlobalProcedures();
4480
+ return id;
4481
+ }
4482
+ async function deactivateGlobalProcedure(id) {
4483
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4484
+ const client = getClient();
4485
+ const result = await client.execute({
4486
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
4487
+ args: [now, id]
4488
+ });
4489
+ await loadGlobalProcedures();
4490
+ return result.rowsAffected > 0;
4491
+ }
4492
+ var _cache, _cacheLoaded;
4493
+ var init_global_procedures = __esm({
4494
+ "src/lib/global-procedures.ts"() {
4495
+ "use strict";
4496
+ init_database();
4497
+ _cache = "";
4498
+ _cacheLoaded = false;
4499
+ }
4500
+ });
4501
+
4301
4502
  // src/lib/store.ts
4302
4503
  var store_exports = {};
4303
4504
  __export(store_exports, {
@@ -4377,6 +4578,11 @@ async function initStore(options) {
4377
4578
  "version-query"
4378
4579
  );
4379
4580
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4581
+ try {
4582
+ const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
4583
+ await loadGlobalProcedures2();
4584
+ } catch {
4585
+ }
4380
4586
  }
4381
4587
  function classifyTier(record) {
4382
4588
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -4418,6 +4624,12 @@ async function writeMemory(record) {
4418
4624
  supersedes_id: record.supersedes_id ?? null
4419
4625
  };
4420
4626
  _pendingRecords.push(dbRow);
4627
+ orgBus.emit({
4628
+ type: "memory_stored",
4629
+ agentId: record.agent_id,
4630
+ project: record.project_name,
4631
+ timestamp: record.timestamp
4632
+ });
4421
4633
  const MAX_PENDING = 1e3;
4422
4634
  if (_pendingRecords.length > MAX_PENDING) {
4423
4635
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -4763,6 +4975,7 @@ var init_store = __esm({
4763
4975
  init_database();
4764
4976
  init_keychain();
4765
4977
  init_config();
4978
+ init_state_bus();
4766
4979
  INIT_MAX_RETRIES = 3;
4767
4980
  INIT_RETRY_DELAY_MS = 1e3;
4768
4981
  _pendingRecords = [];
@@ -5380,7 +5593,7 @@ var init_crm_bridge = __esm({
5380
5593
  // src/lib/exe-daemon-client.ts
5381
5594
  import net from "net";
5382
5595
  import { spawn } from "child_process";
5383
- import { randomUUID as randomUUID5 } from "crypto";
5596
+ import { randomUUID as randomUUID6 } from "crypto";
5384
5597
  import { existsSync as existsSync13, unlinkSync as unlinkSync5, readFileSync as readFileSync10, openSync, closeSync, statSync } from "fs";
5385
5598
  import path17 from "path";
5386
5599
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -5572,7 +5785,7 @@ function sendRequest(texts, priority) {
5572
5785
  resolve({ error: "Not connected" });
5573
5786
  return;
5574
5787
  }
5575
- const id = randomUUID5();
5788
+ const id = randomUUID6();
5576
5789
  const timer = setTimeout(() => {
5577
5790
  _pending.delete(id);
5578
5791
  resolve({ error: "Request timeout" });
@@ -5590,7 +5803,7 @@ function sendRequest(texts, priority) {
5590
5803
  async function pingDaemon() {
5591
5804
  if (!_socket || !_connected) return null;
5592
5805
  return new Promise((resolve) => {
5593
- const id = randomUUID5();
5806
+ const id = randomUUID6();
5594
5807
  const timer = setTimeout(() => {
5595
5808
  _pending.delete(id);
5596
5809
  resolve(null);
@@ -7884,60 +8097,15 @@ function createQuietRenderer() {
7884
8097
  };
7885
8098
  }
7886
8099
 
7887
- // src/runtime/state-bus.ts
7888
- var StateBus = class {
7889
- handlers = /* @__PURE__ */ new Map();
7890
- globalHandlers = /* @__PURE__ */ new Set();
7891
- /** Emit an event to all subscribers */
7892
- emit(event) {
7893
- const typeHandlers = this.handlers.get(event.type);
7894
- if (typeHandlers) {
7895
- for (const handler of typeHandlers) {
7896
- try {
7897
- handler(event);
7898
- } catch {
7899
- }
7900
- }
7901
- }
7902
- for (const handler of this.globalHandlers) {
7903
- try {
7904
- handler(event);
7905
- } catch {
7906
- }
7907
- }
7908
- }
7909
- /** Subscribe to a specific event type */
7910
- on(type, handler) {
7911
- if (!this.handlers.has(type)) {
7912
- this.handlers.set(type, /* @__PURE__ */ new Set());
7913
- }
7914
- this.handlers.get(type).add(handler);
7915
- }
7916
- /** Subscribe to ALL events */
7917
- onAny(handler) {
7918
- this.globalHandlers.add(handler);
7919
- }
7920
- /** Unsubscribe from a specific event type */
7921
- off(type, handler) {
7922
- this.handlers.get(type)?.delete(handler);
7923
- }
7924
- /** Unsubscribe from ALL events */
7925
- offAny(handler) {
7926
- this.globalHandlers.delete(handler);
7927
- }
7928
- /** Remove all listeners */
7929
- clear() {
7930
- this.handlers.clear();
7931
- this.globalHandlers.clear();
7932
- }
7933
- };
7934
- var orgBus = new StateBus();
8100
+ // src/runtime/index.ts
8101
+ init_state_bus();
7935
8102
 
7936
8103
  // src/runtime/session-manager.ts
7937
- import { randomUUID as randomUUID4 } from "crypto";
8104
+ import { randomUUID as randomUUID5 } from "crypto";
8105
+ init_state_bus();
7938
8106
 
7939
8107
  // src/runtime/exe-hooks.ts
7940
- import { randomUUID as randomUUID3 } from "crypto";
8108
+ import { randomUUID as randomUUID4 } from "crypto";
7941
8109
  function createExeOSHooks(config2) {
7942
8110
  let sessionRegistered = false;
7943
8111
  return {
@@ -7996,7 +8164,7 @@ function createExeOSHooks(config2) {
7996
8164
  const toolResponse = result.isError ? { error: result.content } : { output: result.content };
7997
8165
  const rawText = extractSemanticText2(toolName, toolInput, toolResponse);
7998
8166
  await writeMemory2({
7999
- id: randomUUID3(),
8167
+ id: randomUUID4(),
8000
8168
  agent_id: config2.agentId,
8001
8169
  agent_role: "employee",
8002
8170
  session_id: `api-${config2.agentId}`,
@@ -8019,7 +8187,7 @@ function createExeOSHooks(config2) {
8019
8187
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
8020
8188
  if (summary) {
8021
8189
  await writeMemory2({
8022
- id: randomUUID3(),
8190
+ id: randomUUID4(),
8023
8191
  agent_id: config2.agentId,
8024
8192
  agent_role: "employee",
8025
8193
  session_id: `api-${config2.agentId}`,
@@ -8090,7 +8258,7 @@ function createExeOSHooks(config2) {
8090
8258
  await client.execute({
8091
8259
  sql: `INSERT OR IGNORE INTO notifications (id, type, source_agent, message, task_file, created_at, read)
8092
8260
  VALUES (?, 'system', ?, ?, NULL, ?, 0)`,
8093
- args: [randomUUID3(), config2.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
8261
+ args: [randomUUID4(), config2.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
8094
8262
  });
8095
8263
  } catch {
8096
8264
  }
@@ -8106,7 +8274,7 @@ function createExeOSHooks(config2) {
8106
8274
  try {
8107
8275
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
8108
8276
  await writeMemory2({
8109
- id: randomUUID3(),
8277
+ id: randomUUID4(),
8110
8278
  agent_id: config2.agentId,
8111
8279
  agent_role: "employee",
8112
8280
  session_id: `api-${config2.agentId}`,
@@ -8128,7 +8296,7 @@ function createExeOSHooks(config2) {
8128
8296
  try {
8129
8297
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
8130
8298
  await writeMemory2({
8131
- id: randomUUID3(),
8299
+ id: randomUUID4(),
8132
8300
  agent_id: config2.agentId,
8133
8301
  agent_role: "employee",
8134
8302
  session_id: `api-${config2.agentId}`,
@@ -8165,7 +8333,7 @@ function createExeOSHooks(config2) {
8165
8333
  if (tasks.rows.length > 0) {
8166
8334
  const taskList = tasks.rows.map((r) => `- [${String(r.status)}] ${String(r.title)} (${String(r.task_file)})`).join("\n");
8167
8335
  await writeMemory2({
8168
- id: randomUUID3(),
8336
+ id: randomUUID4(),
8169
8337
  agent_id: config2.agentId,
8170
8338
  agent_role: "employee",
8171
8339
  session_id: `api-${config2.agentId}`,
@@ -8191,7 +8359,7 @@ ${taskList}`,
8191
8359
  try {
8192
8360
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
8193
8361
  await writeMemory2({
8194
- id: randomUUID3(),
8362
+ id: randomUUID4(),
8195
8363
  agent_id: config2.agentId,
8196
8364
  agent_role: "employee",
8197
8365
  session_id: `api-${config2.agentId}`,
@@ -8218,7 +8386,7 @@ var SessionManager = class {
8218
8386
  eventHandlers = /* @__PURE__ */ new Set();
8219
8387
  /** Start a new agent session for an employee */
8220
8388
  startSession(employeeId, config2) {
8221
- const sessionId = randomUUID4();
8389
+ const sessionId = randomUUID5();
8222
8390
  const abortController = new AbortController();
8223
8391
  const session = {
8224
8392
  info: {
@@ -8512,6 +8680,9 @@ __export(gateway_exports, {
8512
8680
  validateGatewayConfig: () => validateGatewayConfig
8513
8681
  });
8514
8682
 
8683
+ // src/gateway/gateway.ts
8684
+ init_state_bus();
8685
+
8515
8686
  // src/gateway/router.ts
8516
8687
  function matchesPlatform(msgPlatform, matchPlatform) {
8517
8688
  if (!matchPlatform) return true;
@@ -8948,6 +9119,13 @@ var Gateway = class {
8948
9119
  console.log(
8949
9120
  `[gateway] ${msg.platform}/${msg.senderId} \u2192 ${route.employee} (${route.routeName})`
8950
9121
  );
9122
+ orgBus.emit({
9123
+ type: "gateway_message",
9124
+ platform: msg.platform,
9125
+ senderId: msg.senderId,
9126
+ botId: route.employee,
9127
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
9128
+ });
8951
9129
  const bot = this.botRegistry.get(route.employee);
8952
9130
  if (!bot) {
8953
9131
  console.error(`[gateway] No bot registered for target: ${route.employee}`);
@@ -9741,7 +9919,7 @@ var AnthropicProvider = class {
9741
9919
 
9742
9920
  // src/gateway/providers/openai-compat.ts
9743
9921
  import OpenAI from "openai";
9744
- import { randomUUID as randomUUID6 } from "crypto";
9922
+ import { randomUUID as randomUUID7 } from "crypto";
9745
9923
  var OpenAICompatProvider = class {
9746
9924
  name;
9747
9925
  client;
@@ -9854,7 +10032,7 @@ var OpenAICompatProvider = class {
9854
10032
  }
9855
10033
  content.push({
9856
10034
  type: "tool_use",
9857
- id: call.id ?? randomUUID6(),
10035
+ id: call.id ?? randomUUID7(),
9858
10036
  name: fn.name,
9859
10037
  input
9860
10038
  });
@@ -9876,7 +10054,7 @@ var OpenAICompatProvider = class {
9876
10054
  };
9877
10055
 
9878
10056
  // src/gateway/providers/ollama.ts
9879
- import { randomUUID as randomUUID7 } from "crypto";
10057
+ import { randomUUID as randomUUID8 } from "crypto";
9880
10058
  var OllamaProvider = class {
9881
10059
  name;
9882
10060
  host;
@@ -9945,7 +10123,7 @@ var OllamaProvider = class {
9945
10123
  for (const call of data.message.tool_calls) {
9946
10124
  content.push({
9947
10125
  type: "tool_use",
9948
- id: randomUUID7(),
10126
+ id: randomUUID8(),
9949
10127
  name: call.function.name,
9950
10128
  input: call.function.arguments
9951
10129
  });
@@ -9967,7 +10145,7 @@ var OllamaProvider = class {
9967
10145
  };
9968
10146
 
9969
10147
  // src/gateway/adapters/whatsapp.ts
9970
- import { randomUUID as randomUUID8 } from "crypto";
10148
+ import { randomUUID as randomUUID9 } from "crypto";
9971
10149
  import { homedir } from "os";
9972
10150
  import { join } from "path";
9973
10151
  import { mkdirSync as mkdirSync7 } from "fs";
@@ -10149,7 +10327,7 @@ var WhatsAppAdapter = class {
10149
10327
  const location = this.extractLocation(msg.message);
10150
10328
  const dataCategory = location ? "location" : "message";
10151
10329
  return {
10152
- messageId: msg.key.id ?? randomUUID8(),
10330
+ messageId: msg.key.id ?? randomUUID9(),
10153
10331
  platform: "whatsapp",
10154
10332
  senderId,
10155
10333
  senderName: msg.pushName ?? void 0,
@@ -10194,7 +10372,7 @@ var WhatsAppAdapter = class {
10194
10372
  }
10195
10373
  const timestamp = receipt.readTimestamp ?? receipt.receiptTimestamp ?? Date.now() / 1e3;
10196
10374
  return {
10197
- messageId: randomUUID8(),
10375
+ messageId: randomUUID9(),
10198
10376
  platform: "whatsapp",
10199
10377
  senderId: remoteJid.replace("@s.whatsapp.net", "").replace("@g.us", ""),
10200
10378
  channelId: remoteJid,
@@ -10217,7 +10395,7 @@ var WhatsAppAdapter = class {
10217
10395
  const phone = id.replace("@s.whatsapp.net", "").replace("@g.us", "");
10218
10396
  const name = contact.name ?? contact.notify ?? phone;
10219
10397
  return {
10220
- messageId: randomUUID8(),
10398
+ messageId: randomUUID9(),
10221
10399
  platform: "whatsapp",
10222
10400
  senderId: phone,
10223
10401
  senderName: name,
@@ -10241,7 +10419,7 @@ var WhatsAppAdapter = class {
10241
10419
  const participants = (group.participants ?? []).map((p) => p.id ?? p);
10242
10420
  const admins = (group.participants ?? []).filter((p) => p.admin === "admin" || p.admin === "superadmin").map((p) => p.id ?? p);
10243
10421
  return {
10244
- messageId: randomUUID8(),
10422
+ messageId: randomUUID9(),
10245
10423
  platform: "whatsapp",
10246
10424
  senderId: groupId,
10247
10425
  channelId: groupId,
@@ -10266,7 +10444,7 @@ var WhatsAppAdapter = class {
10266
10444
  if (!reactionData) return null;
10267
10445
  const remoteJid = key.remoteJid ?? "";
10268
10446
  return {
10269
- messageId: randomUUID8(),
10447
+ messageId: randomUUID9(),
10270
10448
  platform: "whatsapp",
10271
10449
  senderId: reactionData.key?.participant ?? reactionData.key?.remoteJid?.replace("@s.whatsapp.net", "") ?? "",
10272
10450
  channelId: remoteJid,
@@ -10288,7 +10466,7 @@ var WhatsAppAdapter = class {
10288
10466
  if (!chatId) return null;
10289
10467
  const caller = call.from?.replace("@s.whatsapp.net", "") ?? "";
10290
10468
  return {
10291
- messageId: randomUUID8(),
10469
+ messageId: randomUUID9(),
10292
10470
  platform: "whatsapp",
10293
10471
  senderId: caller,
10294
10472
  channelId: chatId,
@@ -10331,7 +10509,7 @@ var WhatsAppAdapter = class {
10331
10509
  };
10332
10510
 
10333
10511
  // src/gateway/adapters/signal.ts
10334
- import { randomUUID as randomUUID9 } from "crypto";
10512
+ import { randomUUID as randomUUID10 } from "crypto";
10335
10513
  var DEFAULT_TIMEOUT_MS = 1e4;
10336
10514
  var SignalAdapter = class {
10337
10515
  platform = "signal";
@@ -10416,7 +10594,7 @@ var SignalAdapter = class {
10416
10594
  }
10417
10595
  }
10418
10596
  async rpcRequest(method, params) {
10419
- const id = randomUUID9();
10597
+ const id = randomUUID10();
10420
10598
  const res = await fetch(`${this.baseUrl}/api/v1/rpc`, {
10421
10599
  method: "POST",
10422
10600
  headers: { "Content-Type": "application/json" },
@@ -10506,7 +10684,7 @@ ${val}` : val;
10506
10684
  if (envelope.reactionMessage) {
10507
10685
  const rm = envelope.reactionMessage;
10508
10686
  const normalized2 = {
10509
- messageId: randomUUID9(),
10687
+ messageId: randomUUID10(),
10510
10688
  platform: "signal",
10511
10689
  senderId,
10512
10690
  senderName: envelope.sourceName ?? void 0,
@@ -10534,7 +10712,7 @@ ${val}` : val;
10534
10712
  const rcpt = envelope.receiptMessage;
10535
10713
  for (const ts of rcpt.timestamps) {
10536
10714
  const normalized2 = {
10537
- messageId: randomUUID9(),
10715
+ messageId: randomUUID10(),
10538
10716
  platform: "signal",
10539
10717
  senderId,
10540
10718
  senderName: envelope.sourceName ?? void 0,
@@ -10564,7 +10742,7 @@ ${val}` : val;
10564
10742
  const dm2 = em.dataMessage;
10565
10743
  const isGroup2 = !!dm2.groupInfo?.groupId;
10566
10744
  const normalized2 = {
10567
- messageId: String(dm2.timestamp ?? randomUUID9()),
10745
+ messageId: String(dm2.timestamp ?? randomUUID10()),
10568
10746
  platform: "signal",
10569
10747
  senderId,
10570
10748
  senderName: envelope.sourceName ?? void 0,
@@ -10590,7 +10768,7 @@ ${val}` : val;
10590
10768
  const dm = envelope.dataMessage;
10591
10769
  const isGroup = !!dm.groupInfo?.groupId;
10592
10770
  const normalized = {
10593
- messageId: String(dm.timestamp ?? randomUUID9()),
10771
+ messageId: String(dm.timestamp ?? randomUUID10()),
10594
10772
  platform: "signal",
10595
10773
  senderId,
10596
10774
  senderName: envelope.sourceName ?? void 0,
@@ -10631,7 +10809,7 @@ ${val}` : val;
10631
10809
  if (!phone) continue;
10632
10810
  const name = contact.name ?? contact.profileName ?? phone;
10633
10811
  const normalized = {
10634
- messageId: randomUUID9(),
10812
+ messageId: randomUUID10(),
10635
10813
  platform: "signal",
10636
10814
  senderId: phone,
10637
10815
  senderName: name,
@@ -10660,7 +10838,7 @@ ${val}` : val;
10660
10838
  if (!Array.isArray(groups)) return;
10661
10839
  for (const group of groups) {
10662
10840
  const normalized = {
10663
- messageId: randomUUID9(),
10841
+ messageId: randomUUID10(),
10664
10842
  platform: "signal",
10665
10843
  senderId: `group:${group.id}`,
10666
10844
  channelId: `group:${group.id}`,
@@ -10695,7 +10873,7 @@ ${val}` : val;
10695
10873
  };
10696
10874
 
10697
10875
  // src/gateway/adapters/webchat.ts
10698
- import { randomUUID as randomUUID10 } from "crypto";
10876
+ import { randomUUID as randomUUID11 } from "crypto";
10699
10877
  import { createServer as createServer2 } from "http";
10700
10878
  var WebChatAdapter = class {
10701
10879
  platform = "webchat";
@@ -10791,7 +10969,7 @@ var WebChatAdapter = class {
10791
10969
  res.end(JSON.stringify({ error: "No message text" }));
10792
10970
  return;
10793
10971
  }
10794
- const requestId = randomUUID10();
10972
+ const requestId = randomUUID11();
10795
10973
  const sessionId = parsed.sessionId ?? this.extractSessionId(req);
10796
10974
  const normalized = {
10797
10975
  messageId: requestId,
@@ -10834,7 +11012,7 @@ var WebChatAdapter = class {
10834
11012
  extractSessionId(req) {
10835
11013
  const cookies = req.headers.cookie ?? "";
10836
11014
  const match = cookies.match(/exe_session=([^;]+)/);
10837
- return match?.[1] ?? `anon-${randomUUID10().slice(0, 8)}`;
11015
+ return match?.[1] ?? `anon-${randomUUID11().slice(0, 8)}`;
10838
11016
  }
10839
11017
  };
10840
11018
 
@@ -11116,7 +11294,7 @@ var DiscordAdapter = class {
11116
11294
  };
11117
11295
 
11118
11296
  // src/gateway/adapters/slack.ts
11119
- import { randomUUID as randomUUID11 } from "crypto";
11297
+ import { randomUUID as randomUUID12 } from "crypto";
11120
11298
  var SlackAdapter = class {
11121
11299
  platform = "slack";
11122
11300
  webClient = null;
@@ -11154,7 +11332,7 @@ var SlackAdapter = class {
11154
11332
  if (event.subtype) return;
11155
11333
  const isGroup = event.channel_type !== "im";
11156
11334
  const normalized = {
11157
- messageId: event.client_msg_id ?? event.ts ?? randomUUID11(),
11335
+ messageId: event.client_msg_id ?? event.ts ?? randomUUID12(),
11158
11336
  platform: "slack",
11159
11337
  senderId: event.user ?? "",
11160
11338
  channelId: event.channel ?? "",
@@ -11216,7 +11394,7 @@ var SlackAdapter = class {
11216
11394
  if (!event.text) return;
11217
11395
  const isGroup = event.channel_type !== "im";
11218
11396
  const normalized = {
11219
- messageId: event.ts ?? randomUUID11(),
11397
+ messageId: event.ts ?? randomUUID12(),
11220
11398
  platform: "slack",
11221
11399
  senderId: event.user ?? "",
11222
11400
  senderName: event.user_profile?.display_name ?? event.user_profile?.real_name ?? void 0,
@@ -11601,7 +11779,7 @@ var FailoverExhaustedError = class extends Error {
11601
11779
  };
11602
11780
 
11603
11781
  // src/gateway/session-store.ts
11604
- import { randomUUID as randomUUID12 } from "crypto";
11782
+ import { randomUUID as randomUUID13 } from "crypto";
11605
11783
  var DEFAULT_CONFIG3 = {
11606
11784
  idleTimeoutMs: 30 * 6e4,
11607
11785
  maxMessages: 100
@@ -11628,7 +11806,7 @@ var SessionStore = class {
11628
11806
  existing.status = "closed";
11629
11807
  }
11630
11808
  const session = {
11631
- sessionId: randomUUID12(),
11809
+ sessionId: randomUUID13(),
11632
11810
  customerId,
11633
11811
  botId,
11634
11812
  platform,
@@ -12025,7 +12203,7 @@ function formatAlert(alert) {
12025
12203
  }
12026
12204
 
12027
12205
  // src/gateway/customer-store.ts
12028
- import { randomUUID as randomUUID13 } from "crypto";
12206
+ import { randomUUID as randomUUID14 } from "crypto";
12029
12207
  var CustomerStore = class {
12030
12208
  customers = /* @__PURE__ */ new Map();
12031
12209
  identities = /* @__PURE__ */ new Map();
@@ -12044,7 +12222,7 @@ var CustomerStore = class {
12044
12222
  return customer2;
12045
12223
  }
12046
12224
  const customer = {
12047
- id: randomUUID13(),
12225
+ id: randomUUID14(),
12048
12226
  firstSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
12049
12227
  lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
12050
12228
  interactionCount: 1
@@ -12104,7 +12282,7 @@ async function ensureCRMContact(info) {
12104
12282
 
12105
12283
  // src/automation/trigger-engine.ts
12106
12284
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
12107
- import { randomUUID as randomUUID14 } from "crypto";
12285
+ import { randomUUID as randomUUID15 } from "crypto";
12108
12286
  import path19 from "path";
12109
12287
  import os9 from "os";
12110
12288
  var TRIGGERS_PATH = path19.join(os9.homedir(), ".exe-os", "triggers.json");