@askexenow/exe-os 0.8.83 → 0.8.86

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 (103) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +154 -21
  5. package/dist/bin/cli.js +14678 -12676
  6. package/dist/bin/exe-agent-config.js +242 -0
  7. package/dist/bin/exe-agent.js +100 -91
  8. package/dist/bin/exe-assign.js +1003 -854
  9. package/dist/bin/exe-boot.js +1420 -485
  10. package/dist/bin/exe-call.js +10 -0
  11. package/dist/bin/exe-cloud.js +29 -6
  12. package/dist/bin/exe-dispatch.js +572 -271
  13. package/dist/bin/exe-doctor.js +403 -6
  14. package/dist/bin/exe-export-behaviors.js +175 -72
  15. package/dist/bin/exe-forget.js +102 -3
  16. package/dist/bin/exe-gateway.js +796 -292
  17. package/dist/bin/exe-healthcheck.js +134 -1
  18. package/dist/bin/exe-heartbeat.js +172 -36
  19. package/dist/bin/exe-kill.js +175 -72
  20. package/dist/bin/exe-launch-agent.js +189 -76
  21. package/dist/bin/exe-link.js +927 -82
  22. package/dist/bin/exe-new-employee.js +60 -8
  23. package/dist/bin/exe-pending-messages.js +151 -19
  24. package/dist/bin/exe-pending-notifications.js +97 -2
  25. package/dist/bin/exe-pending-reviews.js +155 -22
  26. package/dist/bin/exe-rename.js +564 -23
  27. package/dist/bin/exe-review.js +231 -73
  28. package/dist/bin/exe-search.js +995 -228
  29. package/dist/bin/exe-session-cleanup.js +4930 -1664
  30. package/dist/bin/exe-settings.js +20 -5
  31. package/dist/bin/exe-start-codex.js +2598 -0
  32. package/dist/bin/exe-start.sh +15 -3
  33. package/dist/bin/exe-status.js +154 -21
  34. package/dist/bin/exe-team.js +97 -2
  35. package/dist/bin/git-sweep.js +1180 -363
  36. package/dist/bin/graph-backfill.js +175 -72
  37. package/dist/bin/graph-export.js +175 -72
  38. package/dist/bin/install.js +60 -7
  39. package/dist/bin/list-providers.js +1 -0
  40. package/dist/bin/scan-tasks.js +1185 -367
  41. package/dist/bin/setup.js +914 -270
  42. package/dist/bin/shard-migrate.js +175 -72
  43. package/dist/bin/update.js +1 -0
  44. package/dist/bin/wiki-sync.js +175 -72
  45. package/dist/gateway/index.js +792 -285
  46. package/dist/hooks/bug-report-worker.js +445 -135
  47. package/dist/hooks/commit-complete.js +1178 -361
  48. package/dist/hooks/error-recall.js +994 -228
  49. package/dist/hooks/ingest-worker.js +1799 -1234
  50. package/dist/hooks/ingest.js +3 -0
  51. package/dist/hooks/instructions-loaded.js +707 -97
  52. package/dist/hooks/notification.js +699 -89
  53. package/dist/hooks/post-compact.js +757 -109
  54. package/dist/hooks/pre-compact.js +1061 -244
  55. package/dist/hooks/pre-tool-use.js +787 -130
  56. package/dist/hooks/prompt-ingest-worker.js +242 -101
  57. package/dist/hooks/prompt-submit.js +1121 -299
  58. package/dist/hooks/response-ingest-worker.js +242 -101
  59. package/dist/hooks/session-end.js +4063 -397
  60. package/dist/hooks/session-start.js +1071 -254
  61. package/dist/hooks/stop.js +768 -120
  62. package/dist/hooks/subagent-stop.js +757 -109
  63. package/dist/hooks/summary-worker.js +1706 -1011
  64. package/dist/index.js +1821 -1098
  65. package/dist/lib/agent-config.js +167 -0
  66. package/dist/lib/cloud-sync.js +932 -88
  67. package/dist/lib/consolidation.js +2 -1
  68. package/dist/lib/database.js +642 -87
  69. package/dist/lib/db-daemon-client.js +503 -0
  70. package/dist/lib/device-registry.js +547 -7
  71. package/dist/lib/embedder.js +14 -28
  72. package/dist/lib/employee-templates.js +84 -74
  73. package/dist/lib/employees.js +9 -0
  74. package/dist/lib/exe-daemon-client.js +16 -29
  75. package/dist/lib/exe-daemon.js +2733 -1575
  76. package/dist/lib/hybrid-search.js +995 -228
  77. package/dist/lib/identity.js +87 -67
  78. package/dist/lib/keychain.js +9 -1
  79. package/dist/lib/messaging.js +103 -40
  80. package/dist/lib/reminders.js +91 -74
  81. package/dist/lib/runtime-table.js +16 -0
  82. package/dist/lib/schedules.js +96 -2
  83. package/dist/lib/session-wrappers.js +22 -0
  84. package/dist/lib/skill-learning.js +103 -85
  85. package/dist/lib/store.js +234 -73
  86. package/dist/lib/tasks.js +348 -134
  87. package/dist/lib/tmux-routing.js +422 -208
  88. package/dist/lib/token-spend.js +273 -0
  89. package/dist/lib/ws-client.js +11 -0
  90. package/dist/mcp/server.js +5742 -696
  91. package/dist/mcp/tools/complete-reminder.js +94 -77
  92. package/dist/mcp/tools/create-reminder.js +94 -77
  93. package/dist/mcp/tools/create-task.js +375 -152
  94. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  95. package/dist/mcp/tools/list-reminders.js +94 -77
  96. package/dist/mcp/tools/list-tasks.js +99 -31
  97. package/dist/mcp/tools/send-message.js +108 -45
  98. package/dist/mcp/tools/update-task.js +162 -77
  99. package/dist/runtime/index.js +1075 -258
  100. package/dist/tui/App.js +1333 -506
  101. package/package.json +6 -1
  102. package/src/commands/exe/agent-config.md +27 -0
  103. package/src/commands/exe/cc-doctor.md +10 -0
@@ -1,8 +1,7 @@
1
- // src/lib/identity.ts
2
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
3
- import { readdirSync } from "fs";
4
- import path3 from "path";
5
- import { createHash } from "crypto";
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res) => function __init() {
3
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ };
6
5
 
7
6
  // src/lib/config.ts
8
7
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
@@ -25,74 +24,88 @@ function resolveDataDir() {
25
24
  }
26
25
  return newDir;
27
26
  }
28
- var EXE_AI_DIR = resolveDataDir();
29
- var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
30
- var MODELS_DIR = path.join(EXE_AI_DIR, "models");
31
- var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
32
- var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
33
- var CURRENT_CONFIG_VERSION = 1;
34
- var DEFAULT_CONFIG = {
35
- config_version: CURRENT_CONFIG_VERSION,
36
- dbPath: DB_PATH,
37
- modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
38
- embeddingDim: 1024,
39
- batchSize: 20,
40
- flushIntervalMs: 1e4,
41
- autoIngestion: true,
42
- autoRetrieval: true,
43
- searchMode: "hybrid",
44
- hookSearchMode: "hybrid",
45
- fileGrepEnabled: true,
46
- splashEffect: true,
47
- consolidationEnabled: true,
48
- consolidationIntervalMs: 6 * 60 * 60 * 1e3,
49
- consolidationModel: "claude-haiku-4-5-20251001",
50
- consolidationMaxCallsPerRun: 20,
51
- selfQueryRouter: true,
52
- selfQueryModel: "claude-haiku-4-5-20251001",
53
- rerankerEnabled: true,
54
- scalingRoadmap: {
55
- rerankerAutoTrigger: {
56
- enabled: true,
57
- broadQueryMinCardinality: 5e4,
58
- fetchTopK: 150,
59
- returnTopK: 5
60
- }
61
- },
62
- graphRagEnabled: true,
63
- wikiEnabled: false,
64
- wikiUrl: "",
65
- wikiApiKey: "",
66
- wikiSyncIntervalMs: 30 * 60 * 1e3,
67
- wikiWorkspaceMapping: {},
68
- wikiAutoUpdate: true,
69
- wikiAutoUpdateThreshold: 0.5,
70
- wikiAutoUpdateCreateNew: true,
71
- skillLearning: true,
72
- skillThreshold: 3,
73
- skillModel: "claude-haiku-4-5-20251001",
74
- exeHeartbeat: {
75
- enabled: true,
76
- intervalSeconds: 60,
77
- staleInProgressThresholdHours: 2
78
- },
79
- sessionLifecycle: {
80
- idleKillEnabled: true,
81
- idleKillTicksRequired: 3,
82
- idleKillIntercomAckWindowMs: 1e4,
83
- maxAutoInstances: 10
84
- },
85
- autoUpdate: {
86
- checkOnBoot: true,
87
- autoInstall: false,
88
- checkIntervalMs: 24 * 60 * 60 * 1e3
27
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG;
28
+ var init_config = __esm({
29
+ "src/lib/config.ts"() {
30
+ "use strict";
31
+ EXE_AI_DIR = resolveDataDir();
32
+ DB_PATH = path.join(EXE_AI_DIR, "memories.db");
33
+ MODELS_DIR = path.join(EXE_AI_DIR, "models");
34
+ CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
35
+ LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
36
+ CURRENT_CONFIG_VERSION = 1;
37
+ DEFAULT_CONFIG = {
38
+ config_version: CURRENT_CONFIG_VERSION,
39
+ dbPath: DB_PATH,
40
+ modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
41
+ embeddingDim: 1024,
42
+ batchSize: 20,
43
+ flushIntervalMs: 1e4,
44
+ autoIngestion: true,
45
+ autoRetrieval: true,
46
+ searchMode: "hybrid",
47
+ hookSearchMode: "hybrid",
48
+ fileGrepEnabled: true,
49
+ splashEffect: true,
50
+ consolidationEnabled: true,
51
+ consolidationIntervalMs: 6 * 60 * 60 * 1e3,
52
+ consolidationModel: "claude-haiku-4-5-20251001",
53
+ consolidationMaxCallsPerRun: 20,
54
+ selfQueryRouter: true,
55
+ selfQueryModel: "claude-haiku-4-5-20251001",
56
+ rerankerEnabled: true,
57
+ scalingRoadmap: {
58
+ rerankerAutoTrigger: {
59
+ enabled: true,
60
+ broadQueryMinCardinality: 5e4,
61
+ fetchTopK: 150,
62
+ returnTopK: 5
63
+ }
64
+ },
65
+ graphRagEnabled: true,
66
+ wikiEnabled: false,
67
+ wikiUrl: "",
68
+ wikiApiKey: "",
69
+ wikiSyncIntervalMs: 30 * 60 * 1e3,
70
+ wikiWorkspaceMapping: {},
71
+ wikiAutoUpdate: true,
72
+ wikiAutoUpdateThreshold: 0.5,
73
+ wikiAutoUpdateCreateNew: true,
74
+ skillLearning: true,
75
+ skillThreshold: 3,
76
+ skillModel: "claude-haiku-4-5-20251001",
77
+ exeHeartbeat: {
78
+ enabled: true,
79
+ intervalSeconds: 60,
80
+ staleInProgressThresholdHours: 2
81
+ },
82
+ sessionLifecycle: {
83
+ idleKillEnabled: true,
84
+ idleKillTicksRequired: 3,
85
+ idleKillIntercomAckWindowMs: 1e4,
86
+ maxAutoInstances: 10
87
+ },
88
+ autoUpdate: {
89
+ checkOnBoot: true,
90
+ autoInstall: false,
91
+ checkIntervalMs: 24 * 60 * 60 * 1e3
92
+ }
93
+ };
89
94
  }
90
- };
95
+ });
96
+
97
+ // src/lib/identity.ts
98
+ init_config();
99
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
100
+ import { readdirSync } from "fs";
101
+ import path3 from "path";
102
+ import { createHash } from "crypto";
91
103
 
92
104
  // src/lib/database.ts
93
105
  import { createClient } from "@libsql/client";
94
106
 
95
107
  // src/lib/employees.ts
108
+ init_config();
96
109
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
97
110
  import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
98
111
  import { execSync } from "child_process";
@@ -102,10 +115,17 @@ var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
102
115
 
103
116
  // src/lib/database.ts
104
117
  var _resilientClient = null;
118
+ var _daemonClient = null;
105
119
  function getClient() {
106
120
  if (!_resilientClient) {
107
121
  throw new Error("Database client not initialized. Call initDatabase() first.");
108
122
  }
123
+ if (process.env.EXE_IS_DAEMON === "1") {
124
+ return _resilientClient;
125
+ }
126
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
127
+ return _daemonClient;
128
+ }
109
129
  return _resilientClient;
110
130
  }
111
131
 
@@ -31,12 +31,20 @@ async function getMasterKey() {
31
31
  }
32
32
  const keyPath = getKeyPath();
33
33
  if (!existsSync(keyPath)) {
34
+ process.stderr.write(
35
+ `[keychain] Key not found at ${keyPath} (HOME=${os.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
36
+ `
37
+ );
34
38
  return null;
35
39
  }
36
40
  try {
37
41
  const content = await readFile(keyPath, "utf-8");
38
42
  return Buffer.from(content.trim(), "base64");
39
- } catch {
43
+ } catch (err) {
44
+ process.stderr.write(
45
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
46
+ `
47
+ );
40
48
  return null;
41
49
  }
42
50
  }
@@ -160,15 +160,22 @@ function getClient() {
160
160
  if (!_resilientClient) {
161
161
  throw new Error("Database client not initialized. Call initDatabase() first.");
162
162
  }
163
+ if (process.env.EXE_IS_DAEMON === "1") {
164
+ return _resilientClient;
165
+ }
166
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
167
+ return _daemonClient;
168
+ }
163
169
  return _resilientClient;
164
170
  }
165
- var _resilientClient;
171
+ var _resilientClient, _daemonClient;
166
172
  var init_database = __esm({
167
173
  "src/lib/database.ts"() {
168
174
  "use strict";
169
175
  init_db_retry();
170
176
  init_employees();
171
177
  _resilientClient = null;
178
+ _daemonClient = null;
172
179
  }
173
180
  });
174
181
 
@@ -354,18 +361,54 @@ var init_provider_table = __esm({
354
361
  }
355
362
  });
356
363
 
357
- // src/lib/intercom-queue.ts
358
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync3, mkdirSync } from "fs";
364
+ // src/lib/runtime-table.ts
365
+ var RUNTIME_TABLE;
366
+ var init_runtime_table = __esm({
367
+ "src/lib/runtime-table.ts"() {
368
+ "use strict";
369
+ RUNTIME_TABLE = {
370
+ codex: {
371
+ binary: "codex",
372
+ launchMode: "exec",
373
+ autoApproveFlag: "--full-auto",
374
+ inlineFlag: "--no-alt-screen",
375
+ apiKeyEnv: "OPENAI_API_KEY",
376
+ defaultModel: "gpt-5.4"
377
+ }
378
+ };
379
+ }
380
+ });
381
+
382
+ // src/lib/agent-config.ts
383
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync } from "fs";
359
384
  import path4 from "path";
385
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
386
+ var init_agent_config = __esm({
387
+ "src/lib/agent-config.ts"() {
388
+ "use strict";
389
+ init_config();
390
+ init_runtime_table();
391
+ AGENT_CONFIG_PATH = path4.join(EXE_AI_DIR, "agent-config.json");
392
+ DEFAULT_MODELS = {
393
+ claude: "claude-opus-4",
394
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
395
+ opencode: "minimax-m2.7"
396
+ };
397
+ }
398
+ });
399
+
400
+ // src/lib/intercom-queue.ts
401
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
402
+ import path5 from "path";
360
403
  import os4 from "os";
361
404
  function ensureDir() {
362
- const dir = path4.dirname(QUEUE_PATH);
363
- if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
405
+ const dir = path5.dirname(QUEUE_PATH);
406
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
364
407
  }
365
408
  function readQueue() {
366
409
  try {
367
- if (!existsSync3(QUEUE_PATH)) return [];
368
- return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
410
+ if (!existsSync4(QUEUE_PATH)) return [];
411
+ return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
369
412
  } catch {
370
413
  return [];
371
414
  }
@@ -373,7 +416,7 @@ function readQueue() {
373
416
  function writeQueue(queue) {
374
417
  ensureDir();
375
418
  const tmp = `${QUEUE_PATH}.tmp`;
376
- writeFileSync2(tmp, JSON.stringify(queue, null, 2));
419
+ writeFileSync3(tmp, JSON.stringify(queue, null, 2));
377
420
  renameSync3(tmp, QUEUE_PATH);
378
421
  }
379
422
  function queueIntercom(targetSession, reason) {
@@ -397,31 +440,31 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
397
440
  var init_intercom_queue = __esm({
398
441
  "src/lib/intercom-queue.ts"() {
399
442
  "use strict";
400
- QUEUE_PATH = path4.join(os4.homedir(), ".exe-os", "intercom-queue.json");
443
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
401
444
  TTL_MS = 60 * 60 * 1e3;
402
- INTERCOM_LOG = path4.join(os4.homedir(), ".exe-os", "intercom.log");
445
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
403
446
  }
404
447
  });
405
448
 
406
449
  // src/lib/license.ts
407
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
450
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
408
451
  import { randomUUID } from "crypto";
409
- import path5 from "path";
452
+ import path6 from "path";
410
453
  import { jwtVerify, importSPKI } from "jose";
411
454
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
412
455
  var init_license = __esm({
413
456
  "src/lib/license.ts"() {
414
457
  "use strict";
415
458
  init_config();
416
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
417
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
418
- DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
459
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
460
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
461
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
419
462
  }
420
463
  });
421
464
 
422
465
  // src/lib/plan-limits.ts
423
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
424
- import path6 from "path";
466
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
467
+ import path7 from "path";
425
468
  var CACHE_PATH2;
426
469
  var init_plan_limits = __esm({
427
470
  "src/lib/plan-limits.ts"() {
@@ -430,13 +473,13 @@ var init_plan_limits = __esm({
430
473
  init_employees();
431
474
  init_license();
432
475
  init_config();
433
- CACHE_PATH2 = path6.join(EXE_AI_DIR, "license-cache.json");
476
+ CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
434
477
  }
435
478
  });
436
479
 
437
480
  // src/lib/tmux-routing.ts
438
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync6, appendFileSync } from "fs";
439
- import path7 from "path";
481
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync7, appendFileSync } from "fs";
482
+ import path8 from "path";
440
483
  import os5 from "os";
441
484
  import { fileURLToPath } from "url";
442
485
  function getMySession() {
@@ -477,7 +520,7 @@ function extractRootExe(name) {
477
520
  }
478
521
  function getParentExe(sessionKey) {
479
522
  try {
480
- const data = JSON.parse(readFileSync6(path7.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
523
+ const data = JSON.parse(readFileSync7(path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
481
524
  return data.parentExe || null;
482
525
  } catch {
483
526
  return null;
@@ -501,32 +544,50 @@ function isEmployeeAlive(sessionName) {
501
544
  }
502
545
  function readDebounceState() {
503
546
  try {
504
- if (!existsSync6(DEBOUNCE_FILE)) return {};
505
- return JSON.parse(readFileSync6(DEBOUNCE_FILE, "utf8"));
547
+ if (!existsSync7(DEBOUNCE_FILE)) return {};
548
+ const raw = JSON.parse(readFileSync7(DEBOUNCE_FILE, "utf8"));
549
+ const state = {};
550
+ for (const [key, val] of Object.entries(raw)) {
551
+ if (typeof val === "number") {
552
+ state[key] = { lastSent: val, pending: 0 };
553
+ } else if (val && typeof val === "object" && "lastSent" in val) {
554
+ state[key] = val;
555
+ }
556
+ }
557
+ return state;
506
558
  } catch {
507
559
  return {};
508
560
  }
509
561
  }
510
562
  function writeDebounceState(state) {
511
563
  try {
512
- if (!existsSync6(SESSION_CACHE)) mkdirSync3(SESSION_CACHE, { recursive: true });
513
- writeFileSync4(DEBOUNCE_FILE, JSON.stringify(state));
564
+ if (!existsSync7(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
565
+ writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
514
566
  } catch {
515
567
  }
516
568
  }
517
569
  function isDebounced(targetSession) {
518
570
  const state = readDebounceState();
519
- const lastSent = state[targetSession] ?? 0;
520
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
571
+ const entry = state[targetSession];
572
+ const lastSent = entry?.lastSent ?? 0;
573
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
574
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
575
+ state[targetSession].pending++;
576
+ writeDebounceState(state);
577
+ return true;
578
+ }
579
+ return false;
521
580
  }
522
581
  function recordDebounce(targetSession) {
523
582
  const state = readDebounceState();
524
- state[targetSession] = Date.now();
583
+ const batched = state[targetSession]?.pending ?? 0;
584
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
525
585
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
526
586
  for (const key of Object.keys(state)) {
527
- if ((state[key] ?? 0) < cutoff) delete state[key];
587
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
528
588
  }
529
589
  writeDebounceState(state);
590
+ return batched;
530
591
  }
531
592
  function logIntercom(msg) {
532
593
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -567,7 +628,7 @@ function sendIntercom(targetSession) {
567
628
  return "skipped_exe";
568
629
  }
569
630
  if (isDebounced(targetSession)) {
570
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
631
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
571
632
  return "debounced";
572
633
  }
573
634
  try {
@@ -579,14 +640,14 @@ function sendIntercom(targetSession) {
579
640
  const sessionState = getSessionState(targetSession);
580
641
  if (sessionState === "no_claude") {
581
642
  queueIntercom(targetSession, "claude not running in session");
582
- recordDebounce(targetSession);
583
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
643
+ const batched2 = recordDebounce(targetSession);
644
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
584
645
  return "queued";
585
646
  }
586
647
  if (sessionState === "thinking" || sessionState === "tool") {
587
648
  queueIntercom(targetSession, "session busy at send time");
588
- recordDebounce(targetSession);
589
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
649
+ const batched2 = recordDebounce(targetSession);
650
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
590
651
  return "queued";
591
652
  }
592
653
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -594,8 +655,8 @@ function sendIntercom(targetSession) {
594
655
  transport.sendKeys(targetSession, "q");
595
656
  }
596
657
  transport.sendKeys(targetSession, "/exe-intercom");
597
- recordDebounce(targetSession);
598
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
658
+ const batched = recordDebounce(targetSession);
659
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
599
660
  return "delivered";
600
661
  } catch {
601
662
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -612,15 +673,17 @@ var init_tmux_routing = __esm({
612
673
  init_cc_agent_support();
613
674
  init_mcp_prefix();
614
675
  init_provider_table();
676
+ init_agent_config();
677
+ init_runtime_table();
615
678
  init_intercom_queue();
616
679
  init_plan_limits();
617
680
  init_employees();
618
- SPAWN_LOCK_DIR = path7.join(os5.homedir(), ".exe-os", "spawn-locks");
619
- SESSION_CACHE = path7.join(os5.homedir(), ".exe-os", "session-cache");
681
+ SPAWN_LOCK_DIR = path8.join(os5.homedir(), ".exe-os", "spawn-locks");
682
+ SESSION_CACHE = path8.join(os5.homedir(), ".exe-os", "session-cache");
620
683
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
621
684
  INTERCOM_DEBOUNCE_MS = 3e4;
622
- INTERCOM_LOG2 = path7.join(os5.homedir(), ".exe-os", "intercom.log");
623
- DEBOUNCE_FILE = path7.join(SESSION_CACHE, "intercom-debounce.json");
685
+ INTERCOM_LOG2 = path8.join(os5.homedir(), ".exe-os", "intercom.log");
686
+ DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
624
687
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
625
688
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
626
689
  }