@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/bin/cli.js CHANGED
@@ -1311,6 +1311,13 @@ async function ensureSchema() {
1311
1311
  });
1312
1312
  } catch {
1313
1313
  }
1314
+ try {
1315
+ await client.execute({
1316
+ sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
1317
+ args: []
1318
+ });
1319
+ } catch {
1320
+ }
1314
1321
  try {
1315
1322
  await client.execute({
1316
1323
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -1757,6 +1764,18 @@ async function ensureSchema() {
1757
1764
  CREATE INDEX IF NOT EXISTS idx_session_kills_agent
1758
1765
  ON session_kills(agent_id);
1759
1766
  `);
1767
+ await client.execute(`
1768
+ CREATE TABLE IF NOT EXISTS global_procedures (
1769
+ id TEXT PRIMARY KEY,
1770
+ title TEXT NOT NULL,
1771
+ content TEXT NOT NULL,
1772
+ priority TEXT NOT NULL DEFAULT 'p0',
1773
+ domain TEXT,
1774
+ active INTEGER NOT NULL DEFAULT 1,
1775
+ created_at TEXT NOT NULL,
1776
+ updated_at TEXT NOT NULL
1777
+ )
1778
+ `);
1760
1779
  await client.executeMultiple(`
1761
1780
  CREATE TABLE IF NOT EXISTS conversations (
1762
1781
  id TEXT PRIMARY KEY,
@@ -2061,6 +2080,61 @@ var init_keychain = __esm({
2061
2080
  }
2062
2081
  });
2063
2082
 
2083
+ // src/lib/state-bus.ts
2084
+ var StateBus, orgBus;
2085
+ var init_state_bus = __esm({
2086
+ "src/lib/state-bus.ts"() {
2087
+ "use strict";
2088
+ StateBus = class {
2089
+ handlers = /* @__PURE__ */ new Map();
2090
+ globalHandlers = /* @__PURE__ */ new Set();
2091
+ /** Emit an event to all subscribers */
2092
+ emit(event) {
2093
+ const typeHandlers = this.handlers.get(event.type);
2094
+ if (typeHandlers) {
2095
+ for (const handler of typeHandlers) {
2096
+ try {
2097
+ handler(event);
2098
+ } catch {
2099
+ }
2100
+ }
2101
+ }
2102
+ for (const handler of this.globalHandlers) {
2103
+ try {
2104
+ handler(event);
2105
+ } catch {
2106
+ }
2107
+ }
2108
+ }
2109
+ /** Subscribe to a specific event type */
2110
+ on(type, handler) {
2111
+ if (!this.handlers.has(type)) {
2112
+ this.handlers.set(type, /* @__PURE__ */ new Set());
2113
+ }
2114
+ this.handlers.get(type).add(handler);
2115
+ }
2116
+ /** Subscribe to ALL events */
2117
+ onAny(handler) {
2118
+ this.globalHandlers.add(handler);
2119
+ }
2120
+ /** Unsubscribe from a specific event type */
2121
+ off(type, handler) {
2122
+ this.handlers.get(type)?.delete(handler);
2123
+ }
2124
+ /** Unsubscribe from ALL events */
2125
+ offAny(handler) {
2126
+ this.globalHandlers.delete(handler);
2127
+ }
2128
+ /** Remove all listeners */
2129
+ clear() {
2130
+ this.handlers.clear();
2131
+ this.globalHandlers.clear();
2132
+ }
2133
+ };
2134
+ orgBus = new StateBus();
2135
+ }
2136
+ });
2137
+
2064
2138
  // src/lib/shard-manager.ts
2065
2139
  var shard_manager_exports = {};
2066
2140
  __export(shard_manager_exports, {
@@ -2302,6 +2376,71 @@ var init_shard_manager = __esm({
2302
2376
  }
2303
2377
  });
2304
2378
 
2379
+ // src/lib/global-procedures.ts
2380
+ var global_procedures_exports = {};
2381
+ __export(global_procedures_exports, {
2382
+ deactivateGlobalProcedure: () => deactivateGlobalProcedure,
2383
+ getGlobalProceduresBlock: () => getGlobalProceduresBlock,
2384
+ loadGlobalProcedures: () => loadGlobalProcedures,
2385
+ storeGlobalProcedure: () => storeGlobalProcedure
2386
+ });
2387
+ import { randomUUID } from "crypto";
2388
+ async function loadGlobalProcedures() {
2389
+ const client = getClient();
2390
+ const result = await client.execute({
2391
+ sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
2392
+ args: []
2393
+ });
2394
+ const procedures = result.rows;
2395
+ if (procedures.length > 0) {
2396
+ _cache = procedures.map((p) => `### ${p.title}
2397
+ ${p.content}`).join("\n\n");
2398
+ } else {
2399
+ _cache = "";
2400
+ }
2401
+ _cacheLoaded = true;
2402
+ return procedures;
2403
+ }
2404
+ function getGlobalProceduresBlock() {
2405
+ if (!_cacheLoaded) return "";
2406
+ if (!_cache) return "";
2407
+ return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
2408
+
2409
+ ${_cache}
2410
+ `;
2411
+ }
2412
+ async function storeGlobalProcedure(input) {
2413
+ const id = randomUUID();
2414
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2415
+ const client = getClient();
2416
+ await client.execute({
2417
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
2418
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
2419
+ args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
2420
+ });
2421
+ await loadGlobalProcedures();
2422
+ return id;
2423
+ }
2424
+ async function deactivateGlobalProcedure(id) {
2425
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2426
+ const client = getClient();
2427
+ const result = await client.execute({
2428
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
2429
+ args: [now, id]
2430
+ });
2431
+ await loadGlobalProcedures();
2432
+ return result.rowsAffected > 0;
2433
+ }
2434
+ var _cache, _cacheLoaded;
2435
+ var init_global_procedures = __esm({
2436
+ "src/lib/global-procedures.ts"() {
2437
+ "use strict";
2438
+ init_database();
2439
+ _cache = "";
2440
+ _cacheLoaded = false;
2441
+ }
2442
+ });
2443
+
2305
2444
  // src/lib/store.ts
2306
2445
  var store_exports = {};
2307
2446
  __export(store_exports, {
@@ -2381,6 +2520,11 @@ async function initStore(options) {
2381
2520
  "version-query"
2382
2521
  );
2383
2522
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2523
+ try {
2524
+ const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
2525
+ await loadGlobalProcedures2();
2526
+ } catch {
2527
+ }
2384
2528
  }
2385
2529
  function classifyTier(record) {
2386
2530
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -2422,6 +2566,12 @@ async function writeMemory(record) {
2422
2566
  supersedes_id: record.supersedes_id ?? null
2423
2567
  };
2424
2568
  _pendingRecords.push(dbRow);
2569
+ orgBus.emit({
2570
+ type: "memory_stored",
2571
+ agentId: record.agent_id,
2572
+ project: record.project_name,
2573
+ timestamp: record.timestamp
2574
+ });
2425
2575
  const MAX_PENDING = 1e3;
2426
2576
  if (_pendingRecords.length > MAX_PENDING) {
2427
2577
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -2767,6 +2917,7 @@ var init_store = __esm({
2767
2917
  init_database();
2768
2918
  init_keychain();
2769
2919
  init_config();
2920
+ init_state_bus();
2770
2921
  INIT_MAX_RETRIES = 3;
2771
2922
  INIT_RETRY_DELAY_MS = 1e3;
2772
2923
  _pendingRecords = [];
@@ -2781,7 +2932,7 @@ var init_store = __esm({
2781
2932
  // src/lib/exe-daemon-client.ts
2782
2933
  import net from "net";
2783
2934
  import { spawn } from "child_process";
2784
- import { randomUUID } from "crypto";
2935
+ import { randomUUID as randomUUID2 } from "crypto";
2785
2936
  import { existsSync as existsSync7, unlinkSync, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2786
2937
  import path7 from "path";
2787
2938
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2973,7 +3124,7 @@ function sendRequest(texts, priority) {
2973
3124
  resolve({ error: "Not connected" });
2974
3125
  return;
2975
3126
  }
2976
- const id = randomUUID();
3127
+ const id = randomUUID2();
2977
3128
  const timer = setTimeout(() => {
2978
3129
  _pending.delete(id);
2979
3130
  resolve({ error: "Request timeout" });
@@ -2991,7 +3142,7 @@ function sendRequest(texts, priority) {
2991
3142
  async function pingDaemon() {
2992
3143
  if (!_socket || !_connected) return null;
2993
3144
  return new Promise((resolve) => {
2994
- const id = randomUUID();
3145
+ const id = randomUUID2();
2995
3146
  const timer = setTimeout(() => {
2996
3147
  _pending.delete(id);
2997
3148
  resolve(null);
@@ -3537,7 +3688,8 @@ __export(employee_templates_exports, {
3537
3688
  function getSessionPrompt(storedPrompt) {
3538
3689
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
3539
3690
  const rolePrompt = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
3540
- return `${rolePrompt}
3691
+ const globalBlock = getGlobalProceduresBlock();
3692
+ return `${globalBlock}${rolePrompt}
3541
3693
  ${BASE_OPERATING_PROCEDURES}`;
3542
3694
  }
3543
3695
  function buildCustomEmployeePrompt(name, role) {
@@ -3577,6 +3729,7 @@ var BASE_OPERATING_PROCEDURES, DEFAULT_EXE, TEMPLATE_VERSION, PROCEDURES_MARKER,
3577
3729
  var init_employee_templates = __esm({
3578
3730
  "src/lib/employee-templates.ts"() {
3579
3731
  "use strict";
3732
+ init_global_procedures();
3580
3733
  BASE_OPERATING_PROCEDURES = `
3581
3734
  EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES (above all work):
3582
3735
 
@@ -4510,7 +4663,7 @@ __export(license_exports, {
4510
4663
  validateLicense: () => validateLicense
4511
4664
  });
4512
4665
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
4513
- import { randomUUID as randomUUID2 } from "crypto";
4666
+ import { randomUUID as randomUUID3 } from "crypto";
4514
4667
  import path11 from "path";
4515
4668
  import { jwtVerify, importSPKI } from "jose";
4516
4669
  async function fetchRetry(url, init) {
@@ -4537,7 +4690,7 @@ function loadDeviceId() {
4537
4690
  }
4538
4691
  } catch {
4539
4692
  }
4540
- const id = randomUUID2();
4693
+ const id = randomUUID3();
4541
4694
  mkdirSync3(EXE_AI_DIR, { recursive: true });
4542
4695
  writeFileSync2(DEVICE_ID_PATH, id, "utf8");
4543
4696
  return id;
@@ -4652,7 +4805,21 @@ function getCacheAgeMs() {
4652
4805
  }
4653
4806
  }
4654
4807
  async function checkLicense() {
4655
- const key = loadLicense();
4808
+ let key = loadLicense();
4809
+ if (!key) {
4810
+ try {
4811
+ const configPath = path11.join(EXE_AI_DIR, "config.json");
4812
+ if (existsSync10(configPath)) {
4813
+ const raw = JSON.parse(readFileSync6(configPath, "utf8"));
4814
+ const cloud = raw.cloud;
4815
+ if (cloud?.apiKey) {
4816
+ key = cloud.apiKey;
4817
+ saveLicense(key);
4818
+ }
4819
+ }
4820
+ } catch {
4821
+ }
4822
+ }
4656
4823
  if (!key) return FREE_LICENSE;
4657
4824
  const cached = await getCachedLicense();
4658
4825
  if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
@@ -12309,6 +12476,73 @@ var init_Sidebar = __esm({
12309
12476
  }
12310
12477
  });
12311
12478
 
12479
+ // src/lib/telemetry.ts
12480
+ var telemetry_exports = {};
12481
+ __export(telemetry_exports, {
12482
+ instrumentServer: () => instrumentServer,
12483
+ withTrace: () => withTrace
12484
+ });
12485
+ async function ensureInit() {
12486
+ if (initialized || !ENABLED) return;
12487
+ initialized = true;
12488
+ try {
12489
+ const { NodeSDK } = await import("@opentelemetry/sdk-node");
12490
+ const { ConsoleSpanExporter } = await import("@opentelemetry/sdk-trace-base");
12491
+ const sdk = new NodeSDK({
12492
+ serviceName: "exe-os",
12493
+ traceExporter: new ConsoleSpanExporter()
12494
+ });
12495
+ sdk.start();
12496
+ process.stderr.write("[exe-os] OpenTelemetry tracing enabled\n");
12497
+ } catch (err) {
12498
+ process.stderr.write(
12499
+ `[exe-os] OpenTelemetry init failed: ${err instanceof Error ? err.message : String(err)}
12500
+ `
12501
+ );
12502
+ }
12503
+ }
12504
+ async function withTrace(toolName, fn) {
12505
+ if (!ENABLED) return fn();
12506
+ await ensureInit();
12507
+ const { trace, SpanStatusCode } = await import("@opentelemetry/api");
12508
+ const tracer = trace.getTracer("exe-os", "1.0.0");
12509
+ return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
12510
+ span.setAttribute("mcp.tool.name", toolName);
12511
+ try {
12512
+ const result = await fn();
12513
+ span.setStatus({ code: SpanStatusCode.OK });
12514
+ return result;
12515
+ } catch (err) {
12516
+ span.setStatus({
12517
+ code: SpanStatusCode.ERROR,
12518
+ message: err instanceof Error ? err.message : String(err)
12519
+ });
12520
+ span.recordException(
12521
+ err instanceof Error ? err : new Error(String(err))
12522
+ );
12523
+ throw err;
12524
+ } finally {
12525
+ span.end();
12526
+ }
12527
+ });
12528
+ }
12529
+ function instrumentServer(server) {
12530
+ if (!ENABLED) return;
12531
+ const original = server.registerTool.bind(server);
12532
+ server.registerTool = (name, config, handler) => {
12533
+ const traced = async (...args2) => withTrace(name, () => handler(...args2));
12534
+ return original(name, config, traced);
12535
+ };
12536
+ }
12537
+ var ENABLED, initialized;
12538
+ var init_telemetry = __esm({
12539
+ "src/lib/telemetry.ts"() {
12540
+ "use strict";
12541
+ ENABLED = process.env.EXE_TELEMETRY === "1";
12542
+ initialized = false;
12543
+ }
12544
+ });
12545
+
12312
12546
  // src/lib/tmux-status.ts
12313
12547
  var tmux_status_exports = {};
12314
12548
  __export(tmux_status_exports, {
@@ -12603,61 +12837,64 @@ function Footer() {
12603
12837
  return () => clearInterval(timer);
12604
12838
  }, []);
12605
12839
  async function loadFooterData() {
12606
- try {
12607
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12608
- const client = getClient2();
12609
- if (client) {
12610
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
12611
- setMemoryCount(Number(result.rows[0]?.cnt ?? 0));
12840
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
12841
+ return withTrace2("tui.footer.loadData", async () => {
12842
+ try {
12843
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12844
+ const client = getClient2();
12845
+ if (client) {
12846
+ const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
12847
+ setMemoryCount(Number(result.rows[0]?.cnt ?? 0));
12848
+ }
12849
+ } catch {
12612
12850
  }
12613
- } catch {
12614
- }
12615
- try {
12616
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12617
- const client = getClient2();
12618
- if (client) {
12619
- const result = await client.execute(
12620
- "SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
12621
- );
12622
- setTaskCount(Number(result.rows[0]?.cnt ?? 0));
12851
+ try {
12852
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12853
+ const client = getClient2();
12854
+ if (client) {
12855
+ const result = await client.execute(
12856
+ "SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
12857
+ );
12858
+ setTaskCount(Number(result.rows[0]?.cnt ?? 0));
12859
+ }
12860
+ } catch {
12623
12861
  }
12624
- } catch {
12625
- }
12626
- try {
12627
- const { existsSync: existsSync22 } = await import("fs");
12628
- const { join } = await import("path");
12629
- const home = process.env.HOME ?? "";
12630
- const pidPath = join(home, ".exe-os", "exed.pid");
12631
- setDaemon(existsSync22(pidPath) ? "running" : "stopped");
12632
- } catch {
12633
- setDaemon("unknown");
12634
- }
12635
- try {
12636
- const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
12637
- const license = await checkLicense2();
12638
- setPlan(license.plan);
12639
- } catch {
12640
- setPlan("free");
12641
- }
12642
- try {
12643
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
12644
- if (inTmux2()) {
12645
- const allSessions = listTmuxSessions2();
12646
- setSessions(allSessions.length);
12647
- if (!currentSession) {
12648
- try {
12649
- const { execSync: execSync13 } = await import("child_process");
12650
- const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
12651
- encoding: "utf8",
12652
- timeout: 2e3
12653
- }).trim();
12654
- if (name) setCurrentSession(name);
12655
- } catch {
12862
+ try {
12863
+ const { existsSync: existsSync22 } = await import("fs");
12864
+ const { join } = await import("path");
12865
+ const home = process.env.HOME ?? "";
12866
+ const pidPath = join(home, ".exe-os", "exed.pid");
12867
+ setDaemon(existsSync22(pidPath) ? "running" : "stopped");
12868
+ } catch {
12869
+ setDaemon("unknown");
12870
+ }
12871
+ try {
12872
+ const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
12873
+ const license = await checkLicense2();
12874
+ setPlan(license.plan);
12875
+ } catch {
12876
+ setPlan("free");
12877
+ }
12878
+ try {
12879
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
12880
+ if (inTmux2()) {
12881
+ const allSessions = listTmuxSessions2();
12882
+ setSessions(allSessions.length);
12883
+ if (!currentSession) {
12884
+ try {
12885
+ const { execSync: execSync13 } = await import("child_process");
12886
+ const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
12887
+ encoding: "utf8",
12888
+ timeout: 2e3
12889
+ }).trim();
12890
+ if (name) setCurrentSession(name);
12891
+ } catch {
12892
+ }
12656
12893
  }
12657
12894
  }
12895
+ } catch {
12658
12896
  }
12659
- } catch {
12660
- }
12897
+ });
12661
12898
  }
12662
12899
  return /* @__PURE__ */ jsxs3(Box_default, { flexDirection: "column", children: [
12663
12900
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns || 120) }),
@@ -12698,6 +12935,8 @@ function Footer() {
12698
12935
  ] }),
12699
12936
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "1-7 navigate" }),
12700
12937
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
12938
+ /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "d debug" }),
12939
+ /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
12701
12940
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "q quit" })
12702
12941
  ] })
12703
12942
  ] })
@@ -14346,7 +14585,7 @@ var init_anthropic = __esm({
14346
14585
 
14347
14586
  // src/gateway/providers/openai-compat.ts
14348
14587
  import OpenAI from "openai";
14349
- import { randomUUID as randomUUID3 } from "crypto";
14588
+ import { randomUUID as randomUUID4 } from "crypto";
14350
14589
  var OpenAICompatProvider;
14351
14590
  var init_openai_compat = __esm({
14352
14591
  "src/gateway/providers/openai-compat.ts"() {
@@ -14463,7 +14702,7 @@ var init_openai_compat = __esm({
14463
14702
  }
14464
14703
  content.push({
14465
14704
  type: "tool_use",
14466
- id: call.id ?? randomUUID3(),
14705
+ id: call.id ?? randomUUID4(),
14467
14706
  name: fn.name,
14468
14707
  input
14469
14708
  });
@@ -15537,7 +15776,7 @@ function CommandCenterView({
15537
15776
  setAgentConfig({
15538
15777
  provider,
15539
15778
  model,
15540
- systemPrompt: "You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise.",
15779
+ systemPrompt: getSessionPrompt("You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise."),
15541
15780
  tools: registry,
15542
15781
  hooks: createDefaultHooks2(),
15543
15782
  permissions,
@@ -15658,6 +15897,7 @@ function CommandCenterView({
15658
15897
  employeeCount: p.employees.length,
15659
15898
  activeCount: p.employees.filter((e) => e.status === "active").length,
15660
15899
  memoryCount: p.employees.length * 4e3,
15900
+ openTaskCount: Math.floor(p.employees.length * 1.5),
15661
15901
  status: p.employees.some((e) => e.status === "active") ? "active" : "idle",
15662
15902
  type: p.projectName.startsWith("exe-") ? "code" : "automation",
15663
15903
  recentTasks: DEMO_RECENT_TASKS[p.projectName] ?? []
@@ -15669,6 +15909,7 @@ function CommandCenterView({
15669
15909
  employeeCount: 0,
15670
15910
  activeCount: 0,
15671
15911
  memoryCount: 0,
15912
+ openTaskCount: 0,
15672
15913
  status: "offline",
15673
15914
  type: "reference",
15674
15915
  recentTasks: []
@@ -15683,182 +15924,122 @@ function CommandCenterView({
15683
15924
  return () => clearInterval(timer);
15684
15925
  }, []);
15685
15926
  async function loadData() {
15686
- try {
15687
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
15688
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
15689
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15690
- const { existsSync: existsSync22 } = await import("fs");
15691
- const { join } = await import("path");
15692
- const registry = listSessions2();
15693
- const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
15694
- const roster = await loadEmployees2();
15695
- const exeSessions = /* @__PURE__ */ new Map();
15696
- for (const entry of registry) {
15697
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
15698
- exeSessions.set(entry.windowName, entry.projectDir);
15699
- }
15700
- }
15701
- for (const s of tmuxSessions) {
15702
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) exeSessions.set(s, "");
15703
- }
15704
- let projectMemoryCounts = /* @__PURE__ */ new Map();
15705
- let projectRecentTasks = /* @__PURE__ */ new Map();
15927
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
15928
+ return withTrace2("tui.commandCenter.loadData", async () => {
15706
15929
  try {
15707
15930
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15931
+ const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
15932
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
15933
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15934
+ const { existsSync: existsSync22 } = await import("fs");
15935
+ const { join } = await import("path");
15708
15936
  const client = getClient2();
15709
- if (client) {
15710
- const memResult = await client.execute(
15711
- "SELECT project_name, COUNT(*) as cnt FROM memories GROUP BY project_name"
15712
- );
15713
- for (const row of memResult.rows) {
15714
- projectMemoryCounts.set(String(row.project_name), Number(row.cnt));
15715
- }
15716
- for (const [, projectDir] of exeSessions) {
15717
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? "";
15718
- if (!projectName) continue;
15719
- const taskResult = await client.execute({
15720
- sql: "SELECT title FROM tasks WHERE project_name = ? AND status = 'done' ORDER BY updated_at DESC LIMIT 3",
15721
- args: [projectName]
15722
- });
15723
- const tasks = taskResult.rows.map((r) => String(r.title));
15724
- if (tasks.length > 0) projectRecentTasks.set(projectName, tasks);
15725
- }
15937
+ if (!client) {
15938
+ setDbError(true);
15939
+ return;
15726
15940
  }
15727
- } catch {
15728
- }
15729
- const projectList = [];
15730
- for (const [exeSession, projectDir] of exeSessions) {
15731
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
15941
+ const dbProjects = await client.execute(
15942
+ `SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
15943
+ UNION
15944
+ SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
15945
+ ORDER BY project_name`
15946
+ );
15947
+ const projectNames = dbProjects.rows.map((r) => String(r.project_name));
15948
+ const memResult = await client.execute(
15949
+ "SELECT project_name, COUNT(*) as cnt FROM memories WHERE project_name IS NOT NULL GROUP BY project_name"
15950
+ );
15951
+ const memoryCounts = /* @__PURE__ */ new Map();
15952
+ for (const row of memResult.rows) {
15953
+ memoryCounts.set(String(row.project_name), Number(row.cnt));
15954
+ }
15955
+ const taskCountResult = await client.execute(
15956
+ "SELECT project_name, COUNT(*) as cnt FROM tasks WHERE status IN ('open', 'in_progress') AND project_name IS NOT NULL GROUP BY project_name"
15957
+ );
15958
+ const openTaskCounts = /* @__PURE__ */ new Map();
15959
+ for (const row of taskCountResult.rows) {
15960
+ openTaskCounts.set(String(row.project_name), Number(row.cnt));
15961
+ }
15962
+ const recentResult = await client.execute(
15963
+ "SELECT project_name, title FROM tasks WHERE status = 'done' AND project_name IS NOT NULL ORDER BY updated_at DESC LIMIT 30"
15964
+ );
15965
+ const recentTasksByProject = /* @__PURE__ */ new Map();
15966
+ for (const row of recentResult.rows) {
15967
+ const name = String(row.project_name);
15968
+ const tasks = recentTasksByProject.get(name) ?? [];
15969
+ if (tasks.length < 3) tasks.push(String(row.title));
15970
+ recentTasksByProject.set(name, tasks);
15971
+ }
15972
+ const registry = listSessions2();
15973
+ const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
15974
+ const roster = await loadEmployees2();
15732
15975
  const employeeNames = roster.map((e) => e.name).filter((n) => n !== "exe");
15733
- let activeCount = tmuxSessions.has(exeSession) ? 1 : 0;
15734
- for (const empName of employeeNames) {
15735
- if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
15736
- }
15737
- const totalCount = 1 + employeeNames.length;
15738
- const memoryCount = projectMemoryCounts.get(projectName) ?? 0;
15739
- const hasGit = projectDir ? existsSync22(join(projectDir, ".git")) : false;
15740
- const hasActivity = memoryCount > 0;
15741
- let type = "automation";
15742
- if (hasGit && hasActivity) type = "code";
15743
- else if (hasGit && !hasActivity) type = "reference";
15744
- projectList.push({
15745
- projectName,
15746
- exeSession,
15747
- projectDir,
15748
- employeeCount: totalCount,
15749
- activeCount,
15750
- memoryCount,
15751
- status: activeCount > 0 ? "active" : "idle",
15752
- type,
15753
- recentTasks: projectRecentTasks.get(projectName) ?? []
15754
- });
15755
- }
15756
- const knownDirs = [
15757
- process.env.HOME ? join(process.env.HOME, "openclaw") : null,
15758
- process.env.HOME ? join(process.env.HOME, "agno") : null
15759
- ].filter(Boolean);
15760
- const activeProjectNames = new Set(projectList.map((p) => p.projectName));
15761
- for (const dir of knownDirs) {
15762
- const name = dir.split("/").filter(Boolean).pop() ?? "";
15763
- if (activeProjectNames.has(name) || !existsSync22(dir) || !existsSync22(join(dir, ".git"))) continue;
15764
- if ((projectMemoryCounts.get(name) ?? 0) > 0) continue;
15765
- projectList.push({
15766
- projectName: name,
15767
- exeSession: "",
15768
- projectDir: dir,
15769
- employeeCount: 0,
15770
- activeCount: 0,
15771
- memoryCount: 0,
15772
- status: "offline",
15773
- type: "reference",
15774
- recentTasks: []
15775
- });
15776
- }
15777
- const dbActiveProjectNames = new Set(projectList.map((p) => p.projectName));
15778
- try {
15779
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15780
- const client = getClient2();
15781
- if (client) {
15782
- const dbProjects = await client.execute(
15783
- `SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
15784
- UNION
15785
- SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
15786
- ORDER BY project_name`
15787
- );
15788
- for (const row of dbProjects.rows) {
15789
- const name = String(row.project_name);
15790
- if (dbActiveProjectNames.has(name)) continue;
15791
- const memCount = projectMemoryCounts.get(name) ?? 0;
15792
- const agentResult = await client.execute({
15793
- sql: "SELECT COUNT(DISTINCT agent_id) as cnt FROM memories WHERE project_name = ?",
15794
- args: [name]
15795
- });
15796
- const agentCount = Number(agentResult.rows[0]?.cnt ?? 0);
15797
- projectList.push({
15798
- projectName: name,
15799
- exeSession: "",
15800
- projectDir: "",
15801
- employeeCount: agentCount,
15802
- activeCount: 0,
15803
- memoryCount: memCount,
15804
- status: "offline",
15805
- type: "code",
15806
- recentTasks: projectRecentTasks.get(name) ?? []
15807
- });
15976
+ const projectSessions = /* @__PURE__ */ new Map();
15977
+ for (const entry of registry) {
15978
+ if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
15979
+ const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
15980
+ if (projName) {
15981
+ projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
15982
+ }
15808
15983
  }
15809
15984
  }
15810
- } catch {
15811
- }
15812
- if (projectList.filter((p) => p.type !== "reference").length === 0) {
15813
- projectList.unshift({
15814
- projectName: "(no active sessions)",
15815
- exeSession: "",
15816
- projectDir: "",
15817
- employeeCount: 0,
15818
- activeCount: 0,
15819
- memoryCount: 0,
15820
- status: "offline",
15821
- type: "code",
15822
- recentTasks: []
15985
+ const projectList = [];
15986
+ for (const name of projectNames) {
15987
+ const session = projectSessions.get(name);
15988
+ const exeSession = session?.exeSession ?? "";
15989
+ const projectDir = session?.projectDir ?? "";
15990
+ let activeCount = 0;
15991
+ if (exeSession && tmuxSessions.has(exeSession)) {
15992
+ activeCount = 1;
15993
+ for (const empName of employeeNames) {
15994
+ if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
15995
+ }
15996
+ }
15997
+ const memoryCount = memoryCounts.get(name) ?? 0;
15998
+ const openTaskCount = openTaskCounts.get(name) ?? 0;
15999
+ const hasGit = projectDir ? existsSync22(join(projectDir, ".git")) : false;
16000
+ const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
16001
+ projectList.push({
16002
+ projectName: name,
16003
+ exeSession,
16004
+ projectDir,
16005
+ employeeCount: activeCount,
16006
+ activeCount,
16007
+ memoryCount,
16008
+ openTaskCount,
16009
+ status: activeCount > 0 ? "active" : "idle",
16010
+ type,
16011
+ recentTasks: recentTasksByProject.get(name) ?? []
16012
+ });
16013
+ }
16014
+ projectList.sort((a, b) => {
16015
+ if (a.activeCount > 0 && b.activeCount === 0) return -1;
16016
+ if (a.activeCount === 0 && b.activeCount > 0) return 1;
16017
+ return b.memoryCount - a.memoryCount;
15823
16018
  });
15824
- }
15825
- setProjects(projectList);
15826
- try {
15827
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15828
- const client = getClient2();
15829
- if (client) {
15830
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
15831
- setHealth((h) => ({ ...h, memories: Number(result.rows[0]?.cnt ?? 0) }));
16019
+ setProjects(projectList);
16020
+ const totalResult = await client.execute("SELECT COUNT(*) as cnt FROM memories");
16021
+ setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
16022
+ try {
16023
+ const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
16024
+ setHealth((h) => ({ ...h, daemon: existsSync22(pidPath) ? "running" : "stopped" }));
16025
+ } catch {
15832
16026
  }
15833
- } catch {
15834
- }
15835
- try {
15836
- const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
15837
- setHealth((h) => ({ ...h, daemon: existsSync22(pidPath) ? "running" : "stopped" }));
15838
- } catch {
15839
- }
15840
- try {
15841
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15842
- const client = getClient2();
15843
- if (client) {
15844
- const activityResult = await client.execute(
15845
- "SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
15846
- );
15847
- if (activityResult.rows.length > 0) {
15848
- setActivity(activityResult.rows.slice(0, 8).map((r) => ({
15849
- time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
15850
- agent: String(r.agent_id ?? "system"),
15851
- action: String(r.text ?? "").slice(0, 60)
15852
- })));
15853
- }
16027
+ const activityResult = await client.execute(
16028
+ "SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
16029
+ );
16030
+ if (activityResult.rows.length > 0) {
16031
+ setActivity(activityResult.rows.slice(0, 8).map((r) => ({
16032
+ time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
16033
+ agent: String(r.agent_id ?? "system"),
16034
+ action: String(r.text ?? "").slice(0, 60)
16035
+ })));
15854
16036
  }
15855
16037
  } catch {
16038
+ setDbError(true);
16039
+ } finally {
16040
+ setLoading(false);
15856
16041
  }
15857
- } catch {
15858
- setDbError(true);
15859
- } finally {
15860
- setLoading(false);
15861
- }
16042
+ });
15862
16043
  }
15863
16044
  const daemonColor = health.daemon === "running" ? "green" : health.daemon === "stopped" ? "red" : "gray";
15864
16045
  const handleChatSubmit = useCallback4((value) => {
@@ -15950,7 +16131,7 @@ function CommandCenterView({
15950
16131
  ] }),
15951
16132
  /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate | c to chat | Enter to open | Escape to detach" }),
15952
16133
  /* @__PURE__ */ jsx7(Text, { children: " " }),
15953
- loading ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "Loading projects..." }) : dbError ? /* @__PURE__ */ jsx7(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : rows.length === 0 ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: " No projects detected." }) : (() => {
16134
+ loading ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "Loading projects..." }) : dbError ? /* @__PURE__ */ jsx7(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : rows.length === 0 ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "No projects yet. Run exe-os in a project directory to get started." }) : (() => {
15954
16135
  const sections = [];
15955
16136
  let currentProjects = [];
15956
16137
  let sectionKey = 0;
@@ -15980,9 +16161,9 @@ function CommandCenterView({
15980
16161
  ] })
15981
16162
  ] }),
15982
16163
  /* @__PURE__ */ jsxs5(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
15983
- entry.employeeCount,
16164
+ entry.openTaskCount,
15984
16165
  " ",
15985
- entry.employeeCount === 1 ? "employee" : "employees",
16166
+ entry.openTaskCount === 1 ? "task" : "tasks",
15986
16167
  " \xB7 ",
15987
16168
  entry.memoryCount.toLocaleString(),
15988
16169
  " memories"
@@ -16060,6 +16241,7 @@ var init_CommandCenter = __esm({
16060
16241
  init_DemoContext();
16061
16242
  init_demo_data();
16062
16243
  init_useAgentLoop();
16244
+ init_employee_templates();
16063
16245
  }
16064
16246
  });
16065
16247
 
@@ -16184,7 +16366,7 @@ var init_TmuxPane = __esm({
16184
16366
  });
16185
16367
 
16186
16368
  // src/lib/task-router.ts
16187
- import { randomUUID as randomUUID4 } from "crypto";
16369
+ import { randomUUID as randomUUID5 } from "crypto";
16188
16370
  function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
16189
16371
  const tier = config.complexityToTier[complexity];
16190
16372
  const rule = config.tierRules[tier];
@@ -16836,9 +17018,15 @@ async function createTaskCore(input) {
16836
17018
  }
16837
17019
  }
16838
17020
  const complexity = input.complexity ?? "standard";
17021
+ let sessionScope = null;
17022
+ try {
17023
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
17024
+ sessionScope = resolveExeSession2();
17025
+ } catch {
17026
+ }
16839
17027
  await client.execute({
16840
- 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)
16841
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
17028
+ 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)
17029
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
16842
17030
  args: [
16843
17031
  id,
16844
17032
  input.title,
@@ -16857,6 +17045,7 @@ async function createTaskCore(input) {
16857
17045
  input.budgetFallbackModel ?? null,
16858
17046
  0,
16859
17047
  null,
17048
+ sessionScope,
16860
17049
  now,
16861
17050
  now
16862
17051
  ]
@@ -16901,6 +17090,15 @@ async function listTasks(input) {
16901
17090
  conditions.push("priority = ?");
16902
17091
  args2.push(input.priority);
16903
17092
  }
17093
+ try {
17094
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
17095
+ const session = resolveExeSession2();
17096
+ if (session) {
17097
+ conditions.push("(session_scope IS NULL OR session_scope = ?)");
17098
+ args2.push(session);
17099
+ }
17100
+ } catch {
17101
+ }
16904
17102
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
16905
17103
  const result = await client.execute({
16906
17104
  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`,
@@ -17265,6 +17463,7 @@ var init_tasks_review = __esm({
17265
17463
  init_tasks_crud();
17266
17464
  init_tmux_routing();
17267
17465
  init_session_key();
17466
+ init_state_bus();
17268
17467
  }
17269
17468
  });
17270
17469
 
@@ -17429,13 +17628,12 @@ function assertSessionScope(actionType, targetProject) {
17429
17628
  };
17430
17629
  }
17431
17630
  process.stderr.write(
17432
- `[session-scope] Cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
17631
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
17433
17632
  `
17434
17633
  );
17435
17634
  return {
17436
- allowed: true,
17437
- // v1: warn-only, don't block
17438
- reason: "cross_session_granted",
17635
+ allowed: false,
17636
+ reason: "cross_session_denied",
17439
17637
  currentProject,
17440
17638
  targetProject,
17441
17639
  targetSession: findSessionForProject(targetProject)?.windowName
@@ -17461,8 +17659,9 @@ async function dispatchTaskToEmployee(input) {
17461
17659
  try {
17462
17660
  const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
17463
17661
  const check = assertSessionScope2("dispatch_task", input.projectName);
17464
- if (check.reason === "cross_session_granted") {
17662
+ if (check.reason === "cross_session_denied") {
17465
17663
  crossProject = true;
17664
+ return { dispatched: "skipped", crossProject: true };
17466
17665
  }
17467
17666
  } catch {
17468
17667
  }
@@ -17925,6 +18124,13 @@ async function updateTask(input) {
17925
18124
  await cascadeUnblock(taskId, input.baseDir, now);
17926
18125
  } catch {
17927
18126
  }
18127
+ orgBus.emit({
18128
+ type: "task_completed",
18129
+ taskId,
18130
+ employee: String(row.assigned_to),
18131
+ result: input.result ?? "",
18132
+ timestamp: now
18133
+ });
17928
18134
  if (row.parent_task_id) {
17929
18135
  try {
17930
18136
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -17992,6 +18198,7 @@ var init_tasks = __esm({
17992
18198
  init_database();
17993
18199
  init_config();
17994
18200
  init_notifications();
18201
+ init_state_bus();
17995
18202
  init_tasks_crud();
17996
18203
  init_tasks_review();
17997
18204
  init_tasks_crud();
@@ -18382,8 +18589,28 @@ function getMySession() {
18382
18589
  return getTransport().getMySession();
18383
18590
  }
18384
18591
  function employeeSessionName(employee, exeSession, instance) {
18592
+ if (!/^exe\d+$/.test(exeSession)) {
18593
+ const root = extractRootExe(exeSession);
18594
+ if (root) {
18595
+ process.stderr.write(
18596
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
18597
+ `
18598
+ );
18599
+ exeSession = root;
18600
+ } else {
18601
+ throw new Error(
18602
+ `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
18603
+ );
18604
+ }
18605
+ }
18385
18606
  const suffix = instance != null && instance > 0 ? String(instance) : "";
18386
- return `${employee}${suffix}-${exeSession}`;
18607
+ const name = `${employee}${suffix}-${exeSession}`;
18608
+ if (!VALID_SESSION_NAME.test(name)) {
18609
+ throw new Error(
18610
+ `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
18611
+ );
18612
+ }
18613
+ return name;
18387
18614
  }
18388
18615
  function parseParentExe(sessionName, agentId) {
18389
18616
  const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -18623,10 +18850,26 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
18623
18850
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
18624
18851
  };
18625
18852
  }
18626
- let effectiveInstance = opts?.instance;
18627
- if (effectiveInstance === void 0 && opts?.autoInstance) {
18628
- const free = findFreeInstance(
18629
- employeeName,
18853
+ if (!/^exe\d+$/.test(exeSession)) {
18854
+ const root = extractRootExe(exeSession);
18855
+ if (root) {
18856
+ process.stderr.write(
18857
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
18858
+ `
18859
+ );
18860
+ exeSession = root;
18861
+ } else {
18862
+ return {
18863
+ status: "failed",
18864
+ sessionName: "",
18865
+ error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
18866
+ };
18867
+ }
18868
+ }
18869
+ let effectiveInstance = opts?.instance;
18870
+ if (effectiveInstance === void 0 && opts?.autoInstance) {
18871
+ const free = findFreeInstance(
18872
+ employeeName,
18630
18873
  exeSession,
18631
18874
  opts.maxAutoInstances ?? 10
18632
18875
  );
@@ -18869,7 +19112,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
18869
19112
  releaseSpawnLock2(sessionName);
18870
19113
  return { sessionName };
18871
19114
  }
18872
- 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;
19115
+ 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;
18873
19116
  var init_tmux_routing = __esm({
18874
19117
  "src/lib/tmux-routing.ts"() {
18875
19118
  "use strict";
@@ -18884,6 +19127,7 @@ var init_tmux_routing = __esm({
18884
19127
  SPAWN_LOCK_DIR = path30.join(os10.homedir(), ".exe-os", "spawn-locks");
18885
19128
  SESSION_CACHE = path30.join(os10.homedir(), ".exe-os", "session-cache");
18886
19129
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
19130
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
18887
19131
  VERIFY_PANE_LINES = 200;
18888
19132
  INTERCOM_DEBOUNCE_MS = 3e4;
18889
19133
  INTERCOM_LOG2 = path30.join(os10.homedir(), ".exe-os", "intercom.log");
@@ -19290,6 +19534,7 @@ function SessionsView({
19290
19534
  const [viewingEmployee, setViewingEmployee] = useState9(null);
19291
19535
  const [viewingProject, setViewingProject] = useState9(null);
19292
19536
  const [loading, setLoading] = useState9(!demo);
19537
+ const [sessionError, setSessionError] = useState9(null);
19293
19538
  const [tmuxAvailable, setTmuxAvailable] = useState9(true);
19294
19539
  const orch = useOrchestrator(!demo);
19295
19540
  const [carouselEmployees, setCarouselEmployees] = useState9([]);
@@ -19465,111 +19710,116 @@ function SessionsView({
19465
19710
  return ACTIVE_PANE_PATTERN.test(lines.join("\n")) ? "active" : "idle";
19466
19711
  }
19467
19712
  async function loadSessions() {
19468
- try {
19469
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
19470
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
19471
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
19472
- const { execSync: execSync13 } = await import("child_process");
19473
- if (!inTmux2()) {
19474
- setTmuxAvailable(false);
19475
- setProjects([]);
19476
- return;
19477
- }
19478
- setTmuxAvailable(true);
19479
- const attachedMap = /* @__PURE__ */ new Map();
19713
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
19714
+ return withTrace2("tui.sessions.loadSessions", async () => {
19480
19715
  try {
19481
- const out = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
19482
- encoding: "utf8",
19483
- timeout: 3e3
19484
- });
19485
- for (const line of out.trim().split("\n").filter(Boolean)) {
19486
- const sepIdx = line.lastIndexOf(":");
19487
- if (sepIdx > 0) {
19488
- attachedMap.set(line.slice(0, sepIdx), line.slice(sepIdx + 1) === "1");
19716
+ const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
19717
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
19718
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
19719
+ const { execSync: execSync13 } = await import("child_process");
19720
+ if (!inTmux2()) {
19721
+ setTmuxAvailable(false);
19722
+ setProjects([]);
19723
+ return;
19724
+ }
19725
+ setTmuxAvailable(true);
19726
+ const attachedMap = /* @__PURE__ */ new Map();
19727
+ try {
19728
+ const out = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
19729
+ encoding: "utf8",
19730
+ timeout: 3e3
19731
+ });
19732
+ for (const line of out.trim().split("\n").filter(Boolean)) {
19733
+ const sepIdx = line.lastIndexOf(":");
19734
+ if (sepIdx > 0) {
19735
+ attachedMap.set(line.slice(0, sepIdx), line.slice(sepIdx + 1) === "1");
19736
+ }
19489
19737
  }
19738
+ } catch {
19490
19739
  }
19491
- } catch {
19492
- }
19493
- const registry = listSessions2();
19494
- const tmuxSessions = new Set(listTmuxSessions2());
19495
- const roster = await loadEmployees2();
19496
- const exeSessions = /* @__PURE__ */ new Map();
19497
- for (const entry of registry) {
19498
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
19499
- exeSessions.set(entry.windowName, entry.projectDir);
19500
- }
19501
- }
19502
- for (const s of tmuxSessions) {
19503
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
19504
- exeSessions.set(s, "");
19505
- }
19506
- }
19507
- const projectList = [];
19508
- for (const [exeSession, projectDir] of exeSessions) {
19509
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
19510
- const exeHasSession = tmuxSessions.has(exeSession);
19511
- let exeStatus = "offline";
19512
- let exeActivity = "";
19513
- if (exeHasSession) {
19514
- const exeLines = capturePaneLines2(exeSession, 10);
19515
- exeStatus = statusFromPaneLines(exeLines);
19516
- exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
19517
- }
19518
- const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
19519
- const sessionName = `${emp.name}-${exeSession}`;
19520
- const hasSession = tmuxSessions.has(sessionName);
19521
- const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
19522
- if (isCrashed) {
19523
- return {
19524
- name: emp.name,
19525
- role: emp.role,
19526
- status: "crashed",
19527
- sessionName,
19528
- activity: "Dead session \u2014 has open tasks",
19529
- attached: false
19530
- };
19740
+ const registry = listSessions2();
19741
+ const tmuxSessions = new Set(listTmuxSessions2());
19742
+ const roster = await loadEmployees2();
19743
+ const exeSessions = /* @__PURE__ */ new Map();
19744
+ for (const entry of registry) {
19745
+ if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
19746
+ exeSessions.set(entry.windowName, entry.projectDir);
19531
19747
  }
19532
- if (!hasSession) {
19748
+ }
19749
+ for (const s of tmuxSessions) {
19750
+ if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
19751
+ exeSessions.set(s, "");
19752
+ }
19753
+ }
19754
+ const projectList = [];
19755
+ for (const [exeSession, projectDir] of exeSessions) {
19756
+ const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
19757
+ const exeHasSession = tmuxSessions.has(exeSession);
19758
+ let exeStatus = "offline";
19759
+ let exeActivity = "";
19760
+ if (exeHasSession) {
19761
+ const exeLines = capturePaneLines2(exeSession, 10);
19762
+ exeStatus = statusFromPaneLines(exeLines);
19763
+ exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
19764
+ }
19765
+ const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
19766
+ const sessionName = `${emp.name}-${exeSession}`;
19767
+ const hasSession = tmuxSessions.has(sessionName);
19768
+ const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
19769
+ if (isCrashed) {
19770
+ return {
19771
+ name: emp.name,
19772
+ role: emp.role,
19773
+ status: "crashed",
19774
+ sessionName,
19775
+ activity: "Dead session \u2014 has open tasks",
19776
+ attached: false
19777
+ };
19778
+ }
19779
+ if (!hasSession) {
19780
+ return {
19781
+ name: emp.name,
19782
+ role: emp.role,
19783
+ status: "offline",
19784
+ sessionName,
19785
+ activity: "",
19786
+ attached: false
19787
+ };
19788
+ }
19789
+ const lines = capturePaneLines2(sessionName, 10);
19533
19790
  return {
19534
19791
  name: emp.name,
19535
19792
  role: emp.role,
19536
- status: "offline",
19793
+ status: statusFromPaneLines(lines),
19537
19794
  sessionName,
19538
- activity: "",
19539
- attached: false
19795
+ activity: lines.length > 0 ? parseActivity2(lines) : "",
19796
+ attached: attachedMap.get(sessionName) ?? false
19540
19797
  };
19541
- }
19542
- const lines = capturePaneLines2(sessionName, 10);
19543
- return {
19544
- name: emp.name,
19545
- role: emp.role,
19546
- status: statusFromPaneLines(lines),
19547
- sessionName,
19548
- activity: lines.length > 0 ? parseActivity2(lines) : "",
19549
- attached: attachedMap.get(sessionName) ?? false
19550
- };
19551
- });
19552
- const employees = [
19553
- {
19554
- name: "exe",
19555
- role: "COO",
19556
- status: exeStatus,
19557
- sessionName: exeSession,
19558
- activity: exeActivity,
19559
- attached: attachedMap.get(exeSession) ?? false
19560
- },
19561
- ...employeeEntries
19562
- ];
19563
- projectList.push({ projectName, exeSession, projectDir, employees });
19564
- }
19565
- if (projectList.length === 0) {
19566
- projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
19798
+ });
19799
+ const employees = [
19800
+ {
19801
+ name: "exe",
19802
+ role: "COO",
19803
+ status: exeStatus,
19804
+ sessionName: exeSession,
19805
+ activity: exeActivity,
19806
+ attached: attachedMap.get(exeSession) ?? false
19807
+ },
19808
+ ...employeeEntries
19809
+ ];
19810
+ projectList.push({ projectName, exeSession, projectDir, employees });
19811
+ }
19812
+ if (projectList.length === 0) {
19813
+ projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
19814
+ }
19815
+ setProjects(projectList);
19816
+ setSessionError(null);
19817
+ } catch (err) {
19818
+ setSessionError(err instanceof Error ? err.message : "Failed to query sessions.");
19819
+ } finally {
19820
+ setLoading(false);
19567
19821
  }
19568
- setProjects(projectList);
19569
- } catch {
19570
- } finally {
19571
- setLoading(false);
19572
- }
19822
+ });
19573
19823
  }
19574
19824
  if (viewingEmployee) {
19575
19825
  const inCarousel = carouselEmployees.length > 0;
@@ -19651,7 +19901,10 @@ function SessionsView({
19651
19901
  ] })
19652
19902
  ] }),
19653
19903
  /* @__PURE__ */ jsx9(Text, { children: " " }),
19654
- loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : !tmuxAvailable ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "tmux not available. Start a tmux session first to manage employees." }) : flatItems.length === 0 ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "No active tmux sessions. Press Enter on an employee to launch one, or start exe in a tmux session first." }) : flatItems.map((item, i) => {
19904
+ loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : sessionError ? /* @__PURE__ */ jsxs7(Text, { color: "#C91B00", children: [
19905
+ "Failed to query sessions: ",
19906
+ sessionError
19907
+ ] }) : !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) => {
19655
19908
  const isSelected = i === selectedIdx;
19656
19909
  const marker = isSelected ? "\u25B8 " : " ";
19657
19910
  if (item.type === "project") {
@@ -19752,7 +20005,7 @@ function TasksView({ onBack }) {
19752
20005
  const demo = useDemo();
19753
20006
  const [allTasks, setAllTasks] = useState10([]);
19754
20007
  const [loading, setLoading] = useState10(!demo);
19755
- const [dbError, setDbError] = useState10(false);
20008
+ const [dbError, setDbError] = useState10(null);
19756
20009
  const [selectedTask, setSelectedTask] = useState10(0);
19757
20010
  const [showDetail, setShowDetail] = useState10(false);
19758
20011
  const [statusFilter, setStatusFilter] = useState10("all");
@@ -19824,40 +20077,43 @@ function TasksView({ onBack }) {
19824
20077
  }
19825
20078
  });
19826
20079
  async function loadTasks() {
19827
- try {
19828
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
19829
- const client = getClient2();
19830
- if (client) {
19831
- const result = await client.execute(
19832
- `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
20080
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
20081
+ return withTrace2("tui.tasks.loadTasks", async () => {
20082
+ try {
20083
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20084
+ const client = getClient2();
20085
+ if (client) {
20086
+ const result = await client.execute(
20087
+ `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
19833
20088
  FROM tasks
19834
20089
  ORDER BY
19835
20090
  CASE status WHEN 'in_progress' THEN 0 WHEN 'open' THEN 1 WHEN 'blocked' THEN 2 WHEN 'needs_review' THEN 3 WHEN 'done' THEN 4 ELSE 5 END,
19836
20091
  CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
19837
20092
  created_at DESC`
19838
- );
19839
- setAllTasks(
19840
- result.rows.map((r) => ({
19841
- id: String(r.id ?? ""),
19842
- priority: String(r.priority ?? "p2").toUpperCase(),
19843
- title: String(r.title ?? ""),
19844
- assignee: String(r.assigned_to ?? ""),
19845
- assignedBy: String(r.assigned_by ?? ""),
19846
- status: String(r.status ?? "open"),
19847
- project: String(r.project_name ?? ""),
19848
- createdAt: String(r.created_at ?? ""),
19849
- result: String(r.result ?? "")
19850
- }))
19851
- );
19852
- setDbError(false);
19853
- } else {
19854
- setDbError(true);
20093
+ );
20094
+ setAllTasks(
20095
+ result.rows.map((r) => ({
20096
+ id: String(r.id ?? ""),
20097
+ priority: String(r.priority ?? "p2").toUpperCase(),
20098
+ title: String(r.title ?? ""),
20099
+ assignee: String(r.assigned_to ?? ""),
20100
+ assignedBy: String(r.assigned_by ?? ""),
20101
+ status: String(r.status ?? "open"),
20102
+ project: String(r.project_name ?? ""),
20103
+ createdAt: String(r.created_at ?? ""),
20104
+ result: String(r.result ?? "")
20105
+ }))
20106
+ );
20107
+ setDbError(null);
20108
+ } else {
20109
+ setDbError("Database client not initialized. Run exe-os setup.");
20110
+ }
20111
+ } catch (err) {
20112
+ setDbError(err instanceof Error ? err.message : "Unknown error");
20113
+ } finally {
20114
+ setLoading(false);
19855
20115
  }
19856
- } catch {
19857
- setDbError(true);
19858
- } finally {
19859
- setLoading(false);
19860
- }
20116
+ });
19861
20117
  }
19862
20118
  const selected = taskRows[selectedTask]?.task;
19863
20119
  if (showDetail && selected) {
@@ -19924,7 +20180,10 @@ function TasksView({ onBack }) {
19924
20180
  filterLabel
19925
20181
  ] }),
19926
20182
  /* @__PURE__ */ jsx10(Text, { children: " " }),
19927
- loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */ jsx10(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : filteredTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "No tasks match current filters." }) : displayRows.map((row, i) => {
20183
+ loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */ jsxs8(Text, { color: "#C91B00", children: [
20184
+ "Failed to load tasks: ",
20185
+ dbError
20186
+ ] }) : allTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#3D3660", children: "No tasks. Create one with create_task." }) : filteredTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#3D3660", children: "No tasks match current filter." }) : displayRows.map((row, i) => {
19928
20187
  if (row.type === "header") {
19929
20188
  return /* @__PURE__ */ jsxs8(Box_default, { marginTop: i > 0 ? 1 : 0, children: [
19930
20189
  /* @__PURE__ */ jsx10(Text, { bold: true, color: "#F0EDE8", children: row.project }),
@@ -20611,6 +20870,31 @@ function GatewayView({ onBack }) {
20611
20870
  /* @__PURE__ */ jsx11(Text, { color: "#6B4C9A", children: "Loading gateway status..." })
20612
20871
  ] });
20613
20872
  }
20873
+ if (!gateway.running && gateway.gatewayUrl && connectionStatus === "disconnected") {
20874
+ return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
20875
+ /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
20876
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20877
+ /* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "Gateway connection failed." }),
20878
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20879
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20880
+ "Gateway URL: ",
20881
+ /* @__PURE__ */ jsx11(Text, { color: "#F0EDE8", children: gateway.gatewayUrl })
20882
+ ] }),
20883
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20884
+ "Process: ",
20885
+ /* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "not running" })
20886
+ ] }),
20887
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20888
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20889
+ "Start the gateway: ",
20890
+ /* @__PURE__ */ jsx11(Text, { bold: true, children: "exe-os gateway" })
20891
+ ] }),
20892
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20893
+ "Or check your config: ",
20894
+ /* @__PURE__ */ jsx11(Text, { bold: true, children: "~/.exe-os/gateway.json" })
20895
+ ] })
20896
+ ] });
20897
+ }
20614
20898
  if (!gateway.running && gateway.adapters.length === 0 && !gateway.gatewayUrl) {
20615
20899
  return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
20616
20900
  /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
@@ -20897,14 +21181,30 @@ var init_agent_status = __esm({
20897
21181
  // src/tui/views/Team.tsx
20898
21182
  import React22, { useState as useState12, useEffect as useEffect14 } from "react";
20899
21183
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
20900
- function TeamView({ onBack }) {
21184
+ function roleBadgeColor(role) {
21185
+ switch (role.toLowerCase()) {
21186
+ case "coo":
21187
+ return "#F5D76E";
21188
+ // gold
21189
+ case "cto":
21190
+ return "#3B82F6";
21191
+ // blue
21192
+ case "cmo":
21193
+ return "#6B4C9A";
21194
+ // purple
21195
+ default:
21196
+ return "#F0EDE8";
21197
+ }
21198
+ }
21199
+ function TeamView({ onBack, onViewSessions }) {
20901
21200
  const demo = useDemo();
20902
21201
  const [members, setMembers] = useState12([]);
20903
21202
  const [externals, setExternals] = useState12([]);
20904
21203
  const [loading, setLoading] = useState12(!demo);
20905
- const [dbError, setDbError] = useState12(false);
21204
+ const [dbError, setDbError] = useState12(null);
20906
21205
  const [selectedIdx, setSelectedIdx] = useState12(0);
20907
21206
  const [showDetail, setShowDetail] = useState12(false);
21207
+ const [showAddHint, setShowAddHint] = useState12(false);
20908
21208
  const orch = useOrchestrator(!demo);
20909
21209
  const orchEmployeeMap = new Map(orch.employees.map((e) => [e.name, e]));
20910
21210
  const allItems = [...members, ...externals.map((e) => ({ ...e, activity: "", memoryCount: 0 }))];
@@ -20919,7 +21219,7 @@ function TeamView({ onBack }) {
20919
21219
  const timer = setInterval(loadTeam, 5e3);
20920
21220
  return () => clearInterval(timer);
20921
21221
  }, []);
20922
- use_input_default((_input, key) => {
21222
+ use_input_default((input, key) => {
20923
21223
  if (showDetail) {
20924
21224
  if (key.escape) setShowDetail(false);
20925
21225
  return;
@@ -20928,91 +21228,103 @@ function TeamView({ onBack }) {
20928
21228
  onBack?.();
20929
21229
  return;
20930
21230
  }
21231
+ if (input === "a") {
21232
+ setShowAddHint(true);
21233
+ setTimeout(() => setShowAddHint(false), 3e3);
21234
+ return;
21235
+ }
21236
+ if (input === "s" && selected) {
21237
+ onViewSessions?.(selected.name);
21238
+ return;
21239
+ }
20931
21240
  if (key.upArrow && selectedIdx > 0) setSelectedIdx(selectedIdx - 1);
20932
21241
  if (key.downArrow && selectedIdx < allItems.length - 1) setSelectedIdx(selectedIdx + 1);
20933
21242
  if (key.return && allItems.length > 0) setShowDetail(true);
20934
21243
  });
20935
21244
  async function loadTeam() {
20936
- try {
20937
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
20938
- const roster = await loadEmployees2();
20939
- let memoryCounts = /* @__PURE__ */ new Map();
20940
- let projectsByEmployee = /* @__PURE__ */ new Map();
20941
- let currentTaskByEmployee = /* @__PURE__ */ new Map();
21245
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
21246
+ return withTrace2("tui.team.loadTeam", async () => {
20942
21247
  try {
20943
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20944
- const client = getClient2();
20945
- if (client) {
20946
- const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
20947
- for (const row of memResult.rows) {
20948
- memoryCounts.set(String(row.agent_id), Number(row.cnt));
20949
- }
20950
- for (const emp of roster) {
20951
- const projResult = await client.execute({
20952
- sql: `SELECT DISTINCT project_name,
21248
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21249
+ const roster = await loadEmployees2();
21250
+ let memoryCounts = /* @__PURE__ */ new Map();
21251
+ let projectsByEmployee = /* @__PURE__ */ new Map();
21252
+ let currentTaskByEmployee = /* @__PURE__ */ new Map();
21253
+ try {
21254
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21255
+ const client = getClient2();
21256
+ if (client) {
21257
+ const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
21258
+ for (const row of memResult.rows) {
21259
+ memoryCounts.set(String(row.agent_id), Number(row.cnt));
21260
+ }
21261
+ for (const emp of roster) {
21262
+ const projResult = await client.execute({
21263
+ sql: `SELECT DISTINCT project_name,
20953
21264
  MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
20954
21265
  FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')
20955
21266
  GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
20956
- args: [emp.name]
20957
- });
20958
- const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
20959
- const urgency = Number(r.urgency);
20960
- let pStatus = "idle";
20961
- if (urgency === 1) pStatus = "active";
20962
- else if (urgency === 2) pStatus = "has_tasks";
20963
- return { name: String(r.project_name), status: pStatus };
20964
- });
20965
- if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
20966
- }
20967
- for (const emp of roster) {
20968
- const taskResult = await client.execute({
20969
- sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
20970
- args: [emp.name]
20971
- });
20972
- if (taskResult.rows.length > 0) {
20973
- currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
21267
+ args: [emp.name]
21268
+ });
21269
+ const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
21270
+ const urgency = Number(r.urgency);
21271
+ let pStatus = "idle";
21272
+ if (urgency === 1) pStatus = "active";
21273
+ else if (urgency === 2) pStatus = "has_tasks";
21274
+ return { name: String(r.project_name), status: pStatus };
21275
+ });
21276
+ if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
21277
+ }
21278
+ for (const emp of roster) {
21279
+ const taskResult = await client.execute({
21280
+ sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
21281
+ args: [emp.name]
21282
+ });
21283
+ if (taskResult.rows.length > 0) {
21284
+ currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
21285
+ }
20974
21286
  }
20975
21287
  }
21288
+ } catch {
20976
21289
  }
20977
- } catch {
20978
- }
20979
- const teamData = roster.map((emp) => {
20980
- const agentSt = getAgentStatus(emp.name);
20981
- return {
20982
- name: emp.name,
20983
- role: emp.role,
20984
- status: agentSt.label,
20985
- activity: agentSt.label === "active" ? "Processing..." : "",
20986
- memoryCount: memoryCounts.get(emp.name) ?? 0,
20987
- projects: projectsByEmployee.get(emp.name),
20988
- currentTask: currentTaskByEmployee.get(emp.name),
20989
- sessionName: agentSt.session
20990
- };
20991
- });
20992
- setMembers(teamData);
20993
- setDbError(false);
20994
- try {
20995
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
20996
- const { join } = await import("path");
20997
- const home = process.env.HOME ?? "";
20998
- const gatewayConfig = join(home, ".exe-os", "gateway.json");
20999
- if (existsSync22(gatewayConfig)) {
21000
- const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
21001
- if (raw.agents && raw.agents.length > 0) {
21002
- setExternals(raw.agents.map((a) => ({
21003
- name: a.name,
21004
- role: a.role ?? "external agent",
21005
- status: "offline"
21006
- })));
21290
+ const teamData = roster.map((emp) => {
21291
+ const agentSt = getAgentStatus(emp.name);
21292
+ return {
21293
+ name: emp.name,
21294
+ role: emp.role,
21295
+ status: agentSt.label,
21296
+ activity: agentSt.label === "active" ? "Processing..." : "",
21297
+ memoryCount: memoryCounts.get(emp.name) ?? 0,
21298
+ projects: projectsByEmployee.get(emp.name),
21299
+ currentTask: currentTaskByEmployee.get(emp.name),
21300
+ sessionName: agentSt.session
21301
+ };
21302
+ });
21303
+ setMembers(teamData);
21304
+ setDbError(null);
21305
+ try {
21306
+ const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21307
+ const { join } = await import("path");
21308
+ const home = process.env.HOME ?? "";
21309
+ const gatewayConfig = join(home, ".exe-os", "gateway.json");
21310
+ if (existsSync22(gatewayConfig)) {
21311
+ const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
21312
+ if (raw.agents && raw.agents.length > 0) {
21313
+ setExternals(raw.agents.map((a) => ({
21314
+ name: a.name,
21315
+ role: a.role ?? "external agent",
21316
+ status: "offline"
21317
+ })));
21318
+ }
21007
21319
  }
21320
+ } catch {
21008
21321
  }
21009
- } catch {
21322
+ } catch (err) {
21323
+ setDbError(err instanceof Error ? err.message : "Unknown error");
21324
+ } finally {
21325
+ setLoading(false);
21010
21326
  }
21011
- } catch {
21012
- setDbError(true);
21013
- } finally {
21014
- setLoading(false);
21015
- }
21327
+ });
21016
21328
  }
21017
21329
  const totalCount = members.length + externals.length;
21018
21330
  const selected = allItems[selectedIdx];
@@ -21029,7 +21341,7 @@ function TeamView({ onBack }) {
21029
21341
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21030
21342
  /* @__PURE__ */ jsxs10(Text, { children: [
21031
21343
  "Role: ",
21032
- /* @__PURE__ */ jsx12(Text, { bold: true, children: selected.role })
21344
+ /* @__PURE__ */ jsx12(Text, { bold: true, color: roleBadgeColor(selected.role), children: selected.role })
21033
21345
  ] }),
21034
21346
  /* @__PURE__ */ jsxs10(Text, { children: [
21035
21347
  "Status: ",
@@ -21058,8 +21370,9 @@ function TeamView({ onBack }) {
21058
21370
  /* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
21059
21371
  /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21060
21372
  totalCount,
21061
- " agents | up/down navigate | Enter for details"
21373
+ " agents | \u2191\u2193 navigate | Enter details | a add | s sessions"
21062
21374
  ] }),
21375
+ showAddHint && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: "Run /exe-new-employee <name> from CLI to add an employee." }),
21063
21376
  !demo && orch.pendingReviews > 0 && /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21064
21377
  orch.pendingReviews,
21065
21378
  " review(s) pending exe attention"
@@ -21067,7 +21380,10 @@ function TeamView({ onBack }) {
21067
21380
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21068
21381
  /* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
21069
21382
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21070
- loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading team..." }) : dbError ? /* @__PURE__ */ jsx12(Text, { color: "#EF4444", children: " Database unavailable. Run exe-os setup to initialize." }) : members.length === 0 ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " No employees found. Run /exe-new-employee to create one." }) : /* @__PURE__ */ jsx12(Box_default, { flexDirection: "row", flexWrap: "wrap", gap: 1, children: members.map((m, i) => {
21383
+ loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading employee roster..." }) : dbError ? /* @__PURE__ */ jsxs10(Text, { color: "#C91B00", children: [
21384
+ " Failed to load roster: ",
21385
+ dbError
21386
+ ] }) : members.length === 0 ? /* @__PURE__ */ jsx12(Text, { color: "#3D3660", children: " No employees configured. Run /exe-new-employee." }) : /* @__PURE__ */ jsx12(Box_default, { flexDirection: "row", flexWrap: "wrap", gap: 1, children: members.map((m, i) => {
21071
21387
  const isSelected = i === selectedIdx;
21072
21388
  const orchEmp = orchEmployeeMap.get(m.name);
21073
21389
  return /* @__PURE__ */ jsxs10(
@@ -21083,7 +21399,7 @@ function TeamView({ onBack }) {
21083
21399
  /* @__PURE__ */ jsxs10(Box_default, { gap: 1, children: [
21084
21400
  /* @__PURE__ */ jsx12(StatusDot, { status: m.status, showLabel: false }),
21085
21401
  /* @__PURE__ */ jsx12(Text, { bold: true, color: isSelected ? "#F5D76E" : "#F0EDE8", children: m.name }),
21086
- /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : "#6B4C9A", children: [
21402
+ /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : roleBadgeColor(m.role), children: [
21087
21403
  "(",
21088
21404
  m.role,
21089
21405
  ")"
@@ -21309,6 +21625,26 @@ var init_wiki_client = __esm({
21309
21625
  import React23, { useState as useState13, useEffect as useEffect15 } from "react";
21310
21626
  import TextInput2 from "ink-text-input";
21311
21627
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
21628
+ function agentColor(agentId) {
21629
+ switch (agentId.toLowerCase()) {
21630
+ case "exe":
21631
+ return "#F5D76E";
21632
+ case "yoshi":
21633
+ return "#3B82F6";
21634
+ case "mari":
21635
+ return "#6B4C9A";
21636
+ default:
21637
+ return "#F0EDE8";
21638
+ }
21639
+ }
21640
+ function formatTimeAgo(iso) {
21641
+ const diff2 = Date.now() - new Date(iso).getTime();
21642
+ const hours = Math.floor(diff2 / 36e5);
21643
+ if (hours < 1) return "just now";
21644
+ if (hours < 24) return `${hours}h ago`;
21645
+ const days = Math.floor(hours / 24);
21646
+ return `${days}d ago`;
21647
+ }
21312
21648
  function WikiView({ onBack }) {
21313
21649
  const demo = useDemo();
21314
21650
  const [activePanel, setActivePanel] = useState13("Workspaces");
@@ -21319,6 +21655,12 @@ function WikiView({ onBack }) {
21319
21655
  const [chatHistory, setChatHistory] = useState13([]);
21320
21656
  const [chatInput, setChatInput] = useState13("");
21321
21657
  const [chatFocused, setChatFocused] = useState13(false);
21658
+ const [searchMode, setSearchMode] = useState13(false);
21659
+ const [searchQuery, setSearchQuery] = useState13("");
21660
+ const [searchResults, setSearchResults] = useState13([]);
21661
+ const [searchLoading, setSearchLoading] = useState13(false);
21662
+ const [selectedResultIdx, setSelectedResultIdx] = useState13(0);
21663
+ const [expandedResultIdx, setExpandedResultIdx] = useState13(null);
21322
21664
  const [loading, setLoading] = useState13(true);
21323
21665
  const [connected, setConnected] = useState13(false);
21324
21666
  const [wikiUrl, setWikiUrl] = useState13("");
@@ -21412,6 +21754,55 @@ function WikiView({ onBack }) {
21412
21754
  setSending(false);
21413
21755
  }
21414
21756
  }
21757
+ async function searchMemories2(query) {
21758
+ if (!query.trim()) {
21759
+ setSearchResults([]);
21760
+ return;
21761
+ }
21762
+ if (demo) {
21763
+ const q = query.toLowerCase();
21764
+ setSearchResults(
21765
+ DEMO_SEARCH_RESULTS.filter((r) => r.rawText.toLowerCase().includes(q))
21766
+ );
21767
+ return;
21768
+ }
21769
+ setSearchLoading(true);
21770
+ try {
21771
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21772
+ const client = getClient2();
21773
+ const result = await client.execute({
21774
+ sql: `SELECT id, agent_id, raw_text, timestamp, project_name
21775
+ FROM memories
21776
+ WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
21777
+ ORDER BY timestamp DESC LIMIT 20`,
21778
+ args: [`%${query}%`]
21779
+ });
21780
+ setSearchResults(
21781
+ result.rows.map((r) => ({
21782
+ id: String(r.id),
21783
+ agentId: String(r.agent_id),
21784
+ rawText: String(r.raw_text),
21785
+ timestamp: String(r.timestamp),
21786
+ projectName: String(r.project_name ?? "")
21787
+ }))
21788
+ );
21789
+ } catch {
21790
+ setSearchResults([]);
21791
+ } finally {
21792
+ setSearchLoading(false);
21793
+ }
21794
+ }
21795
+ useEffect15(() => {
21796
+ if (!searchMode) return;
21797
+ const timer = setTimeout(() => {
21798
+ searchMemories2(searchQuery);
21799
+ }, 300);
21800
+ return () => clearTimeout(timer);
21801
+ }, [searchQuery, searchMode]);
21802
+ useEffect15(() => {
21803
+ setSelectedResultIdx(0);
21804
+ setExpandedResultIdx(null);
21805
+ }, [searchResults]);
21415
21806
  async function selectWorkspace(idx) {
21416
21807
  setSelectedWorkspaceIdx(idx);
21417
21808
  const ws = workspaces[idx];
@@ -21432,6 +21823,32 @@ function WikiView({ onBack }) {
21432
21823
  }
21433
21824
  }
21434
21825
  use_input_default((input, key) => {
21826
+ if (searchMode && !chatFocused) {
21827
+ if (key.escape) {
21828
+ setSearchMode(false);
21829
+ setSearchQuery("");
21830
+ setSearchResults([]);
21831
+ setExpandedResultIdx(null);
21832
+ return;
21833
+ }
21834
+ if (key.upArrow && selectedResultIdx > 0) {
21835
+ setSelectedResultIdx(selectedResultIdx - 1);
21836
+ setExpandedResultIdx(null);
21837
+ return;
21838
+ }
21839
+ if (key.downArrow && selectedResultIdx < searchResults.length - 1) {
21840
+ setSelectedResultIdx(selectedResultIdx + 1);
21841
+ setExpandedResultIdx(null);
21842
+ return;
21843
+ }
21844
+ if (key.return && searchResults.length > 0) {
21845
+ setExpandedResultIdx(
21846
+ expandedResultIdx === selectedResultIdx ? null : selectedResultIdx
21847
+ );
21848
+ return;
21849
+ }
21850
+ return;
21851
+ }
21435
21852
  if (chatFocused) {
21436
21853
  if (key.escape) {
21437
21854
  setChatFocused(false);
@@ -21443,6 +21860,13 @@ function WikiView({ onBack }) {
21443
21860
  }
21444
21861
  return;
21445
21862
  }
21863
+ if (input === "/" && activePanel !== "Chat") {
21864
+ setSearchMode(true);
21865
+ setSearchQuery("");
21866
+ setSearchResults(demo ? DEMO_SEARCH_RESULTS : []);
21867
+ setExpandedResultIdx(null);
21868
+ return;
21869
+ }
21446
21870
  if (key.leftArrow) {
21447
21871
  const panelIdx = PANELS.indexOf(activePanel);
21448
21872
  if (panelIdx === 0) {
@@ -21520,9 +21944,53 @@ function WikiView({ onBack }) {
21520
21944
  /* @__PURE__ */ jsx13(Text, { bold: true, children: selectedWs.name })
21521
21945
  ] }) : null
21522
21946
  ] }),
21523
- /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | / chat | Enter select" }),
21947
+ /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | / search memories | Enter select" }),
21524
21948
  /* @__PURE__ */ jsx13(Text, { children: " " }),
21525
- /* @__PURE__ */ jsxs11(Box_default, { flexGrow: 1, gap: 1, children: [
21949
+ searchMode ? /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", flexGrow: 1, children: [
21950
+ /* @__PURE__ */ jsxs11(Box_default, { paddingX: 1, children: [
21951
+ /* @__PURE__ */ jsx13(Text, { color: "#F5D76E", children: "Search: " }),
21952
+ /* @__PURE__ */ jsx13(
21953
+ TextInput2,
21954
+ {
21955
+ value: searchQuery,
21956
+ onChange: setSearchQuery,
21957
+ placeholder: "search memories...",
21958
+ focus: true
21959
+ }
21960
+ ),
21961
+ /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " (esc to close)" })
21962
+ ] }),
21963
+ /* @__PURE__ */ jsx13(Text, { children: " " }),
21964
+ searchLoading ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " Searching..." }) : searchResults.length === 0 && searchQuery.trim() ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " No results found." }) : searchResults.length === 0 ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " Type to search exe-os memories." }) : searchResults.map((result, i) => {
21965
+ const isSelected = i === selectedResultIdx;
21966
+ const isExpanded = i === expandedResultIdx;
21967
+ const snippet = result.rawText.slice(0, 80);
21968
+ return /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", children: [
21969
+ /* @__PURE__ */ jsxs11(
21970
+ Text,
21971
+ {
21972
+ backgroundColor: isSelected ? "#6B4C9A" : void 0,
21973
+ color: isSelected ? "#F5D76E" : void 0,
21974
+ children: [
21975
+ " ",
21976
+ /* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : agentColor(result.agentId), bold: true, children: result.agentId.padEnd(8) }),
21977
+ /* @__PURE__ */ jsxs11(Text, { color: isSelected ? "#F5D76E" : "#F0EDE8", children: [
21978
+ snippet,
21979
+ result.rawText.length > 80 ? "..." : ""
21980
+ ] }),
21981
+ " ",
21982
+ /* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : "#3D3660", dimColor: !isSelected, children: formatTimeAgo(result.timestamp) })
21983
+ ]
21984
+ }
21985
+ ),
21986
+ isExpanded ? /* @__PURE__ */ jsx13(Box_default, { paddingX: 4, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text, { color: "#F0EDE8", wrap: "wrap", children: result.rawText }) }) : null
21987
+ ] }, result.id);
21988
+ }),
21989
+ searchResults.length > 0 ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
21990
+ /* @__PURE__ */ jsx13(Text, { children: " " }),
21991
+ /* @__PURE__ */ jsx13(Text, { color: "#3D3660", children: " \u2191\u2193 navigate | Enter expand | Esc close" })
21992
+ ] }) : null
21993
+ ] }) : /* @__PURE__ */ jsxs11(Box_default, { flexGrow: 1, gap: 1, children: [
21526
21994
  /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", width: "25%", children: [
21527
21995
  /* @__PURE__ */ jsxs11(Text, { bold: true, backgroundColor: panelHeaderBg("Workspaces"), color: panelHeaderColor("Workspaces"), children: [
21528
21996
  activePanel === "Workspaces" ? "\u25B8 " : " ",
@@ -21604,7 +22072,7 @@ function WikiView({ onBack }) {
21604
22072
  ] })
21605
22073
  ] });
21606
22074
  }
21607
- var PANELS, MAX_VISIBLE_MESSAGES;
22075
+ var PANELS, MAX_VISIBLE_MESSAGES, DEMO_SEARCH_RESULTS;
21608
22076
  var init_Wiki = __esm({
21609
22077
  async "src/tui/views/Wiki.tsx"() {
21610
22078
  "use strict";
@@ -21613,53 +22081,48 @@ var init_Wiki = __esm({
21613
22081
  init_demo_data();
21614
22082
  PANELS = ["Workspaces", "Documents", "Chat"];
21615
22083
  MAX_VISIBLE_MESSAGES = 12;
22084
+ DEMO_SEARCH_RESULTS = [
22085
+ { id: "1", agentId: "yoshi", rawText: "Reviewed PR #42 \u2014 approved with minor comment on error handling pattern", timestamp: new Date(Date.now() - 2 * 36e5).toISOString(), projectName: "exe-os" },
22086
+ { id: "2", agentId: "tom", rawText: "Implemented session routing with deterministic naming: agent-exeN convention", timestamp: new Date(Date.now() - 5 * 36e5).toISOString(), projectName: "exe-os" },
22087
+ { id: "3", agentId: "exe", rawText: "Status brief: 3 tasks completed, 1 blocked on wiki integration", timestamp: new Date(Date.now() - 8 * 36e5).toISOString(), projectName: "exe-os" },
22088
+ { id: "4", agentId: "mari", rawText: "Created brand guidelines document \u2014 Exe Foundry Bold design system", timestamp: new Date(Date.now() - 24 * 36e5).toISOString(), projectName: "exe-os" },
22089
+ { id: "5", agentId: "tom", rawText: "Fixed CommandCenter project filtering \u2014 DB-first, no random directories", timestamp: new Date(Date.now() - 48 * 36e5).toISOString(), projectName: "exe-os" }
22090
+ ];
21616
22091
  }
21617
22092
  });
21618
22093
 
21619
22094
  // src/tui/views/Settings.tsx
21620
- import React24, { useState as useState14, useEffect as useEffect16 } from "react";
22095
+ import React24, { useState as useState14, useEffect as useEffect16, useCallback as useCallback7 } from "react";
21621
22096
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
22097
+ function maskSecret(value) {
22098
+ if (value.length <= 10) return "****";
22099
+ return `${value.slice(0, 6)}...${value.slice(-4)}`;
22100
+ }
21622
22101
  function SettingsView({ onBack }) {
21623
22102
  const [providers, setProviders] = useState14([]);
21624
- const [memory, setMemory] = useState14({
21625
- encryption: "checking...",
21626
- embeddingModel: "checking...",
21627
- cloudSync: "checking...",
21628
- searchMode: "checking..."
21629
- });
21630
- const [runtime, setRuntime] = useState14({
21631
- consolidation: true,
21632
- consolidationModel: "...",
21633
- skillLearning: true,
21634
- skillThreshold: 3,
21635
- skillModel: "...",
21636
- failoverChain: ["anthropic", "opencode", "gemini", "openai"]
21637
- });
21638
- const [employeeModels, setEmployeeModels] = useState14([]);
22103
+ const [cloud, setCloud] = useState14({ configured: false, endpoint: "", apiKey: "" });
22104
+ const [license, setLicense] = useState14({ valid: false, plan: "checking...", expiresAt: null, deviceLimit: 0, employeeLimit: 0, memoryLimit: 0 });
22105
+ const [system, setSystem] = useState14({ daemon: "unknown", version: "...", dbPath: "...", embeddingModel: "...", encryption: "checking..." });
22106
+ const [gateway, setGateway] = useState14({ adapters: [] });
21639
22107
  const [selectedSection, setSelectedSection] = useState14(0);
21640
22108
  const [loading, setLoading] = useState14(true);
21641
- const [claudeCode, setClaudeCode] = useState14({ installed: false, hooksWired: false });
21642
- const [license, setLicense] = useState14({ valid: false, detail: "checking..." });
21643
- const [cloudSync, setCloudSync] = useState14({ configured: false, detail: "checking..." });
21644
- useEffect16(() => {
21645
- loadSettings().finally(() => setLoading(false));
21646
- }, []);
21647
- use_input_default((_input, key) => {
21648
- if (key.leftArrow) {
21649
- onBack?.();
21650
- return;
21651
- }
21652
- if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
21653
- if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
21654
- });
21655
- async function loadSettings() {
21656
- const providerList = [
21657
- { name: "Anthropic", configured: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? "ANTHROPIC_API_KEY set" : "not configured" },
21658
- { name: "OpenCode", configured: !!process.env.OPENCODE_API_KEY, detail: process.env.OPENCODE_API_KEY ? "OPENCODE_API_KEY set" : "not configured" },
21659
- { name: "Gemini", configured: !!process.env.GEMINI_API_KEY, detail: process.env.GEMINI_API_KEY ? "GEMINI_API_KEY set" : "not configured" },
21660
- { name: "OpenAI", configured: !!process.env.OPENAI_API_KEY, detail: process.env.OPENAI_API_KEY ? "OPENAI_API_KEY set" : "not configured" },
21661
- { name: "Chutes", configured: !!process.env.CHUTES_API_KEY, detail: process.env.CHUTES_API_KEY ? "CHUTES_API_KEY set" : "not configured" }
22109
+ const loadSettings = useCallback7(async () => {
22110
+ setLoading(true);
22111
+ const envKeys = [
22112
+ ["Anthropic", "ANTHROPIC_API_KEY"],
22113
+ ["OpenCode", "OPENCODE_API_KEY"],
22114
+ ["Gemini", "GEMINI_API_KEY"],
22115
+ ["OpenAI", "OPENAI_API_KEY"],
22116
+ ["Chutes", "CHUTES_API_KEY"]
21662
22117
  ];
22118
+ const providerList = envKeys.map(([name, envVar]) => {
22119
+ const val = process.env[envVar];
22120
+ return {
22121
+ name,
22122
+ configured: !!val,
22123
+ detail: val ? maskSecret(val) : "not configured"
22124
+ };
22125
+ });
21663
22126
  try {
21664
22127
  const { execSync: execSync13 } = await import("child_process");
21665
22128
  execSync13("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
@@ -21671,112 +22134,101 @@ function SettingsView({ onBack }) {
21671
22134
  try {
21672
22135
  const { existsSync: existsSync22 } = await import("fs");
21673
22136
  const { join } = await import("path");
21674
- const home = process.env.HOME ?? "";
21675
- const hasKey = existsSync22(join(home, ".exe-os", "master.key"));
21676
22137
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21677
22138
  const cfg = await loadConfig2();
21678
- setMemory({
21679
- encryption: hasKey ? "SQLCipher AES-256" : "not configured",
21680
- embeddingModel: cfg.modelFile ?? "unknown",
21681
- cloudSync: cfg.cloud ? "enabled" : "disabled",
21682
- searchMode: cfg.searchMode ?? "hybrid"
21683
- });
21684
- const rawCfg = cfg;
21685
- const chain = Array.isArray(rawCfg.failoverChain) ? rawCfg.failoverChain : ["anthropic", "opencode", "gemini", "openai"];
21686
- setRuntime({
21687
- consolidation: cfg.consolidationEnabled,
21688
- consolidationModel: cfg.consolidationModel,
21689
- skillLearning: cfg.skillLearning,
21690
- skillThreshold: cfg.skillThreshold,
21691
- skillModel: cfg.skillModel,
21692
- failoverChain: chain
21693
- });
21694
- } catch {
21695
- }
21696
- try {
21697
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21698
- const roster = await loadEmployees2();
21699
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21700
- const { join } = await import("path");
21701
22139
  const home = process.env.HOME ?? "";
21702
- const configPath = join(home, ".exe-os", "config.json");
21703
- let empConfig = {};
21704
- if (existsSync22(configPath)) {
21705
- try {
21706
- const raw = JSON.parse(readFileSync18(configPath, "utf8"));
21707
- if (raw.employees && typeof raw.employees === "object") {
21708
- empConfig = raw.employees;
21709
- }
21710
- } catch {
21711
- }
22140
+ const hasKey = existsSync22(join(home, ".exe-os", "master.key"));
22141
+ if (cfg.cloud) {
22142
+ setCloud({
22143
+ configured: true,
22144
+ endpoint: cfg.cloud.endpoint,
22145
+ apiKey: maskSecret(cfg.cloud.apiKey)
22146
+ });
22147
+ } else {
22148
+ setCloud({ configured: false, endpoint: "", apiKey: "" });
21712
22149
  }
21713
- setEmployeeModels(roster.filter((e) => e.name !== "exe").map((e) => ({
21714
- name: e.name,
21715
- model: empConfig[e.name]?.model ?? "claude-sonnet-4-6",
21716
- provider: empConfig[e.name]?.provider ?? "anthropic"
21717
- })));
21718
- } catch {
21719
- }
21720
- try {
21721
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21722
- const { join } = await import("path");
21723
- const home = process.env.HOME ?? "";
21724
- const ccSettingsPath = join(home, ".claude", "settings.json");
21725
- const installed = existsSync22(ccSettingsPath);
21726
- let hooksWired = false;
21727
- if (installed) {
21728
- try {
21729
- const settings = JSON.parse(readFileSync18(ccSettingsPath, "utf8"));
21730
- const hooks = settings.hooks;
21731
- if (hooks) {
21732
- hooksWired = Object.values(hooks).flat().some((h) => h.command?.includes("exe-os") || h.command?.includes("exe-mem"));
21733
- }
21734
- } catch {
21735
- }
22150
+ const pidPath = join(home, ".exe-os", "exed.pid");
22151
+ let daemon = "unknown";
22152
+ try {
22153
+ daemon = existsSync22(pidPath) ? "running" : "stopped";
22154
+ } catch {
21736
22155
  }
21737
- setClaudeCode({ installed, hooksWired });
21738
- } catch {
21739
- }
21740
- try {
21741
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21742
- const { join } = await import("path");
21743
- const home = process.env.HOME ?? "";
21744
- const licensePath = join(home, ".exe-os", "license.json");
21745
- if (existsSync22(licensePath)) {
22156
+ let version = "unknown";
22157
+ try {
22158
+ const { readFileSync: readFileSync18 } = await import("fs");
22159
+ const { createRequire } = await import("module");
22160
+ const require2 = createRequire(import.meta.url);
22161
+ const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
22162
+ const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
22163
+ version = pkg.version;
22164
+ } catch {
21746
22165
  try {
21747
- const lic = JSON.parse(readFileSync18(licensePath, "utf8"));
21748
- setLicense({ valid: lic.valid !== false, detail: lic.plan ?? "licensed" });
22166
+ const { readFileSync: readFileSync18 } = await import("fs");
22167
+ const { join: joinPath } = await import("path");
22168
+ const pkg = JSON.parse(readFileSync18(joinPath(process.cwd(), "package.json"), "utf8"));
22169
+ version = pkg.version;
21749
22170
  } catch {
21750
- setLicense({ valid: false, detail: "invalid license file" });
21751
22171
  }
21752
- } else {
21753
- setLicense({ valid: false, detail: "no license" });
21754
22172
  }
22173
+ setSystem({
22174
+ daemon,
22175
+ version,
22176
+ dbPath: cfg.dbPath,
22177
+ embeddingModel: cfg.modelFile ?? "unknown",
22178
+ encryption: hasKey ? "SQLCipher AES-256" : "not configured"
22179
+ });
21755
22180
  } catch {
21756
22181
  }
21757
22182
  try {
21758
- const { existsSync: existsSync22 } = await import("fs");
21759
- const { join } = await import("path");
21760
- const home = process.env.HOME ?? "";
21761
- const cloudPath = join(home, ".exe-os", "cloud.json");
21762
- if (existsSync22(cloudPath)) {
21763
- setCloudSync({ configured: true, detail: "configured" });
21764
- } else {
21765
- setCloudSync({ configured: false, detail: "not configured" });
21766
- }
22183
+ const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
22184
+ const lic = await checkLicense2();
22185
+ setLicense({
22186
+ valid: lic.valid,
22187
+ plan: lic.plan.toUpperCase(),
22188
+ expiresAt: lic.expiresAt,
22189
+ deviceLimit: lic.deviceLimit,
22190
+ employeeLimit: lic.employeeLimit,
22191
+ memoryLimit: lic.memoryLimit
22192
+ });
21767
22193
  } catch {
22194
+ setLicense({ valid: false, plan: "FREE", expiresAt: null, deviceLimit: 1, employeeLimit: 1, memoryLimit: 5e3 });
21768
22195
  }
21769
- }
21770
- const statusColor = (ok) => ok ? "#22C55E" : "#6B4C9A";
21771
- const sectionColor = (idx) => selectedSection === idx ? "#F5D76E" : void 0;
21772
- const sectionBg = (idx) => selectedSection === idx ? "#6B4C9A" : void 0;
22196
+ const gatewayAdapters = [
22197
+ { name: "WhatsApp", configured: !!process.env.WHATSAPP_API_TOKEN || !!process.env.WHATSAPP_PHONE_NUMBER_ID },
22198
+ { name: "Telegram", configured: !!process.env.TELEGRAM_BOT_TOKEN },
22199
+ { name: "Discord", configured: !!process.env.DISCORD_BOT_TOKEN },
22200
+ { name: "Slack", configured: !!process.env.SLACK_BOT_TOKEN }
22201
+ ];
22202
+ setGateway({ adapters: gatewayAdapters });
22203
+ setLoading(false);
22204
+ }, []);
22205
+ useEffect16(() => {
22206
+ loadSettings();
22207
+ }, [loadSettings]);
22208
+ use_input_default((input, key) => {
22209
+ if (key.leftArrow) {
22210
+ onBack?.();
22211
+ return;
22212
+ }
22213
+ if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
22214
+ if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
22215
+ if (input === "r") {
22216
+ loadSettings();
22217
+ }
22218
+ });
22219
+ const statusDot = (ok) => ok ? "\u2022" : "\u2022";
22220
+ const statusColor = (ok) => ok ? GREEN : DIM;
22221
+ const sectionColor = (idx) => selectedSection === idx ? YELLOW : void 0;
22222
+ const sectionBg = (idx) => selectedSection === idx ? DIM : void 0;
21773
22223
  const sectionMarker = (idx) => selectedSection === idx ? "\u25B8 " : " ";
22224
+ const anyGateway = gateway.adapters.some((a) => a.configured);
22225
+ const formatLimit = (n) => n === -1 ? "unlimited" : n.toLocaleString();
21774
22226
  return /* @__PURE__ */ jsxs12(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
21775
22227
  /* @__PURE__ */ jsx14(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx14(Text, { bold: true, children: "Settings" }) }),
21776
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate sections" }),
22228
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "\u2191\u2193 navigate sections r refresh" }),
21777
22229
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21778
22230
  loading && /* @__PURE__ */ jsxs12(Fragment5, { children: [
21779
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "Loading settings..." }),
22231
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Loading settings..." }),
21780
22232
  /* @__PURE__ */ jsx14(Text, { children: " " })
21781
22233
  ] }),
21782
22234
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
@@ -21786,6 +22238,8 @@ function SettingsView({ onBack }) {
21786
22238
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21787
22239
  providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
21788
22240
  " ",
22241
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
22242
+ " ",
21789
22243
  p.name,
21790
22244
  ": ",
21791
22245
  /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
@@ -21793,131 +22247,274 @@ function SettingsView({ onBack }) {
21793
22247
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21794
22248
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
21795
22249
  sectionMarker(1),
21796
- "Memory"
22250
+ "Cloud Sync"
21797
22251
  ] }),
21798
22252
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21799
- /* @__PURE__ */ jsxs12(Text, { children: [
22253
+ cloud.configured ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
22254
+ /* @__PURE__ */ jsxs12(Text, { children: [
22255
+ " ",
22256
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: statusDot(true) }),
22257
+ " Status: ",
22258
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: "connected" })
22259
+ ] }),
22260
+ /* @__PURE__ */ jsxs12(Text, { children: [
22261
+ " ",
22262
+ "Endpoint: ",
22263
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: cloud.endpoint })
22264
+ ] }),
22265
+ /* @__PURE__ */ jsxs12(Text, { children: [
22266
+ " ",
22267
+ "API key: ",
22268
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: cloud.apiKey })
22269
+ ] })
22270
+ ] }) : /* @__PURE__ */ jsxs12(Text, { children: [
21800
22271
  " ",
21801
- "Encryption: ",
21802
- /* @__PURE__ */ jsx14(Text, { color: memory.encryption.includes("AES") ? "#22C55E" : "#6B4C9A", children: memory.encryption })
22272
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
22273
+ " ",
22274
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Not configured \u2014 run /exe-cloud" })
21803
22275
  ] }),
21804
- /* @__PURE__ */ jsxs12(Text, { children: [
21805
- " ",
21806
- "Embedding: ",
21807
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: memory.embeddingModel })
22276
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22277
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
22278
+ sectionMarker(2),
22279
+ "License"
21808
22280
  ] }),
22281
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
21809
22282
  /* @__PURE__ */ jsxs12(Text, { children: [
21810
22283
  " ",
21811
- "Cloud sync: ",
21812
- /* @__PURE__ */ jsx14(Text, { color: memory.cloudSync === "enabled" ? "#22C55E" : "#6B4C9A", children: memory.cloudSync })
22284
+ "Plan: ",
22285
+ /* @__PURE__ */ jsx14(Text, { color: license.plan === "FREE" ? YELLOW : GREEN, children: license.plan })
21813
22286
  ] }),
21814
- /* @__PURE__ */ jsxs12(Text, { children: [
22287
+ license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
21815
22288
  " ",
21816
- "Search mode: ",
21817
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: memory.searchMode })
21818
- ] }),
21819
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21820
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
21821
- sectionMarker(2),
21822
- "Runtime"
22289
+ "Expires: ",
22290
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
21823
22291
  ] }),
21824
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21825
22292
  /* @__PURE__ */ jsxs12(Text, { children: [
21826
22293
  " ",
21827
- "Consolidation: ",
21828
- /* @__PURE__ */ jsx14(Text, { color: runtime.consolidation ? "#22C55E" : "#6B4C9A", children: runtime.consolidation ? "enabled" : "disabled" }),
21829
- " (",
21830
- runtime.consolidationModel,
21831
- ")"
22294
+ "Devices: ",
22295
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
21832
22296
  ] }),
21833
22297
  /* @__PURE__ */ jsxs12(Text, { children: [
21834
22298
  " ",
21835
- "Skill learning: ",
21836
- /* @__PURE__ */ jsx14(Text, { color: runtime.skillLearning ? "#22C55E" : "#6B4C9A", children: runtime.skillLearning ? "enabled" : "disabled" }),
21837
- " (threshold: ",
21838
- runtime.skillThreshold,
21839
- ")"
22299
+ "Employees: ",
22300
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
21840
22301
  ] }),
21841
22302
  /* @__PURE__ */ jsxs12(Text, { children: [
21842
22303
  " ",
21843
- "Failover chain: ",
21844
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: runtime.failoverChain.join(" \u2192 ") })
22304
+ "Memory limit: ",
22305
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
21845
22306
  ] }),
21846
22307
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21847
22308
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
21848
22309
  sectionMarker(3),
21849
- "Employee Models"
22310
+ "System"
21850
22311
  ] }),
21851
22312
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21852
- loading ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21853
- " ",
21854
- "Loading..."
21855
- ] }) : employeeModels.length === 0 ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
22313
+ /* @__PURE__ */ jsxs12(Text, { children: [
21856
22314
  " ",
21857
- "No employees configured"
21858
- ] }) : employeeModels.map((e) => /* @__PURE__ */ jsxs12(Text, { children: [
22315
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: statusDot(system.daemon === "running") }),
22316
+ " Daemon: ",
22317
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: system.daemon })
22318
+ ] }),
22319
+ /* @__PURE__ */ jsxs12(Text, { children: [
21859
22320
  " ",
21860
- e.name,
21861
- ": ",
21862
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: e.model }),
21863
- " ",
21864
- /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21865
- "via ",
21866
- e.provider
21867
- ] })
21868
- ] }, e.name)),
21869
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21870
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
21871
- sectionMarker(4),
21872
- "Integrations"
22321
+ "Version: ",
22322
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
21873
22323
  ] }),
21874
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21875
22324
  /* @__PURE__ */ jsxs12(Text, { children: [
21876
22325
  " ",
21877
- "Claude Code: ",
21878
- /* @__PURE__ */ jsx14(Text, { color: claudeCode.installed ? "#22C55E" : "#6B4C9A", children: claudeCode.installed ? "installed" : "not found" }),
21879
- claudeCode.installed && /* @__PURE__ */ jsxs12(Text, { color: claudeCode.hooksWired ? "#22C55E" : "#6B4C9A", children: [
21880
- " \u2014 hooks ",
21881
- claudeCode.hooksWired ? "wired" : "not wired"
21882
- ] })
22326
+ "Encryption: ",
22327
+ /* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
21883
22328
  ] }),
21884
22329
  /* @__PURE__ */ jsxs12(Text, { children: [
21885
22330
  " ",
21886
- "License: ",
21887
- /* @__PURE__ */ jsx14(Text, { color: license.valid ? "#22C55E" : "#6B4C9A", children: license.detail })
22331
+ "Embedding: ",
22332
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
21888
22333
  ] }),
21889
22334
  /* @__PURE__ */ jsxs12(Text, { children: [
21890
22335
  " ",
21891
- "Cloud sync: ",
21892
- /* @__PURE__ */ jsx14(Text, { color: cloudSync.configured ? "#22C55E" : "#6B4C9A", children: cloudSync.detail })
22336
+ "DB path: ",
22337
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: system.dbPath })
22338
+ ] }),
22339
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22340
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
22341
+ sectionMarker(4),
22342
+ "Gateway"
22343
+ ] }),
22344
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22345
+ anyGateway ? gateway.adapters.map((a) => /* @__PURE__ */ jsxs12(Text, { children: [
22346
+ " ",
22347
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: statusDot(a.configured) }),
22348
+ " ",
22349
+ a.name,
22350
+ ": ",
22351
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: a.configured ? "configured" : "not configured" })
22352
+ ] }, a.name)) : /* @__PURE__ */ jsxs12(Text, { children: [
22353
+ " ",
22354
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
22355
+ " ",
22356
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "No gateway configured" })
21893
22357
  ] })
21894
22358
  ] });
21895
22359
  }
21896
- var SECTION_NAMES;
22360
+ var SECTION_NAMES, GREEN, DIM, YELLOW;
21897
22361
  var init_Settings = __esm({
21898
22362
  async "src/tui/views/Settings.tsx"() {
21899
22363
  "use strict";
21900
22364
  await init_ink2();
21901
- SECTION_NAMES = ["Providers", "Memory", "Runtime", "Employee Models", "Integrations"];
22365
+ SECTION_NAMES = ["Providers", "Cloud Sync", "License", "System", "Gateway"];
22366
+ GREEN = "#22C55E";
22367
+ DIM = "#6B4C9A";
22368
+ YELLOW = "#F5D76E";
22369
+ }
22370
+ });
22371
+
22372
+ // src/tui/views/DebugPanel.tsx
22373
+ import React25, { useState as useState15, useEffect as useEffect17 } from "react";
22374
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
22375
+ function formatEvent(e) {
22376
+ switch (e.type) {
22377
+ case "task_completed":
22378
+ return `${e.employee}: "${e.result.slice(0, 60)}"`;
22379
+ case "review_created":
22380
+ return `${e.employee} \u2192 ${e.reviewer}`;
22381
+ case "employee_status":
22382
+ return `${e.employee}: ${e.status}`;
22383
+ case "memory_stored":
22384
+ return `${e.agentId} [${e.project}]`;
22385
+ case "session_started":
22386
+ return `${e.employee} (${e.sessionId.slice(0, 8)})`;
22387
+ case "session_ended":
22388
+ return `${e.employee} (${e.sessionId.slice(0, 8)})`;
22389
+ case "gateway_message":
22390
+ return `${e.platform}/${e.senderId} \u2192 ${e.botId}`;
22391
+ }
22392
+ }
22393
+ function DebugPanel() {
22394
+ const [events, setEvents] = useState15([]);
22395
+ useEffect17(() => {
22396
+ let counter = 0;
22397
+ const handler = (event) => {
22398
+ setEvents((prev) => {
22399
+ const next = [...prev, { event, id: counter++ }];
22400
+ return next.slice(-MAX_EVENTS);
22401
+ });
22402
+ };
22403
+ orgBus.onAny(handler);
22404
+ return () => {
22405
+ orgBus.offAny(handler);
22406
+ };
22407
+ }, []);
22408
+ return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", borderStyle: "single", borderColor: "#6B4C9A", flexGrow: 1, paddingX: 1, children: [
22409
+ /* @__PURE__ */ jsx15(Text, { bold: true, color: "#F5D76E", children: "Debug \u2014 Live Events" }),
22410
+ /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "\u2500".repeat(40) }),
22411
+ events.length === 0 ? /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "Waiting for events..." }) : events.slice(-DISPLAY_EVENTS).map(({ event, id }) => {
22412
+ const time = event.timestamp?.slice(11, 19) ?? "??:??:??";
22413
+ const color = EVENT_COLORS[event.type] ?? "#6B4C9A";
22414
+ const summary = formatEvent(event);
22415
+ return /* @__PURE__ */ jsxs13(Text, { color, children: [
22416
+ "[",
22417
+ time,
22418
+ "] ",
22419
+ event.type,
22420
+ " \u2014 ",
22421
+ summary
22422
+ ] }, id);
22423
+ })
22424
+ ] });
22425
+ }
22426
+ var MAX_EVENTS, DISPLAY_EVENTS, EVENT_COLORS;
22427
+ var init_DebugPanel = __esm({
22428
+ async "src/tui/views/DebugPanel.tsx"() {
22429
+ "use strict";
22430
+ await init_ink2();
22431
+ init_state_bus();
22432
+ MAX_EVENTS = 50;
22433
+ DISPLAY_EVENTS = 20;
22434
+ EVENT_COLORS = {
22435
+ task_completed: "#22C55E",
22436
+ review_created: "#F59E0B",
22437
+ employee_status: "#3B82F6",
22438
+ memory_stored: "#8B5CF6",
22439
+ session_started: "#06B6D4",
22440
+ session_ended: "#EF4444",
22441
+ gateway_message: "#EC4899"
22442
+ };
22443
+ }
22444
+ });
22445
+
22446
+ // src/tui/components/HelpOverlay.tsx
22447
+ import React26 from "react";
22448
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
22449
+ function HelpOverlay({ tabName, shortcuts }) {
22450
+ return /* @__PURE__ */ jsxs14(
22451
+ Box_default,
22452
+ {
22453
+ flexDirection: "column",
22454
+ borderStyle: "double",
22455
+ borderColor: "#6B4C9A",
22456
+ paddingX: 2,
22457
+ paddingY: 1,
22458
+ width: 50,
22459
+ alignSelf: "center",
22460
+ children: [
22461
+ /* @__PURE__ */ jsxs14(Text, { bold: true, color: "#F5D76E", children: [
22462
+ "Keyboard Shortcuts \u2014 ",
22463
+ tabName
22464
+ ] }),
22465
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22466
+ shortcuts.map((s, i) => /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22467
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: s.key.padEnd(10) }),
22468
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: s.description })
22469
+ ] }, i)),
22470
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22471
+ /* @__PURE__ */ jsx16(Text, { bold: true, color: "#F5D76E", children: "Global" }),
22472
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22473
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22474
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "1-7".padEnd(10) }),
22475
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Switch tabs" })
22476
+ ] }),
22477
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22478
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "q".padEnd(10) }),
22479
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Quit" })
22480
+ ] }),
22481
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22482
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "?".padEnd(10) }),
22483
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Toggle this help" })
22484
+ ] }),
22485
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22486
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "Esc".padEnd(10) }),
22487
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Back to sidebar" })
22488
+ ] }),
22489
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22490
+ /* @__PURE__ */ jsx16(Text, { color: "#6B4C9A", children: "Press ? to close" })
22491
+ ]
22492
+ }
22493
+ );
22494
+ }
22495
+ var init_HelpOverlay = __esm({
22496
+ async "src/tui/components/HelpOverlay.tsx"() {
22497
+ "use strict";
22498
+ await init_ink2();
21902
22499
  }
21903
22500
  });
21904
22501
 
21905
22502
  // src/tui/App.tsx
21906
22503
  var App_exports = {};
21907
- import { useState as useState15, useEffect as useEffect17, useCallback as useCallback7 } from "react";
21908
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
22504
+ import { useState as useState16, useEffect as useEffect18, useCallback as useCallback8 } from "react";
22505
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
21909
22506
  function App2() {
21910
- const [section, setSection] = useState15("command-center");
22507
+ const [section, setSection] = useState16("command-center");
21911
22508
  const { exit } = use_app_default();
21912
- const [, forceUpdate] = useState15(0);
21913
- useEffect17(() => {
22509
+ const [, forceUpdate] = useState16(0);
22510
+ useEffect18(() => {
21914
22511
  const handleResize = () => forceUpdate((n) => n + 1);
21915
22512
  process.stdout.on("resize", handleResize);
21916
22513
  return () => {
21917
22514
  process.stdout.off("resize", handleResize);
21918
22515
  };
21919
22516
  }, []);
21920
- useMouseEvent(useCallback7((event) => {
22517
+ useMouseEvent(useCallback8((event) => {
21921
22518
  if (event.button !== 0) return;
21922
22519
  if (event.col <= 26) {
21923
22520
  const tabIdx = event.row - 4;
@@ -21929,22 +22526,33 @@ function App2() {
21929
22526
  setFocus("content");
21930
22527
  }
21931
22528
  }, []));
21932
- const [focus, setFocus] = useState15("sidebar");
21933
- const [focusedProject, setFocusedProject] = useState15(null);
21934
- const handleSelectProject = useCallback7((projectName) => {
22529
+ const [focus, setFocus] = useState16("sidebar");
22530
+ const [showDebug, setShowDebug] = useState16(false);
22531
+ const [showHelp, setShowHelp] = useState16(false);
22532
+ const [focusedProject, setFocusedProject] = useState16(null);
22533
+ const handleSelectProject = useCallback8((projectName) => {
21935
22534
  setFocusedProject(projectName);
21936
22535
  setSection("sessions");
21937
22536
  setFocus("content");
21938
22537
  }, []);
21939
- const handleBackToCommandCenter = useCallback7(() => {
22538
+ const handleBackToCommandCenter = useCallback8(() => {
21940
22539
  setSection("command-center");
21941
22540
  setFocus("sidebar");
21942
22541
  }, []);
21943
22542
  use_input_default((input, key) => {
22543
+ if (input === "?" || key.shift && input === "/") {
22544
+ setShowHelp((prev) => !prev);
22545
+ return;
22546
+ }
21944
22547
  const idx = parseInt(input, 10);
21945
22548
  if (idx >= 1 && idx <= SECTIONS.length) {
21946
22549
  setSection(SECTIONS[idx - 1].key);
21947
22550
  setFocus("sidebar");
22551
+ setShowHelp(false);
22552
+ return;
22553
+ }
22554
+ if (input === "d" && !key.ctrl) {
22555
+ setShowDebug((prev) => !prev);
21948
22556
  return;
21949
22557
  }
21950
22558
  if (input === "q" && !key.ctrl) {
@@ -21972,27 +22580,37 @@ function App2() {
21972
22580
  }
21973
22581
  }
21974
22582
  });
21975
- const consumeFocusedProject = useCallback7(() => {
22583
+ const consumeFocusedProject = useCallback8(() => {
21976
22584
  setFocusedProject(null);
21977
22585
  }, []);
21978
22586
  const views = {
21979
- "command-center": /* @__PURE__ */ jsx15(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
21980
- sessions: /* @__PURE__ */ jsx15(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
21981
- tasks: /* @__PURE__ */ jsx15(TasksView, { onBack: () => setFocus("sidebar") }),
21982
- team: /* @__PURE__ */ jsx15(TeamView, { onBack: () => setFocus("sidebar") }),
21983
- gateway: /* @__PURE__ */ jsx15(GatewayView, { onBack: () => setFocus("sidebar") }),
21984
- wiki: /* @__PURE__ */ jsx15(WikiView, { onBack: () => setFocus("sidebar") }),
21985
- settings: /* @__PURE__ */ jsx15(SettingsView, { onBack: () => setFocus("sidebar") })
22587
+ "command-center": /* @__PURE__ */ jsx17(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
22588
+ sessions: /* @__PURE__ */ jsx17(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
22589
+ tasks: /* @__PURE__ */ jsx17(TasksView, { onBack: () => setFocus("sidebar") }),
22590
+ team: /* @__PURE__ */ jsx17(TeamView, { onBack: () => setFocus("sidebar"), onViewSessions: (name) => {
22591
+ setFocusedProject(name);
22592
+ setSection("sessions");
22593
+ setFocus("content");
22594
+ } }),
22595
+ gateway: /* @__PURE__ */ jsx17(GatewayView, { onBack: () => setFocus("sidebar") }),
22596
+ wiki: /* @__PURE__ */ jsx17(WikiView, { onBack: () => setFocus("sidebar") }),
22597
+ settings: /* @__PURE__ */ jsx17(SettingsView, { onBack: () => setFocus("sidebar") })
21986
22598
  };
21987
- return /* @__PURE__ */ jsx15(ErrorBoundary2, { children: /* @__PURE__ */ jsx15(AlternateScreen, { children: /* @__PURE__ */ jsx15(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", flexGrow: 1, children: [
21988
- /* @__PURE__ */ jsxs13(Box_default, { flexGrow: 1, children: [
21989
- /* @__PURE__ */ jsx15(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
21990
- views[section]
22599
+ return /* @__PURE__ */ jsx17(ErrorBoundary2, { children: /* @__PURE__ */ jsx17(AlternateScreen, { children: /* @__PURE__ */ jsx17(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs15(Box_default, { flexDirection: "column", flexGrow: 1, children: [
22600
+ /* @__PURE__ */ jsxs15(Box_default, { flexGrow: 1, children: [
22601
+ /* @__PURE__ */ jsx17(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
22602
+ showHelp ? /* @__PURE__ */ jsx17(
22603
+ HelpOverlay,
22604
+ {
22605
+ tabName: TAB_SHORTCUTS[section].name,
22606
+ shortcuts: TAB_SHORTCUTS[section].shortcuts
22607
+ }
22608
+ ) : showDebug ? /* @__PURE__ */ jsx17(DebugPanel, {}) : views[section]
21991
22609
  ] }),
21992
- /* @__PURE__ */ jsx15(Footer, {})
22610
+ /* @__PURE__ */ jsx17(Footer, {})
21993
22611
  ] }) }) }) });
21994
22612
  }
21995
- var isDemo;
22613
+ var TAB_SHORTCUTS, isDemo;
21996
22614
  var init_App2 = __esm({
21997
22615
  async "src/tui/App.tsx"() {
21998
22616
  "use strict";
@@ -22010,6 +22628,73 @@ var init_App2 = __esm({
22010
22628
  await init_Team();
22011
22629
  await init_Wiki();
22012
22630
  await init_Settings();
22631
+ await init_DebugPanel();
22632
+ await init_HelpOverlay();
22633
+ TAB_SHORTCUTS = {
22634
+ "command-center": {
22635
+ name: "Command Center",
22636
+ shortcuts: [
22637
+ { key: "\u2191\u2193", description: "Navigate projects" },
22638
+ { key: "Enter", description: "Select project" },
22639
+ { key: "/", description: "Enter chat mode" },
22640
+ { key: "n", description: "New task" }
22641
+ ]
22642
+ },
22643
+ sessions: {
22644
+ name: "Sessions",
22645
+ shortcuts: [
22646
+ { key: "\u2191\u2193", description: "Navigate sessions" },
22647
+ { key: "Enter", description: "View session output" },
22648
+ { key: "k", description: "Kill session" },
22649
+ { key: "r", description: "Restart agent" }
22650
+ ]
22651
+ },
22652
+ tasks: {
22653
+ name: "Tasks",
22654
+ shortcuts: [
22655
+ { key: "\u2191\u2193", description: "Navigate tasks" },
22656
+ { key: "Enter", description: "View task detail" },
22657
+ { key: "n", description: "Create new task" },
22658
+ { key: "f", description: "Cycle status filter" },
22659
+ { key: "p", description: "Filter by priority" }
22660
+ ]
22661
+ },
22662
+ team: {
22663
+ name: "Team",
22664
+ shortcuts: [
22665
+ { key: "\u2191\u2193", description: "Navigate employees" },
22666
+ { key: "Enter", description: "View detail" },
22667
+ { key: "a", description: "Add employee" },
22668
+ { key: "s", description: "View sessions" }
22669
+ ]
22670
+ },
22671
+ gateway: {
22672
+ name: "Gateway",
22673
+ shortcuts: [
22674
+ { key: "\u2191\u2193", description: "Navigate sections" },
22675
+ { key: "f", description: "Cycle platform filter" },
22676
+ { key: "\u2192", description: "Next conversation" },
22677
+ { key: "r", description: "Reconnect" }
22678
+ ]
22679
+ },
22680
+ wiki: {
22681
+ name: "Wiki",
22682
+ shortcuts: [
22683
+ { key: "\u2190\u2192", description: "Switch panels" },
22684
+ { key: "\u2191\u2193", description: "Navigate items" },
22685
+ { key: "/", description: "Search memories" },
22686
+ { key: "Enter", description: "Select" }
22687
+ ]
22688
+ },
22689
+ settings: {
22690
+ name: "Settings",
22691
+ shortcuts: [
22692
+ { key: "\u2191\u2193", description: "Navigate sections" },
22693
+ { key: "Enter", description: "Edit setting" },
22694
+ { key: "r", description: "Refresh status" }
22695
+ ]
22696
+ }
22697
+ };
22013
22698
  isDemo = process.argv.includes("--demo");
22014
22699
  process.stderr.write(`[exe-tui] Terminal: ${TERMINAL_TYPE}
22015
22700
  `);
@@ -22050,7 +22735,7 @@ var init_App2 = __esm({
22050
22735
  stdin.unref = () => stdin;
22051
22736
  }
22052
22737
  }
22053
- render_default(/* @__PURE__ */ jsx15(App2, {}));
22738
+ render_default(/* @__PURE__ */ jsx17(App2, {}));
22054
22739
  }
22055
22740
  });
22056
22741