@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
@@ -427,6 +427,443 @@ var init_db_retry = __esm({
427
427
  }
428
428
  });
429
429
 
430
+ // src/lib/exe-daemon-client.ts
431
+ import net from "net";
432
+ import { spawn } from "child_process";
433
+ import { randomUUID } from "crypto";
434
+ import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
435
+ import path3 from "path";
436
+ import { fileURLToPath } from "url";
437
+ function handleData(chunk) {
438
+ _buffer += chunk.toString();
439
+ if (_buffer.length > MAX_BUFFER) {
440
+ _buffer = "";
441
+ return;
442
+ }
443
+ let newlineIdx;
444
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
445
+ const line = _buffer.slice(0, newlineIdx).trim();
446
+ _buffer = _buffer.slice(newlineIdx + 1);
447
+ if (!line) continue;
448
+ try {
449
+ const response = JSON.parse(line);
450
+ const id = response.id;
451
+ if (!id) continue;
452
+ const entry = _pending.get(id);
453
+ if (entry) {
454
+ clearTimeout(entry.timer);
455
+ _pending.delete(id);
456
+ entry.resolve(response);
457
+ }
458
+ } catch {
459
+ }
460
+ }
461
+ }
462
+ function cleanupStaleFiles() {
463
+ if (existsSync3(PID_PATH)) {
464
+ try {
465
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
466
+ if (pid > 0) {
467
+ try {
468
+ process.kill(pid, 0);
469
+ return;
470
+ } catch {
471
+ }
472
+ }
473
+ } catch {
474
+ }
475
+ try {
476
+ unlinkSync2(PID_PATH);
477
+ } catch {
478
+ }
479
+ try {
480
+ unlinkSync2(SOCKET_PATH);
481
+ } catch {
482
+ }
483
+ }
484
+ }
485
+ function findPackageRoot() {
486
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
487
+ const { root } = path3.parse(dir);
488
+ while (dir !== root) {
489
+ if (existsSync3(path3.join(dir, "package.json"))) return dir;
490
+ dir = path3.dirname(dir);
491
+ }
492
+ return null;
493
+ }
494
+ function spawnDaemon() {
495
+ const pkgRoot = findPackageRoot();
496
+ if (!pkgRoot) {
497
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
498
+ return;
499
+ }
500
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
501
+ if (!existsSync3(daemonPath)) {
502
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
503
+ `);
504
+ return;
505
+ }
506
+ const resolvedPath = daemonPath;
507
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
508
+ `);
509
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
510
+ let stderrFd = "ignore";
511
+ try {
512
+ stderrFd = openSync(logPath, "a");
513
+ } catch {
514
+ }
515
+ const child = spawn(process.execPath, [resolvedPath], {
516
+ detached: true,
517
+ stdio: ["ignore", "ignore", stderrFd],
518
+ env: {
519
+ ...process.env,
520
+ TMUX: void 0,
521
+ // Daemon is global — must not inherit session scope
522
+ TMUX_PANE: void 0,
523
+ // Prevents resolveExeSession() from scoping to one session
524
+ EXE_DAEMON_SOCK: SOCKET_PATH,
525
+ EXE_DAEMON_PID: PID_PATH
526
+ }
527
+ });
528
+ child.unref();
529
+ if (typeof stderrFd === "number") {
530
+ try {
531
+ closeSync(stderrFd);
532
+ } catch {
533
+ }
534
+ }
535
+ }
536
+ function acquireSpawnLock() {
537
+ try {
538
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
539
+ closeSync(fd);
540
+ return true;
541
+ } catch {
542
+ try {
543
+ const stat = statSync(SPAWN_LOCK_PATH);
544
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
545
+ try {
546
+ unlinkSync2(SPAWN_LOCK_PATH);
547
+ } catch {
548
+ }
549
+ try {
550
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
551
+ closeSync(fd);
552
+ return true;
553
+ } catch {
554
+ }
555
+ }
556
+ } catch {
557
+ }
558
+ return false;
559
+ }
560
+ }
561
+ function releaseSpawnLock() {
562
+ try {
563
+ unlinkSync2(SPAWN_LOCK_PATH);
564
+ } catch {
565
+ }
566
+ }
567
+ function connectToSocket() {
568
+ return new Promise((resolve) => {
569
+ if (_socket && _connected) {
570
+ resolve(true);
571
+ return;
572
+ }
573
+ const socket = net.createConnection({ path: SOCKET_PATH });
574
+ const connectTimeout = setTimeout(() => {
575
+ socket.destroy();
576
+ resolve(false);
577
+ }, 2e3);
578
+ socket.on("connect", () => {
579
+ clearTimeout(connectTimeout);
580
+ _socket = socket;
581
+ _connected = true;
582
+ _buffer = "";
583
+ socket.on("data", handleData);
584
+ socket.on("close", () => {
585
+ _connected = false;
586
+ _socket = null;
587
+ for (const [id, entry] of _pending) {
588
+ clearTimeout(entry.timer);
589
+ _pending.delete(id);
590
+ entry.resolve({ error: "Connection closed" });
591
+ }
592
+ });
593
+ socket.on("error", () => {
594
+ _connected = false;
595
+ _socket = null;
596
+ });
597
+ resolve(true);
598
+ });
599
+ socket.on("error", () => {
600
+ clearTimeout(connectTimeout);
601
+ resolve(false);
602
+ });
603
+ });
604
+ }
605
+ async function connectEmbedDaemon() {
606
+ if (_socket && _connected) return true;
607
+ if (await connectToSocket()) return true;
608
+ if (acquireSpawnLock()) {
609
+ try {
610
+ cleanupStaleFiles();
611
+ spawnDaemon();
612
+ } finally {
613
+ releaseSpawnLock();
614
+ }
615
+ }
616
+ const start = Date.now();
617
+ let delay2 = 100;
618
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
619
+ await new Promise((r) => setTimeout(r, delay2));
620
+ if (await connectToSocket()) return true;
621
+ delay2 = Math.min(delay2 * 2, 3e3);
622
+ }
623
+ return false;
624
+ }
625
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
626
+ return new Promise((resolve) => {
627
+ if (!_socket || !_connected) {
628
+ resolve({ error: "Not connected" });
629
+ return;
630
+ }
631
+ const id = randomUUID();
632
+ const timer = setTimeout(() => {
633
+ _pending.delete(id);
634
+ resolve({ error: "Request timeout" });
635
+ }, timeoutMs);
636
+ _pending.set(id, { resolve, timer });
637
+ try {
638
+ _socket.write(JSON.stringify({ id, ...payload }) + "\n");
639
+ } catch {
640
+ clearTimeout(timer);
641
+ _pending.delete(id);
642
+ resolve({ error: "Write failed" });
643
+ }
644
+ });
645
+ }
646
+ function isClientConnected() {
647
+ return _connected;
648
+ }
649
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
650
+ var init_exe_daemon_client = __esm({
651
+ "src/lib/exe-daemon-client.ts"() {
652
+ "use strict";
653
+ init_config();
654
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
655
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
656
+ SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
657
+ SPAWN_LOCK_STALE_MS = 3e4;
658
+ CONNECT_TIMEOUT_MS = 15e3;
659
+ REQUEST_TIMEOUT_MS = 3e4;
660
+ _socket = null;
661
+ _connected = false;
662
+ _buffer = "";
663
+ _pending = /* @__PURE__ */ new Map();
664
+ MAX_BUFFER = 1e7;
665
+ }
666
+ });
667
+
668
+ // src/lib/daemon-protocol.ts
669
+ function serializeValue(v) {
670
+ if (v === null || v === void 0) return null;
671
+ if (typeof v === "bigint") return Number(v);
672
+ if (typeof v === "boolean") return v ? 1 : 0;
673
+ if (v instanceof Uint8Array) {
674
+ return { __blob: Buffer.from(v).toString("base64") };
675
+ }
676
+ if (ArrayBuffer.isView(v)) {
677
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
678
+ }
679
+ if (v instanceof ArrayBuffer) {
680
+ return { __blob: Buffer.from(v).toString("base64") };
681
+ }
682
+ if (typeof v === "string" || typeof v === "number") return v;
683
+ return String(v);
684
+ }
685
+ function deserializeValue(v) {
686
+ if (v === null) return null;
687
+ if (typeof v === "object" && v !== null && "__blob" in v) {
688
+ const buf = Buffer.from(v.__blob, "base64");
689
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
690
+ }
691
+ return v;
692
+ }
693
+ function deserializeResultSet(srs) {
694
+ const rows = srs.rows.map((obj) => {
695
+ const values = srs.columns.map(
696
+ (col) => deserializeValue(obj[col] ?? null)
697
+ );
698
+ const row = values;
699
+ for (let i = 0; i < srs.columns.length; i++) {
700
+ const col = srs.columns[i];
701
+ if (col !== void 0) {
702
+ row[col] = values[i] ?? null;
703
+ }
704
+ }
705
+ Object.defineProperty(row, "length", {
706
+ value: values.length,
707
+ enumerable: false
708
+ });
709
+ return row;
710
+ });
711
+ return {
712
+ columns: srs.columns,
713
+ columnTypes: srs.columnTypes ?? [],
714
+ rows,
715
+ rowsAffected: srs.rowsAffected,
716
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
717
+ toJSON: () => ({
718
+ columns: srs.columns,
719
+ columnTypes: srs.columnTypes ?? [],
720
+ rows: srs.rows,
721
+ rowsAffected: srs.rowsAffected,
722
+ lastInsertRowid: srs.lastInsertRowid
723
+ })
724
+ };
725
+ }
726
+ var init_daemon_protocol = __esm({
727
+ "src/lib/daemon-protocol.ts"() {
728
+ "use strict";
729
+ }
730
+ });
731
+
732
+ // src/lib/db-daemon-client.ts
733
+ var db_daemon_client_exports = {};
734
+ __export(db_daemon_client_exports, {
735
+ createDaemonDbClient: () => createDaemonDbClient,
736
+ initDaemonDbClient: () => initDaemonDbClient
737
+ });
738
+ function normalizeStatement(stmt) {
739
+ if (typeof stmt === "string") {
740
+ return { sql: stmt, args: [] };
741
+ }
742
+ const sql = stmt.sql;
743
+ let args = [];
744
+ if (Array.isArray(stmt.args)) {
745
+ args = stmt.args.map((v) => serializeValue(v));
746
+ } else if (stmt.args && typeof stmt.args === "object") {
747
+ const named = {};
748
+ for (const [key, val] of Object.entries(stmt.args)) {
749
+ named[key] = serializeValue(val);
750
+ }
751
+ return { sql, args: named };
752
+ }
753
+ return { sql, args };
754
+ }
755
+ function createDaemonDbClient(fallbackClient) {
756
+ let _useDaemon = false;
757
+ const client = {
758
+ async execute(stmt) {
759
+ if (!_useDaemon || !isClientConnected()) {
760
+ return fallbackClient.execute(stmt);
761
+ }
762
+ const { sql, args } = normalizeStatement(stmt);
763
+ const response = await sendDaemonRequest({
764
+ type: "db-execute",
765
+ sql,
766
+ args
767
+ });
768
+ if (response.error) {
769
+ const errMsg = String(response.error);
770
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
771
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
772
+ `);
773
+ return fallbackClient.execute(stmt);
774
+ }
775
+ throw new Error(errMsg);
776
+ }
777
+ if (response.db) {
778
+ return deserializeResultSet(response.db);
779
+ }
780
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
781
+ return fallbackClient.execute(stmt);
782
+ },
783
+ async batch(stmts, mode) {
784
+ if (!_useDaemon || !isClientConnected()) {
785
+ return fallbackClient.batch(stmts, mode);
786
+ }
787
+ const statements = stmts.map(normalizeStatement);
788
+ const response = await sendDaemonRequest({
789
+ type: "db-batch",
790
+ statements,
791
+ mode: mode ?? "deferred"
792
+ });
793
+ if (response.error) {
794
+ const errMsg = String(response.error);
795
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
796
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
797
+ `);
798
+ return fallbackClient.batch(stmts, mode);
799
+ }
800
+ throw new Error(errMsg);
801
+ }
802
+ const batchResults = response["db-batch"];
803
+ if (batchResults) {
804
+ return batchResults.map(deserializeResultSet);
805
+ }
806
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
807
+ return fallbackClient.batch(stmts, mode);
808
+ },
809
+ // Transaction support — delegate to fallback (transactions need direct connection)
810
+ async transaction(mode) {
811
+ return fallbackClient.transaction(mode);
812
+ },
813
+ // executeMultiple — delegate to fallback (used only for schema migrations)
814
+ async executeMultiple(sql) {
815
+ return fallbackClient.executeMultiple(sql);
816
+ },
817
+ // migrate — delegate to fallback
818
+ async migrate(stmts) {
819
+ return fallbackClient.migrate(stmts);
820
+ },
821
+ // Sync mode — delegate to fallback
822
+ sync() {
823
+ return fallbackClient.sync();
824
+ },
825
+ close() {
826
+ _useDaemon = false;
827
+ },
828
+ get closed() {
829
+ return fallbackClient.closed;
830
+ },
831
+ get protocol() {
832
+ return fallbackClient.protocol;
833
+ }
834
+ };
835
+ return {
836
+ ...client,
837
+ /** Enable daemon routing (call after confirming daemon is connected) */
838
+ _enableDaemon() {
839
+ _useDaemon = true;
840
+ },
841
+ /** Check if daemon routing is active */
842
+ _isDaemonActive() {
843
+ return _useDaemon && isClientConnected();
844
+ }
845
+ };
846
+ }
847
+ async function initDaemonDbClient(fallbackClient) {
848
+ if (process.env.EXE_IS_DAEMON === "1") return null;
849
+ const connected = await connectEmbedDaemon();
850
+ if (!connected) {
851
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
852
+ return null;
853
+ }
854
+ const client = createDaemonDbClient(fallbackClient);
855
+ client._enableDaemon();
856
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
857
+ return client;
858
+ }
859
+ var init_db_daemon_client = __esm({
860
+ "src/lib/db-daemon-client.ts"() {
861
+ "use strict";
862
+ init_exe_daemon_client();
863
+ init_daemon_protocol();
864
+ }
865
+ });
866
+
430
867
  // src/lib/database.ts
431
868
  var database_exports = {};
432
869
  __export(database_exports, {
@@ -435,6 +872,7 @@ __export(database_exports, {
435
872
  ensureSchema: () => ensureSchema,
436
873
  getClient: () => getClient,
437
874
  getRawClient: () => getRawClient,
875
+ initDaemonClient: () => initDaemonClient,
438
876
  initDatabase: () => initDatabase,
439
877
  initTurso: () => initTurso,
440
878
  isInitialized: () => isInitialized
@@ -462,8 +900,27 @@ function getClient() {
462
900
  if (!_resilientClient) {
463
901
  throw new Error("Database client not initialized. Call initDatabase() first.");
464
902
  }
903
+ if (process.env.EXE_IS_DAEMON === "1") {
904
+ return _resilientClient;
905
+ }
906
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
907
+ return _daemonClient;
908
+ }
465
909
  return _resilientClient;
466
910
  }
911
+ async function initDaemonClient() {
912
+ if (process.env.EXE_IS_DAEMON === "1") return;
913
+ if (!_resilientClient) return;
914
+ try {
915
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
916
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
917
+ } catch (err) {
918
+ process.stderr.write(
919
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
920
+ `
921
+ );
922
+ }
923
+ }
467
924
  function getRawClient() {
468
925
  if (!_client) {
469
926
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -950,6 +1407,12 @@ async function ensureSchema() {
950
1407
  } catch {
951
1408
  }
952
1409
  }
1410
+ try {
1411
+ await client.execute(
1412
+ `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
1413
+ );
1414
+ } catch {
1415
+ }
953
1416
  await client.executeMultiple(`
954
1417
  CREATE TABLE IF NOT EXISTS entities (
955
1418
  id TEXT PRIMARY KEY,
@@ -1002,7 +1465,30 @@ async function ensureSchema() {
1002
1465
  entity_id TEXT NOT NULL,
1003
1466
  PRIMARY KEY (hyperedge_id, entity_id)
1004
1467
  );
1468
+
1469
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
1470
+ name,
1471
+ content=entities,
1472
+ content_rowid=rowid
1473
+ );
1474
+
1475
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
1476
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1477
+ END;
1478
+
1479
+ CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
1480
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1481
+ END;
1482
+
1483
+ CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
1484
+ INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
1485
+ INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
1486
+ END;
1005
1487
  `);
1488
+ try {
1489
+ await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
1490
+ } catch {
1491
+ }
1006
1492
  await client.executeMultiple(`
1007
1493
  CREATE TABLE IF NOT EXISTS entity_aliases (
1008
1494
  alias TEXT NOT NULL PRIMARY KEY,
@@ -1183,6 +1669,33 @@ async function ensureSchema() {
1183
1669
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
1184
1670
  ON conversations(channel_id);
1185
1671
  `);
1672
+ await client.executeMultiple(`
1673
+ CREATE TABLE IF NOT EXISTS session_agent_map (
1674
+ session_uuid TEXT PRIMARY KEY,
1675
+ agent_id TEXT NOT NULL,
1676
+ session_name TEXT,
1677
+ task_id TEXT,
1678
+ project_name TEXT,
1679
+ started_at TEXT NOT NULL
1680
+ );
1681
+
1682
+ CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
1683
+ ON session_agent_map(agent_id);
1684
+ `);
1685
+ try {
1686
+ const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
1687
+ if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
1688
+ await client.execute({
1689
+ sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
1690
+ SELECT session_id, agent_id, '', MIN(timestamp)
1691
+ FROM memories
1692
+ WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
1693
+ GROUP BY session_id, agent_id`,
1694
+ args: []
1695
+ });
1696
+ }
1697
+ } catch {
1698
+ }
1186
1699
  try {
1187
1700
  await client.execute({
1188
1701
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -1316,15 +1829,41 @@ async function ensureSchema() {
1316
1829
  });
1317
1830
  } catch {
1318
1831
  }
1832
+ for (const col of [
1833
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
1834
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
1835
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
1836
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
1837
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
1838
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
1839
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
1840
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
1841
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
1842
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
1843
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
1844
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
1845
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
1846
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
1847
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1848
+ ]) {
1849
+ try {
1850
+ await client.execute(col);
1851
+ } catch {
1852
+ }
1853
+ }
1319
1854
  }
1320
1855
  async function disposeDatabase() {
1856
+ if (_daemonClient) {
1857
+ _daemonClient.close();
1858
+ _daemonClient = null;
1859
+ }
1321
1860
  if (_client) {
1322
1861
  _client.close();
1323
1862
  _client = null;
1324
1863
  _resilientClient = null;
1325
1864
  }
1326
1865
  }
1327
- var _client, _resilientClient, initTurso, disposeTurso;
1866
+ var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
1328
1867
  var init_database = __esm({
1329
1868
  "src/lib/database.ts"() {
1330
1869
  "use strict";
@@ -1332,6 +1871,7 @@ var init_database = __esm({
1332
1871
  init_employees();
1333
1872
  _client = null;
1334
1873
  _resilientClient = null;
1874
+ _daemonClient = null;
1335
1875
  initTurso = initDatabase;
1336
1876
  disposeTurso = disposeDatabase;
1337
1877
  }
@@ -1455,7 +1995,7 @@ __export(global_procedures_exports, {
1455
1995
  loadGlobalProcedures: () => loadGlobalProcedures,
1456
1996
  storeGlobalProcedure: () => storeGlobalProcedure
1457
1997
  });
1458
- import { randomUUID } from "crypto";
1998
+ import { randomUUID as randomUUID2 } from "crypto";
1459
1999
  async function loadGlobalProcedures() {
1460
2000
  const client = getClient();
1461
2001
  const result = await client.execute({
@@ -1484,7 +2024,7 @@ ${sections.join("\n\n")}
1484
2024
  `;
1485
2025
  }
1486
2026
  async function storeGlobalProcedure(input) {
1487
- const id = randomUUID();
2027
+ const id = randomUUID2();
1488
2028
  const now = (/* @__PURE__ */ new Date()).toISOString();
1489
2029
  const client = getClient();
1490
2030
  await client.execute({
@@ -1535,14 +2075,14 @@ __export(keychain_exports, {
1535
2075
  setMasterKey: () => setMasterKey
1536
2076
  });
1537
2077
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1538
- import { existsSync as existsSync3 } from "fs";
1539
- import path3 from "path";
2078
+ import { existsSync as existsSync4 } from "fs";
2079
+ import path4 from "path";
1540
2080
  import os3 from "os";
1541
2081
  function getKeyDir() {
1542
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
2082
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os3.homedir(), ".exe-os");
1543
2083
  }
1544
2084
  function getKeyPath() {
1545
- return path3.join(getKeyDir(), "master.key");
2085
+ return path4.join(getKeyDir(), "master.key");
1546
2086
  }
1547
2087
  async function tryKeytar() {
1548
2088
  try {
@@ -1563,13 +2103,21 @@ async function getMasterKey() {
1563
2103
  }
1564
2104
  }
1565
2105
  const keyPath = getKeyPath();
1566
- if (!existsSync3(keyPath)) {
2106
+ if (!existsSync4(keyPath)) {
2107
+ process.stderr.write(
2108
+ `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2109
+ `
2110
+ );
1567
2111
  return null;
1568
2112
  }
1569
2113
  try {
1570
2114
  const content = await readFile3(keyPath, "utf-8");
1571
2115
  return Buffer.from(content.trim(), "base64");
1572
- } catch {
2116
+ } catch (err) {
2117
+ process.stderr.write(
2118
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
2119
+ `
2120
+ );
1573
2121
  return null;
1574
2122
  }
1575
2123
  }
@@ -1598,7 +2146,7 @@ async function deleteMasterKey() {
1598
2146
  }
1599
2147
  }
1600
2148
  const keyPath = getKeyPath();
1601
- if (existsSync3(keyPath)) {
2149
+ if (existsSync4(keyPath)) {
1602
2150
  await unlink(keyPath);
1603
2151
  }
1604
2152
  }
@@ -1708,12 +2256,12 @@ __export(shard_manager_exports, {
1708
2256
  listShards: () => listShards,
1709
2257
  shardExists: () => shardExists
1710
2258
  });
1711
- import path4 from "path";
1712
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
2259
+ import path5 from "path";
2260
+ import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
1713
2261
  import { createClient as createClient2 } from "@libsql/client";
1714
2262
  function initShardManager(encryptionKey) {
1715
2263
  _encryptionKey = encryptionKey;
1716
- if (!existsSync4(SHARDS_DIR)) {
2264
+ if (!existsSync5(SHARDS_DIR)) {
1717
2265
  mkdirSync(SHARDS_DIR, { recursive: true });
1718
2266
  }
1719
2267
  _shardingEnabled = true;
@@ -1734,7 +2282,7 @@ function getShardClient(projectName) {
1734
2282
  }
1735
2283
  const cached = _shards.get(safeName);
1736
2284
  if (cached) return cached;
1737
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
2285
+ const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1738
2286
  const client = createClient2({
1739
2287
  url: `file:${dbPath}`,
1740
2288
  encryptionKey: _encryptionKey
@@ -1744,10 +2292,10 @@ function getShardClient(projectName) {
1744
2292
  }
1745
2293
  function shardExists(projectName) {
1746
2294
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1747
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
2295
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1748
2296
  }
1749
2297
  function listShards() {
1750
- if (!existsSync4(SHARDS_DIR)) return [];
2298
+ if (!existsSync5(SHARDS_DIR)) return [];
1751
2299
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1752
2300
  }
1753
2301
  async function ensureShardSchema(client) {
@@ -1933,7 +2481,7 @@ var init_shard_manager = __esm({
1933
2481
  "src/lib/shard-manager.ts"() {
1934
2482
  "use strict";
1935
2483
  init_config();
1936
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2484
+ SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
1937
2485
  _shards = /* @__PURE__ */ new Map();
1938
2486
  _encryptionKey = null;
1939
2487
  _shardingEnabled = false;
@@ -1941,6 +2489,7 @@ var init_shard_manager = __esm({
1941
2489
  });
1942
2490
 
1943
2491
  // src/lib/store.ts
2492
+ import { createHash } from "crypto";
1944
2493
  function isBusyError2(err) {
1945
2494
  if (err instanceof Error) {
1946
2495
  const msg = err.message.toLowerCase();
@@ -2036,13 +2585,13 @@ __export(session_registry_exports, {
2036
2585
  pruneStaleSessions: () => pruneStaleSessions,
2037
2586
  registerSession: () => registerSession
2038
2587
  });
2039
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
2588
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
2040
2589
  import { execSync as execSync2 } from "child_process";
2041
- import path5 from "path";
2590
+ import path6 from "path";
2042
2591
  import os4 from "os";
2043
2592
  function registerSession(entry) {
2044
- const dir = path5.dirname(REGISTRY_PATH);
2045
- if (!existsSync5(dir)) {
2593
+ const dir = path6.dirname(REGISTRY_PATH);
2594
+ if (!existsSync6(dir)) {
2046
2595
  mkdirSync2(dir, { recursive: true });
2047
2596
  }
2048
2597
  const sessions = listSessions();
@@ -2056,7 +2605,7 @@ function registerSession(entry) {
2056
2605
  }
2057
2606
  function listSessions() {
2058
2607
  try {
2059
- const raw = readFileSync3(REGISTRY_PATH, "utf8");
2608
+ const raw = readFileSync4(REGISTRY_PATH, "utf8");
2060
2609
  return JSON.parse(raw);
2061
2610
  } catch {
2062
2611
  return [];
@@ -2085,7 +2634,7 @@ var REGISTRY_PATH;
2085
2634
  var init_session_registry = __esm({
2086
2635
  "src/lib/session-registry.ts"() {
2087
2636
  "use strict";
2088
- REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
2637
+ REGISTRY_PATH = path6.join(os4.homedir(), ".exe-os", "session-registry.json");
2089
2638
  }
2090
2639
  });
2091
2640
 
@@ -2304,18 +2853,69 @@ var init_provider_table = __esm({
2304
2853
  }
2305
2854
  });
2306
2855
 
2856
+ // src/lib/runtime-table.ts
2857
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
2858
+ var init_runtime_table = __esm({
2859
+ "src/lib/runtime-table.ts"() {
2860
+ "use strict";
2861
+ RUNTIME_TABLE = {
2862
+ codex: {
2863
+ binary: "codex",
2864
+ launchMode: "exec",
2865
+ autoApproveFlag: "--full-auto",
2866
+ inlineFlag: "--no-alt-screen",
2867
+ apiKeyEnv: "OPENAI_API_KEY",
2868
+ defaultModel: "gpt-5.4"
2869
+ }
2870
+ };
2871
+ DEFAULT_RUNTIME = "claude";
2872
+ }
2873
+ });
2874
+
2875
+ // src/lib/agent-config.ts
2876
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
2877
+ import path7 from "path";
2878
+ function loadAgentConfig() {
2879
+ if (!existsSync7(AGENT_CONFIG_PATH)) return {};
2880
+ try {
2881
+ return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
2882
+ } catch {
2883
+ return {};
2884
+ }
2885
+ }
2886
+ function getAgentRuntime(agentId) {
2887
+ const config = loadAgentConfig();
2888
+ const entry = config[agentId];
2889
+ if (entry) return entry;
2890
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
2891
+ }
2892
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
2893
+ var init_agent_config = __esm({
2894
+ "src/lib/agent-config.ts"() {
2895
+ "use strict";
2896
+ init_config();
2897
+ init_runtime_table();
2898
+ AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
2899
+ DEFAULT_MODELS = {
2900
+ claude: "claude-opus-4",
2901
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
2902
+ opencode: "minimax-m2.7"
2903
+ };
2904
+ }
2905
+ });
2906
+
2307
2907
  // src/lib/intercom-queue.ts
2308
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
2309
- import path6 from "path";
2908
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2909
+ import path8 from "path";
2310
2910
  import os5 from "os";
2311
2911
  function ensureDir() {
2312
- const dir = path6.dirname(QUEUE_PATH);
2313
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2912
+ const dir = path8.dirname(QUEUE_PATH);
2913
+ if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
2314
2914
  }
2315
2915
  function readQueue() {
2316
2916
  try {
2317
- if (!existsSync6(QUEUE_PATH)) return [];
2318
- return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
2917
+ if (!existsSync8(QUEUE_PATH)) return [];
2918
+ return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
2319
2919
  } catch {
2320
2920
  return [];
2321
2921
  }
@@ -2323,7 +2923,7 @@ function readQueue() {
2323
2923
  function writeQueue(queue) {
2324
2924
  ensureDir();
2325
2925
  const tmp = `${QUEUE_PATH}.tmp`;
2326
- writeFileSync3(tmp, JSON.stringify(queue, null, 2));
2926
+ writeFileSync4(tmp, JSON.stringify(queue, null, 2));
2327
2927
  renameSync3(tmp, QUEUE_PATH);
2328
2928
  }
2329
2929
  function queueIntercom(targetSession, reason) {
@@ -2347,9 +2947,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
2347
2947
  var init_intercom_queue = __esm({
2348
2948
  "src/lib/intercom-queue.ts"() {
2349
2949
  "use strict";
2350
- QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2950
+ QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2351
2951
  TTL_MS = 60 * 60 * 1e3;
2352
- INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
2952
+ INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
2353
2953
  }
2354
2954
  });
2355
2955
 
@@ -2370,9 +2970,9 @@ __export(license_exports, {
2370
2970
  stopLicenseRevalidation: () => stopLicenseRevalidation,
2371
2971
  validateLicense: () => validateLicense
2372
2972
  });
2373
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2374
- import { randomUUID as randomUUID2 } from "crypto";
2375
- import path7 from "path";
2973
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
2974
+ import { randomUUID as randomUUID3 } from "crypto";
2975
+ import path9 from "path";
2376
2976
  import { jwtVerify, importSPKI } from "jose";
2377
2977
  async function fetchRetry(url, init) {
2378
2978
  try {
@@ -2383,37 +2983,37 @@ async function fetchRetry(url, init) {
2383
2983
  }
2384
2984
  }
2385
2985
  function loadDeviceId() {
2386
- const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
2986
+ const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
2387
2987
  try {
2388
- if (existsSync7(deviceJsonPath)) {
2389
- const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
2988
+ if (existsSync9(deviceJsonPath)) {
2989
+ const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
2390
2990
  if (data.deviceId) return data.deviceId;
2391
2991
  }
2392
2992
  } catch {
2393
2993
  }
2394
2994
  try {
2395
- if (existsSync7(DEVICE_ID_PATH)) {
2396
- const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
2995
+ if (existsSync9(DEVICE_ID_PATH)) {
2996
+ const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
2397
2997
  if (id2) return id2;
2398
2998
  }
2399
2999
  } catch {
2400
3000
  }
2401
- const id = randomUUID2();
2402
- mkdirSync4(EXE_AI_DIR, { recursive: true });
2403
- writeFileSync4(DEVICE_ID_PATH, id, "utf8");
3001
+ const id = randomUUID3();
3002
+ mkdirSync5(EXE_AI_DIR, { recursive: true });
3003
+ writeFileSync5(DEVICE_ID_PATH, id, "utf8");
2404
3004
  return id;
2405
3005
  }
2406
3006
  function loadLicense() {
2407
3007
  try {
2408
- if (!existsSync7(LICENSE_PATH)) return null;
2409
- return readFileSync5(LICENSE_PATH, "utf8").trim();
3008
+ if (!existsSync9(LICENSE_PATH)) return null;
3009
+ return readFileSync7(LICENSE_PATH, "utf8").trim();
2410
3010
  } catch {
2411
3011
  return null;
2412
3012
  }
2413
3013
  }
2414
3014
  function saveLicense(apiKey) {
2415
- mkdirSync4(EXE_AI_DIR, { recursive: true });
2416
- writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3015
+ mkdirSync5(EXE_AI_DIR, { recursive: true });
3016
+ writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
2417
3017
  }
2418
3018
  async function verifyLicenseJwt(token) {
2419
3019
  try {
@@ -2439,8 +3039,8 @@ async function verifyLicenseJwt(token) {
2439
3039
  }
2440
3040
  async function getCachedLicense() {
2441
3041
  try {
2442
- if (!existsSync7(CACHE_PATH)) return null;
2443
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
3042
+ if (!existsSync9(CACHE_PATH)) return null;
3043
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
2444
3044
  if (!raw.token || typeof raw.token !== "string") return null;
2445
3045
  return await verifyLicenseJwt(raw.token);
2446
3046
  } catch {
@@ -2449,8 +3049,8 @@ async function getCachedLicense() {
2449
3049
  }
2450
3050
  function readCachedToken() {
2451
3051
  try {
2452
- if (!existsSync7(CACHE_PATH)) return null;
2453
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
3052
+ if (!existsSync9(CACHE_PATH)) return null;
3053
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
2454
3054
  return typeof raw.token === "string" ? raw.token : null;
2455
3055
  } catch {
2456
3056
  return null;
@@ -2484,7 +3084,7 @@ function getRawCachedPlan() {
2484
3084
  }
2485
3085
  function cacheResponse(token) {
2486
3086
  try {
2487
- writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
3087
+ writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
2488
3088
  } catch {
2489
3089
  }
2490
3090
  }
@@ -2548,9 +3148,9 @@ async function checkLicense() {
2548
3148
  let key = loadLicense();
2549
3149
  if (!key) {
2550
3150
  try {
2551
- const configPath = path7.join(EXE_AI_DIR, "config.json");
2552
- if (existsSync7(configPath)) {
2553
- const raw = JSON.parse(readFileSync5(configPath, "utf8"));
3151
+ const configPath = path9.join(EXE_AI_DIR, "config.json");
3152
+ if (existsSync9(configPath)) {
3153
+ const raw = JSON.parse(readFileSync7(configPath, "utf8"));
2554
3154
  const cloud = raw.cloud;
2555
3155
  if (cloud?.apiKey) {
2556
3156
  key = cloud.apiKey;
@@ -2709,9 +3309,9 @@ var init_license = __esm({
2709
3309
  "src/lib/license.ts"() {
2710
3310
  "use strict";
2711
3311
  init_config();
2712
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2713
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2714
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
3312
+ LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3313
+ CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3314
+ DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2715
3315
  API_BASE = "https://askexe.com/cloud";
2716
3316
  RETRY_DELAY_MS = 500;
2717
3317
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2741,12 +3341,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2741
3341
  });
2742
3342
 
2743
3343
  // src/lib/plan-limits.ts
2744
- import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2745
- import path8 from "path";
3344
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
3345
+ import path10 from "path";
2746
3346
  function getLicenseSync() {
2747
3347
  try {
2748
- if (!existsSync8(CACHE_PATH2)) return freeLicense();
2749
- const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
3348
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
3349
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
2750
3350
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2751
3351
  const parts = raw.token.split(".");
2752
3352
  if (parts.length !== 3) return freeLicense();
@@ -2784,8 +3384,8 @@ function assertEmployeeLimitSync(rosterPath) {
2784
3384
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2785
3385
  let count = 0;
2786
3386
  try {
2787
- if (existsSync8(filePath)) {
2788
- const raw = readFileSync6(filePath, "utf8");
3387
+ if (existsSync10(filePath)) {
3388
+ const raw = readFileSync8(filePath, "utf8");
2789
3389
  const employees = JSON.parse(raw);
2790
3390
  count = Array.isArray(employees) ? employees.length : 0;
2791
3391
  }
@@ -2814,19 +3414,19 @@ var init_plan_limits = __esm({
2814
3414
  this.name = "PlanLimitError";
2815
3415
  }
2816
3416
  };
2817
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3417
+ CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2818
3418
  }
2819
3419
  });
2820
3420
 
2821
3421
  // src/lib/notifications.ts
2822
3422
  import crypto from "crypto";
2823
- import path9 from "path";
3423
+ import path11 from "path";
2824
3424
  import os6 from "os";
2825
3425
  import {
2826
- readFileSync as readFileSync7,
3426
+ readFileSync as readFileSync9,
2827
3427
  readdirSync as readdirSync2,
2828
- unlinkSync as unlinkSync2,
2829
- existsSync as existsSync9,
3428
+ unlinkSync as unlinkSync3,
3429
+ existsSync as existsSync11,
2830
3430
  rmdirSync
2831
3431
  } from "fs";
2832
3432
  async function writeNotification(notification) {
@@ -2939,9 +3539,9 @@ async function markDoneTaskNotificationsAsRead() {
2939
3539
  }
2940
3540
  }
2941
3541
  async function migrateJsonNotifications() {
2942
- const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path9.join(os6.homedir(), ".exe-os");
2943
- const notifDir = path9.join(base, "notifications");
2944
- if (!existsSync9(notifDir)) return 0;
3542
+ const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path11.join(os6.homedir(), ".exe-os");
3543
+ const notifDir = path11.join(base, "notifications");
3544
+ if (!existsSync11(notifDir)) return 0;
2945
3545
  let migrated = 0;
2946
3546
  try {
2947
3547
  const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
@@ -2949,8 +3549,8 @@ async function migrateJsonNotifications() {
2949
3549
  const client = getClient();
2950
3550
  for (const file of files) {
2951
3551
  try {
2952
- const filePath = path9.join(notifDir, file);
2953
- const data = JSON.parse(readFileSync7(filePath, "utf8"));
3552
+ const filePath = path11.join(notifDir, file);
3553
+ const data = JSON.parse(readFileSync9(filePath, "utf8"));
2954
3554
  await client.execute({
2955
3555
  sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
2956
3556
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -2966,7 +3566,7 @@ async function migrateJsonNotifications() {
2966
3566
  data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
2967
3567
  ]
2968
3568
  });
2969
- unlinkSync2(filePath);
3569
+ unlinkSync3(filePath);
2970
3570
  migrated++;
2971
3571
  } catch {
2972
3572
  }
@@ -3097,10 +3697,11 @@ var init_session_kill_telemetry = __esm({
3097
3697
 
3098
3698
  // src/lib/tasks-crud.ts
3099
3699
  import crypto3 from "crypto";
3100
- import path10 from "path";
3700
+ import path12 from "path";
3701
+ import os7 from "os";
3101
3702
  import { execSync as execSync5 } from "child_process";
3102
3703
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3103
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
3704
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
3104
3705
  async function writeCheckpoint(input) {
3105
3706
  const client = getClient();
3106
3707
  const row = await resolveTask(client, input.taskId);
@@ -3141,6 +3742,35 @@ function extractParentFromContext(contextBody) {
3141
3742
  function slugify(title) {
3142
3743
  return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3143
3744
  }
3745
+ function buildKeywordIndex() {
3746
+ const idx = /* @__PURE__ */ new Map();
3747
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
3748
+ for (const kw of keywords) {
3749
+ const existing = idx.get(kw) ?? [];
3750
+ existing.push(role);
3751
+ idx.set(kw, existing);
3752
+ }
3753
+ }
3754
+ return idx;
3755
+ }
3756
+ function checkLaneAffinity(title, context, assigneeName) {
3757
+ const employees = loadEmployeesSync();
3758
+ const employee = employees.find((e) => e.name === assigneeName);
3759
+ if (!employee) return void 0;
3760
+ const assigneeRole = employee.role;
3761
+ const text = `${title} ${context}`.toLowerCase();
3762
+ const matchedRoles = /* @__PURE__ */ new Set();
3763
+ for (const [keyword, roles] of KEYWORD_INDEX) {
3764
+ if (text.includes(keyword)) {
3765
+ for (const role of roles) matchedRoles.add(role);
3766
+ }
3767
+ }
3768
+ if (matchedRoles.size === 0) return void 0;
3769
+ if (matchedRoles.has(assigneeRole)) return void 0;
3770
+ if (assigneeRole === "COO") return void 0;
3771
+ const expectedRoles = Array.from(matchedRoles).join(" or ");
3772
+ return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
3773
+ }
3144
3774
  async function resolveTask(client, identifier, scopeSession) {
3145
3775
  const scope = sessionScopeFilter(scopeSession);
3146
3776
  let result = await client.execute({
@@ -3190,7 +3820,14 @@ async function createTaskCore(input) {
3190
3820
  const id = crypto3.randomUUID();
3191
3821
  const now = (/* @__PURE__ */ new Date()).toISOString();
3192
3822
  const slug = slugify(input.title);
3193
- const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
3823
+ let earlySessionScope = null;
3824
+ try {
3825
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3826
+ earlySessionScope = resolveExeSession2();
3827
+ } catch {
3828
+ }
3829
+ const scope = earlySessionScope ?? "default";
3830
+ const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
3194
3831
  let blockedById = null;
3195
3832
  const initialStatus = input.blockedBy ? "blocked" : "open";
3196
3833
  if (input.blockedBy) {
@@ -3230,22 +3867,24 @@ async function createTaskCore(input) {
3230
3867
  if (dupCheck.rows.length > 0) {
3231
3868
  warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
3232
3869
  }
3870
+ if (!process.env.DISABLE_LANE_AFFINITY) {
3871
+ const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
3872
+ if (laneWarning) {
3873
+ warning = warning ? `${warning}
3874
+ ${laneWarning}` : laneWarning;
3875
+ }
3876
+ }
3233
3877
  if (input.baseDir) {
3234
3878
  try {
3235
- await mkdir4(path10.join(input.baseDir, "exe", "output"), { recursive: true });
3236
- await mkdir4(path10.join(input.baseDir, "exe", "research"), { recursive: true });
3879
+ await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
3880
+ await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
3237
3881
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3238
3882
  await ensureGitignoreExe(input.baseDir);
3239
3883
  } catch {
3240
3884
  }
3241
3885
  }
3242
3886
  const complexity = input.complexity ?? "standard";
3243
- let sessionScope = null;
3244
- try {
3245
- const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3246
- sessionScope = resolveExeSession2();
3247
- } catch {
3248
- }
3887
+ const sessionScope = earlySessionScope;
3249
3888
  await client.execute({
3250
3889
  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)
3251
3890
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -3272,6 +3911,43 @@ async function createTaskCore(input) {
3272
3911
  now
3273
3912
  ]
3274
3913
  });
3914
+ if (input.baseDir) {
3915
+ try {
3916
+ const EXE_OS_DIR = path12.join(os7.homedir(), ".exe-os");
3917
+ const mdPath = path12.join(EXE_OS_DIR, taskFile);
3918
+ const mdDir = path12.dirname(mdPath);
3919
+ if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
3920
+ const reviewer = input.reviewer ?? input.assignedBy;
3921
+ const mdContent = `# ${input.title}
3922
+
3923
+ **ID:** ${id}
3924
+ **Status:** ${initialStatus}
3925
+ **Priority:** ${input.priority}
3926
+ **Assigned by:** ${input.assignedBy}
3927
+ **Assigned to:** ${input.assignedTo}
3928
+ **Project:** ${input.projectName}
3929
+ **Created:** ${now.split("T")[0]}${parentTaskId ? `
3930
+ **Parent task:** ${parentTaskId}` : ""}
3931
+ **Reviewer:** ${reviewer}
3932
+
3933
+ ## Context
3934
+
3935
+ ${input.context}
3936
+
3937
+ ## MANDATORY: When done
3938
+
3939
+ You MUST call update_task with status "done" and a result summary when finished.
3940
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3941
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
3942
+ `;
3943
+ await writeFile4(mdPath, mdContent, "utf-8");
3944
+ } catch (err) {
3945
+ process.stderr.write(
3946
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
3947
+ `
3948
+ );
3949
+ }
3950
+ }
3275
3951
  return {
3276
3952
  id,
3277
3953
  title: input.title,
@@ -3464,7 +4140,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
3464
4140
  return { row, taskFile, now, taskId };
3465
4141
  }
3466
4142
  }
3467
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
4143
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
3468
4144
  process.stderr.write(
3469
4145
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
3470
4146
  `
@@ -3529,9 +4205,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3529
4205
  return { taskFile, assignedTo, assignedBy, taskSlug };
3530
4206
  }
3531
4207
  async function ensureArchitectureDoc(baseDir, projectName) {
3532
- const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
4208
+ const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
3533
4209
  try {
3534
- if (existsSync10(archPath)) return;
4210
+ if (existsSync12(archPath)) return;
3535
4211
  const template = [
3536
4212
  `# ${projectName} \u2014 System Architecture`,
3537
4213
  "",
@@ -3564,10 +4240,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3564
4240
  }
3565
4241
  }
3566
4242
  async function ensureGitignoreExe(baseDir) {
3567
- const gitignorePath = path10.join(baseDir, ".gitignore");
4243
+ const gitignorePath = path12.join(baseDir, ".gitignore");
3568
4244
  try {
3569
- if (existsSync10(gitignorePath)) {
3570
- const content = readFileSync8(gitignorePath, "utf-8");
4245
+ if (existsSync12(gitignorePath)) {
4246
+ const content = readFileSync10(gitignorePath, "utf-8");
3571
4247
  if (/^\/?exe\/?$/m.test(content)) return;
3572
4248
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3573
4249
  } else {
@@ -3576,20 +4252,30 @@ async function ensureGitignoreExe(baseDir) {
3576
4252
  } catch {
3577
4253
  }
3578
4254
  }
3579
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
4255
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
3580
4256
  var init_tasks_crud = __esm({
3581
4257
  "src/lib/tasks-crud.ts"() {
3582
4258
  "use strict";
3583
4259
  init_database();
3584
4260
  init_task_scope();
4261
+ init_employees();
4262
+ LANE_KEYWORDS = {
4263
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
4264
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
4265
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
4266
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
4267
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
4268
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
4269
+ };
4270
+ KEYWORD_INDEX = buildKeywordIndex();
3585
4271
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
3586
4272
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
3587
4273
  }
3588
4274
  });
3589
4275
 
3590
4276
  // src/lib/tasks-review.ts
3591
- import path11 from "path";
3592
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
4277
+ import path13 from "path";
4278
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
3593
4279
  async function countPendingReviews(sessionScope) {
3594
4280
  const client = getClient();
3595
4281
  if (sessionScope) {
@@ -3611,7 +4297,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
3611
4297
  const result2 = await client.execute({
3612
4298
  sql: `SELECT COUNT(*) as cnt FROM tasks
3613
4299
  WHERE status = 'needs_review' AND updated_at > ?
3614
- AND (session_scope = ? OR session_scope IS NULL)`,
4300
+ AND session_scope = ?`,
3615
4301
  args: [sinceIso, sessionScope]
3616
4302
  });
3617
4303
  return Number(result2.rows[0]?.cnt) || 0;
@@ -3629,7 +4315,7 @@ async function listPendingReviews(limit, sessionScope) {
3629
4315
  const result2 = await client.execute({
3630
4316
  sql: `SELECT title, assigned_to, project_name FROM tasks
3631
4317
  WHERE status = 'needs_review'
3632
- AND (session_scope = ? OR session_scope IS NULL)
4318
+ AND session_scope = ?
3633
4319
  ORDER BY priority ASC, created_at DESC LIMIT ?`,
3634
4320
  args: [sessionScope, limit]
3635
4321
  });
@@ -3750,14 +4436,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3750
4436
  if (parts.length >= 3 && parts[0] === "review") {
3751
4437
  const agent = parts[1];
3752
4438
  const slug = parts.slice(2).join("-");
3753
- const originalTaskFile = `exe/${agent}/${slug}.md`;
4439
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
3754
4440
  const result = await client.execute({
3755
- sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
3756
- args: [now, originalTaskFile]
4441
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
4442
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
3757
4443
  });
3758
4444
  if (result.rowsAffected > 0) {
3759
4445
  process.stderr.write(
3760
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
4446
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
3761
4447
  `
3762
4448
  );
3763
4449
  }
@@ -3770,11 +4456,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3770
4456
  );
3771
4457
  }
3772
4458
  try {
3773
- const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
3774
- if (existsSync11(cacheDir)) {
4459
+ const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
4460
+ if (existsSync13(cacheDir)) {
3775
4461
  for (const f of readdirSync3(cacheDir)) {
3776
4462
  if (f.startsWith("review-notified-")) {
3777
- unlinkSync3(path11.join(cacheDir, f));
4463
+ unlinkSync4(path13.join(cacheDir, f));
3778
4464
  }
3779
4465
  }
3780
4466
  }
@@ -3795,7 +4481,7 @@ var init_tasks_review = __esm({
3795
4481
  });
3796
4482
 
3797
4483
  // src/lib/tasks-chain.ts
3798
- import path12 from "path";
4484
+ import path14 from "path";
3799
4485
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3800
4486
  async function cascadeUnblock(taskId, baseDir, now) {
3801
4487
  const client = getClient();
@@ -3812,7 +4498,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3812
4498
  });
3813
4499
  for (const ur of unblockedRows.rows) {
3814
4500
  try {
3815
- const ubFile = path12.join(baseDir, String(ur.task_file));
4501
+ const ubFile = path14.join(baseDir, String(ur.task_file));
3816
4502
  let ubContent = await readFile4(ubFile, "utf-8");
3817
4503
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3818
4504
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3886,7 +4572,7 @@ __export(project_name_exports, {
3886
4572
  getProjectName: () => getProjectName
3887
4573
  });
3888
4574
  import { execSync as execSync6 } from "child_process";
3889
- import path13 from "path";
4575
+ import path15 from "path";
3890
4576
  function getProjectName(cwd) {
3891
4577
  const dir = cwd ?? process.cwd();
3892
4578
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3899,7 +4585,7 @@ function getProjectName(cwd) {
3899
4585
  timeout: 2e3,
3900
4586
  stdio: ["pipe", "pipe", "pipe"]
3901
4587
  }).trim();
3902
- repoRoot = path13.dirname(gitCommonDir);
4588
+ repoRoot = path15.dirname(gitCommonDir);
3903
4589
  } catch {
3904
4590
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3905
4591
  cwd: dir,
@@ -3908,11 +4594,11 @@ function getProjectName(cwd) {
3908
4594
  stdio: ["pipe", "pipe", "pipe"]
3909
4595
  }).trim();
3910
4596
  }
3911
- _cached2 = path13.basename(repoRoot);
4597
+ _cached2 = path15.basename(repoRoot);
3912
4598
  _cachedCwd = dir;
3913
4599
  return _cached2;
3914
4600
  } catch {
3915
- _cached2 = path13.basename(dir);
4601
+ _cached2 = path15.basename(dir);
3916
4602
  _cachedCwd = dir;
3917
4603
  return _cached2;
3918
4604
  }
@@ -3948,7 +4634,7 @@ function findSessionForProject(projectName) {
3948
4634
  const sessions = listSessions();
3949
4635
  for (const s of sessions) {
3950
4636
  const proj = s.projectDir.split("/").filter(Boolean).pop();
3951
- if (proj === projectName && (s.agentId === "exe" || isCoordinatorName(s.agentId))) return s;
4637
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
3952
4638
  }
3953
4639
  return null;
3954
4640
  }
@@ -3994,7 +4680,7 @@ var init_session_scope = __esm({
3994
4680
 
3995
4681
  // src/lib/tasks-notify.ts
3996
4682
  async function dispatchTaskToEmployee(input) {
3997
- if (input.assignedTo === "exe" || isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
4683
+ if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
3998
4684
  let crossProject = false;
3999
4685
  if (input.projectName) {
4000
4686
  try {
@@ -4389,8 +5075,8 @@ __export(tasks_exports, {
4389
5075
  updateTaskStatus: () => updateTaskStatus,
4390
5076
  writeCheckpoint: () => writeCheckpoint
4391
5077
  });
4392
- import path14 from "path";
4393
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
5078
+ import path16 from "path";
5079
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
4394
5080
  async function createTask(input) {
4395
5081
  const result = await createTaskCore(input);
4396
5082
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -4409,14 +5095,14 @@ async function updateTask(input) {
4409
5095
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4410
5096
  try {
4411
5097
  const agent = String(row.assigned_to);
4412
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4413
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
5098
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5099
+ const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
4414
5100
  if (input.status === "in_progress") {
4415
- mkdirSync5(cacheDir, { recursive: true });
4416
- writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
5101
+ mkdirSync6(cacheDir, { recursive: true });
5102
+ writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4417
5103
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
4418
5104
  try {
4419
- unlinkSync4(cachePath);
5105
+ unlinkSync5(cachePath);
4420
5106
  } catch {
4421
5107
  }
4422
5108
  }
@@ -4473,7 +5159,7 @@ async function updateTask(input) {
4473
5159
  }
4474
5160
  const isTerminal = input.status === "done" || input.status === "needs_review";
4475
5161
  if (isTerminal) {
4476
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
5162
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
4477
5163
  if (!isCoordinator) {
4478
5164
  notifyTaskDone();
4479
5165
  }
@@ -4498,7 +5184,7 @@ async function updateTask(input) {
4498
5184
  }
4499
5185
  }
4500
5186
  }
4501
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
5187
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4502
5188
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
4503
5189
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
4504
5190
  taskId,
@@ -4514,7 +5200,7 @@ async function updateTask(input) {
4514
5200
  });
4515
5201
  }
4516
5202
  let nextTask;
4517
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
5203
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
4518
5204
  try {
4519
5205
  nextTask = await findNextTask(String(row.assigned_to));
4520
5206
  } catch {
@@ -4858,7 +5544,7 @@ var init_capacity_monitor = __esm({
4858
5544
  // src/lib/tmux-routing.ts
4859
5545
  var tmux_routing_exports = {};
4860
5546
  __export(tmux_routing_exports, {
4861
- acquireSpawnLock: () => acquireSpawnLock,
5547
+ acquireSpawnLock: () => acquireSpawnLock2,
4862
5548
  employeeSessionName: () => employeeSessionName,
4863
5549
  ensureEmployee: () => ensureEmployee,
4864
5550
  extractRootExe: () => extractRootExe,
@@ -4873,20 +5559,20 @@ __export(tmux_routing_exports, {
4873
5559
  notifyParentExe: () => notifyParentExe,
4874
5560
  parseParentExe: () => parseParentExe,
4875
5561
  registerParentExe: () => registerParentExe,
4876
- releaseSpawnLock: () => releaseSpawnLock,
5562
+ releaseSpawnLock: () => releaseSpawnLock2,
4877
5563
  resolveExeSession: () => resolveExeSession,
4878
5564
  sendIntercom: () => sendIntercom,
4879
5565
  spawnEmployee: () => spawnEmployee,
4880
5566
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4881
5567
  });
4882
5568
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4883
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
4884
- import path15 from "path";
4885
- import os7 from "os";
4886
- import { fileURLToPath } from "url";
4887
- import { unlinkSync as unlinkSync5 } from "fs";
5569
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
5570
+ import path17 from "path";
5571
+ import os8 from "os";
5572
+ import { fileURLToPath as fileURLToPath2 } from "url";
5573
+ import { unlinkSync as unlinkSync6 } from "fs";
4888
5574
  function spawnLockPath(sessionName) {
4889
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5575
+ return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4890
5576
  }
4891
5577
  function isProcessAlive(pid) {
4892
5578
  try {
@@ -4896,14 +5582,14 @@ function isProcessAlive(pid) {
4896
5582
  return false;
4897
5583
  }
4898
5584
  }
4899
- function acquireSpawnLock(sessionName) {
4900
- if (!existsSync12(SPAWN_LOCK_DIR)) {
4901
- mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
5585
+ function acquireSpawnLock2(sessionName) {
5586
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
5587
+ mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
4902
5588
  }
4903
5589
  const lockFile = spawnLockPath(sessionName);
4904
- if (existsSync12(lockFile)) {
5590
+ if (existsSync14(lockFile)) {
4905
5591
  try {
4906
- const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
5592
+ const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
4907
5593
  const age = Date.now() - lock.timestamp;
4908
5594
  if (isProcessAlive(lock.pid) && age < 6e4) {
4909
5595
  return false;
@@ -4911,25 +5597,25 @@ function acquireSpawnLock(sessionName) {
4911
5597
  } catch {
4912
5598
  }
4913
5599
  }
4914
- writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
5600
+ writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4915
5601
  return true;
4916
5602
  }
4917
- function releaseSpawnLock(sessionName) {
5603
+ function releaseSpawnLock2(sessionName) {
4918
5604
  try {
4919
- unlinkSync5(spawnLockPath(sessionName));
5605
+ unlinkSync6(spawnLockPath(sessionName));
4920
5606
  } catch {
4921
5607
  }
4922
5608
  }
4923
5609
  function resolveBehaviorsExporterScript() {
4924
5610
  try {
4925
- const thisFile = fileURLToPath(import.meta.url);
4926
- const scriptPath = path15.join(
4927
- path15.dirname(thisFile),
5611
+ const thisFile = fileURLToPath2(import.meta.url);
5612
+ const scriptPath = path17.join(
5613
+ path17.dirname(thisFile),
4928
5614
  "..",
4929
5615
  "bin",
4930
5616
  "exe-export-behaviors.js"
4931
5617
  );
4932
- return existsSync12(scriptPath) ? scriptPath : null;
5618
+ return existsSync14(scriptPath) ? scriptPath : null;
4933
5619
  } catch {
4934
5620
  return null;
4935
5621
  }
@@ -4995,12 +5681,12 @@ function extractRootExe(name) {
4995
5681
  return parts.length > 0 ? parts[parts.length - 1] : null;
4996
5682
  }
4997
5683
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4998
- if (!existsSync12(SESSION_CACHE)) {
4999
- mkdirSync6(SESSION_CACHE, { recursive: true });
5684
+ if (!existsSync14(SESSION_CACHE)) {
5685
+ mkdirSync7(SESSION_CACHE, { recursive: true });
5000
5686
  }
5001
5687
  const rootExe = extractRootExe(parentExe) ?? parentExe;
5002
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5003
- writeFileSync6(filePath, JSON.stringify({
5688
+ const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5689
+ writeFileSync7(filePath, JSON.stringify({
5004
5690
  parentExe: rootExe,
5005
5691
  dispatchedBy: dispatchedBy || rootExe,
5006
5692
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -5008,7 +5694,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5008
5694
  }
5009
5695
  function getParentExe(sessionKey) {
5010
5696
  try {
5011
- const data = JSON.parse(readFileSync9(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5697
+ const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5012
5698
  return data.parentExe || null;
5013
5699
  } catch {
5014
5700
  return null;
@@ -5016,8 +5702,8 @@ function getParentExe(sessionKey) {
5016
5702
  }
5017
5703
  function getDispatchedBy(sessionKey) {
5018
5704
  try {
5019
- const data = JSON.parse(readFileSync9(
5020
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5705
+ const data = JSON.parse(readFileSync11(
5706
+ path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5021
5707
  "utf8"
5022
5708
  ));
5023
5709
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5043,10 +5729,10 @@ function isEmployeeAlive(sessionName) {
5043
5729
  }
5044
5730
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
5045
5731
  const base = employeeSessionName(employeeName, exeSession);
5046
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
5732
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
5047
5733
  for (let i = 2; i <= maxInstances; i++) {
5048
5734
  const candidate = employeeSessionName(employeeName, exeSession, i);
5049
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
5735
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
5050
5736
  }
5051
5737
  return null;
5052
5738
  }
@@ -5078,32 +5764,50 @@ async function verifyPaneAtCapacity(sessionName) {
5078
5764
  }
5079
5765
  function readDebounceState() {
5080
5766
  try {
5081
- if (!existsSync12(DEBOUNCE_FILE)) return {};
5082
- return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
5767
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
5768
+ const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
5769
+ const state = {};
5770
+ for (const [key, val] of Object.entries(raw)) {
5771
+ if (typeof val === "number") {
5772
+ state[key] = { lastSent: val, pending: 0 };
5773
+ } else if (val && typeof val === "object" && "lastSent" in val) {
5774
+ state[key] = val;
5775
+ }
5776
+ }
5777
+ return state;
5083
5778
  } catch {
5084
5779
  return {};
5085
5780
  }
5086
5781
  }
5087
5782
  function writeDebounceState(state) {
5088
5783
  try {
5089
- if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5090
- writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
5784
+ if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
5785
+ writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
5091
5786
  } catch {
5092
5787
  }
5093
5788
  }
5094
5789
  function isDebounced(targetSession) {
5095
5790
  const state = readDebounceState();
5096
- const lastSent = state[targetSession] ?? 0;
5097
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
5791
+ const entry = state[targetSession];
5792
+ const lastSent = entry?.lastSent ?? 0;
5793
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
5794
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
5795
+ state[targetSession].pending++;
5796
+ writeDebounceState(state);
5797
+ return true;
5798
+ }
5799
+ return false;
5098
5800
  }
5099
5801
  function recordDebounce(targetSession) {
5100
5802
  const state = readDebounceState();
5101
- state[targetSession] = Date.now();
5803
+ const batched = state[targetSession]?.pending ?? 0;
5804
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
5102
5805
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
5103
5806
  for (const key of Object.keys(state)) {
5104
- if ((state[key] ?? 0) < cutoff) delete state[key];
5807
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
5105
5808
  }
5106
5809
  writeDebounceState(state);
5810
+ return batched;
5107
5811
  }
5108
5812
  function logIntercom(msg) {
5109
5813
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -5148,7 +5852,7 @@ function sendIntercom(targetSession) {
5148
5852
  return "skipped_exe";
5149
5853
  }
5150
5854
  if (isDebounced(targetSession)) {
5151
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
5855
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
5152
5856
  return "debounced";
5153
5857
  }
5154
5858
  try {
@@ -5160,14 +5864,14 @@ function sendIntercom(targetSession) {
5160
5864
  const sessionState = getSessionState(targetSession);
5161
5865
  if (sessionState === "no_claude") {
5162
5866
  queueIntercom(targetSession, "claude not running in session");
5163
- recordDebounce(targetSession);
5164
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
5867
+ const batched2 = recordDebounce(targetSession);
5868
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
5165
5869
  return "queued";
5166
5870
  }
5167
5871
  if (sessionState === "thinking" || sessionState === "tool") {
5168
5872
  queueIntercom(targetSession, "session busy at send time");
5169
- recordDebounce(targetSession);
5170
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
5873
+ const batched2 = recordDebounce(targetSession);
5874
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
5171
5875
  return "queued";
5172
5876
  }
5173
5877
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -5175,8 +5879,8 @@ function sendIntercom(targetSession) {
5175
5879
  transport.sendKeys(targetSession, "q");
5176
5880
  }
5177
5881
  transport.sendKeys(targetSession, "/exe-intercom");
5178
- recordDebounce(targetSession);
5179
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
5882
+ const batched = recordDebounce(targetSession);
5883
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
5180
5884
  return "delivered";
5181
5885
  } catch {
5182
5886
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -5206,7 +5910,7 @@ function notifyParentExe(sessionKey) {
5206
5910
  return true;
5207
5911
  }
5208
5912
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
5209
- if (employeeName === "exe" || isCoordinatorName(employeeName)) {
5913
+ if (isCoordinatorName(employeeName)) {
5210
5914
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
5211
5915
  }
5212
5916
  try {
@@ -5278,26 +5982,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5278
5982
  const transport = getTransport();
5279
5983
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5280
5984
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5281
- const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
5282
- const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5283
- if (!existsSync12(logDir)) {
5284
- mkdirSync6(logDir, { recursive: true });
5985
+ const logDir = path17.join(os8.homedir(), ".exe-os", "session-logs");
5986
+ const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5987
+ if (!existsSync14(logDir)) {
5988
+ mkdirSync7(logDir, { recursive: true });
5285
5989
  }
5286
5990
  transport.kill(sessionName);
5287
5991
  let cleanupSuffix = "";
5288
5992
  try {
5289
- const thisFile = fileURLToPath(import.meta.url);
5290
- const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5291
- if (existsSync12(cleanupScript)) {
5993
+ const thisFile = fileURLToPath2(import.meta.url);
5994
+ const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5995
+ if (existsSync14(cleanupScript)) {
5292
5996
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5293
5997
  }
5294
5998
  } catch {
5295
5999
  }
5296
6000
  try {
5297
- const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
6001
+ const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
5298
6002
  let claudeJson = {};
5299
6003
  try {
5300
- claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
6004
+ claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
5301
6005
  } catch {
5302
6006
  }
5303
6007
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -5305,17 +6009,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5305
6009
  const trustDir = opts?.cwd ?? projectDir;
5306
6010
  if (!projects[trustDir]) projects[trustDir] = {};
5307
6011
  projects[trustDir].hasTrustDialogAccepted = true;
5308
- writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6012
+ writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5309
6013
  } catch {
5310
6014
  }
5311
6015
  try {
5312
- const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
6016
+ const settingsDir = path17.join(os8.homedir(), ".claude", "projects");
5313
6017
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5314
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
5315
- const settingsPath = path15.join(projSettingsDir, "settings.json");
6018
+ const projSettingsDir = path17.join(settingsDir, normalizedKey);
6019
+ const settingsPath = path17.join(projSettingsDir, "settings.json");
5316
6020
  let settings = {};
5317
6021
  try {
5318
- settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
6022
+ settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
5319
6023
  } catch {
5320
6024
  }
5321
6025
  const perms = settings.permissions ?? {};
@@ -5343,21 +6047,24 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5343
6047
  if (changed) {
5344
6048
  perms.allow = allow;
5345
6049
  settings.permissions = perms;
5346
- mkdirSync6(projSettingsDir, { recursive: true });
5347
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6050
+ mkdirSync7(projSettingsDir, { recursive: true });
6051
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5348
6052
  }
5349
6053
  } catch {
5350
6054
  }
5351
6055
  const spawnCwd = opts?.cwd ?? projectDir;
5352
6056
  const useExeAgent = !!(opts?.model && opts?.provider);
5353
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
6057
+ const agentRtConfig = getAgentRuntime(employeeName);
6058
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
6059
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
6060
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
5354
6061
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
5355
6062
  let identityFlag = "";
5356
6063
  let behaviorsFlag = "";
5357
6064
  let legacyFallbackWarned = false;
5358
6065
  if (!useExeAgent && !useBinSymlink) {
5359
- const identityPath = path15.join(
5360
- os7.homedir(),
6066
+ const identityPath = path17.join(
6067
+ os8.homedir(),
5361
6068
  ".exe-os",
5362
6069
  "identity",
5363
6070
  `${employeeName}.md`
@@ -5366,13 +6073,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5366
6073
  const hasAgentFlag = claudeSupportsAgentFlag();
5367
6074
  if (hasAgentFlag) {
5368
6075
  identityFlag = ` --agent ${employeeName}`;
5369
- } else if (existsSync12(identityPath)) {
6076
+ } else if (existsSync14(identityPath)) {
5370
6077
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
5371
6078
  legacyFallbackWarned = true;
5372
6079
  }
5373
6080
  const behaviorsFile = exportBehaviorsSync(
5374
6081
  employeeName,
5375
- path15.basename(spawnCwd),
6082
+ path17.basename(spawnCwd),
5376
6083
  sessionName
5377
6084
  );
5378
6085
  if (behaviorsFile) {
@@ -5387,16 +6094,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5387
6094
  }
5388
6095
  let sessionContextFlag = "";
5389
6096
  try {
5390
- const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
5391
- mkdirSync6(ctxDir, { recursive: true });
5392
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
6097
+ const ctxDir = path17.join(os8.homedir(), ".exe-os", "session-cache");
6098
+ mkdirSync7(ctxDir, { recursive: true });
6099
+ const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
5393
6100
  const ctxContent = [
5394
6101
  `## Session Context`,
5395
6102
  `You are running in tmux session: ${sessionName}.`,
5396
6103
  `Your parent coordinator session is ${exeSession}.`,
5397
6104
  `Your employees (if any) use the -${exeSession} suffix.`
5398
6105
  ].join("\n");
5399
- writeFileSync6(ctxFile, ctxContent);
6106
+ writeFileSync7(ctxFile, ctxContent);
5400
6107
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
5401
6108
  } catch {
5402
6109
  }
@@ -5410,9 +6117,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5410
6117
  }
5411
6118
  }
5412
6119
  }
6120
+ if (useCodex) {
6121
+ const codexCfg = RUNTIME_TABLE.codex;
6122
+ if (codexCfg?.apiKeyEnv) {
6123
+ const keyVal = process.env[codexCfg.apiKeyEnv];
6124
+ if (keyVal) {
6125
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
6126
+ }
6127
+ }
6128
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
6129
+ }
6130
+ if (useOpencode) {
6131
+ const ocCfg = PROVIDER_TABLE.opencode;
6132
+ if (ocCfg?.apiKeyEnv) {
6133
+ const keyVal = process.env[ocCfg.apiKeyEnv];
6134
+ if (keyVal) {
6135
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
6136
+ }
6137
+ }
6138
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6139
+ }
6140
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6141
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
6142
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
6143
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6144
+ }
6145
+ }
5413
6146
  let spawnCommand;
5414
6147
  if (useExeAgent) {
5415
6148
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
6149
+ } else if (useCodex) {
6150
+ process.stderr.write(
6151
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
6152
+ `
6153
+ );
6154
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
6155
+ } else if (useOpencode) {
6156
+ const binName = `${employeeName}-opencode`;
6157
+ process.stderr.write(
6158
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
6159
+ `
6160
+ );
6161
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
5416
6162
  } else if (useBinSymlink) {
5417
6163
  const binName = `${employeeName}-${ccProvider}`;
5418
6164
  process.stderr.write(
@@ -5428,17 +6174,19 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5428
6174
  command: spawnCommand
5429
6175
  });
5430
6176
  if (spawnResult.error) {
5431
- releaseSpawnLock(sessionName);
6177
+ releaseSpawnLock2(sessionName);
5432
6178
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
5433
6179
  }
5434
6180
  transport.pipeLog(sessionName, logFile);
5435
6181
  try {
5436
6182
  const mySession = getMySession();
5437
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5438
- writeFileSync6(dispatchInfo, JSON.stringify({
6183
+ const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6184
+ writeFileSync7(dispatchInfo, JSON.stringify({
5439
6185
  dispatchedBy: mySession,
5440
6186
  rootExe: exeSession,
5441
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
6187
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
6188
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
6189
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
5442
6190
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5443
6191
  }));
5444
6192
  } catch {
@@ -5456,6 +6204,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5456
6204
  booted = true;
5457
6205
  break;
5458
6206
  }
6207
+ } else if (useCodex) {
6208
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
6209
+ booted = true;
6210
+ break;
6211
+ }
5459
6212
  } else {
5460
6213
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
5461
6214
  booted = true;
@@ -5466,10 +6219,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5466
6219
  }
5467
6220
  }
5468
6221
  if (!booted) {
5469
- releaseSpawnLock(sessionName);
5470
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
6222
+ releaseSpawnLock2(sessionName);
6223
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
6224
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
5471
6225
  }
5472
- if (!useExeAgent) {
6226
+ if (!useExeAgent && !useCodex) {
5473
6227
  try {
5474
6228
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
5475
6229
  } catch {
@@ -5483,7 +6237,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5483
6237
  pid: 0,
5484
6238
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
5485
6239
  });
5486
- releaseSpawnLock(sessionName);
6240
+ releaseSpawnLock2(sessionName);
5487
6241
  return { sessionName };
5488
6242
  }
5489
6243
  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;
@@ -5496,17 +6250,19 @@ var init_tmux_routing = __esm({
5496
6250
  init_cc_agent_support();
5497
6251
  init_mcp_prefix();
5498
6252
  init_provider_table();
6253
+ init_agent_config();
6254
+ init_runtime_table();
5499
6255
  init_intercom_queue();
5500
6256
  init_plan_limits();
5501
6257
  init_employees();
5502
- SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
5503
- SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
6258
+ SPAWN_LOCK_DIR = path17.join(os8.homedir(), ".exe-os", "spawn-locks");
6259
+ SESSION_CACHE = path17.join(os8.homedir(), ".exe-os", "session-cache");
5504
6260
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5505
6261
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5506
6262
  VERIFY_PANE_LINES = 200;
5507
6263
  INTERCOM_DEBOUNCE_MS = 3e4;
5508
- INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
5509
- DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
6264
+ INTERCOM_LOG2 = path17.join(os8.homedir(), ".exe-os", "intercom.log");
6265
+ DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
5510
6266
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5511
6267
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5512
6268
  }
@@ -5541,126 +6297,8 @@ var task_scanner_exports = {};
5541
6297
  __export(task_scanner_exports, {
5542
6298
  PRIORITY_RE: () => PRIORITY_RE,
5543
6299
  STATUS_RE: () => STATUS_RE,
5544
- TITLE_RE: () => TITLE_RE,
5545
- formatJson: () => formatJson,
5546
- formatMandatory: () => formatMandatory,
5547
- formatText: () => formatText,
5548
- scanAgentTasks: () => scanAgentTasks
6300
+ TITLE_RE: () => TITLE_RE
5549
6301
  });
5550
- import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync13, statSync } from "fs";
5551
- import { execSync as execSync9 } from "child_process";
5552
- import path17 from "path";
5553
- function getProjectRoot() {
5554
- try {
5555
- return execSync9("git rev-parse --show-toplevel", {
5556
- encoding: "utf8",
5557
- stdio: ["pipe", "pipe", "pipe"],
5558
- timeout: 5e3
5559
- }).trim();
5560
- } catch {
5561
- return process.cwd();
5562
- }
5563
- }
5564
- function scanAgentTasks(agentId) {
5565
- const taskDir = path17.join(getProjectRoot(), "exe", agentId);
5566
- const open = [];
5567
- const inProgress = [];
5568
- let done = 0;
5569
- let total = 0;
5570
- if (!existsSync13(taskDir)) return { open, inProgress, done, total };
5571
- try {
5572
- const files = readdirSync5(taskDir).filter((f) => f.endsWith(".md"));
5573
- total = files.length;
5574
- for (const f of files) {
5575
- try {
5576
- const content = readFileSync11(path17.join(taskDir, f), "utf8");
5577
- const statusMatch = content.match(STATUS_RE);
5578
- const status = statusMatch ? statusMatch[1].toLowerCase() : null;
5579
- if (status === "done") {
5580
- done++;
5581
- continue;
5582
- }
5583
- if (status !== "open" && status !== "in_progress") continue;
5584
- const priMatch = content.match(PRIORITY_RE);
5585
- const titleMatch = content.match(TITLE_RE);
5586
- const task = {
5587
- file: f,
5588
- title: titleMatch ? titleMatch[1] : f.replace(".md", ""),
5589
- priority: priMatch ? priMatch[1] : "P2",
5590
- status,
5591
- slug: f.replace(".md", "")
5592
- };
5593
- if (status === "in_progress") {
5594
- inProgress.push(task);
5595
- } else {
5596
- open.push(task);
5597
- }
5598
- } catch {
5599
- }
5600
- }
5601
- } catch {
5602
- }
5603
- open.sort((a, b) => a.priority.localeCompare(b.priority));
5604
- inProgress.sort((a, b) => a.priority.localeCompare(b.priority));
5605
- return { open, inProgress, done, total };
5606
- }
5607
- function formatText(agentId, result) {
5608
- const lines = [];
5609
- if (result.inProgress.length > 0) {
5610
- lines.push(`IN_PROGRESS (${result.inProgress.length}):`);
5611
- for (const t of result.inProgress) {
5612
- lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
5613
- }
5614
- }
5615
- if (result.open.length > 0) {
5616
- lines.push(`OPEN (${result.open.length}):`);
5617
- for (const t of result.open) {
5618
- lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
5619
- }
5620
- }
5621
- lines.push(`DONE: ${result.done}`);
5622
- return lines.join("\n");
5623
- }
5624
- function formatMandatory(agentId, result) {
5625
- const { open, inProgress } = result;
5626
- if (open.length === 0 && inProgress.length === 0) return "";
5627
- const lines = [];
5628
- if (inProgress.length > 0) {
5629
- const current = inProgress[0];
5630
- let stale = false;
5631
- try {
5632
- const stat = statSync(path17.join(getProjectRoot(), "exe", agentId, current.file));
5633
- const ageMin = (Date.now() - stat.mtimeMs) / 6e4;
5634
- if (ageMin > 30) stale = true;
5635
- } catch {
5636
- }
5637
- if (stale) {
5638
- lines.push(`MANDATORY: Update task status for: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
5639
- lines.push("This task has been in_progress for over 30 minutes without updates.");
5640
- lines.push("If work is done, mark done. If blocked, update status to blocked.");
5641
- } else {
5642
- lines.push(`Continue working on: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
5643
- }
5644
- if (open.length > 0) {
5645
- lines.push("Queued: " + open.map((t) => `${t.title} [${t.priority}]`).join(", "));
5646
- }
5647
- } else {
5648
- const top = open[0];
5649
- lines.push(`MANDATORY: You have ${open.length} unstarted task(s).`);
5650
- lines.push(`Highest priority: ${top.title} [${top.priority}]`);
5651
- lines.push(`File: exe/${agentId}/${top.file}`);
5652
- lines.push("Read this task file and START WORKING NOW.");
5653
- }
5654
- return lines.join("\n");
5655
- }
5656
- function formatJson(result) {
5657
- return JSON.stringify({
5658
- open: result.open.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
5659
- in_progress: result.inProgress.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
5660
- done: result.done,
5661
- total: result.total
5662
- });
5663
- }
5664
6302
  var STATUS_RE, PRIORITY_RE, TITLE_RE;
5665
6303
  var init_task_scanner = __esm({
5666
6304
  "src/lib/task-scanner.ts"() {
@@ -5681,15 +6319,15 @@ __export(worker_gate_exports, {
5681
6319
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
5682
6320
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
5683
6321
  });
5684
- import { readdirSync as readdirSync6, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync14 } from "fs";
5685
- import path18 from "path";
6322
+ import { readdirSync as readdirSync5, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync15 } from "fs";
6323
+ import path19 from "path";
5686
6324
  function tryAcquireWorkerSlot() {
5687
6325
  try {
5688
- mkdirSync8(WORKER_PID_DIR, { recursive: true });
6326
+ mkdirSync9(WORKER_PID_DIR, { recursive: true });
5689
6327
  const reservationId = `res-${process.pid}-${Date.now()}`;
5690
- const reservationPath = path18.join(WORKER_PID_DIR, `${reservationId}.pid`);
5691
- writeFileSync8(reservationPath, String(process.pid));
5692
- const files = readdirSync6(WORKER_PID_DIR);
6328
+ const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
6329
+ writeFileSync9(reservationPath, String(process.pid));
6330
+ const files = readdirSync5(WORKER_PID_DIR);
5693
6331
  let alive = 0;
5694
6332
  for (const f of files) {
5695
6333
  if (!f.endsWith(".pid")) continue;
@@ -5705,20 +6343,20 @@ function tryAcquireWorkerSlot() {
5705
6343
  alive++;
5706
6344
  } catch {
5707
6345
  try {
5708
- unlinkSync7(path18.join(WORKER_PID_DIR, f));
6346
+ unlinkSync8(path19.join(WORKER_PID_DIR, f));
5709
6347
  } catch {
5710
6348
  }
5711
6349
  }
5712
6350
  }
5713
6351
  if (alive > MAX_CONCURRENT_WORKERS) {
5714
6352
  try {
5715
- unlinkSync7(reservationPath);
6353
+ unlinkSync8(reservationPath);
5716
6354
  } catch {
5717
6355
  }
5718
6356
  return false;
5719
6357
  }
5720
6358
  try {
5721
- unlinkSync7(reservationPath);
6359
+ unlinkSync8(reservationPath);
5722
6360
  } catch {
5723
6361
  }
5724
6362
  return true;
@@ -5728,21 +6366,21 @@ function tryAcquireWorkerSlot() {
5728
6366
  }
5729
6367
  function registerWorkerPid(pid) {
5730
6368
  try {
5731
- mkdirSync8(WORKER_PID_DIR, { recursive: true });
5732
- writeFileSync8(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6369
+ mkdirSync9(WORKER_PID_DIR, { recursive: true });
6370
+ writeFileSync9(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
5733
6371
  } catch {
5734
6372
  }
5735
6373
  }
5736
6374
  function cleanupWorkerPid() {
5737
6375
  try {
5738
- unlinkSync7(path18.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6376
+ unlinkSync8(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
5739
6377
  } catch {
5740
6378
  }
5741
6379
  }
5742
6380
  function tryAcquireBackfillLock() {
5743
6381
  try {
5744
- mkdirSync8(WORKER_PID_DIR, { recursive: true });
5745
- if (existsSync14(BACKFILL_LOCK)) {
6382
+ mkdirSync9(WORKER_PID_DIR, { recursive: true });
6383
+ if (existsSync15(BACKFILL_LOCK)) {
5746
6384
  try {
5747
6385
  const pid = parseInt(
5748
6386
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -5758,7 +6396,7 @@ function tryAcquireBackfillLock() {
5758
6396
  } catch {
5759
6397
  }
5760
6398
  }
5761
- writeFileSync8(BACKFILL_LOCK, String(process.pid));
6399
+ writeFileSync9(BACKFILL_LOCK, String(process.pid));
5762
6400
  return true;
5763
6401
  } catch {
5764
6402
  return true;
@@ -5766,7 +6404,7 @@ function tryAcquireBackfillLock() {
5766
6404
  }
5767
6405
  function releaseBackfillLock() {
5768
6406
  try {
5769
- unlinkSync7(BACKFILL_LOCK);
6407
+ unlinkSync8(BACKFILL_LOCK);
5770
6408
  } catch {
5771
6409
  }
5772
6410
  }
@@ -5775,9 +6413,9 @@ var init_worker_gate = __esm({
5775
6413
  "src/lib/worker-gate.ts"() {
5776
6414
  "use strict";
5777
6415
  init_config();
5778
- WORKER_PID_DIR = path18.join(EXE_AI_DIR, "worker-pids");
6416
+ WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
5779
6417
  MAX_CONCURRENT_WORKERS = 3;
5780
- BACKFILL_LOCK = path18.join(WORKER_PID_DIR, "backfill.lock");
6418
+ BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
5781
6419
  }
5782
6420
  });
5783
6421
 
@@ -5860,6 +6498,232 @@ var init_compress = __esm({
5860
6498
  }
5861
6499
  });
5862
6500
 
6501
+ // src/lib/crdt-sync.ts
6502
+ var crdt_sync_exports = {};
6503
+ __export(crdt_sync_exports, {
6504
+ _setStatePath: () => _setStatePath,
6505
+ applyRemoteUpdate: () => applyRemoteUpdate,
6506
+ destroyCrdtDoc: () => destroyCrdtDoc,
6507
+ getDiffUpdate: () => getDiffUpdate,
6508
+ getFullState: () => getFullState,
6509
+ getStateVector: () => getStateVector,
6510
+ importExistingBehaviors: () => importExistingBehaviors,
6511
+ importExistingMemories: () => importExistingMemories,
6512
+ initCrdtDoc: () => initCrdtDoc,
6513
+ isCrdtSyncEnabled: () => isCrdtSyncEnabled,
6514
+ onUpdate: () => onUpdate,
6515
+ readAllBehaviors: () => readAllBehaviors,
6516
+ readAllMemories: () => readAllMemories,
6517
+ rebuildFromDb: () => rebuildFromDb
6518
+ });
6519
+ import * as Y from "yjs";
6520
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
6521
+ import path20 from "path";
6522
+ import { homedir } from "os";
6523
+ function getStatePath() {
6524
+ return _statePathOverride ?? DEFAULT_STATE_PATH;
6525
+ }
6526
+ function _setStatePath(p) {
6527
+ _statePathOverride = p;
6528
+ }
6529
+ function initCrdtDoc() {
6530
+ if (doc) return doc;
6531
+ doc = new Y.Doc();
6532
+ const sp = getStatePath();
6533
+ if (existsSync16(sp)) {
6534
+ try {
6535
+ const state = readFileSync13(sp);
6536
+ Y.applyUpdate(doc, new Uint8Array(state));
6537
+ } catch {
6538
+ console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
6539
+ try {
6540
+ unlinkSync9(sp);
6541
+ } catch {
6542
+ }
6543
+ rebuildFromDb().catch((err) => {
6544
+ console.warn("[crdt-sync] rebuild from DB failed:", err);
6545
+ });
6546
+ }
6547
+ }
6548
+ doc.on("update", () => {
6549
+ persistState();
6550
+ });
6551
+ return doc;
6552
+ }
6553
+ function getMemoriesMap() {
6554
+ const d = initCrdtDoc();
6555
+ return d.getMap("memories");
6556
+ }
6557
+ function getBehaviorsMap() {
6558
+ const d = initCrdtDoc();
6559
+ return d.getMap("behaviors");
6560
+ }
6561
+ function applyRemoteUpdate(update) {
6562
+ const d = initCrdtDoc();
6563
+ Y.applyUpdate(d, update);
6564
+ }
6565
+ function getFullState() {
6566
+ const d = initCrdtDoc();
6567
+ return Y.encodeStateAsUpdate(d);
6568
+ }
6569
+ function getDiffUpdate(remoteStateVector) {
6570
+ const d = initCrdtDoc();
6571
+ return Y.encodeStateAsUpdate(d, remoteStateVector);
6572
+ }
6573
+ function getStateVector() {
6574
+ const d = initCrdtDoc();
6575
+ return Y.encodeStateVector(d);
6576
+ }
6577
+ function importExistingMemories(memories) {
6578
+ const map = getMemoriesMap();
6579
+ const d = initCrdtDoc();
6580
+ let imported = 0;
6581
+ d.transact(() => {
6582
+ for (const mem of memories) {
6583
+ if (!mem.id) continue;
6584
+ if (map.has(mem.id)) continue;
6585
+ const entry = new Y.Map();
6586
+ entry.set("id", mem.id);
6587
+ entry.set("agent_id", mem.agent_id ?? null);
6588
+ entry.set("agent_role", mem.agent_role ?? null);
6589
+ entry.set("session_id", mem.session_id ?? null);
6590
+ entry.set("timestamp", mem.timestamp ?? null);
6591
+ entry.set("tool_name", mem.tool_name ?? null);
6592
+ entry.set("project_name", mem.project_name ?? null);
6593
+ entry.set("has_error", mem.has_error ?? 0);
6594
+ entry.set("raw_text", mem.raw_text ?? "");
6595
+ entry.set("version", mem.version ?? 0);
6596
+ entry.set("author_device_id", mem.author_device_id ?? null);
6597
+ entry.set("scope", mem.scope ?? "business");
6598
+ map.set(mem.id, entry);
6599
+ imported++;
6600
+ }
6601
+ });
6602
+ return imported;
6603
+ }
6604
+ function importExistingBehaviors(behaviors) {
6605
+ const map = getBehaviorsMap();
6606
+ const d = initCrdtDoc();
6607
+ let imported = 0;
6608
+ d.transact(() => {
6609
+ for (const beh of behaviors) {
6610
+ if (!beh.id) continue;
6611
+ if (map.has(beh.id)) continue;
6612
+ const entry = new Y.Map();
6613
+ entry.set("id", beh.id);
6614
+ entry.set("agent_id", beh.agent_id ?? null);
6615
+ entry.set("project_name", beh.project_name ?? null);
6616
+ entry.set("domain", beh.domain ?? null);
6617
+ entry.set("content", beh.content ?? null);
6618
+ entry.set("active", beh.active ?? 1);
6619
+ entry.set("priority", beh.priority ?? "p1");
6620
+ entry.set("created_at", beh.created_at ?? null);
6621
+ entry.set("updated_at", beh.updated_at ?? null);
6622
+ map.set(beh.id, entry);
6623
+ imported++;
6624
+ }
6625
+ });
6626
+ return imported;
6627
+ }
6628
+ function readAllMemories() {
6629
+ const map = getMemoriesMap();
6630
+ const records = [];
6631
+ map.forEach((entry, id) => {
6632
+ records.push({
6633
+ id,
6634
+ agent_id: entry.get("agent_id"),
6635
+ agent_role: entry.get("agent_role"),
6636
+ session_id: entry.get("session_id"),
6637
+ timestamp: entry.get("timestamp"),
6638
+ tool_name: entry.get("tool_name"),
6639
+ project_name: entry.get("project_name"),
6640
+ has_error: entry.get("has_error"),
6641
+ raw_text: entry.get("raw_text"),
6642
+ version: entry.get("version"),
6643
+ author_device_id: entry.get("author_device_id"),
6644
+ scope: entry.get("scope")
6645
+ });
6646
+ });
6647
+ return records;
6648
+ }
6649
+ function readAllBehaviors() {
6650
+ const map = getBehaviorsMap();
6651
+ const records = [];
6652
+ map.forEach((entry, id) => {
6653
+ records.push({
6654
+ id,
6655
+ agent_id: entry.get("agent_id"),
6656
+ project_name: entry.get("project_name"),
6657
+ domain: entry.get("domain"),
6658
+ content: entry.get("content"),
6659
+ active: entry.get("active"),
6660
+ priority: entry.get("priority"),
6661
+ created_at: entry.get("created_at"),
6662
+ updated_at: entry.get("updated_at")
6663
+ });
6664
+ });
6665
+ return records;
6666
+ }
6667
+ function onUpdate(callback) {
6668
+ const d = initCrdtDoc();
6669
+ const handler = (update) => callback(update);
6670
+ d.on("update", handler);
6671
+ return () => d.off("update", handler);
6672
+ }
6673
+ function persistState() {
6674
+ if (!doc) return;
6675
+ try {
6676
+ const sp = getStatePath();
6677
+ const dir = path20.dirname(sp);
6678
+ if (!existsSync16(dir)) mkdirSync10(dir, { recursive: true });
6679
+ const state = Y.encodeStateAsUpdate(doc);
6680
+ writeFileSync10(sp, Buffer.from(state));
6681
+ } catch {
6682
+ }
6683
+ }
6684
+ function isCrdtSyncEnabled() {
6685
+ return process.env.EXE_CRDT_SYNC !== "0";
6686
+ }
6687
+ async function rebuildFromDb() {
6688
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6689
+ const client = getClient2();
6690
+ const result = await client.execute(
6691
+ "SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
6692
+ );
6693
+ const memories = result.rows.map((row) => ({
6694
+ id: String(row.id),
6695
+ agent_id: row.agent_id,
6696
+ agent_role: row.agent_role,
6697
+ session_id: row.session_id,
6698
+ timestamp: row.timestamp,
6699
+ tool_name: row.tool_name,
6700
+ project_name: row.project_name,
6701
+ has_error: row.has_error,
6702
+ raw_text: row.raw_text,
6703
+ version: row.version,
6704
+ author_device_id: row.author_device_id,
6705
+ scope: row.scope
6706
+ }));
6707
+ const count = importExistingMemories(memories);
6708
+ persistState();
6709
+ return count;
6710
+ }
6711
+ function destroyCrdtDoc() {
6712
+ if (doc) {
6713
+ doc.destroy();
6714
+ doc = null;
6715
+ }
6716
+ }
6717
+ var DEFAULT_STATE_PATH, _statePathOverride, doc;
6718
+ var init_crdt_sync = __esm({
6719
+ "src/lib/crdt-sync.ts"() {
6720
+ "use strict";
6721
+ DEFAULT_STATE_PATH = path20.join(homedir(), ".exe-os", "crdt-state.bin");
6722
+ _statePathOverride = null;
6723
+ doc = null;
6724
+ }
6725
+ });
6726
+
5863
6727
  // src/lib/cloud-sync.ts
5864
6728
  var cloud_sync_exports = {};
5865
6729
  __export(cloud_sync_exports, {
@@ -5888,16 +6752,16 @@ __export(cloud_sync_exports, {
5888
6752
  mergeRosterFromRemote: () => mergeRosterFromRemote,
5889
6753
  recordRosterDeletion: () => recordRosterDeletion
5890
6754
  });
5891
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync15, readdirSync as readdirSync7, mkdirSync as mkdirSync9, appendFileSync as appendFileSync2, unlinkSync as unlinkSync8, openSync, closeSync } from "fs";
6755
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync17, readdirSync as readdirSync6, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
5892
6756
  import crypto7 from "crypto";
5893
- import path19 from "path";
5894
- import { homedir } from "os";
6757
+ import path21 from "path";
6758
+ import { homedir as homedir2 } from "os";
5895
6759
  function sqlSafe(v) {
5896
6760
  return v === void 0 ? null : v;
5897
6761
  }
5898
6762
  function logError(msg) {
5899
6763
  try {
5900
- const logPath = path19.join(homedir(), ".exe-os", "workers.log");
6764
+ const logPath = path21.join(homedir2(), ".exe-os", "workers.log");
5901
6765
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
5902
6766
  `);
5903
6767
  } catch {
@@ -5905,20 +6769,20 @@ function logError(msg) {
5905
6769
  }
5906
6770
  async function withRosterLock(fn) {
5907
6771
  try {
5908
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
5909
- closeSync(fd);
5910
- writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
6772
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
6773
+ closeSync2(fd);
6774
+ writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
5911
6775
  } catch (err) {
5912
6776
  if (err.code === "EEXIST") {
5913
6777
  try {
5914
- const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
6778
+ const ts = parseInt(readFileSync14(ROSTER_LOCK_PATH, "utf-8"), 10);
5915
6779
  if (Date.now() - ts < LOCK_STALE_MS) {
5916
6780
  throw new Error("Roster merge already in progress \u2014 another sync is running");
5917
6781
  }
5918
- unlinkSync8(ROSTER_LOCK_PATH);
5919
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
5920
- closeSync(fd);
5921
- writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
6782
+ unlinkSync10(ROSTER_LOCK_PATH);
6783
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
6784
+ closeSync2(fd);
6785
+ writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
5922
6786
  } catch (retryErr) {
5923
6787
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
5924
6788
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -5931,7 +6795,7 @@ async function withRosterLock(fn) {
5931
6795
  return await fn();
5932
6796
  } finally {
5933
6797
  try {
5934
- unlinkSync8(ROSTER_LOCK_PATH);
6798
+ unlinkSync10(ROSTER_LOCK_PATH);
5935
6799
  } catch {
5936
6800
  }
5937
6801
  }
@@ -6076,29 +6940,75 @@ async function cloudSync(config) {
6076
6940
  const pullResult = await cloudPull(lastPullVersion, config);
6077
6941
  let pulled = 0;
6078
6942
  if (pullResult.records.length > 0) {
6079
- const stmts = pullResult.records.map((rec) => ({
6080
- sql: `INSERT OR REPLACE INTO memories
6081
- (id, agent_id, agent_role, session_id, timestamp,
6082
- tool_name, project_name, has_error, raw_text, version,
6083
- author_device_id, scope)
6084
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6085
- args: [
6086
- sqlSafe(rec.id),
6087
- sqlSafe(rec.agent_id),
6088
- sqlSafe(rec.agent_role),
6089
- sqlSafe(rec.session_id),
6090
- sqlSafe(rec.timestamp),
6091
- sqlSafe(rec.tool_name),
6092
- sqlSafe(rec.project_name),
6093
- sqlSafe(rec.has_error ?? 0),
6094
- sqlSafe(rec.raw_text ?? ""),
6095
- sqlSafe(rec.version ?? 0),
6096
- sqlSafe(rec.author_device_id),
6097
- sqlSafe(rec.scope ?? "business")
6098
- ]
6099
- }));
6100
- await client.batch(stmts, "write");
6101
- pulled = pullResult.records.length;
6943
+ if (isCrdtSyncEnabled()) {
6944
+ const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
6945
+ initCrdtDoc2();
6946
+ importExistingMemories2(
6947
+ pullResult.records.map((rec) => ({
6948
+ id: String(rec.id ?? ""),
6949
+ agent_id: rec.agent_id,
6950
+ agent_role: rec.agent_role,
6951
+ session_id: rec.session_id,
6952
+ timestamp: rec.timestamp,
6953
+ tool_name: rec.tool_name,
6954
+ project_name: rec.project_name,
6955
+ has_error: rec.has_error ?? 0,
6956
+ raw_text: rec.raw_text ?? "",
6957
+ version: rec.version ?? 0,
6958
+ author_device_id: rec.author_device_id,
6959
+ scope: rec.scope ?? "business"
6960
+ }))
6961
+ );
6962
+ const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
6963
+ const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
6964
+ const stmts = merged.map((rec) => ({
6965
+ sql: `INSERT OR REPLACE INTO memories
6966
+ (id, agent_id, agent_role, session_id, timestamp,
6967
+ tool_name, project_name, has_error, raw_text, version,
6968
+ author_device_id, scope)
6969
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6970
+ args: [
6971
+ sqlSafe(rec.id),
6972
+ sqlSafe(rec.agent_id),
6973
+ sqlSafe(rec.agent_role),
6974
+ sqlSafe(rec.session_id),
6975
+ sqlSafe(rec.timestamp),
6976
+ sqlSafe(rec.tool_name),
6977
+ sqlSafe(rec.project_name),
6978
+ sqlSafe(rec.has_error ?? 0),
6979
+ sqlSafe(rec.raw_text ?? ""),
6980
+ sqlSafe(rec.version ?? 0),
6981
+ sqlSafe(rec.author_device_id),
6982
+ sqlSafe(rec.scope ?? "business")
6983
+ ]
6984
+ }));
6985
+ if (stmts.length > 0) await client.batch(stmts, "write");
6986
+ pulled = pullResult.records.length;
6987
+ } else {
6988
+ const stmts = pullResult.records.map((rec) => ({
6989
+ sql: `INSERT OR REPLACE INTO memories
6990
+ (id, agent_id, agent_role, session_id, timestamp,
6991
+ tool_name, project_name, has_error, raw_text, version,
6992
+ author_device_id, scope)
6993
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6994
+ args: [
6995
+ sqlSafe(rec.id),
6996
+ sqlSafe(rec.agent_id),
6997
+ sqlSafe(rec.agent_role),
6998
+ sqlSafe(rec.session_id),
6999
+ sqlSafe(rec.timestamp),
7000
+ sqlSafe(rec.tool_name),
7001
+ sqlSafe(rec.project_name),
7002
+ sqlSafe(rec.has_error ?? 0),
7003
+ sqlSafe(rec.raw_text ?? ""),
7004
+ sqlSafe(rec.version ?? 0),
7005
+ sqlSafe(rec.author_device_id),
7006
+ sqlSafe(rec.scope ?? "business")
7007
+ ]
7008
+ }));
7009
+ await client.batch(stmts, "write");
7010
+ pulled = pullResult.records.length;
7011
+ }
6102
7012
  }
6103
7013
  if (pullResult.maxVersion > lastPullVersion) {
6104
7014
  await client.execute({
@@ -6246,9 +7156,9 @@ async function cloudSync(config) {
6246
7156
  try {
6247
7157
  const employees = await loadEmployees();
6248
7158
  rosterResult.employees = employees.length;
6249
- const idDir = path19.join(EXE_AI_DIR, "identity");
6250
- if (existsSync15(idDir)) {
6251
- rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
7159
+ const idDir = path21.join(EXE_AI_DIR, "identity");
7160
+ if (existsSync17(idDir)) {
7161
+ rosterResult.identities = readdirSync6(idDir).filter((f) => f.endsWith(".md")).length;
6252
7162
  }
6253
7163
  } catch {
6254
7164
  }
@@ -6268,55 +7178,63 @@ async function cloudSync(config) {
6268
7178
  function recordRosterDeletion(name) {
6269
7179
  let deletions = [];
6270
7180
  try {
6271
- if (existsSync15(ROSTER_DELETIONS_PATH)) {
6272
- deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
7181
+ if (existsSync17(ROSTER_DELETIONS_PATH)) {
7182
+ deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
6273
7183
  }
6274
7184
  } catch {
6275
7185
  }
6276
7186
  if (!deletions.includes(name)) deletions.push(name);
6277
- writeFileSync9(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
7187
+ writeFileSync11(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
6278
7188
  }
6279
7189
  function consumeRosterDeletions() {
6280
7190
  try {
6281
- if (!existsSync15(ROSTER_DELETIONS_PATH)) return [];
6282
- const deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
6283
- writeFileSync9(ROSTER_DELETIONS_PATH, "[]");
7191
+ if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
7192
+ const deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
7193
+ writeFileSync11(ROSTER_DELETIONS_PATH, "[]");
6284
7194
  return deletions;
6285
7195
  } catch {
6286
7196
  return [];
6287
7197
  }
6288
7198
  }
6289
7199
  function buildRosterBlob(paths) {
6290
- const rosterPath = paths?.rosterPath ?? path19.join(EXE_AI_DIR, "exe-employees.json");
6291
- const identityDir = paths?.identityDir ?? path19.join(EXE_AI_DIR, "identity");
6292
- const configPath = paths?.configPath ?? path19.join(EXE_AI_DIR, "config.json");
7200
+ const rosterPath = paths?.rosterPath ?? path21.join(EXE_AI_DIR, "exe-employees.json");
7201
+ const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
7202
+ const configPath = paths?.configPath ?? path21.join(EXE_AI_DIR, "config.json");
6293
7203
  let roster = [];
6294
- if (existsSync15(rosterPath)) {
7204
+ if (existsSync17(rosterPath)) {
6295
7205
  try {
6296
- roster = JSON.parse(readFileSync12(rosterPath, "utf-8"));
7206
+ roster = JSON.parse(readFileSync14(rosterPath, "utf-8"));
6297
7207
  } catch {
6298
7208
  }
6299
7209
  }
6300
7210
  const identities = {};
6301
- if (existsSync15(identityDir)) {
6302
- for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
7211
+ if (existsSync17(identityDir)) {
7212
+ for (const file of readdirSync6(identityDir).filter((f) => f.endsWith(".md"))) {
6303
7213
  try {
6304
- identities[file] = readFileSync12(path19.join(identityDir, file), "utf-8");
7214
+ identities[file] = readFileSync14(path21.join(identityDir, file), "utf-8");
6305
7215
  } catch {
6306
7216
  }
6307
7217
  }
6308
7218
  }
6309
7219
  let config;
6310
- if (existsSync15(configPath)) {
7220
+ if (existsSync17(configPath)) {
6311
7221
  try {
6312
- config = JSON.parse(readFileSync12(configPath, "utf-8"));
7222
+ config = JSON.parse(readFileSync14(configPath, "utf-8"));
7223
+ } catch {
7224
+ }
7225
+ }
7226
+ let agentConfig;
7227
+ const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
7228
+ if (existsSync17(agentConfigPath)) {
7229
+ try {
7230
+ agentConfig = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
6313
7231
  } catch {
6314
7232
  }
6315
7233
  }
6316
7234
  const deletedNames = consumeRosterDeletions();
6317
- const content = JSON.stringify({ roster, identities, config, deletedNames });
7235
+ const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
6318
7236
  const hash = crypto7.createHash("sha256").update(content).digest("hex").slice(0, 16);
6319
- return { roster, identities, config, deletedNames, version: hash };
7237
+ return { roster, identities, config, agentConfig, deletedNames, version: hash };
6320
7238
  }
6321
7239
  async function cloudPushRoster(config) {
6322
7240
  assertSecureEndpoint(config.endpoint);
@@ -6385,23 +7303,23 @@ async function cloudPullRoster(config) {
6385
7303
  }
6386
7304
  }
6387
7305
  function mergeConfig(remoteConfig, configPath) {
6388
- const cfgPath = configPath ?? path19.join(EXE_AI_DIR, "config.json");
7306
+ const cfgPath = configPath ?? path21.join(EXE_AI_DIR, "config.json");
6389
7307
  let local = {};
6390
- if (existsSync15(cfgPath)) {
7308
+ if (existsSync17(cfgPath)) {
6391
7309
  try {
6392
- local = JSON.parse(readFileSync12(cfgPath, "utf-8"));
7310
+ local = JSON.parse(readFileSync14(cfgPath, "utf-8"));
6393
7311
  } catch {
6394
7312
  }
6395
7313
  }
6396
7314
  const merged = { ...remoteConfig, ...local };
6397
- const dir = path19.dirname(cfgPath);
6398
- if (!existsSync15(dir)) mkdirSync9(dir, { recursive: true });
6399
- writeFileSync9(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
7315
+ const dir = path21.dirname(cfgPath);
7316
+ if (!existsSync17(dir)) mkdirSync11(dir, { recursive: true });
7317
+ writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
6400
7318
  }
6401
7319
  async function mergeRosterFromRemote(remote, paths) {
6402
7320
  return withRosterLock(async () => {
6403
7321
  const rosterPath = paths?.rosterPath ?? void 0;
6404
- const identityDir = paths?.identityDir ?? path19.join(EXE_AI_DIR, "identity");
7322
+ const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
6405
7323
  const localEmployees = await loadEmployees(rosterPath);
6406
7324
  const localNames = new Set(localEmployees.map((e) => e.name));
6407
7325
  let added = 0;
@@ -6422,15 +7340,15 @@ async function mergeRosterFromRemote(remote, paths) {
6422
7340
  ) ?? lookupKey;
6423
7341
  const remoteIdentity = remote.identities[matchedKey];
6424
7342
  if (remoteIdentity) {
6425
- if (!existsSync15(identityDir)) mkdirSync9(identityDir, { recursive: true });
6426
- const idPath = path19.join(identityDir, `${remoteEmp.name}.md`);
7343
+ if (!existsSync17(identityDir)) mkdirSync11(identityDir, { recursive: true });
7344
+ const idPath = path21.join(identityDir, `${remoteEmp.name}.md`);
6427
7345
  let localIdentity = null;
6428
7346
  try {
6429
- localIdentity = existsSync15(idPath) ? readFileSync12(idPath, "utf-8") : null;
7347
+ localIdentity = existsSync17(idPath) ? readFileSync14(idPath, "utf-8") : null;
6430
7348
  } catch {
6431
7349
  }
6432
7350
  if (localIdentity !== remoteIdentity) {
6433
- writeFileSync9(idPath, remoteIdentity, "utf-8");
7351
+ writeFileSync11(idPath, remoteIdentity, "utf-8");
6434
7352
  identitiesUpdated++;
6435
7353
  }
6436
7354
  }
@@ -6454,6 +7372,21 @@ async function mergeRosterFromRemote(remote, paths) {
6454
7372
  } catch {
6455
7373
  }
6456
7374
  }
7375
+ if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
7376
+ try {
7377
+ const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
7378
+ let local = {};
7379
+ if (existsSync17(agentConfigPath)) {
7380
+ try {
7381
+ local = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
7382
+ } catch {
7383
+ }
7384
+ }
7385
+ const merged = { ...remote.agentConfig, ...local };
7386
+ writeFileSync11(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
7387
+ } catch {
7388
+ }
7389
+ }
6457
7390
  return { added, identitiesUpdated };
6458
7391
  });
6459
7392
  }
@@ -6883,13 +7816,14 @@ var init_cloud_sync = __esm({
6883
7816
  init_compress();
6884
7817
  init_license();
6885
7818
  init_config();
7819
+ init_crdt_sync();
6886
7820
  init_employees();
6887
7821
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
6888
7822
  FETCH_TIMEOUT_MS = 3e4;
6889
7823
  PUSH_BATCH_SIZE = 5e3;
6890
- ROSTER_LOCK_PATH = path19.join(EXE_AI_DIR, "roster-merge.lock");
7824
+ ROSTER_LOCK_PATH = path21.join(EXE_AI_DIR, "roster-merge.lock");
6891
7825
  LOCK_STALE_MS = 3e4;
6892
- ROSTER_DELETIONS_PATH = path19.join(EXE_AI_DIR, "roster-deletions.json");
7826
+ ROSTER_DELETIONS_PATH = path21.join(EXE_AI_DIR, "roster-deletions.json");
6893
7827
  }
6894
7828
  });
6895
7829
 
@@ -6902,7 +7836,7 @@ __export(schedules_exports, {
6902
7836
  parseHumanCron: () => parseHumanCron
6903
7837
  });
6904
7838
  import crypto8 from "crypto";
6905
- import { execSync as execSync10 } from "child_process";
7839
+ import { execSync as execSync9 } from "child_process";
6906
7840
  async function ensureDb() {
6907
7841
  if (!isInitialized()) {
6908
7842
  await initStore();
@@ -7045,7 +7979,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
7045
7979
  const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
7046
7980
  const escapedPrompt = prompt.replace(/"/g, '\\"');
7047
7981
  const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
7048
- execSync10(
7982
+ execSync9(
7049
7983
  `(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
7050
7984
  { timeout: 5e3, stdio: "ignore" }
7051
7985
  );
@@ -7054,7 +7988,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
7054
7988
  }
7055
7989
  function removeFromCrontab(id) {
7056
7990
  try {
7057
- execSync10(
7991
+ execSync9(
7058
7992
  `crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
7059
7993
  { timeout: 5e3, stdio: "ignore" }
7060
7994
  );
@@ -7071,10 +8005,10 @@ var init_schedules = __esm({
7071
8005
 
7072
8006
  // src/bin/exe-boot.ts
7073
8007
  init_employees();
7074
- import path20 from "path";
8008
+ import path22 from "path";
7075
8009
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
7076
- import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync as readdirSync8, unlinkSync as unlinkSync9 } from "fs";
7077
- import os8 from "os";
8010
+ import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync7, unlinkSync as unlinkSync11 } from "fs";
8011
+ import os9 from "os";
7078
8012
 
7079
8013
  // src/lib/employee-templates.ts
7080
8014
  init_global_procedures();
@@ -7532,12 +8466,13 @@ init_task_scope();
7532
8466
 
7533
8467
  // src/lib/is-main.ts
7534
8468
  import { realpathSync } from "fs";
7535
- import { fileURLToPath as fileURLToPath2 } from "url";
8469
+ import { fileURLToPath as fileURLToPath3 } from "url";
7536
8470
  function isMainModule(importMetaUrl) {
7537
8471
  if (process.argv[1] == null) return false;
8472
+ if (process.argv[1].includes("mcp/server")) return false;
7538
8473
  try {
7539
8474
  const scriptPath = realpathSync(process.argv[1]);
7540
- const modulePath = realpathSync(fileURLToPath2(importMetaUrl));
8475
+ const modulePath = realpathSync(fileURLToPath3(importMetaUrl));
7541
8476
  return scriptPath === modulePath;
7542
8477
  } catch {
7543
8478
  return importMetaUrl === `file://${process.argv[1]}` || importMetaUrl === new URL(process.argv[1], "file://").href;
@@ -7549,24 +8484,24 @@ init_notifications();
7549
8484
 
7550
8485
  // src/adapters/claude/active-agent.ts
7551
8486
  init_config();
7552
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6, readdirSync as readdirSync4 } from "fs";
8487
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync4 } from "fs";
7553
8488
  import { execSync as execSync8 } from "child_process";
7554
- import path16 from "path";
8489
+ import path18 from "path";
7555
8490
 
7556
8491
  // src/adapters/claude/session-key.ts
7557
8492
  init_session_key();
7558
8493
 
7559
8494
  // src/adapters/claude/active-agent.ts
7560
8495
  init_employees();
7561
- var CACHE_DIR = path16.join(EXE_AI_DIR, "session-cache");
8496
+ var CACHE_DIR = path18.join(EXE_AI_DIR, "session-cache");
7562
8497
  var STALE_MS = 24 * 60 * 60 * 1e3;
7563
8498
  function getMarkerPath() {
7564
- return path16.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
8499
+ return path18.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
7565
8500
  }
7566
8501
  function writeActiveAgent(agentId, agentRole) {
7567
8502
  try {
7568
- mkdirSync7(CACHE_DIR, { recursive: true });
7569
- writeFileSync7(
8503
+ mkdirSync8(CACHE_DIR, { recursive: true });
8504
+ writeFileSync8(
7570
8505
  getMarkerPath(),
7571
8506
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
7572
8507
  );
@@ -7576,11 +8511,11 @@ function writeActiveAgent(agentId, agentRole) {
7576
8511
  function cleanupSessionMarkers() {
7577
8512
  const key = getSessionKey();
7578
8513
  try {
7579
- unlinkSync6(path16.join(CACHE_DIR, `active-agent-${key}.json`));
8514
+ unlinkSync7(path18.join(CACHE_DIR, `active-agent-${key}.json`));
7580
8515
  } catch {
7581
8516
  }
7582
8517
  try {
7583
- unlinkSync6(path16.join(CACHE_DIR, "active-agent-undefined.json"));
8518
+ unlinkSync7(path18.join(CACHE_DIR, "active-agent-undefined.json"));
7584
8519
  } catch {
7585
8520
  }
7586
8521
  }
@@ -7662,18 +8597,18 @@ async function boot(options) {
7662
8597
  } catch {
7663
8598
  }
7664
8599
  try {
7665
- const { readdirSync: readdirSync9, readFileSync: readFs } = await import("fs");
8600
+ const { readdirSync: readdirSync8, readFileSync: readFs } = await import("fs");
7666
8601
  const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
7667
8602
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
7668
8603
  const exeDir = "exe";
7669
- const entries = readdirSync9(exeDir, { withFileTypes: true });
8604
+ const entries = readdirSync8(exeDir, { withFileTypes: true });
7670
8605
  const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
7671
8606
  for (const dir of employeeDirs) {
7672
8607
  const employee = dir.name;
7673
- const taskDir = path20.join(exeDir, employee);
8608
+ const taskDir = path22.join(exeDir, employee);
7674
8609
  let files;
7675
8610
  try {
7676
- files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
8611
+ files = readdirSync8(taskDir).filter((f) => f.endsWith(".md"));
7677
8612
  } catch {
7678
8613
  continue;
7679
8614
  }
@@ -7681,7 +8616,7 @@ async function boot(options) {
7681
8616
  const taskFilePath = `exe/${employee}/${file}`;
7682
8617
  let content;
7683
8618
  try {
7684
- content = readFs(path20.join(taskDir, file), "utf8");
8619
+ content = readFs(path22.join(taskDir, file), "utf8");
7685
8620
  } catch {
7686
8621
  continue;
7687
8622
  }
@@ -7767,12 +8702,12 @@ async function boot(options) {
7767
8702
  }
7768
8703
  try {
7769
8704
  for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
7770
- const reviewDir = path20.join(process.cwd(), "exe", reviewDirName);
7771
- if (existsSync16(reviewDir)) {
7772
- for (const f of readdirSync8(reviewDir)) {
8705
+ const reviewDir = path22.join(process.cwd(), "exe", reviewDirName);
8706
+ if (existsSync18(reviewDir)) {
8707
+ for (const f of readdirSync7(reviewDir)) {
7773
8708
  if (f.startsWith("review-") && f.endsWith(".md")) {
7774
8709
  try {
7775
- unlinkSync9(path20.join(reviewDir, f));
8710
+ unlinkSync11(path22.join(reviewDir, f));
7776
8711
  } catch {
7777
8712
  }
7778
8713
  }
@@ -7818,12 +8753,12 @@ async function boot(options) {
7818
8753
  });
7819
8754
  const taskFile = String(r.task_file);
7820
8755
  try {
7821
- const filePath = path20.join(process.cwd(), taskFile);
7822
- if (existsSync16(filePath)) {
7823
- let content = readFileSync13(filePath, "utf8");
8756
+ const filePath = path22.join(process.cwd(), taskFile);
8757
+ if (existsSync18(filePath)) {
8758
+ let content = readFileSync15(filePath, "utf8");
7824
8759
  content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
7825
- const { writeFileSync: writeFileSync10 } = await import("fs");
7826
- writeFileSync10(filePath, content);
8760
+ const { writeFileSync: writeFileSync12 } = await import("fs");
8761
+ writeFileSync12(filePath, content);
7827
8762
  }
7828
8763
  } catch {
7829
8764
  }
@@ -8154,13 +9089,13 @@ async function boot(options) {
8154
9089
  }));
8155
9090
  const assignedResult = await client.execute({
8156
9091
  sql: `SELECT COUNT(*) as cnt FROM tasks
8157
- WHERE project_name = ? AND (assigned_by = ? OR assigned_by = 'exe')
9092
+ WHERE project_name = ? AND assigned_by = ?
8158
9093
  AND created_at > datetime('now', '-7 days')${pScope.sql}`,
8159
9094
  args: [p.projectName, coordinatorName, ...pScope.args]
8160
9095
  });
8161
9096
  const tasksAssigned = Number(assignedResult.rows[0]?.cnt) || 0;
8162
9097
  if (tasksAssigned > 0) {
8163
- const exeEntry = activity.find((a) => a.name === coordinatorName || a.name === "exe");
9098
+ const exeEntry = activity.find((a) => a.name === coordinatorName || isCoordinatorName(a.name));
8164
9099
  if (exeEntry) {
8165
9100
  exeEntry.tasksAssigned = tasksAssigned;
8166
9101
  } else {
@@ -8184,7 +9119,7 @@ async function boot(options) {
8184
9119
  const projName = s.projectDir.split("/").pop() ?? "";
8185
9120
  const proj = projects.find((p) => p.projectName === projName);
8186
9121
  if (!proj || proj.sessionName) continue;
8187
- if (s.agentId === coordinatorName || s.agentId === "exe") {
9122
+ if (s.agentId === coordinatorName || isCoordinatorName(s.agentId)) {
8188
9123
  proj.sessionName = s.windowName;
8189
9124
  } else if (s.parentExe) {
8190
9125
  proj.sessionName = s.parentExe;
@@ -8268,8 +9203,8 @@ async function boot(options) {
8268
9203
  updatedAt: String(row.updated_at)
8269
9204
  }));
8270
9205
  try {
8271
- const { execSync: execSync11 } = await import("child_process");
8272
- const gitLog = execSync11(
9206
+ const { execSync: execSync10 } = await import("child_process");
9207
+ const gitLog = execSync10(
8273
9208
  "git log --oneline -10 --grep='Co-Authored-By: Claude' --grep='task(' --all-match 2>/dev/null || git log --oneline -5 --grep='Co-Authored-By: Claude' 2>/dev/null || git log --oneline -5 --grep='^task(' 2>/dev/null",
8274
9209
  {
8275
9210
  encoding: "utf8",
@@ -8292,19 +9227,19 @@ async function boot(options) {
8292
9227
  })()
8293
9228
  ]);
8294
9229
  try {
8295
- const configPath = path20.join(
8296
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path20.join(os8.homedir(), ".exe-os"),
9230
+ const configPath = path22.join(
9231
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
8297
9232
  "config.json"
8298
9233
  );
8299
- if (existsSync16(configPath)) {
8300
- const raw = JSON.parse(readFileSync13(configPath, "utf8"));
9234
+ if (existsSync18(configPath)) {
9235
+ const raw = JSON.parse(readFileSync15(configPath, "utf8"));
8301
9236
  briefData.cloudConnected = !!(raw.cloud || raw.turso);
8302
9237
  }
8303
9238
  } catch {
8304
9239
  }
8305
9240
  try {
8306
- const backfillFlagPath = path20.join(EXE_AI_DIR, "session-cache", "needs-backfill");
8307
- const isBackfillNeeded = () => existsSync16(backfillFlagPath);
9241
+ const backfillFlagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
9242
+ const isBackfillNeeded = () => existsSync18(backfillFlagPath);
8308
9243
  const coverageResult = await client.execute({
8309
9244
  sql: `SELECT COUNT(*) as total,
8310
9245
  SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
@@ -8326,16 +9261,16 @@ async function boot(options) {
8326
9261
  let daemonRunning = false;
8327
9262
  let daemonUptime;
8328
9263
  let daemonRequestsServed;
8329
- const socketPath = path20.join(EXE_AI_DIR, "exed.sock");
8330
- if (existsSync16(socketPath)) {
9264
+ const socketPath = path22.join(EXE_AI_DIR, "exed.sock");
9265
+ if (existsSync18(socketPath)) {
8331
9266
  try {
8332
- const net = await import("net");
9267
+ const net2 = await import("net");
8333
9268
  const health = await new Promise((resolve) => {
8334
9269
  const timer = setTimeout(() => {
8335
9270
  sock.destroy();
8336
9271
  resolve(null);
8337
9272
  }, 2e3);
8338
- const sock = net.createConnection({ path: socketPath });
9273
+ const sock = net2.createConnection({ path: socketPath });
8339
9274
  let buf = "";
8340
9275
  sock.on("connect", () => {
8341
9276
  sock.write(JSON.stringify({ id: "boot-health", type: "health" }) + "\n");
@@ -8369,10 +9304,10 @@ async function boot(options) {
8369
9304
  }
8370
9305
  }
8371
9306
  if (!daemonRunning) {
8372
- const pidPath = path20.join(EXE_AI_DIR, "exed.pid");
8373
- if (existsSync16(pidPath)) {
9307
+ const pidPath = path22.join(EXE_AI_DIR, "exed.pid");
9308
+ if (existsSync18(pidPath)) {
8374
9309
  try {
8375
- const pid = parseInt(readFileSync13(pidPath, "utf8").trim(), 10);
9310
+ const pid = parseInt(readFileSync15(pidPath, "utf8").trim(), 10);
8376
9311
  if (pid > 0) {
8377
9312
  process.kill(pid, 0);
8378
9313
  daemonRunning = true;
@@ -8383,10 +9318,10 @@ async function boot(options) {
8383
9318
  }
8384
9319
  if (nullCount === 0) {
8385
9320
  try {
8386
- const flagPath = path20.join(EXE_AI_DIR, "session-cache", "needs-backfill");
8387
- if (existsSync16(flagPath)) {
8388
- const { unlinkSync: unlinkSync10 } = await import("fs");
8389
- unlinkSync10(flagPath);
9321
+ const flagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
9322
+ if (existsSync18(flagPath)) {
9323
+ const { unlinkSync: unlinkSync12 } = await import("fs");
9324
+ unlinkSync12(flagPath);
8390
9325
  }
8391
9326
  } catch {
8392
9327
  }
@@ -8407,26 +9342,26 @@ async function boot(options) {
8407
9342
  if (!tryAcquireWorkerSlot2()) {
8408
9343
  process.stderr.write("[exe-boot] Backfill needed but worker gate full \u2014 skipping\n");
8409
9344
  } else {
8410
- const { spawn } = await import("child_process");
8411
- const { fileURLToPath: fileURLToPath3 } = await import("url");
8412
- const thisFile = fileURLToPath3(import.meta.url);
8413
- const backfillPath = path20.resolve(path20.dirname(thisFile), "backfill-vectors.js");
8414
- if (existsSync16(backfillPath)) {
8415
- const { openSync: openSync2, closeSync: closeSync2 } = await import("fs");
8416
- const workerLogPath = path20.join(EXE_AI_DIR, "workers.log");
9345
+ const { spawn: spawn2 } = await import("child_process");
9346
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
9347
+ const thisFile = fileURLToPath4(import.meta.url);
9348
+ const backfillPath = path22.resolve(path22.dirname(thisFile), "backfill-vectors.js");
9349
+ if (existsSync18(backfillPath)) {
9350
+ const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
9351
+ const workerLogPath = path22.join(EXE_AI_DIR, "workers.log");
8417
9352
  let stderrFd = "ignore";
8418
9353
  try {
8419
- stderrFd = openSync2(workerLogPath, "a");
9354
+ stderrFd = openSync3(workerLogPath, "a");
8420
9355
  } catch {
8421
9356
  }
8422
- const child = spawn(process.execPath, [backfillPath], {
9357
+ const child = spawn2(process.execPath, [backfillPath], {
8423
9358
  detached: true,
8424
9359
  stdio: ["ignore", "ignore", stderrFd]
8425
9360
  });
8426
9361
  child.unref();
8427
9362
  if (child.pid) registerWorkerPid2(child.pid);
8428
9363
  if (typeof stderrFd === "number") try {
8429
- closeSync2(stderrFd);
9364
+ closeSync3(stderrFd);
8430
9365
  } catch {
8431
9366
  }
8432
9367
  briefData.embedding.backfillRunning = true;
@@ -8438,13 +9373,13 @@ async function boot(options) {
8438
9373
  } catch {
8439
9374
  }
8440
9375
  try {
8441
- const { fileURLToPath: fileURLToPath3 } = await import("url");
8442
- const thisFile = fileURLToPath3(import.meta.url);
9376
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
9377
+ const thisFile = fileURLToPath4(import.meta.url);
8443
9378
  const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
8444
9379
  const missing = [];
8445
9380
  for (const bin of criticalBinaries) {
8446
- const binPath = path20.resolve(path20.dirname(thisFile), bin);
8447
- if (!existsSync16(binPath)) {
9381
+ const binPath = path22.resolve(path22.dirname(thisFile), bin);
9382
+ if (!existsSync18(binPath)) {
8448
9383
  missing.push(`dist/bin/${bin}`);
8449
9384
  }
8450
9385
  }
@@ -8473,7 +9408,7 @@ async function boot(options) {
8473
9408
  console.log(brief);
8474
9409
  return;
8475
9410
  }
8476
- const sessionDir = path20.join(EXE_AI_DIR, "sessions", coordinatorName);
9411
+ const sessionDir = path22.join(EXE_AI_DIR, "sessions", coordinatorName);
8477
9412
  await mkdir5(sessionDir, { recursive: true });
8478
9413
  const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
8479
9414
 
@@ -8482,7 +9417,7 @@ async function boot(options) {
8482
9417
  # Status Brief
8483
9418
 
8484
9419
  ${brief}`;
8485
- await writeFile6(path20.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
9420
+ await writeFile6(path22.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
8486
9421
  const unread = await readUnreadNotifications();
8487
9422
  if (unread.length > 0) {
8488
9423
  console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
@@ -8491,12 +9426,12 @@ ${brief}`;
8491
9426
  await cleanupOldNotifications();
8492
9427
  console.log(brief);
8493
9428
  try {
8494
- const configPath2 = path20.join(
8495
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path20.join(os8.homedir(), ".exe-os"),
9429
+ const configPath2 = path22.join(
9430
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
8496
9431
  "config.json"
8497
9432
  );
8498
- if (existsSync16(configPath2)) {
8499
- const rawCfg = JSON.parse(readFileSync13(configPath2, "utf8"));
9433
+ if (existsSync18(configPath2)) {
9434
+ const rawCfg = JSON.parse(readFileSync15(configPath2, "utf8"));
8500
9435
  const cloudCfg = rawCfg.cloud;
8501
9436
  if (cloudCfg?.apiKey) {
8502
9437
  const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
@@ -8556,11 +9491,11 @@ async function updateIdleKillSuspectStreak(client, killsToday, liveSessions, tod
8556
9491
  }
8557
9492
  function runSplash() {
8558
9493
  try {
8559
- const { execSync: execSync11 } = __require("child_process");
9494
+ const { execSync: execSync10 } = __require("child_process");
8560
9495
  const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
8561
9496
  const config = loadConfigSync2();
8562
9497
  if (!config.splashEffect) return;
8563
- execSync11(
9498
+ execSync10(
8564
9499
  'echo "EXE OS" | python3 -m terminaltexteffects decrypt --typing-speed 2 --ciphertext-colors 6B4C9A F5D76E --final-gradient-stops F5D76E F0EDE8 --final-gradient-direction vertical',
8565
9500
  { stdio: "inherit", timeout: 5e3 }
8566
9501
  );