@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
@@ -3963,7 +3963,7 @@ async function hybridSearch(queryText, agentId, options) {
3963
3963
  process.stderr.write("[hybrid-search] Embed daemon unavailable \u2014 FTS-only mode\n");
3964
3964
  }
3965
3965
  let grepPromise = Promise.resolve([]);
3966
- if (config.fileGrepEnabled !== false) {
3966
+ if (config.fileGrepEnabled === true) {
3967
3967
  try {
3968
3968
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
3969
3969
  const projectRoot = process.cwd();
@@ -4232,7 +4232,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
4232
4232
  source_type: row.source_type ?? null
4233
4233
  }));
4234
4234
  }
4235
- async function recentRecords(agentId, options, limit) {
4235
+ async function recentRecords(agentId, options, limit, textFilter) {
4236
4236
  const client = getClient();
4237
4237
  const statusFilter = options?.includeArchived ? "" : `
4238
4238
  AND COALESCE(status, 'active') = 'active'`;
@@ -4272,6 +4272,10 @@ async function recentRecords(agentId, options, limit) {
4272
4272
  sql += ` AND memory_type = ?`;
4273
4273
  args.push(options.memoryType);
4274
4274
  }
4275
+ if (textFilter) {
4276
+ sql += ` AND raw_text LIKE '%' || ? || '%'`;
4277
+ args.push(textFilter);
4278
+ }
4275
4279
  sql += ` ORDER BY timestamp DESC LIMIT ?`;
4276
4280
  args.push(limit);
4277
4281
  const result = await client.execute({ sql, args });
@@ -4386,6 +4390,7 @@ export {
4386
4390
  estimateCardinality,
4387
4391
  hybridSearch,
4388
4392
  lightweightSearch,
4393
+ recentRecords,
4389
4394
  rrfMerge,
4390
4395
  rrfMergeMulti
4391
4396
  };
@@ -361,18 +361,54 @@ var init_provider_table = __esm({
361
361
  }
362
362
  });
363
363
 
364
- // src/lib/intercom-queue.ts
365
- 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";
366
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";
367
403
  import os4 from "os";
368
404
  function ensureDir() {
369
- const dir = path4.dirname(QUEUE_PATH);
370
- if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
405
+ const dir = path5.dirname(QUEUE_PATH);
406
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
371
407
  }
372
408
  function readQueue() {
373
409
  try {
374
- if (!existsSync3(QUEUE_PATH)) return [];
375
- return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
410
+ if (!existsSync4(QUEUE_PATH)) return [];
411
+ return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
376
412
  } catch {
377
413
  return [];
378
414
  }
@@ -380,7 +416,7 @@ function readQueue() {
380
416
  function writeQueue(queue) {
381
417
  ensureDir();
382
418
  const tmp = `${QUEUE_PATH}.tmp`;
383
- writeFileSync2(tmp, JSON.stringify(queue, null, 2));
419
+ writeFileSync3(tmp, JSON.stringify(queue, null, 2));
384
420
  renameSync3(tmp, QUEUE_PATH);
385
421
  }
386
422
  function queueIntercom(targetSession, reason) {
@@ -404,31 +440,31 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
404
440
  var init_intercom_queue = __esm({
405
441
  "src/lib/intercom-queue.ts"() {
406
442
  "use strict";
407
- QUEUE_PATH = path4.join(os4.homedir(), ".exe-os", "intercom-queue.json");
443
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
408
444
  TTL_MS = 60 * 60 * 1e3;
409
- INTERCOM_LOG = path4.join(os4.homedir(), ".exe-os", "intercom.log");
445
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
410
446
  }
411
447
  });
412
448
 
413
449
  // src/lib/license.ts
414
- 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";
415
451
  import { randomUUID } from "crypto";
416
- import path5 from "path";
452
+ import path6 from "path";
417
453
  import { jwtVerify, importSPKI } from "jose";
418
454
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
419
455
  var init_license = __esm({
420
456
  "src/lib/license.ts"() {
421
457
  "use strict";
422
458
  init_config();
423
- LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
424
- CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
425
- 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");
426
462
  }
427
463
  });
428
464
 
429
465
  // src/lib/plan-limits.ts
430
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
431
- import path6 from "path";
466
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
467
+ import path7 from "path";
432
468
  var CACHE_PATH2;
433
469
  var init_plan_limits = __esm({
434
470
  "src/lib/plan-limits.ts"() {
@@ -437,13 +473,13 @@ var init_plan_limits = __esm({
437
473
  init_employees();
438
474
  init_license();
439
475
  init_config();
440
- CACHE_PATH2 = path6.join(EXE_AI_DIR, "license-cache.json");
476
+ CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
441
477
  }
442
478
  });
443
479
 
444
480
  // src/lib/tmux-routing.ts
445
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync6, appendFileSync } from "fs";
446
- 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";
447
483
  import os5 from "os";
448
484
  import { fileURLToPath } from "url";
449
485
  function getMySession() {
@@ -484,7 +520,7 @@ function extractRootExe(name) {
484
520
  }
485
521
  function getParentExe(sessionKey) {
486
522
  try {
487
- 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"));
488
524
  return data.parentExe || null;
489
525
  } catch {
490
526
  return null;
@@ -508,32 +544,50 @@ function isEmployeeAlive(sessionName) {
508
544
  }
509
545
  function readDebounceState() {
510
546
  try {
511
- if (!existsSync6(DEBOUNCE_FILE)) return {};
512
- 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;
513
558
  } catch {
514
559
  return {};
515
560
  }
516
561
  }
517
562
  function writeDebounceState(state) {
518
563
  try {
519
- if (!existsSync6(SESSION_CACHE)) mkdirSync3(SESSION_CACHE, { recursive: true });
520
- writeFileSync4(DEBOUNCE_FILE, JSON.stringify(state));
564
+ if (!existsSync7(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
565
+ writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
521
566
  } catch {
522
567
  }
523
568
  }
524
569
  function isDebounced(targetSession) {
525
570
  const state = readDebounceState();
526
- const lastSent = state[targetSession] ?? 0;
527
- 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;
528
580
  }
529
581
  function recordDebounce(targetSession) {
530
582
  const state = readDebounceState();
531
- state[targetSession] = Date.now();
583
+ const batched = state[targetSession]?.pending ?? 0;
584
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
532
585
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
533
586
  for (const key of Object.keys(state)) {
534
- if ((state[key] ?? 0) < cutoff) delete state[key];
587
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
535
588
  }
536
589
  writeDebounceState(state);
590
+ return batched;
537
591
  }
538
592
  function logIntercom(msg) {
539
593
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -574,7 +628,7 @@ function sendIntercom(targetSession) {
574
628
  return "skipped_exe";
575
629
  }
576
630
  if (isDebounced(targetSession)) {
577
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
631
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
578
632
  return "debounced";
579
633
  }
580
634
  try {
@@ -586,14 +640,14 @@ function sendIntercom(targetSession) {
586
640
  const sessionState = getSessionState(targetSession);
587
641
  if (sessionState === "no_claude") {
588
642
  queueIntercom(targetSession, "claude not running in session");
589
- recordDebounce(targetSession);
590
- 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]` : ""}`);
591
645
  return "queued";
592
646
  }
593
647
  if (sessionState === "thinking" || sessionState === "tool") {
594
648
  queueIntercom(targetSession, "session busy at send time");
595
- recordDebounce(targetSession);
596
- 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]` : ""}`);
597
651
  return "queued";
598
652
  }
599
653
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -601,8 +655,8 @@ function sendIntercom(targetSession) {
601
655
  transport.sendKeys(targetSession, "q");
602
656
  }
603
657
  transport.sendKeys(targetSession, "/exe-intercom");
604
- recordDebounce(targetSession);
605
- 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)`);
606
660
  return "delivered";
607
661
  } catch {
608
662
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -619,15 +673,17 @@ var init_tmux_routing = __esm({
619
673
  init_cc_agent_support();
620
674
  init_mcp_prefix();
621
675
  init_provider_table();
676
+ init_agent_config();
677
+ init_runtime_table();
622
678
  init_intercom_queue();
623
679
  init_plan_limits();
624
680
  init_employees();
625
- SPAWN_LOCK_DIR = path7.join(os5.homedir(), ".exe-os", "spawn-locks");
626
- 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");
627
683
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
628
684
  INTERCOM_DEBOUNCE_MS = 3e4;
629
- INTERCOM_LOG2 = path7.join(os5.homedir(), ".exe-os", "intercom.log");
630
- 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");
631
687
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
632
688
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
633
689
  }
@@ -0,0 +1,16 @@
1
+ // src/lib/runtime-table.ts
2
+ var RUNTIME_TABLE = {
3
+ codex: {
4
+ binary: "codex",
5
+ launchMode: "exec",
6
+ autoApproveFlag: "--full-auto",
7
+ inlineFlag: "--no-alt-screen",
8
+ apiKeyEnv: "OPENAI_API_KEY",
9
+ defaultModel: "gpt-5.4"
10
+ }
11
+ };
12
+ var DEFAULT_RUNTIME = "claude";
13
+ export {
14
+ DEFAULT_RUNTIME,
15
+ RUNTIME_TABLE
16
+ };
@@ -63,6 +63,28 @@ exec "${exeStartDst}" "$0" "$@"
63
63
  created++;
64
64
  }
65
65
  }
66
+ const codexLauncherCandidates = [
67
+ path.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
68
+ path.join(packageRoot, "src", "bin", "exe-start-codex.ts")
69
+ ];
70
+ let codexLauncher = null;
71
+ for (const c of codexLauncherCandidates) {
72
+ if (existsSync(c)) {
73
+ codexLauncher = c;
74
+ break;
75
+ }
76
+ }
77
+ if (codexLauncher) {
78
+ for (const emp of employees) {
79
+ const wrapperPath = path.join(binDir, `${emp.name}-codex`);
80
+ const content = `#!/bin/bash
81
+ exec node "${codexLauncher}" --agent ${emp.name} "$@"
82
+ `;
83
+ writeFileSync(wrapperPath, content);
84
+ chmodSync(wrapperPath, 493);
85
+ created++;
86
+ }
87
+ }
66
88
  const pathConfigured = ensurePath(home, binDir);
67
89
  return { created, pathConfigured };
68
90
  }