@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
@@ -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");
@@ -284,6 +284,61 @@ var init_notifications = __esm({
284
284
  }
285
285
  });
286
286
 
287
+ // src/lib/state-bus.ts
288
+ var StateBus, orgBus;
289
+ var init_state_bus = __esm({
290
+ "src/lib/state-bus.ts"() {
291
+ "use strict";
292
+ StateBus = class {
293
+ handlers = /* @__PURE__ */ new Map();
294
+ globalHandlers = /* @__PURE__ */ new Set();
295
+ /** Emit an event to all subscribers */
296
+ emit(event) {
297
+ const typeHandlers = this.handlers.get(event.type);
298
+ if (typeHandlers) {
299
+ for (const handler of typeHandlers) {
300
+ try {
301
+ handler(event);
302
+ } catch {
303
+ }
304
+ }
305
+ }
306
+ for (const handler of this.globalHandlers) {
307
+ try {
308
+ handler(event);
309
+ } catch {
310
+ }
311
+ }
312
+ }
313
+ /** Subscribe to a specific event type */
314
+ on(type, handler) {
315
+ if (!this.handlers.has(type)) {
316
+ this.handlers.set(type, /* @__PURE__ */ new Set());
317
+ }
318
+ this.handlers.get(type).add(handler);
319
+ }
320
+ /** Subscribe to ALL events */
321
+ onAny(handler) {
322
+ this.globalHandlers.add(handler);
323
+ }
324
+ /** Unsubscribe from a specific event type */
325
+ off(type, handler) {
326
+ this.handlers.get(type)?.delete(handler);
327
+ }
328
+ /** Unsubscribe from ALL events */
329
+ offAny(handler) {
330
+ this.globalHandlers.delete(handler);
331
+ }
332
+ /** Remove all listeners */
333
+ clear() {
334
+ this.handlers.clear();
335
+ this.globalHandlers.clear();
336
+ }
337
+ };
338
+ orgBus = new StateBus();
339
+ }
340
+ });
341
+
287
342
  // src/lib/tasks-crud.ts
288
343
  import crypto2 from "crypto";
289
344
  import path3 from "path";
@@ -1007,6 +1062,7 @@ var init_tasks_review = __esm({
1007
1062
  init_tasks_crud();
1008
1063
  init_tmux_routing();
1009
1064
  init_session_key();
1065
+ init_state_bus();
1010
1066
  }
1011
1067
  });
1012
1068
 
@@ -1489,6 +1545,13 @@ async function updateTask(input) {
1489
1545
  await cascadeUnblock(taskId, input.baseDir, now);
1490
1546
  } catch {
1491
1547
  }
1548
+ orgBus.emit({
1549
+ type: "task_completed",
1550
+ taskId,
1551
+ employee: String(row.assigned_to),
1552
+ result: input.result ?? "",
1553
+ timestamp: now
1554
+ });
1492
1555
  if (row.parent_task_id) {
1493
1556
  try {
1494
1557
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -1543,6 +1606,7 @@ var init_tasks = __esm({
1543
1606
  init_database();
1544
1607
  init_config();
1545
1608
  init_notifications();
1609
+ init_state_bus();
1546
1610
  init_tasks_crud();
1547
1611
  init_tasks_review();
1548
1612
  init_tasks_crud();
@@ -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,
@@ -1677,6 +1696,61 @@ var init_session_kill_telemetry = __esm({
1677
1696
  }
1678
1697
  });
1679
1698
 
1699
+ // src/lib/state-bus.ts
1700
+ var StateBus, orgBus;
1701
+ var init_state_bus = __esm({
1702
+ "src/lib/state-bus.ts"() {
1703
+ "use strict";
1704
+ StateBus = class {
1705
+ handlers = /* @__PURE__ */ new Map();
1706
+ globalHandlers = /* @__PURE__ */ new Set();
1707
+ /** Emit an event to all subscribers */
1708
+ emit(event) {
1709
+ const typeHandlers = this.handlers.get(event.type);
1710
+ if (typeHandlers) {
1711
+ for (const handler of typeHandlers) {
1712
+ try {
1713
+ handler(event);
1714
+ } catch {
1715
+ }
1716
+ }
1717
+ }
1718
+ for (const handler of this.globalHandlers) {
1719
+ try {
1720
+ handler(event);
1721
+ } catch {
1722
+ }
1723
+ }
1724
+ }
1725
+ /** Subscribe to a specific event type */
1726
+ on(type, handler) {
1727
+ if (!this.handlers.has(type)) {
1728
+ this.handlers.set(type, /* @__PURE__ */ new Set());
1729
+ }
1730
+ this.handlers.get(type).add(handler);
1731
+ }
1732
+ /** Subscribe to ALL events */
1733
+ onAny(handler) {
1734
+ this.globalHandlers.add(handler);
1735
+ }
1736
+ /** Unsubscribe from a specific event type */
1737
+ off(type, handler) {
1738
+ this.handlers.get(type)?.delete(handler);
1739
+ }
1740
+ /** Unsubscribe from ALL events */
1741
+ offAny(handler) {
1742
+ this.globalHandlers.delete(handler);
1743
+ }
1744
+ /** Remove all listeners */
1745
+ clear() {
1746
+ this.handlers.clear();
1747
+ this.globalHandlers.clear();
1748
+ }
1749
+ };
1750
+ orgBus = new StateBus();
1751
+ }
1752
+ });
1753
+
1680
1754
  // src/lib/tasks-crud.ts
1681
1755
  import crypto3 from "crypto";
1682
1756
  import path9 from "path";
@@ -1820,9 +1894,15 @@ async function createTaskCore(input) {
1820
1894
  }
1821
1895
  }
1822
1896
  const complexity = input.complexity ?? "standard";
1897
+ let sessionScope = null;
1898
+ try {
1899
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
1900
+ sessionScope = resolveExeSession2();
1901
+ } catch {
1902
+ }
1823
1903
  await client.execute({
1824
- 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)
1825
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1904
+ 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)
1905
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1826
1906
  args: [
1827
1907
  id,
1828
1908
  input.title,
@@ -1841,6 +1921,7 @@ async function createTaskCore(input) {
1841
1921
  input.budgetFallbackModel ?? null,
1842
1922
  0,
1843
1923
  null,
1924
+ sessionScope,
1844
1925
  now,
1845
1926
  now
1846
1927
  ]
@@ -1885,6 +1966,15 @@ async function listTasks(input) {
1885
1966
  conditions.push("priority = ?");
1886
1967
  args.push(input.priority);
1887
1968
  }
1969
+ try {
1970
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
1971
+ const session = resolveExeSession2();
1972
+ if (session) {
1973
+ conditions.push("(session_scope IS NULL OR session_scope = ?)");
1974
+ args.push(session);
1975
+ }
1976
+ } catch {
1977
+ }
1888
1978
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1889
1979
  const result = await client.execute({
1890
1980
  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`,
@@ -2249,6 +2339,7 @@ var init_tasks_review = __esm({
2249
2339
  init_tasks_crud();
2250
2340
  init_tmux_routing();
2251
2341
  init_session_key();
2342
+ init_state_bus();
2252
2343
  }
2253
2344
  });
2254
2345
 
@@ -2413,13 +2504,12 @@ function assertSessionScope(actionType, targetProject) {
2413
2504
  };
2414
2505
  }
2415
2506
  process.stderr.write(
2416
- `[session-scope] Cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
2507
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
2417
2508
  `
2418
2509
  );
2419
2510
  return {
2420
- allowed: true,
2421
- // v1: warn-only, don't block
2422
- reason: "cross_session_granted",
2511
+ allowed: false,
2512
+ reason: "cross_session_denied",
2423
2513
  currentProject,
2424
2514
  targetProject,
2425
2515
  targetSession: findSessionForProject(targetProject)?.windowName
@@ -2445,8 +2535,9 @@ async function dispatchTaskToEmployee(input) {
2445
2535
  try {
2446
2536
  const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
2447
2537
  const check = assertSessionScope2("dispatch_task", input.projectName);
2448
- if (check.reason === "cross_session_granted") {
2538
+ if (check.reason === "cross_session_denied") {
2449
2539
  crossProject = true;
2540
+ return { dispatched: "skipped", crossProject: true };
2450
2541
  }
2451
2542
  } catch {
2452
2543
  }
@@ -2971,6 +3062,13 @@ async function updateTask(input) {
2971
3062
  await cascadeUnblock(taskId, input.baseDir, now);
2972
3063
  } catch {
2973
3064
  }
3065
+ orgBus.emit({
3066
+ type: "task_completed",
3067
+ taskId,
3068
+ employee: String(row.assigned_to),
3069
+ result: input.result ?? "",
3070
+ timestamp: now
3071
+ });
2974
3072
  if (row.parent_task_id) {
2975
3073
  try {
2976
3074
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -3038,6 +3136,7 @@ var init_tasks = __esm({
3038
3136
  init_database();
3039
3137
  init_config();
3040
3138
  init_notifications();
3139
+ init_state_bus();
3041
3140
  init_tasks_crud();
3042
3141
  init_tasks_review();
3043
3142
  init_tasks_crud();
@@ -3428,8 +3527,28 @@ function getMySession() {
3428
3527
  return getTransport().getMySession();
3429
3528
  }
3430
3529
  function employeeSessionName(employee, exeSession, instance) {
3530
+ if (!/^exe\d+$/.test(exeSession)) {
3531
+ const root = extractRootExe(exeSession);
3532
+ if (root) {
3533
+ process.stderr.write(
3534
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
3535
+ `
3536
+ );
3537
+ exeSession = root;
3538
+ } else {
3539
+ throw new Error(
3540
+ `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
3541
+ );
3542
+ }
3543
+ }
3431
3544
  const suffix = instance != null && instance > 0 ? String(instance) : "";
3432
- return `${employee}${suffix}-${exeSession}`;
3545
+ const name = `${employee}${suffix}-${exeSession}`;
3546
+ if (!VALID_SESSION_NAME.test(name)) {
3547
+ throw new Error(
3548
+ `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
3549
+ );
3550
+ }
3551
+ return name;
3433
3552
  }
3434
3553
  function parseParentExe(sessionName, agentId) {
3435
3554
  const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3669,6 +3788,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
3669
3788
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
3670
3789
  };
3671
3790
  }
3791
+ if (!/^exe\d+$/.test(exeSession)) {
3792
+ const root = extractRootExe(exeSession);
3793
+ if (root) {
3794
+ process.stderr.write(
3795
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
3796
+ `
3797
+ );
3798
+ exeSession = root;
3799
+ } else {
3800
+ return {
3801
+ status: "failed",
3802
+ sessionName: "",
3803
+ error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
3804
+ };
3805
+ }
3806
+ }
3672
3807
  let effectiveInstance = opts?.instance;
3673
3808
  if (effectiveInstance === void 0 && opts?.autoInstance) {
3674
3809
  const free = findFreeInstance(
@@ -3915,7 +4050,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3915
4050
  releaseSpawnLock(sessionName);
3916
4051
  return { sessionName };
3917
4052
  }
3918
- 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;
4053
+ 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;
3919
4054
  var init_tmux_routing = __esm({
3920
4055
  "src/lib/tmux-routing.ts"() {
3921
4056
  "use strict";
@@ -3930,6 +4065,7 @@ var init_tmux_routing = __esm({
3930
4065
  SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
3931
4066
  SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
3932
4067
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4068
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
3933
4069
  VERIFY_PANE_LINES = 200;
3934
4070
  INTERCOM_DEBOUNCE_MS = 3e4;
3935
4071
  INTERCOM_LOG2 = path14.join(os6.homedir(), ".exe-os", "intercom.log");
@@ -4239,6 +4375,71 @@ var init_shard_manager = __esm({
4239
4375
  }
4240
4376
  });
4241
4377
 
4378
+ // src/lib/global-procedures.ts
4379
+ var global_procedures_exports = {};
4380
+ __export(global_procedures_exports, {
4381
+ deactivateGlobalProcedure: () => deactivateGlobalProcedure,
4382
+ getGlobalProceduresBlock: () => getGlobalProceduresBlock,
4383
+ loadGlobalProcedures: () => loadGlobalProcedures,
4384
+ storeGlobalProcedure: () => storeGlobalProcedure
4385
+ });
4386
+ import { randomUUID as randomUUID3 } from "crypto";
4387
+ async function loadGlobalProcedures() {
4388
+ const client = getClient();
4389
+ const result = await client.execute({
4390
+ sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
4391
+ args: []
4392
+ });
4393
+ const procedures = result.rows;
4394
+ if (procedures.length > 0) {
4395
+ _cache = procedures.map((p) => `### ${p.title}
4396
+ ${p.content}`).join("\n\n");
4397
+ } else {
4398
+ _cache = "";
4399
+ }
4400
+ _cacheLoaded = true;
4401
+ return procedures;
4402
+ }
4403
+ function getGlobalProceduresBlock() {
4404
+ if (!_cacheLoaded) return "";
4405
+ if (!_cache) return "";
4406
+ return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
4407
+
4408
+ ${_cache}
4409
+ `;
4410
+ }
4411
+ async function storeGlobalProcedure(input) {
4412
+ const id = randomUUID3();
4413
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4414
+ const client = getClient();
4415
+ await client.execute({
4416
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
4417
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
4418
+ args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
4419
+ });
4420
+ await loadGlobalProcedures();
4421
+ return id;
4422
+ }
4423
+ async function deactivateGlobalProcedure(id) {
4424
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4425
+ const client = getClient();
4426
+ const result = await client.execute({
4427
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
4428
+ args: [now, id]
4429
+ });
4430
+ await loadGlobalProcedures();
4431
+ return result.rowsAffected > 0;
4432
+ }
4433
+ var _cache, _cacheLoaded;
4434
+ var init_global_procedures = __esm({
4435
+ "src/lib/global-procedures.ts"() {
4436
+ "use strict";
4437
+ init_database();
4438
+ _cache = "";
4439
+ _cacheLoaded = false;
4440
+ }
4441
+ });
4442
+
4242
4443
  // src/lib/store.ts
4243
4444
  var store_exports = {};
4244
4445
  __export(store_exports, {
@@ -4318,6 +4519,11 @@ async function initStore(options) {
4318
4519
  "version-query"
4319
4520
  );
4320
4521
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4522
+ try {
4523
+ const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
4524
+ await loadGlobalProcedures2();
4525
+ } catch {
4526
+ }
4321
4527
  }
4322
4528
  function classifyTier(record) {
4323
4529
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -4359,6 +4565,12 @@ async function writeMemory(record) {
4359
4565
  supersedes_id: record.supersedes_id ?? null
4360
4566
  };
4361
4567
  _pendingRecords.push(dbRow);
4568
+ orgBus.emit({
4569
+ type: "memory_stored",
4570
+ agentId: record.agent_id,
4571
+ project: record.project_name,
4572
+ timestamp: record.timestamp
4573
+ });
4362
4574
  const MAX_PENDING = 1e3;
4363
4575
  if (_pendingRecords.length > MAX_PENDING) {
4364
4576
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -4704,6 +4916,7 @@ var init_store = __esm({
4704
4916
  init_database();
4705
4917
  init_keychain();
4706
4918
  init_config();
4919
+ init_state_bus();
4707
4920
  INIT_MAX_RETRIES = 3;
4708
4921
  INIT_RETRY_DELAY_MS = 1e3;
4709
4922
  _pendingRecords = [];
@@ -6669,60 +6882,15 @@ function createQuietRenderer() {
6669
6882
  };
6670
6883
  }
6671
6884
 
6672
- // src/runtime/state-bus.ts
6673
- var StateBus = class {
6674
- handlers = /* @__PURE__ */ new Map();
6675
- globalHandlers = /* @__PURE__ */ new Set();
6676
- /** Emit an event to all subscribers */
6677
- emit(event) {
6678
- const typeHandlers = this.handlers.get(event.type);
6679
- if (typeHandlers) {
6680
- for (const handler of typeHandlers) {
6681
- try {
6682
- handler(event);
6683
- } catch {
6684
- }
6685
- }
6686
- }
6687
- for (const handler of this.globalHandlers) {
6688
- try {
6689
- handler(event);
6690
- } catch {
6691
- }
6692
- }
6693
- }
6694
- /** Subscribe to a specific event type */
6695
- on(type, handler) {
6696
- if (!this.handlers.has(type)) {
6697
- this.handlers.set(type, /* @__PURE__ */ new Set());
6698
- }
6699
- this.handlers.get(type).add(handler);
6700
- }
6701
- /** Subscribe to ALL events */
6702
- onAny(handler) {
6703
- this.globalHandlers.add(handler);
6704
- }
6705
- /** Unsubscribe from a specific event type */
6706
- off(type, handler) {
6707
- this.handlers.get(type)?.delete(handler);
6708
- }
6709
- /** Unsubscribe from ALL events */
6710
- offAny(handler) {
6711
- this.globalHandlers.delete(handler);
6712
- }
6713
- /** Remove all listeners */
6714
- clear() {
6715
- this.handlers.clear();
6716
- this.globalHandlers.clear();
6717
- }
6718
- };
6719
- var orgBus = new StateBus();
6885
+ // src/runtime/index.ts
6886
+ init_state_bus();
6720
6887
 
6721
6888
  // src/runtime/session-manager.ts
6722
- import { randomUUID as randomUUID4 } from "crypto";
6889
+ import { randomUUID as randomUUID5 } from "crypto";
6890
+ init_state_bus();
6723
6891
 
6724
6892
  // src/runtime/exe-hooks.ts
6725
- import { randomUUID as randomUUID3 } from "crypto";
6893
+ import { randomUUID as randomUUID4 } from "crypto";
6726
6894
  function createExeOSHooks(config) {
6727
6895
  let sessionRegistered = false;
6728
6896
  return {
@@ -6781,7 +6949,7 @@ function createExeOSHooks(config) {
6781
6949
  const toolResponse = result.isError ? { error: result.content } : { output: result.content };
6782
6950
  const rawText = extractSemanticText2(toolName, toolInput, toolResponse);
6783
6951
  await writeMemory2({
6784
- id: randomUUID3(),
6952
+ id: randomUUID4(),
6785
6953
  agent_id: config.agentId,
6786
6954
  agent_role: "employee",
6787
6955
  session_id: `api-${config.agentId}`,
@@ -6804,7 +6972,7 @@ function createExeOSHooks(config) {
6804
6972
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
6805
6973
  if (summary) {
6806
6974
  await writeMemory2({
6807
- id: randomUUID3(),
6975
+ id: randomUUID4(),
6808
6976
  agent_id: config.agentId,
6809
6977
  agent_role: "employee",
6810
6978
  session_id: `api-${config.agentId}`,
@@ -6875,7 +7043,7 @@ function createExeOSHooks(config) {
6875
7043
  await client.execute({
6876
7044
  sql: `INSERT OR IGNORE INTO notifications (id, type, source_agent, message, task_file, created_at, read)
6877
7045
  VALUES (?, 'system', ?, ?, NULL, ?, 0)`,
6878
- args: [randomUUID3(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
7046
+ args: [randomUUID4(), config.agentId, message.slice(0, 500), (/* @__PURE__ */ new Date()).toISOString()]
6879
7047
  });
6880
7048
  } catch {
6881
7049
  }
@@ -6891,7 +7059,7 @@ function createExeOSHooks(config) {
6891
7059
  try {
6892
7060
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
6893
7061
  await writeMemory2({
6894
- id: randomUUID3(),
7062
+ id: randomUUID4(),
6895
7063
  agent_id: config.agentId,
6896
7064
  agent_role: "employee",
6897
7065
  session_id: `api-${config.agentId}`,
@@ -6913,7 +7081,7 @@ function createExeOSHooks(config) {
6913
7081
  try {
6914
7082
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
6915
7083
  await writeMemory2({
6916
- id: randomUUID3(),
7084
+ id: randomUUID4(),
6917
7085
  agent_id: config.agentId,
6918
7086
  agent_role: "employee",
6919
7087
  session_id: `api-${config.agentId}`,
@@ -6950,7 +7118,7 @@ function createExeOSHooks(config) {
6950
7118
  if (tasks.rows.length > 0) {
6951
7119
  const taskList = tasks.rows.map((r) => `- [${String(r.status)}] ${String(r.title)} (${String(r.task_file)})`).join("\n");
6952
7120
  await writeMemory2({
6953
- id: randomUUID3(),
7121
+ id: randomUUID4(),
6954
7122
  agent_id: config.agentId,
6955
7123
  agent_role: "employee",
6956
7124
  session_id: `api-${config.agentId}`,
@@ -6976,7 +7144,7 @@ ${taskList}`,
6976
7144
  try {
6977
7145
  const { writeMemory: writeMemory2 } = await Promise.resolve().then(() => (init_store(), store_exports));
6978
7146
  await writeMemory2({
6979
- id: randomUUID3(),
7147
+ id: randomUUID4(),
6980
7148
  agent_id: config.agentId,
6981
7149
  agent_role: "employee",
6982
7150
  session_id: `api-${config.agentId}`,
@@ -7003,7 +7171,7 @@ var SessionManager = class {
7003
7171
  eventHandlers = /* @__PURE__ */ new Set();
7004
7172
  /** Start a new agent session for an employee */
7005
7173
  startSession(employeeId, config) {
7006
- const sessionId = randomUUID4();
7174
+ const sessionId = randomUUID5();
7007
7175
  const abortController = new AbortController();
7008
7176
  const session = {
7009
7177
  info: {