@askexenow/exe-os 0.8.53 → 0.8.55

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 (67) hide show
  1. package/dist/bin/backfill-conversations.js +113 -10
  2. package/dist/bin/backfill-responses.js +113 -10
  3. package/dist/bin/backfill-vectors.js +147 -13
  4. package/dist/bin/cleanup-stale-review-tasks.js +113 -10
  5. package/dist/bin/cli.js +337 -211
  6. package/dist/bin/exe-agent.js +99 -4
  7. package/dist/bin/exe-assign.js +113 -10
  8. package/dist/bin/exe-boot.js +276 -85
  9. package/dist/bin/exe-call.js +107 -5
  10. package/dist/bin/exe-doctor.js +183 -13
  11. package/dist/bin/exe-export-behaviors.js +113 -10
  12. package/dist/bin/exe-forget.js +113 -10
  13. package/dist/bin/exe-gateway.js +131 -12
  14. package/dist/bin/exe-heartbeat.js +121 -11
  15. package/dist/bin/exe-kill.js +113 -10
  16. package/dist/bin/exe-launch-agent.js +113 -10
  17. package/dist/bin/exe-link.js +10 -2
  18. package/dist/bin/exe-new-employee.js +95 -0
  19. package/dist/bin/exe-pending-messages.js +113 -10
  20. package/dist/bin/exe-pending-notifications.js +113 -10
  21. package/dist/bin/exe-pending-reviews.js +122 -11
  22. package/dist/bin/exe-rename.js +95 -0
  23. package/dist/bin/exe-review.js +113 -10
  24. package/dist/bin/exe-search.js +113 -10
  25. package/dist/bin/exe-session-cleanup.js +131 -12
  26. package/dist/bin/exe-status.js +113 -10
  27. package/dist/bin/exe-team.js +113 -10
  28. package/dist/bin/git-sweep.js +131 -12
  29. package/dist/bin/graph-backfill.js +113 -10
  30. package/dist/bin/graph-export.js +113 -10
  31. package/dist/bin/scan-tasks.js +131 -12
  32. package/dist/bin/setup.js +107 -5
  33. package/dist/bin/shard-migrate.js +113 -10
  34. package/dist/bin/wiki-sync.js +113 -10
  35. package/dist/gateway/index.js +131 -12
  36. package/dist/hooks/bug-report-worker.js +131 -12
  37. package/dist/hooks/commit-complete.js +131 -12
  38. package/dist/hooks/error-recall.js +113 -10
  39. package/dist/hooks/ingest-worker.js +131 -12
  40. package/dist/hooks/instructions-loaded.js +113 -10
  41. package/dist/hooks/notification.js +113 -10
  42. package/dist/hooks/post-compact.js +113 -10
  43. package/dist/hooks/pre-compact.js +131 -12
  44. package/dist/hooks/pre-tool-use.js +113 -10
  45. package/dist/hooks/prompt-ingest-worker.js +113 -10
  46. package/dist/hooks/prompt-submit.js +140 -14
  47. package/dist/hooks/response-ingest-worker.js +113 -10
  48. package/dist/hooks/session-end.js +113 -10
  49. package/dist/hooks/session-start.js +113 -10
  50. package/dist/hooks/stop.js +113 -10
  51. package/dist/hooks/subagent-stop.js +113 -10
  52. package/dist/hooks/summary-worker.js +231 -114
  53. package/dist/index.js +131 -12
  54. package/dist/lib/cloud-sync.js +10 -2
  55. package/dist/lib/employee-templates.js +99 -4
  56. package/dist/lib/exe-daemon.js +4859 -4706
  57. package/dist/lib/hybrid-search.js +113 -10
  58. package/dist/lib/schedules.js +113 -10
  59. package/dist/lib/store.js +113 -10
  60. package/dist/lib/tasks.js +18 -2
  61. package/dist/lib/tmux-routing.js +18 -2
  62. package/dist/mcp/server.js +214 -28
  63. package/dist/mcp/tools/create-task.js +18 -2
  64. package/dist/mcp/tools/list-tasks.js +18 -2
  65. package/dist/runtime/index.js +131 -12
  66. package/dist/tui/App.js +337 -211
  67. package/package.json +2 -2
package/dist/tui/App.js CHANGED
@@ -2811,6 +2811,103 @@ var init_agent_loop = __esm({
2811
2811
  }
2812
2812
  });
2813
2813
 
2814
+ // src/lib/platform-procedures.ts
2815
+ var PLATFORM_PROCEDURES, PLATFORM_PROCEDURE_TITLES;
2816
+ var init_platform_procedures = __esm({
2817
+ "src/lib/platform-procedures.ts"() {
2818
+ "use strict";
2819
+ PLATFORM_PROCEDURES = [
2820
+ // --- Foundation: what is exe-os ---
2821
+ {
2822
+ title: "What is exe-os \u2014 the operating model every agent must understand",
2823
+ domain: "architecture",
2824
+ priority: "p0",
2825
+ content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO (exe), CTO (yoshi), CMO (mari), engineers (tom), content (sasha). Each agent has identity, expertise, and experience layers \u2014 persistent memory that makes them better over time. All data is local-first, E2EE, owned by the user. The MCP server is the ONLY data interface \u2014 never access the DB directly."
2826
+ },
2827
+ {
2828
+ title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
2829
+ domain: "architecture",
2830
+ priority: "p0",
2831
+ content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC, runs /exe to boot the COO. exe manages employees in tmux sessions. Each exeN is a separate CC window/project. Employees (yoshi, tom, mari) run in their own tmux panes via create_task auto-spawn. The founder talks to exe; exe orchestrates the team. CC is the shell, exe-os is the brain."
2832
+ },
2833
+ {
2834
+ title: "Sessions explained \u2014 what exeN means and how projects work",
2835
+ domain: "architecture",
2836
+ priority: "p0",
2837
+ content: "Each exeN (exe1, exe2, exe3) is an isolated project session. exe1 might be exe-os development, exe2 might be exe-wiki. Each session spawns its own employees: exe1\u2192yoshi-exe1\u2192tom-exe1. Sessions share the same memory DB but tasks are scoped to the session that created them. A founder can run multiple projects simultaneously. Sessions never interfere with each other."
2838
+ },
2839
+ // --- Hierarchy and dispatch ---
2840
+ {
2841
+ title: "Chain of command \u2014 who talks to whom",
2842
+ domain: "workflow",
2843
+ priority: "p0",
2844
+ content: "Founder \u2192 exe (COO) \u2192 yoshi (CTO) / mari (CMO). Yoshi \u2192 tom (engineer). Mari \u2192 sasha (content). Never skip levels: exe never assigns directly to tom. Tom never reports directly to exe. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
2845
+ },
2846
+ {
2847
+ title: "Single dispatch path \u2014 create_task only",
2848
+ domain: "workflow",
2849
+ priority: "p0",
2850
+ content: "create_task is the ONLY way to dispatch work to another agent. No direct ensureEmployee calls, no manual tmux spawns, no send_message for actionable work. create_task \u2192 system auto-spawns \u2192 session correctly named. ONE PATH. No backdoors. No exceptions."
2851
+ },
2852
+ // --- Session isolation ---
2853
+ {
2854
+ title: "Session scoping \u2014 stay in your exe boundary",
2855
+ domain: "security",
2856
+ priority: "p0",
2857
+ content: "Session scoping is mandatory. Managers dispatch to workers within their own exe session ONLY. exe1\u2192yoshi-exe1\u2192tom-exe1. exe2\u2192yoshi-exe2\u2192tom2-exe2. Cross-session dispatch is blocked by the system. Verify session names before dispatch. Tasks are scoped to the creating exe session."
2858
+ },
2859
+ {
2860
+ title: "Session isolation \u2014 never touch another session's work",
2861
+ domain: "workflow",
2862
+ priority: "p0",
2863
+ content: `Sessions are isolated. exeN owns ONLY tasks it dispatched. (1) Never close/update/cancel tasks from another exe session. (2) Never review work from a different session \u2014 report "belongs to exeN" and skip. (3) Ignore other sessions' items in list_tasks results. (4) Employees inherit session: yoshi-exe1 works ONLY on exe1 tasks. Cross-session work is a system violation.`
2864
+ },
2865
+ // --- Engineering: session scoping in code ---
2866
+ {
2867
+ title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
2868
+ domain: "architecture",
2869
+ priority: "p0",
2870
+ content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching current exeN. (2) Project \u2014 filter by project_name. (3) Role \u2014 agents only see data at their hierarchy level. When writing ANY function that touches tasks, reviews, messages, or notifications: always accept a sessionScope parameter and pass it to the SQL WHERE clause. Unscoped queries are bugs. Test by running 2+ exe sessions simultaneously."
2871
+ },
2872
+ // --- Hard constraints ---
2873
+ {
2874
+ title: "What you CANNOT do in exe-os \u2014 hard constraints",
2875
+ domain: "security",
2876
+ priority: "p0",
2877
+ content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 exe reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
2878
+ },
2879
+ // --- Operations ---
2880
+ {
2881
+ title: "Managers must supervise deployed workers",
2882
+ domain: "workflow",
2883
+ priority: "p0",
2884
+ content: `Every manager (COO/CTO/CMO) who dispatches work to a worker MUST actively monitor them. Check tmux capture-pane every 10 minutes. Verify they're working, not stuck. If idle at prompt with in_progress task \u2192 send intercom. If stuck \u2192 unblock or escalate. "Standing by" without checking is negligence.`
2885
+ },
2886
+ {
2887
+ title: "COO boot health check \u2014 memory, cloud sync, daemon on every launch",
2888
+ domain: "workflow",
2889
+ priority: "p0",
2890
+ content: "On every /exe boot, COO MUST check system health BEFORE other work: (1) daemon \u2014 is exed PID alive, (2) cloud sync \u2014 grep workers.log for recent cloud-sync errors, (3) memory count \u2014 total in DB, (4) sync delta \u2014 local vs cloud storage_bytes. Report as 4-line status table. If ANY check fails, surface to founder immediately. Do not proceed to tasks until health confirmed."
2891
+ },
2892
+ {
2893
+ title: "exe-build-adv mandatory for 3+ files",
2894
+ domain: "workflow",
2895
+ priority: "p0",
2896
+ content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
2897
+ },
2898
+ {
2899
+ title: "Desktop and TUI are the same product",
2900
+ domain: "architecture",
2901
+ priority: "p0",
2902
+ content: "Desktop and TUI are the SAME product in different renderers. Same data contracts, same interactions, same acceptance criteria. Desktop tab specs in ARCHITECTURE.md ARE the TUI specs. When building TUI, cross-reference Desktop spec. Different tab names, identical behavior. Never treat them as separate products."
2903
+ }
2904
+ ];
2905
+ PLATFORM_PROCEDURE_TITLES = new Set(
2906
+ PLATFORM_PROCEDURES.map((p) => p.title)
2907
+ );
2908
+ }
2909
+ });
2910
+
2814
2911
  // src/lib/global-procedures.ts
2815
2912
  var global_procedures_exports = {};
2816
2913
  __export(global_procedures_exports, {
@@ -2826,22 +2923,25 @@ async function loadGlobalProcedures() {
2826
2923
  sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
2827
2924
  args: []
2828
2925
  });
2829
- const procedures = result.rows;
2830
- if (procedures.length > 0) {
2831
- _cache = procedures.map((p) => `### ${p.title}
2926
+ const allRows = result.rows;
2927
+ const customerOnly = allRows.filter((p) => !PLATFORM_PROCEDURE_TITLES.has(p.title));
2928
+ if (customerOnly.length > 0) {
2929
+ _customerCache = customerOnly.map((p) => `### ${p.title}
2832
2930
  ${p.content}`).join("\n\n");
2833
2931
  } else {
2834
- _cache = "";
2932
+ _customerCache = "";
2835
2933
  }
2836
2934
  _cacheLoaded = true;
2837
- return procedures;
2935
+ return customerOnly;
2838
2936
  }
2839
2937
  function getGlobalProceduresBlock() {
2840
- if (!_cacheLoaded) return "";
2841
- if (!_cache) return "";
2938
+ const sections = [];
2939
+ if (_platformCache) sections.push(_platformCache);
2940
+ if (_cacheLoaded && _customerCache) sections.push(_customerCache);
2941
+ if (sections.length === 0) return "";
2842
2942
  return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
2843
2943
 
2844
- ${_cache}
2944
+ ${sections.join("\n\n")}
2845
2945
  `;
2846
2946
  }
2847
2947
  async function storeGlobalProcedure(input) {
@@ -2866,13 +2966,16 @@ async function deactivateGlobalProcedure(id) {
2866
2966
  await loadGlobalProcedures();
2867
2967
  return result.rowsAffected > 0;
2868
2968
  }
2869
- var _cache, _cacheLoaded;
2969
+ var _customerCache, _cacheLoaded, _platformCache;
2870
2970
  var init_global_procedures = __esm({
2871
2971
  "src/lib/global-procedures.ts"() {
2872
2972
  "use strict";
2873
2973
  init_database();
2874
- _cache = "";
2974
+ init_platform_procedures();
2975
+ _customerCache = "";
2875
2976
  _cacheLoaded = false;
2977
+ _platformCache = PLATFORM_PROCEDURES.map((p) => `### ${p.title}
2978
+ ${p.content}`).join("\n\n");
2876
2979
  }
2877
2980
  });
2878
2981
 
@@ -5189,16 +5292,32 @@ var init_tasks_crud = __esm({
5189
5292
  // src/lib/tasks-review.ts
5190
5293
  import path16 from "path";
5191
5294
  import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
5192
- async function countPendingReviews() {
5295
+ async function countPendingReviews(sessionScope) {
5193
5296
  const client = getClient();
5297
+ if (sessionScope) {
5298
+ const result2 = await client.execute({
5299
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
5300
+ args: [sessionScope]
5301
+ });
5302
+ return Number(result2.rows[0]?.cnt) || 0;
5303
+ }
5194
5304
  const result = await client.execute({
5195
5305
  sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
5196
5306
  args: []
5197
5307
  });
5198
5308
  return Number(result.rows[0]?.cnt) || 0;
5199
5309
  }
5200
- async function countNewPendingReviewsSince(sinceIso) {
5310
+ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
5201
5311
  const client = getClient();
5312
+ if (sessionScope) {
5313
+ const result2 = await client.execute({
5314
+ sql: `SELECT COUNT(*) as cnt FROM tasks
5315
+ WHERE status = 'needs_review' AND updated_at > ?
5316
+ AND (session_scope = ? OR session_scope IS NULL)`,
5317
+ args: [sinceIso, sessionScope]
5318
+ });
5319
+ return Number(result2.rows[0]?.cnt) || 0;
5320
+ }
5202
5321
  const result = await client.execute({
5203
5322
  sql: `SELECT COUNT(*) as cnt FROM tasks
5204
5323
  WHERE status = 'needs_review' AND updated_at > ?`,
@@ -14338,8 +14457,8 @@ ${info.componentStack ?? ""}
14338
14457
  }
14339
14458
  render() {
14340
14459
  if (this.state.error) {
14341
- return /* @__PURE__ */ jsxs(Box_default, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
14342
- /* @__PURE__ */ jsxs(Box_default, { children: [
14460
+ return /* @__PURE__ */ jsxs(Box_default, { testID: "error-boundary", flexDirection: "column", paddingX: 1, paddingY: 1, children: [
14461
+ /* @__PURE__ */ jsxs(Box_default, { testID: "error-message", children: [
14343
14462
  /* @__PURE__ */ jsx2(Text, { backgroundColor: "red", color: "white", children: " ERROR " }),
14344
14463
  /* @__PURE__ */ jsxs(Text, { children: [
14345
14464
  " ",
@@ -14394,7 +14513,7 @@ function AlternateScreen({ children }) {
14394
14513
  process.stdout.write(SHOW_CURSOR + EXIT_ALT_SCREEN);
14395
14514
  };
14396
14515
  }, []);
14397
- return /* @__PURE__ */ jsx3(Box_default, { flexDirection: "column", height: rows, width: "100%", children });
14516
+ return /* @__PURE__ */ jsx3(Box_default, { testID: "alternate-screen", flexDirection: "column", height: rows, width: "100%", children });
14398
14517
  }
14399
14518
 
14400
14519
  // src/tui/terminal.ts
@@ -14451,6 +14570,7 @@ function Sidebar({
14451
14570
  return /* @__PURE__ */ jsxs2(
14452
14571
  Box_default,
14453
14572
  {
14573
+ testID: "sidebar-nav",
14454
14574
  flexDirection: "column",
14455
14575
  width: 26,
14456
14576
  borderStyle: "single",
@@ -14464,19 +14584,19 @@ function Sidebar({
14464
14584
  SECTIONS.map(({ key, label, shortcut }) => {
14465
14585
  const isActive = active === key;
14466
14586
  if (isActive && focused) {
14467
- return /* @__PURE__ */ jsxs2(Text, { backgroundColor: "#6B4C9A", color: "#F5D76E", bold: true, children: [
14587
+ return /* @__PURE__ */ jsx4(Box_default, { testID: `sidebar-item-${key}`, children: /* @__PURE__ */ jsxs2(Text, { backgroundColor: "#6B4C9A", color: "#F5D76E", bold: true, children: [
14468
14588
  "\u25B8 ",
14469
14589
  shortcut,
14470
14590
  " ",
14471
14591
  label
14472
- ] }, key);
14592
+ ] }) }, key);
14473
14593
  }
14474
- return /* @__PURE__ */ jsxs2(Text, { color: isActive ? "#F5D76E" : "#6B4C9A", children: [
14594
+ return /* @__PURE__ */ jsx4(Box_default, { testID: `sidebar-item-${key}`, children: /* @__PURE__ */ jsxs2(Text, { color: isActive ? "#F5D76E" : "#6B4C9A", children: [
14475
14595
  isActive ? "\u25B8 " : " ",
14476
14596
  shortcut,
14477
14597
  " ",
14478
14598
  label
14479
- ] }, key);
14599
+ ] }) }, key);
14480
14600
  })
14481
14601
  ] }),
14482
14602
  /* @__PURE__ */ jsx4(Text, { color: "#6B4C9A", children: " \u2715 Quit (q)" })
@@ -14569,9 +14689,9 @@ function Footer() {
14569
14689
  }
14570
14690
  });
14571
14691
  }
14572
- return /* @__PURE__ */ jsxs3(Box_default, { flexDirection: "column", children: [
14692
+ return /* @__PURE__ */ jsxs3(Box_default, { testID: "footer", flexDirection: "column", children: [
14573
14693
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns || 120) }),
14574
- /* @__PURE__ */ jsxs3(Box_default, { paddingX: 1, justifyContent: "space-between", children: [
14694
+ /* @__PURE__ */ jsxs3(Box_default, { testID: "footer-stats", paddingX: 1, justifyContent: "space-between", children: [
14575
14695
  /* @__PURE__ */ jsxs3(Box_default, { gap: 1, children: [
14576
14696
  currentSession ? /* @__PURE__ */ jsxs3(Fragment, { children: [
14577
14697
  /* @__PURE__ */ jsx5(Text, { color: "#22C55E", children: currentSession }),
@@ -14597,7 +14717,7 @@ function Footer() {
14597
14717
  daemon
14598
14718
  ] })
14599
14719
  ] }),
14600
- /* @__PURE__ */ jsxs3(Box_default, { gap: 1, children: [
14720
+ /* @__PURE__ */ jsxs3(Box_default, { testID: "footer-hints", gap: 1, children: [
14601
14721
  plan && plan !== "free" && /* @__PURE__ */ jsxs3(Fragment, { children: [
14602
14722
  /* @__PURE__ */ jsx5(Text, { color: "#22C55E", children: plan.toUpperCase() }),
14603
14723
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" })
@@ -14633,7 +14753,7 @@ var STATUS_CONFIG = {
14633
14753
  };
14634
14754
  function StatusDot({ status, showLabel = true }) {
14635
14755
  const { char, dotColor, label, labelColor } = STATUS_CONFIG[status];
14636
- return /* @__PURE__ */ jsxs4(Box_default, { gap: 0, children: [
14756
+ return /* @__PURE__ */ jsxs4(Box_default, { testID: "status-dot", gap: 0, children: [
14637
14757
  /* @__PURE__ */ jsx6(Text, { color: dotColor, children: char }),
14638
14758
  showLabel && /* @__PURE__ */ jsxs4(Text, { color: labelColor, children: [
14639
14759
  " ",
@@ -15766,8 +15886,8 @@ function CommandCenterView({
15766
15886
  setChatInput("");
15767
15887
  }, [sendMessage, isThinking]);
15768
15888
  if (chatMode) {
15769
- return /* @__PURE__ */ jsxs5(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
15770
- /* @__PURE__ */ jsxs5(Box_default, { justifyContent: "space-between", alignItems: "center", children: [
15889
+ return /* @__PURE__ */ jsxs5(Box_default, { testID: "command-center-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
15890
+ /* @__PURE__ */ jsxs5(Box_default, { testID: "command-center-header", justifyContent: "space-between", alignItems: "center", children: [
15771
15891
  /* @__PURE__ */ jsx7(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, children: /* @__PURE__ */ jsx7(Text, { bold: true, children: "Chat" }) }),
15772
15892
  /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "Ctrl+L clear | Escape to return" })
15773
15893
  ] }),
@@ -15827,8 +15947,8 @@ function CommandCenterView({
15827
15947
  ] })
15828
15948
  ] });
15829
15949
  }
15830
- return /* @__PURE__ */ jsxs5(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
15831
- /* @__PURE__ */ jsxs5(Box_default, { justifyContent: "space-between", alignItems: "center", children: [
15950
+ return /* @__PURE__ */ jsxs5(Box_default, { testID: "command-center-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
15951
+ /* @__PURE__ */ jsxs5(Box_default, { testID: "command-center-header", justifyContent: "space-between", alignItems: "center", children: [
15832
15952
  /* @__PURE__ */ jsx7(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, children: /* @__PURE__ */ jsx7(Text, { bold: true, children: "Command Center" }) }),
15833
15953
  /* @__PURE__ */ jsx7(Text, { color: selectableProjects.some((p) => p.status === "active") ? "green" : "gray", children: selectableProjects.some((p) => p.status === "active") ? "\u25CF Active" : "\u25CF Idle" })
15834
15954
  ] }),
@@ -15897,7 +16017,7 @@ function CommandCenterView({
15897
16017
  );
15898
16018
  });
15899
16019
  sections.push(
15900
- /* @__PURE__ */ jsx7(Box_default, { flexDirection: "row", flexWrap: "wrap", gap: 1, children: cards }, `cards-${sectionKey++}`)
16020
+ /* @__PURE__ */ jsx7(Box_default, { testID: "command-center-cards", flexDirection: "row", flexWrap: "wrap", gap: 1, children: cards }, `cards-${sectionKey++}`)
15901
16021
  );
15902
16022
  currentProjects = [];
15903
16023
  };
@@ -16027,7 +16147,7 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
16027
16147
  });
16028
16148
  const modeLabel = demo ? "DEMO" : alive ? "INTERACTIVE" : "DISCONNECTED";
16029
16149
  const modeColor = demo ? "#6B4C9A" : alive ? "#22C55E" : "#6B7280";
16030
- return /* @__PURE__ */ jsxs6(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16150
+ return /* @__PURE__ */ jsxs6(Box_default, { testID: "tmux-pane", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16031
16151
  /* @__PURE__ */ jsxs6(Box_default, { justifyContent: "space-between", children: [
16032
16152
  /* @__PURE__ */ jsxs6(Text, { bold: true, children: [
16033
16153
  employeeName,
@@ -16053,6 +16173,7 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
16053
16173
  /* @__PURE__ */ jsx8(
16054
16174
  Box_default,
16055
16175
  {
16176
+ testID: "tmux-pane-output",
16056
16177
  flexDirection: "column",
16057
16178
  flexGrow: 1,
16058
16179
  paddingX: 1,
@@ -16506,7 +16627,7 @@ function SessionsView({
16506
16627
  const inCarousel = carouselEmployees.length > 0;
16507
16628
  const prevName = inCarousel && carouselIdx > 0 ? carouselEmployees[carouselIdx - 1].name : null;
16508
16629
  const nextName = inCarousel && carouselIdx < carouselEmployees.length - 1 ? carouselEmployees[carouselIdx + 1].name : null;
16509
- return /* @__PURE__ */ jsxs7(Box_default, { flexDirection: "column", flexGrow: 1, children: [
16630
+ return /* @__PURE__ */ jsxs7(Box_default, { testID: "sessions-view", flexDirection: "column", flexGrow: 1, children: [
16510
16631
  inCarousel && /* @__PURE__ */ jsxs7(Box_default, { paddingX: 1, justifyContent: "space-between", children: [
16511
16632
  /* @__PURE__ */ jsxs7(Text, { children: [
16512
16633
  /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: prevName ? `\u25C4 ${prevName}` : "\u25C4 back" }),
@@ -16548,7 +16669,7 @@ function SessionsView({
16548
16669
  ] });
16549
16670
  }
16550
16671
  const flatItems = buildFlatList();
16551
- return /* @__PURE__ */ jsxs7(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16672
+ return /* @__PURE__ */ jsxs7(Box_default, { testID: "sessions-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16552
16673
  /* @__PURE__ */ jsx9(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx9(Text, { bold: true, children: "Sessions" }) }),
16553
16674
  /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Arrows to navigate, Enter to view/launch, k to kill, Escape to detach" }),
16554
16675
  !demo && !orch.isLoading && /* @__PURE__ */ jsxs7(Box_default, { gap: 2, marginTop: 0, children: [
@@ -16585,7 +16706,7 @@ function SessionsView({
16585
16706
  loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : sessionError ? /* @__PURE__ */ jsxs7(Text, { color: "#C91B00", children: [
16586
16707
  "Failed to query sessions: ",
16587
16708
  sessionError
16588
- ] }) : !tmuxAvailable ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "tmux not available. Start a tmux session first to manage employees." }) : flatItems.length === 0 ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "No active sessions. Create a task to spawn an employee." }) : flatItems.map((item, i) => {
16709
+ ] }) : !tmuxAvailable ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "tmux not available. Start a tmux session first to manage employees." }) : flatItems.length === 0 ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "No active sessions. Create a task to spawn an employee." }) : /* @__PURE__ */ jsx9(Box_default, { testID: "sessions-tree", flexDirection: "column", children: flatItems.map((item, i) => {
16589
16710
  const isSelected = i === selectedIdx;
16590
16711
  const marker = isSelected ? "\u25B8 " : " ";
16591
16712
  if (item.type === "project") {
@@ -16623,7 +16744,7 @@ function SessionsView({
16623
16744
  ] }),
16624
16745
  activityPreview && emp.status !== "offline" && /* @__PURE__ */ jsx9(Box_default, { paddingLeft: 4, children: /* @__PURE__ */ jsx9(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", backgroundColor: isSelected ? "#6B4C9A" : void 0, children: activityPreview }) })
16625
16746
  ] }, `emp-${item.projectName}-${emp.name}`);
16626
- })
16747
+ }) })
16627
16748
  ] });
16628
16749
  }
16629
16750
 
@@ -16798,7 +16919,7 @@ function TasksView({ onBack }) {
16798
16919
  }
16799
16920
  const selected = taskRows[selectedTask]?.task;
16800
16921
  if (showDetail && selected) {
16801
- return /* @__PURE__ */ jsxs8(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16922
+ return /* @__PURE__ */ jsxs8(Box_default, { testID: "tasks-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16802
16923
  /* @__PURE__ */ jsx10(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx10(Text, { bold: true, children: "Task Detail" }) }),
16803
16924
  /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Press Escape to go back" }),
16804
16925
  /* @__PURE__ */ jsx10(Text, { children: " " }),
@@ -16847,8 +16968,8 @@ function TasksView({ onBack }) {
16847
16968
  if (assigneeFilter !== "all") filterParts.push(`assignee:${assigneeFilter}`);
16848
16969
  if (projectFilter !== "all") filterParts.push(`project:${projectFilter}`);
16849
16970
  const filterLabel = filterParts.length > 0 ? filterParts.join(" ") : "none";
16850
- return /* @__PURE__ */ jsxs8(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16851
- /* @__PURE__ */ jsx10(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx10(Text, { bold: true, children: "Task Board" }) }),
16971
+ return /* @__PURE__ */ jsxs8(Box_default, { testID: "tasks-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
16972
+ /* @__PURE__ */ jsx10(Box_default, { testID: "tasks-list", borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx10(Text, { bold: true, children: "Task Board" }) }),
16852
16973
  /* @__PURE__ */ jsx10(Text, { children: " " }),
16853
16974
  /* @__PURE__ */ jsxs8(Text, { color: "#6B4C9A", children: [
16854
16975
  filteredTasks.length,
@@ -17353,7 +17474,7 @@ function GatewayView({ onBack }) {
17353
17474
  /* @__PURE__ */ jsx11(Text, { color: "#6B4C9A", children: "External agents and communication channels will appear here once set up." })
17354
17475
  ] });
17355
17476
  }
17356
- return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17477
+ return /* @__PURE__ */ jsxs9(Box_default, { testID: "gateway-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17357
17478
  /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
17358
17479
  /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17359
17480
  "\u2191\u2193",
@@ -17394,62 +17515,64 @@ function GatewayView({ onBack }) {
17394
17515
  /* @__PURE__ */ jsx11(Text, { color: "#F5D76E", bold: true, children: totalUnread })
17395
17516
  ] }),
17396
17517
  /* @__PURE__ */ jsx11(Text, { children: " " }),
17397
- /* @__PURE__ */ jsxs9(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
17398
- marker(1),
17399
- "Conversations",
17400
- /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17401
- " (",
17402
- filteredConversations.length,
17403
- ")"
17404
- ] })
17405
- ] }),
17406
- /* @__PURE__ */ jsx11(Box_default, { paddingLeft: 4, gap: 1, children: PLATFORM_FILTERS.map((p) => /* @__PURE__ */ jsxs9(
17407
- Text,
17408
- {
17409
- color: platformFilter === p ? "#F5D76E" : "#6B4C9A",
17410
- bold: platformFilter === p,
17411
- backgroundColor: platformFilter === p ? "#6B4C9A" : void 0,
17412
- children: [
17413
- " ",
17414
- PLATFORM_FILTER_LABELS[p],
17415
- " "
17416
- ]
17417
- },
17418
- p
17419
- )) }),
17420
- /* @__PURE__ */ jsx11(Text, { children: " " }),
17421
- filteredConversations.length === 0 ? /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17422
- " ",
17423
- "No conversations",
17424
- connectionStatus === "connected" ? " \u2014 waiting for activity" : ""
17425
- ] }) : filteredConversations.slice(0, VISIBLE_CONVERSATIONS).map((conv, idx) => {
17426
- const isSelected = selectedSection === 1 && idx === selectedConvIdx;
17427
- return /* @__PURE__ */ jsxs9(Box_default, { paddingLeft: 4, backgroundColor: isSelected ? "#6B4C9A" : void 0, children: [
17428
- /* @__PURE__ */ jsxs9(Text, { color: platformColor(conv.platform), children: [
17429
- "\u25CF",
17430
- " "
17431
- ] }),
17432
- /* @__PURE__ */ jsx11(Text, { color: isSelected ? "#F5D76E" : "#C77DBA", children: padOrTruncate(conv.senderId, SENDER_MAX_LENGTH) }),
17433
- /* @__PURE__ */ jsxs9(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
17434
- " ",
17435
- truncateText(conv.lastMessage, MESSAGE_TEXT_MAX_LENGTH)
17436
- ] }),
17518
+ /* @__PURE__ */ jsxs9(Box_default, { testID: "gateway-chat-panel", flexDirection: "column", children: [
17519
+ /* @__PURE__ */ jsxs9(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
17520
+ marker(1),
17521
+ "Conversations",
17437
17522
  /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17438
- " ",
17439
- timeAgo(conv.lastMessageTime)
17440
- ] }),
17441
- conv.unread > 0 && /* @__PURE__ */ jsxs9(Text, { color: "#F5D76E", bold: true, children: [
17442
17523
  " (",
17443
- conv.unread,
17524
+ filteredConversations.length,
17444
17525
  ")"
17445
17526
  ] })
17446
- ] }, conv.id);
17447
- }),
17448
- filteredConversations.length > VISIBLE_CONVERSATIONS && /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17449
- " ",
17450
- "+",
17451
- filteredConversations.length - VISIBLE_CONVERSATIONS,
17452
- " more"
17527
+ ] }),
17528
+ /* @__PURE__ */ jsx11(Box_default, { paddingLeft: 4, gap: 1, children: PLATFORM_FILTERS.map((p) => /* @__PURE__ */ jsxs9(
17529
+ Text,
17530
+ {
17531
+ color: platformFilter === p ? "#F5D76E" : "#6B4C9A",
17532
+ bold: platformFilter === p,
17533
+ backgroundColor: platformFilter === p ? "#6B4C9A" : void 0,
17534
+ children: [
17535
+ " ",
17536
+ PLATFORM_FILTER_LABELS[p],
17537
+ " "
17538
+ ]
17539
+ },
17540
+ p
17541
+ )) }),
17542
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
17543
+ filteredConversations.length === 0 ? /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17544
+ " ",
17545
+ "No conversations",
17546
+ connectionStatus === "connected" ? " \u2014 waiting for activity" : ""
17547
+ ] }) : filteredConversations.slice(0, VISIBLE_CONVERSATIONS).map((conv, idx) => {
17548
+ const isSelected = selectedSection === 1 && idx === selectedConvIdx;
17549
+ return /* @__PURE__ */ jsxs9(Box_default, { paddingLeft: 4, backgroundColor: isSelected ? "#6B4C9A" : void 0, children: [
17550
+ /* @__PURE__ */ jsxs9(Text, { color: platformColor(conv.platform), children: [
17551
+ "\u25CF",
17552
+ " "
17553
+ ] }),
17554
+ /* @__PURE__ */ jsx11(Text, { color: isSelected ? "#F5D76E" : "#C77DBA", children: padOrTruncate(conv.senderId, SENDER_MAX_LENGTH) }),
17555
+ /* @__PURE__ */ jsxs9(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
17556
+ " ",
17557
+ truncateText(conv.lastMessage, MESSAGE_TEXT_MAX_LENGTH)
17558
+ ] }),
17559
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17560
+ " ",
17561
+ timeAgo(conv.lastMessageTime)
17562
+ ] }),
17563
+ conv.unread > 0 && /* @__PURE__ */ jsxs9(Text, { color: "#F5D76E", bold: true, children: [
17564
+ " (",
17565
+ conv.unread,
17566
+ ")"
17567
+ ] })
17568
+ ] }, conv.id);
17569
+ }),
17570
+ filteredConversations.length > VISIBLE_CONVERSATIONS && /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
17571
+ " ",
17572
+ "+",
17573
+ filteredConversations.length - VISIBLE_CONVERSATIONS,
17574
+ " more"
17575
+ ] })
17453
17576
  ] }),
17454
17577
  /* @__PURE__ */ jsx11(Text, { children: " " }),
17455
17578
  /* @__PURE__ */ jsxs9(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
@@ -17679,7 +17802,7 @@ function TeamView({ onBack, onViewSessions }) {
17679
17802
  const totalCount = members.length + externals.length;
17680
17803
  const selected = allItems[selectedIdx];
17681
17804
  if (showDetail && selected) {
17682
- return /* @__PURE__ */ jsxs10(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17805
+ return /* @__PURE__ */ jsxs10(Box_default, { testID: "team-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17683
17806
  /* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Employee Detail" }) }),
17684
17807
  /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: "Press Escape to go back" }),
17685
17808
  /* @__PURE__ */ jsx12(Text, { children: " " }),
@@ -17716,8 +17839,8 @@ function TeamView({ onBack, onViewSessions }) {
17716
17839
  ] })
17717
17840
  ] });
17718
17841
  }
17719
- return /* @__PURE__ */ jsxs10(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17720
- /* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
17842
+ return /* @__PURE__ */ jsxs10(Box_default, { testID: "team-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
17843
+ /* @__PURE__ */ jsx12(Box_default, { testID: "team-roster", borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
17721
17844
  /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
17722
17845
  totalCount,
17723
17846
  " agents | \u2191\u2193 navigate | Enter details | a add | s sessions"
@@ -18136,7 +18259,7 @@ function WikiView({ onBack }) {
18136
18259
  ] }) : null
18137
18260
  ] });
18138
18261
  }
18139
- return /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
18262
+ return /* @__PURE__ */ jsxs11(Box_default, { testID: "wiki-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
18140
18263
  /* @__PURE__ */ jsx13(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx13(Text, { bold: true, children: "Wiki" }) }),
18141
18264
  /* @__PURE__ */ jsxs11(Box_default, { gap: 2, children: [
18142
18265
  /* @__PURE__ */ jsxs11(Text, { color: "#6B4C9A", children: [
@@ -18413,7 +18536,7 @@ function SettingsView({ onBack }) {
18413
18536
  const sectionMarker = (idx) => selectedSection === idx ? "\u25B8 " : " ";
18414
18537
  const anyGateway = gateway.adapters.some((a) => a.configured);
18415
18538
  const formatLimit = (n) => n === -1 ? "unlimited" : n.toLocaleString();
18416
- return /* @__PURE__ */ jsxs12(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
18539
+ return /* @__PURE__ */ jsxs12(Box_default, { testID: "settings-view", flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
18417
18540
  /* @__PURE__ */ jsx14(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx14(Text, { bold: true, children: "Settings" }) }),
18418
18541
  /* @__PURE__ */ jsx14(Text, { color: DIM, children: "\u2191\u2193 navigate sections r refresh" }),
18419
18542
  /* @__PURE__ */ jsx14(Text, { children: " " }),
@@ -18421,129 +18544,131 @@ function SettingsView({ onBack }) {
18421
18544
  /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Loading settings..." }),
18422
18545
  /* @__PURE__ */ jsx14(Text, { children: " " })
18423
18546
  ] }),
18424
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
18425
- sectionMarker(0),
18426
- "Providers"
18427
- ] }),
18428
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18429
- providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
18430
- " ",
18431
- /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
18432
- " ",
18433
- p.name,
18434
- ": ",
18435
- /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
18436
- ] }, p.name)),
18437
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18438
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
18439
- sectionMarker(1),
18440
- "Cloud Sync"
18441
- ] }),
18442
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18443
- cloud.configured ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
18547
+ /* @__PURE__ */ jsxs12(Box_default, { testID: "settings-sections", flexDirection: "column", children: [
18548
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
18549
+ sectionMarker(0),
18550
+ "Providers"
18551
+ ] }),
18552
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18553
+ providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
18554
+ " ",
18555
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
18556
+ " ",
18557
+ p.name,
18558
+ ": ",
18559
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
18560
+ ] }, p.name)),
18561
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18562
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
18563
+ sectionMarker(1),
18564
+ "Cloud Sync"
18565
+ ] }),
18566
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18567
+ cloud.configured ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
18568
+ /* @__PURE__ */ jsxs12(Text, { children: [
18569
+ " ",
18570
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: statusDot(true) }),
18571
+ " Status: ",
18572
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: "connected" })
18573
+ ] }),
18574
+ /* @__PURE__ */ jsxs12(Text, { children: [
18575
+ " ",
18576
+ "Endpoint: ",
18577
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: cloud.endpoint })
18578
+ ] }),
18579
+ /* @__PURE__ */ jsxs12(Text, { children: [
18580
+ " ",
18581
+ "API key: ",
18582
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: cloud.apiKey })
18583
+ ] })
18584
+ ] }) : /* @__PURE__ */ jsxs12(Text, { children: [
18585
+ " ",
18586
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
18587
+ " ",
18588
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Not configured \u2014 run /exe-cloud" })
18589
+ ] }),
18590
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18591
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
18592
+ sectionMarker(2),
18593
+ "License"
18594
+ ] }),
18595
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18444
18596
  /* @__PURE__ */ jsxs12(Text, { children: [
18445
18597
  " ",
18446
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: statusDot(true) }),
18447
- " Status: ",
18448
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: "connected" })
18598
+ "Plan: ",
18599
+ /* @__PURE__ */ jsx14(Text, { color: license.plan === "FREE" ? YELLOW : GREEN, children: license.plan })
18600
+ ] }),
18601
+ license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
18602
+ " ",
18603
+ "Expires: ",
18604
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
18449
18605
  ] }),
18450
18606
  /* @__PURE__ */ jsxs12(Text, { children: [
18451
18607
  " ",
18452
- "Endpoint: ",
18453
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: cloud.endpoint })
18608
+ "Devices: ",
18609
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
18454
18610
  ] }),
18455
18611
  /* @__PURE__ */ jsxs12(Text, { children: [
18456
18612
  " ",
18457
- "API key: ",
18458
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: cloud.apiKey })
18613
+ "Employees: ",
18614
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
18615
+ ] }),
18616
+ /* @__PURE__ */ jsxs12(Text, { children: [
18617
+ " ",
18618
+ "Memory limit: ",
18619
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
18620
+ ] }),
18621
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18622
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
18623
+ sectionMarker(3),
18624
+ "System"
18625
+ ] }),
18626
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18627
+ /* @__PURE__ */ jsxs12(Text, { children: [
18628
+ " ",
18629
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: statusDot(system.daemon === "running") }),
18630
+ " Daemon: ",
18631
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: system.daemon })
18632
+ ] }),
18633
+ /* @__PURE__ */ jsxs12(Text, { children: [
18634
+ " ",
18635
+ "Version: ",
18636
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
18637
+ ] }),
18638
+ /* @__PURE__ */ jsxs12(Text, { children: [
18639
+ " ",
18640
+ "Encryption: ",
18641
+ /* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
18642
+ ] }),
18643
+ /* @__PURE__ */ jsxs12(Text, { children: [
18644
+ " ",
18645
+ "Embedding: ",
18646
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
18647
+ ] }),
18648
+ /* @__PURE__ */ jsxs12(Text, { children: [
18649
+ " ",
18650
+ "DB path: ",
18651
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: system.dbPath })
18652
+ ] }),
18653
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18654
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
18655
+ sectionMarker(4),
18656
+ "Gateway"
18657
+ ] }),
18658
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
18659
+ anyGateway ? gateway.adapters.map((a) => /* @__PURE__ */ jsxs12(Text, { children: [
18660
+ " ",
18661
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: statusDot(a.configured) }),
18662
+ " ",
18663
+ a.name,
18664
+ ": ",
18665
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: a.configured ? "configured" : "not configured" })
18666
+ ] }, a.name)) : /* @__PURE__ */ jsxs12(Text, { children: [
18667
+ " ",
18668
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
18669
+ " ",
18670
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "No gateway configured" })
18459
18671
  ] })
18460
- ] }) : /* @__PURE__ */ jsxs12(Text, { children: [
18461
- " ",
18462
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
18463
- " ",
18464
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Not configured \u2014 run /exe-cloud" })
18465
- ] }),
18466
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18467
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
18468
- sectionMarker(2),
18469
- "License"
18470
- ] }),
18471
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18472
- /* @__PURE__ */ jsxs12(Text, { children: [
18473
- " ",
18474
- "Plan: ",
18475
- /* @__PURE__ */ jsx14(Text, { color: license.plan === "FREE" ? YELLOW : GREEN, children: license.plan })
18476
- ] }),
18477
- license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
18478
- " ",
18479
- "Expires: ",
18480
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
18481
- ] }),
18482
- /* @__PURE__ */ jsxs12(Text, { children: [
18483
- " ",
18484
- "Devices: ",
18485
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
18486
- ] }),
18487
- /* @__PURE__ */ jsxs12(Text, { children: [
18488
- " ",
18489
- "Employees: ",
18490
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
18491
- ] }),
18492
- /* @__PURE__ */ jsxs12(Text, { children: [
18493
- " ",
18494
- "Memory limit: ",
18495
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
18496
- ] }),
18497
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18498
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
18499
- sectionMarker(3),
18500
- "System"
18501
- ] }),
18502
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18503
- /* @__PURE__ */ jsxs12(Text, { children: [
18504
- " ",
18505
- /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: statusDot(system.daemon === "running") }),
18506
- " Daemon: ",
18507
- /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: system.daemon })
18508
- ] }),
18509
- /* @__PURE__ */ jsxs12(Text, { children: [
18510
- " ",
18511
- "Version: ",
18512
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
18513
- ] }),
18514
- /* @__PURE__ */ jsxs12(Text, { children: [
18515
- " ",
18516
- "Encryption: ",
18517
- /* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
18518
- ] }),
18519
- /* @__PURE__ */ jsxs12(Text, { children: [
18520
- " ",
18521
- "Embedding: ",
18522
- /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
18523
- ] }),
18524
- /* @__PURE__ */ jsxs12(Text, { children: [
18525
- " ",
18526
- "DB path: ",
18527
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: system.dbPath })
18528
- ] }),
18529
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18530
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
18531
- sectionMarker(4),
18532
- "Gateway"
18533
- ] }),
18534
- /* @__PURE__ */ jsx14(Text, { children: " " }),
18535
- anyGateway ? gateway.adapters.map((a) => /* @__PURE__ */ jsxs12(Text, { children: [
18536
- " ",
18537
- /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: statusDot(a.configured) }),
18538
- " ",
18539
- a.name,
18540
- ": ",
18541
- /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: a.configured ? "configured" : "not configured" })
18542
- ] }, a.name)) : /* @__PURE__ */ jsxs12(Text, { children: [
18543
- " ",
18544
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
18545
- " ",
18546
- /* @__PURE__ */ jsx14(Text, { color: DIM, children: "No gateway configured" })
18547
18672
  ] })
18548
18673
  ] });
18549
18674
  }
@@ -18596,7 +18721,7 @@ function DebugPanel() {
18596
18721
  orgBus.offAny(handler);
18597
18722
  };
18598
18723
  }, []);
18599
- return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", borderStyle: "single", borderColor: "#6B4C9A", flexGrow: 1, paddingX: 1, children: [
18724
+ return /* @__PURE__ */ jsxs13(Box_default, { testID: "debug-panel", flexDirection: "column", borderStyle: "single", borderColor: "#6B4C9A", flexGrow: 1, paddingX: 1, children: [
18600
18725
  /* @__PURE__ */ jsx15(Text, { bold: true, color: "#F5D76E", children: "Debug \u2014 Live Events" }),
18601
18726
  /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "\u2500".repeat(40) }),
18602
18727
  events.length === 0 ? /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "Waiting for events..." }) : events.slice(-DISPLAY_EVENTS).map(({ event, id }) => {
@@ -18622,6 +18747,7 @@ function HelpOverlay({ tabName, shortcuts }) {
18622
18747
  return /* @__PURE__ */ jsxs14(
18623
18748
  Box_default,
18624
18749
  {
18750
+ testID: "help-overlay",
18625
18751
  flexDirection: "column",
18626
18752
  borderStyle: "double",
18627
18753
  borderColor: "#6B4C9A",
@@ -18848,8 +18974,8 @@ function App2() {
18848
18974
  wiki: /* @__PURE__ */ jsx17(WikiView, { onBack: () => setFocus("sidebar") }),
18849
18975
  settings: /* @__PURE__ */ jsx17(SettingsView, { onBack: () => setFocus("sidebar") })
18850
18976
  };
18851
- return /* @__PURE__ */ jsx17(ErrorBoundary2, { children: /* @__PURE__ */ jsx17(AlternateScreen, { children: /* @__PURE__ */ jsx17(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs15(Box_default, { flexDirection: "column", flexGrow: 1, children: [
18852
- /* @__PURE__ */ jsxs15(Box_default, { flexGrow: 1, children: [
18977
+ return /* @__PURE__ */ jsx17(ErrorBoundary2, { children: /* @__PURE__ */ jsx17(AlternateScreen, { children: /* @__PURE__ */ jsx17(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs15(Box_default, { testID: "app-root", flexDirection: "column", flexGrow: 1, children: [
18978
+ /* @__PURE__ */ jsxs15(Box_default, { testID: "app-content", flexGrow: 1, children: [
18853
18979
  /* @__PURE__ */ jsx17(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
18854
18980
  showHelp ? /* @__PURE__ */ jsx17(
18855
18981
  HelpOverlay,