@askexenow/exe-os 0.8.85 → 0.8.87

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 (57) hide show
  1. package/dist/bin/cleanup-stale-review-tasks.js +57 -19
  2. package/dist/bin/cli.js +510 -340
  3. package/dist/bin/exe-agent-config.js +242 -0
  4. package/dist/bin/exe-agent.js +3 -3
  5. package/dist/bin/exe-boot.js +344 -346
  6. package/dist/bin/exe-dispatch.js +375 -250
  7. package/dist/bin/exe-forget.js +5 -1
  8. package/dist/bin/exe-gateway.js +260 -135
  9. package/dist/bin/exe-healthcheck.js +133 -1
  10. package/dist/bin/exe-heartbeat.js +72 -31
  11. package/dist/bin/exe-link.js +25 -2
  12. package/dist/bin/exe-new-employee.js +22 -0
  13. package/dist/bin/exe-pending-messages.js +55 -17
  14. package/dist/bin/exe-pending-reviews.js +57 -19
  15. package/dist/bin/exe-search.js +6 -2
  16. package/dist/bin/exe-session-cleanup.js +260 -135
  17. package/dist/bin/exe-start-codex.js +2598 -0
  18. package/dist/bin/exe-start.sh +15 -3
  19. package/dist/bin/exe-status.js +57 -19
  20. package/dist/bin/git-sweep.js +391 -266
  21. package/dist/bin/install.js +22 -0
  22. package/dist/bin/scan-tasks.js +394 -269
  23. package/dist/bin/setup.js +50 -5
  24. package/dist/gateway/index.js +257 -132
  25. package/dist/hooks/bug-report-worker.js +242 -117
  26. package/dist/hooks/commit-complete.js +389 -264
  27. package/dist/hooks/error-recall.js +6 -2
  28. package/dist/hooks/ingest-worker.js +314 -193
  29. package/dist/hooks/post-compact.js +84 -46
  30. package/dist/hooks/pre-compact.js +272 -147
  31. package/dist/hooks/pre-tool-use.js +104 -66
  32. package/dist/hooks/prompt-submit.js +126 -66
  33. package/dist/hooks/session-end.js +277 -152
  34. package/dist/hooks/session-start.js +70 -28
  35. package/dist/hooks/stop.js +90 -52
  36. package/dist/hooks/subagent-stop.js +84 -46
  37. package/dist/hooks/summary-worker.js +175 -114
  38. package/dist/index.js +296 -171
  39. package/dist/lib/agent-config.js +167 -0
  40. package/dist/lib/cloud-sync.js +25 -2
  41. package/dist/lib/exe-daemon.js +338 -213
  42. package/dist/lib/hybrid-search.js +7 -2
  43. package/dist/lib/messaging.js +95 -39
  44. package/dist/lib/runtime-table.js +16 -0
  45. package/dist/lib/session-wrappers.js +22 -0
  46. package/dist/lib/tasks.js +242 -117
  47. package/dist/lib/tmux-routing.js +314 -189
  48. package/dist/mcp/server.js +573 -274
  49. package/dist/mcp/tools/create-task.js +260 -135
  50. package/dist/mcp/tools/list-tasks.js +68 -30
  51. package/dist/mcp/tools/send-message.js +100 -44
  52. package/dist/mcp/tools/update-task.js +123 -67
  53. package/dist/runtime/index.js +276 -151
  54. package/dist/tui/App.js +479 -354
  55. package/package.json +1 -1
  56. package/src/commands/exe/agent-config.md +27 -0
  57. package/src/commands/exe/cc-doctor.md +10 -0
@@ -412,39 +412,75 @@ var init_provider_table = __esm({
412
412
  }
413
413
  });
414
414
 
415
- // src/lib/intercom-queue.ts
416
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync4, mkdirSync } from "fs";
415
+ // src/lib/runtime-table.ts
416
+ var RUNTIME_TABLE;
417
+ var init_runtime_table = __esm({
418
+ "src/lib/runtime-table.ts"() {
419
+ "use strict";
420
+ RUNTIME_TABLE = {
421
+ codex: {
422
+ binary: "codex",
423
+ launchMode: "exec",
424
+ autoApproveFlag: "--full-auto",
425
+ inlineFlag: "--no-alt-screen",
426
+ apiKeyEnv: "OPENAI_API_KEY",
427
+ defaultModel: "gpt-5.4"
428
+ }
429
+ };
430
+ }
431
+ });
432
+
433
+ // src/lib/agent-config.ts
434
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync } from "fs";
417
435
  import path5 from "path";
436
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
437
+ var init_agent_config = __esm({
438
+ "src/lib/agent-config.ts"() {
439
+ "use strict";
440
+ init_config();
441
+ init_runtime_table();
442
+ AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
443
+ DEFAULT_MODELS = {
444
+ claude: "claude-opus-4",
445
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
446
+ opencode: "minimax-m2.7"
447
+ };
448
+ }
449
+ });
450
+
451
+ // src/lib/intercom-queue.ts
452
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
453
+ import path6 from "path";
418
454
  import os5 from "os";
419
455
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
420
456
  var init_intercom_queue = __esm({
421
457
  "src/lib/intercom-queue.ts"() {
422
458
  "use strict";
423
- QUEUE_PATH = path5.join(os5.homedir(), ".exe-os", "intercom-queue.json");
459
+ QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
424
460
  TTL_MS = 60 * 60 * 1e3;
425
- INTERCOM_LOG = path5.join(os5.homedir(), ".exe-os", "intercom.log");
461
+ INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
426
462
  }
427
463
  });
428
464
 
429
465
  // src/lib/license.ts
430
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
466
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
431
467
  import { randomUUID } from "crypto";
432
- import path6 from "path";
468
+ import path7 from "path";
433
469
  import { jwtVerify, importSPKI } from "jose";
434
470
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
435
471
  var init_license = __esm({
436
472
  "src/lib/license.ts"() {
437
473
  "use strict";
438
474
  init_config();
439
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
440
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
441
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
475
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
476
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
477
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
442
478
  }
443
479
  });
444
480
 
445
481
  // src/lib/plan-limits.ts
446
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
447
- import path7 from "path";
482
+ import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
483
+ import path8 from "path";
448
484
  var CACHE_PATH2;
449
485
  var init_plan_limits = __esm({
450
486
  "src/lib/plan-limits.ts"() {
@@ -453,13 +489,13 @@ var init_plan_limits = __esm({
453
489
  init_employees();
454
490
  init_license();
455
491
  init_config();
456
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
492
+ CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
457
493
  }
458
494
  });
459
495
 
460
496
  // src/lib/tmux-routing.ts
461
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync7, appendFileSync } from "fs";
462
- import path8 from "path";
497
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync8, appendFileSync } from "fs";
498
+ import path9 from "path";
463
499
  import os6 from "os";
464
500
  import { fileURLToPath } from "url";
465
501
  function getMySession() {
@@ -473,7 +509,7 @@ function extractRootExe(name) {
473
509
  }
474
510
  function getParentExe(sessionKey) {
475
511
  try {
476
- const data = JSON.parse(readFileSync7(path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
512
+ const data = JSON.parse(readFileSync8(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
477
513
  return data.parentExe || null;
478
514
  } catch {
479
515
  return null;
@@ -502,13 +538,15 @@ var init_tmux_routing = __esm({
502
538
  init_cc_agent_support();
503
539
  init_mcp_prefix();
504
540
  init_provider_table();
541
+ init_agent_config();
542
+ init_runtime_table();
505
543
  init_intercom_queue();
506
544
  init_plan_limits();
507
545
  init_employees();
508
- SPAWN_LOCK_DIR = path8.join(os6.homedir(), ".exe-os", "spawn-locks");
509
- SESSION_CACHE = path8.join(os6.homedir(), ".exe-os", "session-cache");
510
- INTERCOM_LOG2 = path8.join(os6.homedir(), ".exe-os", "intercom.log");
511
- DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
546
+ SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
547
+ SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
548
+ INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
549
+ DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
512
550
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
513
551
  }
514
552
  });
@@ -539,11 +577,11 @@ var init_task_scope = __esm({
539
577
 
540
578
  // src/lib/tasks-crud.ts
541
579
  import crypto2 from "crypto";
542
- import path9 from "path";
580
+ import path10 from "path";
543
581
  import os7 from "os";
544
582
  import { execSync as execSync4 } from "child_process";
545
583
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
546
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
584
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
547
585
  function buildKeywordIndex() {
548
586
  const idx = /* @__PURE__ */ new Map();
549
587
  for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
@@ -625,8 +663,8 @@ var init_tasks_crud = __esm({
625
663
  });
626
664
 
627
665
  // src/lib/tasks-review.ts
628
- import path10 from "path";
629
- import { existsSync as existsSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
666
+ import path11 from "path";
667
+ import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
630
668
  var init_tasks_review = __esm({
631
669
  "src/lib/tasks-review.ts"() {
632
670
  "use strict";
@@ -641,7 +679,7 @@ var init_tasks_review = __esm({
641
679
  });
642
680
 
643
681
  // src/lib/tasks-chain.ts
644
- import path11 from "path";
682
+ import path12 from "path";
645
683
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
646
684
  var init_tasks_chain = __esm({
647
685
  "src/lib/tasks-chain.ts"() {
@@ -665,8 +703,8 @@ var init_tasks_notify = __esm({
665
703
  });
666
704
 
667
705
  // src/lib/tasks.ts
668
- import path12 from "path";
669
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
706
+ import path13 from "path";
707
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
670
708
  var init_tasks = __esm({
671
709
  "src/lib/tasks.ts"() {
672
710
  "use strict";
@@ -686,7 +724,7 @@ var init_tasks = __esm({
686
724
 
687
725
  // src/lib/project-name.ts
688
726
  import { execSync as execSync5 } from "child_process";
689
- import path13 from "path";
727
+ import path14 from "path";
690
728
  function getProjectName(cwd) {
691
729
  const dir = cwd ?? process.cwd();
692
730
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -699,7 +737,7 @@ function getProjectName(cwd) {
699
737
  timeout: 2e3,
700
738
  stdio: ["pipe", "pipe", "pipe"]
701
739
  }).trim();
702
- repoRoot = path13.dirname(gitCommonDir);
740
+ repoRoot = path14.dirname(gitCommonDir);
703
741
  } catch {
704
742
  repoRoot = execSync5("git rev-parse --show-toplevel", {
705
743
  cwd: dir,
@@ -708,11 +746,11 @@ function getProjectName(cwd) {
708
746
  stdio: ["pipe", "pipe", "pipe"]
709
747
  }).trim();
710
748
  }
711
- _cached2 = path13.basename(repoRoot);
749
+ _cached2 = path14.basename(repoRoot);
712
750
  _cachedCwd = dir;
713
751
  return _cached2;
714
752
  } catch {
715
- _cached2 = path13.basename(dir);
753
+ _cached2 = path14.basename(dir);
716
754
  _cachedCwd = dir;
717
755
  return _cached2;
718
756
  }
@@ -364,18 +364,54 @@ var init_provider_table = __esm({
364
364
  }
365
365
  });
366
366
 
367
- // src/lib/intercom-queue.ts
368
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync3, mkdirSync } from "fs";
367
+ // src/lib/runtime-table.ts
368
+ var RUNTIME_TABLE;
369
+ var init_runtime_table = __esm({
370
+ "src/lib/runtime-table.ts"() {
371
+ "use strict";
372
+ RUNTIME_TABLE = {
373
+ codex: {
374
+ binary: "codex",
375
+ launchMode: "exec",
376
+ autoApproveFlag: "--full-auto",
377
+ inlineFlag: "--no-alt-screen",
378
+ apiKeyEnv: "OPENAI_API_KEY",
379
+ defaultModel: "gpt-5.4"
380
+ }
381
+ };
382
+ }
383
+ });
384
+
385
+ // src/lib/agent-config.ts
386
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync } from "fs";
369
387
  import path4 from "path";
388
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
389
+ var init_agent_config = __esm({
390
+ "src/lib/agent-config.ts"() {
391
+ "use strict";
392
+ init_config();
393
+ init_runtime_table();
394
+ AGENT_CONFIG_PATH = path4.join(EXE_AI_DIR, "agent-config.json");
395
+ DEFAULT_MODELS = {
396
+ claude: "claude-opus-4",
397
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
398
+ opencode: "minimax-m2.7"
399
+ };
400
+ }
401
+ });
402
+
403
+ // src/lib/intercom-queue.ts
404
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
405
+ import path5 from "path";
370
406
  import os4 from "os";
371
407
  function ensureDir() {
372
- const dir = path4.dirname(QUEUE_PATH);
373
- if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
408
+ const dir = path5.dirname(QUEUE_PATH);
409
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
374
410
  }
375
411
  function readQueue() {
376
412
  try {
377
- if (!existsSync3(QUEUE_PATH)) return [];
378
- return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
413
+ if (!existsSync4(QUEUE_PATH)) return [];
414
+ return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
379
415
  } catch {
380
416
  return [];
381
417
  }
@@ -383,7 +419,7 @@ function readQueue() {
383
419
  function writeQueue(queue) {
384
420
  ensureDir();
385
421
  const tmp = `${QUEUE_PATH}.tmp`;
386
- writeFileSync2(tmp, JSON.stringify(queue, null, 2));
422
+ writeFileSync3(tmp, JSON.stringify(queue, null, 2));
387
423
  renameSync3(tmp, QUEUE_PATH);
388
424
  }
389
425
  function queueIntercom(targetSession, reason) {
@@ -407,31 +443,31 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
407
443
  var init_intercom_queue = __esm({
408
444
  "src/lib/intercom-queue.ts"() {
409
445
  "use strict";
410
- QUEUE_PATH = path4.join(os4.homedir(), ".exe-os", "intercom-queue.json");
446
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
411
447
  TTL_MS = 60 * 60 * 1e3;
412
- INTERCOM_LOG = path4.join(os4.homedir(), ".exe-os", "intercom.log");
448
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
413
449
  }
414
450
  });
415
451
 
416
452
  // src/lib/license.ts
417
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
453
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
418
454
  import { randomUUID } from "crypto";
419
- import path5 from "path";
455
+ import path6 from "path";
420
456
  import { jwtVerify, importSPKI } from "jose";
421
457
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
422
458
  var init_license = __esm({
423
459
  "src/lib/license.ts"() {
424
460
  "use strict";
425
461
  init_config();
426
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
427
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
428
- DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
462
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
463
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
464
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
429
465
  }
430
466
  });
431
467
 
432
468
  // src/lib/plan-limits.ts
433
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
434
- import path6 from "path";
469
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
470
+ import path7 from "path";
435
471
  var CACHE_PATH2;
436
472
  var init_plan_limits = __esm({
437
473
  "src/lib/plan-limits.ts"() {
@@ -440,13 +476,13 @@ var init_plan_limits = __esm({
440
476
  init_employees();
441
477
  init_license();
442
478
  init_config();
443
- CACHE_PATH2 = path6.join(EXE_AI_DIR, "license-cache.json");
479
+ CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
444
480
  }
445
481
  });
446
482
 
447
483
  // src/lib/tmux-routing.ts
448
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync6, appendFileSync } from "fs";
449
- import path7 from "path";
484
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync7, appendFileSync } from "fs";
485
+ import path8 from "path";
450
486
  import os5 from "os";
451
487
  import { fileURLToPath } from "url";
452
488
  function getMySession() {
@@ -487,7 +523,7 @@ function extractRootExe(name) {
487
523
  }
488
524
  function getParentExe(sessionKey) {
489
525
  try {
490
- const data = JSON.parse(readFileSync6(path7.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
526
+ const data = JSON.parse(readFileSync7(path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
491
527
  return data.parentExe || null;
492
528
  } catch {
493
529
  return null;
@@ -511,32 +547,50 @@ function isEmployeeAlive(sessionName) {
511
547
  }
512
548
  function readDebounceState() {
513
549
  try {
514
- if (!existsSync6(DEBOUNCE_FILE)) return {};
515
- return JSON.parse(readFileSync6(DEBOUNCE_FILE, "utf8"));
550
+ if (!existsSync7(DEBOUNCE_FILE)) return {};
551
+ const raw = JSON.parse(readFileSync7(DEBOUNCE_FILE, "utf8"));
552
+ const state = {};
553
+ for (const [key, val] of Object.entries(raw)) {
554
+ if (typeof val === "number") {
555
+ state[key] = { lastSent: val, pending: 0 };
556
+ } else if (val && typeof val === "object" && "lastSent" in val) {
557
+ state[key] = val;
558
+ }
559
+ }
560
+ return state;
516
561
  } catch {
517
562
  return {};
518
563
  }
519
564
  }
520
565
  function writeDebounceState(state) {
521
566
  try {
522
- if (!existsSync6(SESSION_CACHE)) mkdirSync3(SESSION_CACHE, { recursive: true });
523
- writeFileSync4(DEBOUNCE_FILE, JSON.stringify(state));
567
+ if (!existsSync7(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
568
+ writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
524
569
  } catch {
525
570
  }
526
571
  }
527
572
  function isDebounced(targetSession) {
528
573
  const state = readDebounceState();
529
- const lastSent = state[targetSession] ?? 0;
530
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
574
+ const entry = state[targetSession];
575
+ const lastSent = entry?.lastSent ?? 0;
576
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
577
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
578
+ state[targetSession].pending++;
579
+ writeDebounceState(state);
580
+ return true;
581
+ }
582
+ return false;
531
583
  }
532
584
  function recordDebounce(targetSession) {
533
585
  const state = readDebounceState();
534
- state[targetSession] = Date.now();
586
+ const batched = state[targetSession]?.pending ?? 0;
587
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
535
588
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
536
589
  for (const key of Object.keys(state)) {
537
- if ((state[key] ?? 0) < cutoff) delete state[key];
590
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
538
591
  }
539
592
  writeDebounceState(state);
593
+ return batched;
540
594
  }
541
595
  function logIntercom(msg) {
542
596
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -577,7 +631,7 @@ function sendIntercom(targetSession) {
577
631
  return "skipped_exe";
578
632
  }
579
633
  if (isDebounced(targetSession)) {
580
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
634
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
581
635
  return "debounced";
582
636
  }
583
637
  try {
@@ -589,14 +643,14 @@ function sendIntercom(targetSession) {
589
643
  const sessionState = getSessionState(targetSession);
590
644
  if (sessionState === "no_claude") {
591
645
  queueIntercom(targetSession, "claude not running in session");
592
- recordDebounce(targetSession);
593
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
646
+ const batched2 = recordDebounce(targetSession);
647
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
594
648
  return "queued";
595
649
  }
596
650
  if (sessionState === "thinking" || sessionState === "tool") {
597
651
  queueIntercom(targetSession, "session busy at send time");
598
- recordDebounce(targetSession);
599
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
652
+ const batched2 = recordDebounce(targetSession);
653
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
600
654
  return "queued";
601
655
  }
602
656
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -604,8 +658,8 @@ function sendIntercom(targetSession) {
604
658
  transport.sendKeys(targetSession, "q");
605
659
  }
606
660
  transport.sendKeys(targetSession, "/exe-intercom");
607
- recordDebounce(targetSession);
608
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
661
+ const batched = recordDebounce(targetSession);
662
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
609
663
  return "delivered";
610
664
  } catch {
611
665
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -622,15 +676,17 @@ var init_tmux_routing = __esm({
622
676
  init_cc_agent_support();
623
677
  init_mcp_prefix();
624
678
  init_provider_table();
679
+ init_agent_config();
680
+ init_runtime_table();
625
681
  init_intercom_queue();
626
682
  init_plan_limits();
627
683
  init_employees();
628
- SPAWN_LOCK_DIR = path7.join(os5.homedir(), ".exe-os", "spawn-locks");
629
- SESSION_CACHE = path7.join(os5.homedir(), ".exe-os", "session-cache");
684
+ SPAWN_LOCK_DIR = path8.join(os5.homedir(), ".exe-os", "spawn-locks");
685
+ SESSION_CACHE = path8.join(os5.homedir(), ".exe-os", "session-cache");
630
686
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
631
687
  INTERCOM_DEBOUNCE_MS = 3e4;
632
- INTERCOM_LOG2 = path7.join(os5.homedir(), ".exe-os", "intercom.log");
633
- DEBOUNCE_FILE = path7.join(SESSION_CACHE, "intercom-debounce.json");
688
+ INTERCOM_LOG2 = path8.join(os5.homedir(), ".exe-os", "intercom.log");
689
+ DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
634
690
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
635
691
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
636
692
  }
@@ -783,16 +839,16 @@ async function markFailed(messageId, reason) {
783
839
 
784
840
  // src/adapters/claude/active-agent.ts
785
841
  init_config();
786
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2, readdirSync } from "fs";
842
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, readdirSync } from "fs";
787
843
  import { execSync as execSync4 } from "child_process";
788
- import path8 from "path";
844
+ import path9 from "path";
789
845
 
790
846
  // src/adapters/claude/session-key.ts
791
847
  init_session_key();
792
848
 
793
849
  // src/adapters/claude/active-agent.ts
794
850
  init_employees();
795
- var CACHE_DIR = path8.join(EXE_AI_DIR, "session-cache");
851
+ var CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
796
852
  var STALE_MS = 24 * 60 * 60 * 1e3;
797
853
  function isNameWithOptionalInstance(candidate, baseName) {
798
854
  if (candidate === baseName) return true;
@@ -837,12 +893,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
837
893
  return null;
838
894
  }
839
895
  function getMarkerPath() {
840
- return path8.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
896
+ return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
841
897
  }
842
898
  function getActiveAgent() {
843
899
  try {
844
900
  const markerPath = getMarkerPath();
845
- const raw = readFileSync7(markerPath, "utf8");
901
+ const raw = readFileSync8(markerPath, "utf8");
846
902
  const data = JSON.parse(raw);
847
903
  if (data.agentId) {
848
904
  if (data.startedAt) {