@askexenow/exe-os 0.8.83 → 0.8.85

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 (95) 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 +97 -2
  5. package/dist/bin/cli.js +14350 -12518
  6. package/dist/bin/exe-agent.js +97 -88
  7. package/dist/bin/exe-assign.js +1003 -854
  8. package/dist/bin/exe-boot.js +1257 -320
  9. package/dist/bin/exe-call.js +10 -0
  10. package/dist/bin/exe-cloud.js +29 -6
  11. package/dist/bin/exe-dispatch.js +210 -34
  12. package/dist/bin/exe-doctor.js +403 -6
  13. package/dist/bin/exe-export-behaviors.js +175 -72
  14. package/dist/bin/exe-forget.js +97 -2
  15. package/dist/bin/exe-gateway.js +550 -171
  16. package/dist/bin/exe-healthcheck.js +1 -0
  17. package/dist/bin/exe-heartbeat.js +100 -5
  18. package/dist/bin/exe-kill.js +175 -72
  19. package/dist/bin/exe-launch-agent.js +189 -76
  20. package/dist/bin/exe-link.js +902 -80
  21. package/dist/bin/exe-new-employee.js +38 -8
  22. package/dist/bin/exe-pending-messages.js +96 -2
  23. package/dist/bin/exe-pending-notifications.js +97 -2
  24. package/dist/bin/exe-pending-reviews.js +98 -3
  25. package/dist/bin/exe-rename.js +564 -23
  26. package/dist/bin/exe-review.js +231 -73
  27. package/dist/bin/exe-search.js +989 -226
  28. package/dist/bin/exe-session-cleanup.js +4806 -1665
  29. package/dist/bin/exe-settings.js +20 -5
  30. package/dist/bin/exe-status.js +97 -2
  31. package/dist/bin/exe-team.js +97 -2
  32. package/dist/bin/git-sweep.js +899 -207
  33. package/dist/bin/graph-backfill.js +175 -72
  34. package/dist/bin/graph-export.js +175 -72
  35. package/dist/bin/install.js +38 -7
  36. package/dist/bin/list-providers.js +1 -0
  37. package/dist/bin/scan-tasks.js +904 -211
  38. package/dist/bin/setup.js +867 -268
  39. package/dist/bin/shard-migrate.js +175 -72
  40. package/dist/bin/update.js +1 -0
  41. package/dist/bin/wiki-sync.js +175 -72
  42. package/dist/gateway/index.js +548 -166
  43. package/dist/hooks/bug-report-worker.js +208 -23
  44. package/dist/hooks/commit-complete.js +897 -205
  45. package/dist/hooks/error-recall.js +988 -226
  46. package/dist/hooks/ingest-worker.js +1638 -1194
  47. package/dist/hooks/ingest.js +3 -0
  48. package/dist/hooks/instructions-loaded.js +707 -97
  49. package/dist/hooks/notification.js +699 -89
  50. package/dist/hooks/post-compact.js +714 -104
  51. package/dist/hooks/pre-compact.js +897 -205
  52. package/dist/hooks/pre-tool-use.js +742 -123
  53. package/dist/hooks/prompt-ingest-worker.js +242 -101
  54. package/dist/hooks/prompt-submit.js +995 -233
  55. package/dist/hooks/response-ingest-worker.js +242 -101
  56. package/dist/hooks/session-end.js +3941 -400
  57. package/dist/hooks/session-start.js +1001 -226
  58. package/dist/hooks/stop.js +725 -115
  59. package/dist/hooks/subagent-stop.js +714 -104
  60. package/dist/hooks/summary-worker.js +1964 -1330
  61. package/dist/index.js +1651 -1053
  62. package/dist/lib/cloud-sync.js +907 -86
  63. package/dist/lib/consolidation.js +2 -1
  64. package/dist/lib/database.js +642 -87
  65. package/dist/lib/db-daemon-client.js +503 -0
  66. package/dist/lib/device-registry.js +547 -7
  67. package/dist/lib/embedder.js +14 -28
  68. package/dist/lib/employee-templates.js +84 -74
  69. package/dist/lib/employees.js +9 -0
  70. package/dist/lib/exe-daemon-client.js +16 -29
  71. package/dist/lib/exe-daemon.js +1955 -922
  72. package/dist/lib/hybrid-search.js +988 -226
  73. package/dist/lib/identity.js +87 -67
  74. package/dist/lib/keychain.js +9 -1
  75. package/dist/lib/messaging.js +8 -1
  76. package/dist/lib/reminders.js +91 -74
  77. package/dist/lib/schedules.js +96 -2
  78. package/dist/lib/skill-learning.js +103 -85
  79. package/dist/lib/store.js +234 -73
  80. package/dist/lib/tasks.js +111 -22
  81. package/dist/lib/tmux-routing.js +120 -31
  82. package/dist/lib/token-spend.js +273 -0
  83. package/dist/lib/ws-client.js +11 -0
  84. package/dist/mcp/server.js +5222 -475
  85. package/dist/mcp/tools/complete-reminder.js +94 -77
  86. package/dist/mcp/tools/create-reminder.js +94 -77
  87. package/dist/mcp/tools/create-task.js +120 -22
  88. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  89. package/dist/mcp/tools/list-reminders.js +94 -77
  90. package/dist/mcp/tools/list-tasks.js +31 -1
  91. package/dist/mcp/tools/send-message.js +8 -1
  92. package/dist/mcp/tools/update-task.js +39 -10
  93. package/dist/runtime/index.js +911 -219
  94. package/dist/tui/App.js +997 -295
  95. package/package.json +6 -1
@@ -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
 
@@ -2305,17 +2854,17 @@ var init_provider_table = __esm({
2305
2854
  });
2306
2855
 
2307
2856
  // 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";
2857
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
2858
+ import path7 from "path";
2310
2859
  import os5 from "os";
2311
2860
  function ensureDir() {
2312
- const dir = path6.dirname(QUEUE_PATH);
2313
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2861
+ const dir = path7.dirname(QUEUE_PATH);
2862
+ if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2314
2863
  }
2315
2864
  function readQueue() {
2316
2865
  try {
2317
- if (!existsSync6(QUEUE_PATH)) return [];
2318
- return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
2866
+ if (!existsSync7(QUEUE_PATH)) return [];
2867
+ return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
2319
2868
  } catch {
2320
2869
  return [];
2321
2870
  }
@@ -2347,9 +2896,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
2347
2896
  var init_intercom_queue = __esm({
2348
2897
  "src/lib/intercom-queue.ts"() {
2349
2898
  "use strict";
2350
- QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2899
+ QUEUE_PATH = path7.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2351
2900
  TTL_MS = 60 * 60 * 1e3;
2352
- INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
2901
+ INTERCOM_LOG = path7.join(os5.homedir(), ".exe-os", "intercom.log");
2353
2902
  }
2354
2903
  });
2355
2904
 
@@ -2370,9 +2919,9 @@ __export(license_exports, {
2370
2919
  stopLicenseRevalidation: () => stopLicenseRevalidation,
2371
2920
  validateLicense: () => validateLicense
2372
2921
  });
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";
2922
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2923
+ import { randomUUID as randomUUID3 } from "crypto";
2924
+ import path8 from "path";
2376
2925
  import { jwtVerify, importSPKI } from "jose";
2377
2926
  async function fetchRetry(url, init) {
2378
2927
  try {
@@ -2383,30 +2932,30 @@ async function fetchRetry(url, init) {
2383
2932
  }
2384
2933
  }
2385
2934
  function loadDeviceId() {
2386
- const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
2935
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
2387
2936
  try {
2388
- if (existsSync7(deviceJsonPath)) {
2389
- const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
2937
+ if (existsSync8(deviceJsonPath)) {
2938
+ const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
2390
2939
  if (data.deviceId) return data.deviceId;
2391
2940
  }
2392
2941
  } catch {
2393
2942
  }
2394
2943
  try {
2395
- if (existsSync7(DEVICE_ID_PATH)) {
2396
- const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
2944
+ if (existsSync8(DEVICE_ID_PATH)) {
2945
+ const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
2397
2946
  if (id2) return id2;
2398
2947
  }
2399
2948
  } catch {
2400
2949
  }
2401
- const id = randomUUID2();
2950
+ const id = randomUUID3();
2402
2951
  mkdirSync4(EXE_AI_DIR, { recursive: true });
2403
2952
  writeFileSync4(DEVICE_ID_PATH, id, "utf8");
2404
2953
  return id;
2405
2954
  }
2406
2955
  function loadLicense() {
2407
2956
  try {
2408
- if (!existsSync7(LICENSE_PATH)) return null;
2409
- return readFileSync5(LICENSE_PATH, "utf8").trim();
2957
+ if (!existsSync8(LICENSE_PATH)) return null;
2958
+ return readFileSync6(LICENSE_PATH, "utf8").trim();
2410
2959
  } catch {
2411
2960
  return null;
2412
2961
  }
@@ -2439,8 +2988,8 @@ async function verifyLicenseJwt(token) {
2439
2988
  }
2440
2989
  async function getCachedLicense() {
2441
2990
  try {
2442
- if (!existsSync7(CACHE_PATH)) return null;
2443
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
2991
+ if (!existsSync8(CACHE_PATH)) return null;
2992
+ const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2444
2993
  if (!raw.token || typeof raw.token !== "string") return null;
2445
2994
  return await verifyLicenseJwt(raw.token);
2446
2995
  } catch {
@@ -2449,8 +2998,8 @@ async function getCachedLicense() {
2449
2998
  }
2450
2999
  function readCachedToken() {
2451
3000
  try {
2452
- if (!existsSync7(CACHE_PATH)) return null;
2453
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
3001
+ if (!existsSync8(CACHE_PATH)) return null;
3002
+ const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2454
3003
  return typeof raw.token === "string" ? raw.token : null;
2455
3004
  } catch {
2456
3005
  return null;
@@ -2537,8 +3086,8 @@ async function validateLicense(apiKey, deviceId) {
2537
3086
  }
2538
3087
  function getCacheAgeMs() {
2539
3088
  try {
2540
- const { statSync: statSync2 } = __require("fs");
2541
- const s = statSync2(CACHE_PATH);
3089
+ const { statSync: statSync3 } = __require("fs");
3090
+ const s = statSync3(CACHE_PATH);
2542
3091
  return Date.now() - s.mtimeMs;
2543
3092
  } catch {
2544
3093
  return Infinity;
@@ -2548,9 +3097,9 @@ async function checkLicense() {
2548
3097
  let key = loadLicense();
2549
3098
  if (!key) {
2550
3099
  try {
2551
- const configPath = path7.join(EXE_AI_DIR, "config.json");
2552
- if (existsSync7(configPath)) {
2553
- const raw = JSON.parse(readFileSync5(configPath, "utf8"));
3100
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
3101
+ if (existsSync8(configPath)) {
3102
+ const raw = JSON.parse(readFileSync6(configPath, "utf8"));
2554
3103
  const cloud = raw.cloud;
2555
3104
  if (cloud?.apiKey) {
2556
3105
  key = cloud.apiKey;
@@ -2709,9 +3258,9 @@ var init_license = __esm({
2709
3258
  "src/lib/license.ts"() {
2710
3259
  "use strict";
2711
3260
  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");
3261
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
3262
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
3263
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2715
3264
  API_BASE = "https://askexe.com/cloud";
2716
3265
  RETRY_DELAY_MS = 500;
2717
3266
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2741,12 +3290,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2741
3290
  });
2742
3291
 
2743
3292
  // src/lib/plan-limits.ts
2744
- import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2745
- import path8 from "path";
3293
+ import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
3294
+ import path9 from "path";
2746
3295
  function getLicenseSync() {
2747
3296
  try {
2748
- if (!existsSync8(CACHE_PATH2)) return freeLicense();
2749
- const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
3297
+ if (!existsSync9(CACHE_PATH2)) return freeLicense();
3298
+ const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
2750
3299
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2751
3300
  const parts = raw.token.split(".");
2752
3301
  if (parts.length !== 3) return freeLicense();
@@ -2784,8 +3333,8 @@ function assertEmployeeLimitSync(rosterPath) {
2784
3333
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2785
3334
  let count = 0;
2786
3335
  try {
2787
- if (existsSync8(filePath)) {
2788
- const raw = readFileSync6(filePath, "utf8");
3336
+ if (existsSync9(filePath)) {
3337
+ const raw = readFileSync7(filePath, "utf8");
2789
3338
  const employees = JSON.parse(raw);
2790
3339
  count = Array.isArray(employees) ? employees.length : 0;
2791
3340
  }
@@ -2814,19 +3363,19 @@ var init_plan_limits = __esm({
2814
3363
  this.name = "PlanLimitError";
2815
3364
  }
2816
3365
  };
2817
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3366
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2818
3367
  }
2819
3368
  });
2820
3369
 
2821
3370
  // src/lib/notifications.ts
2822
3371
  import crypto from "crypto";
2823
- import path9 from "path";
3372
+ import path10 from "path";
2824
3373
  import os6 from "os";
2825
3374
  import {
2826
- readFileSync as readFileSync7,
3375
+ readFileSync as readFileSync8,
2827
3376
  readdirSync as readdirSync2,
2828
- unlinkSync as unlinkSync2,
2829
- existsSync as existsSync9,
3377
+ unlinkSync as unlinkSync3,
3378
+ existsSync as existsSync10,
2830
3379
  rmdirSync
2831
3380
  } from "fs";
2832
3381
  async function writeNotification(notification) {
@@ -2939,9 +3488,9 @@ async function markDoneTaskNotificationsAsRead() {
2939
3488
  }
2940
3489
  }
2941
3490
  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;
3491
+ const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path10.join(os6.homedir(), ".exe-os");
3492
+ const notifDir = path10.join(base, "notifications");
3493
+ if (!existsSync10(notifDir)) return 0;
2945
3494
  let migrated = 0;
2946
3495
  try {
2947
3496
  const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
@@ -2949,8 +3498,8 @@ async function migrateJsonNotifications() {
2949
3498
  const client = getClient();
2950
3499
  for (const file of files) {
2951
3500
  try {
2952
- const filePath = path9.join(notifDir, file);
2953
- const data = JSON.parse(readFileSync7(filePath, "utf8"));
3501
+ const filePath = path10.join(notifDir, file);
3502
+ const data = JSON.parse(readFileSync8(filePath, "utf8"));
2954
3503
  await client.execute({
2955
3504
  sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
2956
3505
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -2966,7 +3515,7 @@ async function migrateJsonNotifications() {
2966
3515
  data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
2967
3516
  ]
2968
3517
  });
2969
- unlinkSync2(filePath);
3518
+ unlinkSync3(filePath);
2970
3519
  migrated++;
2971
3520
  } catch {
2972
3521
  }
@@ -3097,10 +3646,11 @@ var init_session_kill_telemetry = __esm({
3097
3646
 
3098
3647
  // src/lib/tasks-crud.ts
3099
3648
  import crypto3 from "crypto";
3100
- import path10 from "path";
3649
+ import path11 from "path";
3650
+ import os7 from "os";
3101
3651
  import { execSync as execSync5 } from "child_process";
3102
3652
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3103
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
3653
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
3104
3654
  async function writeCheckpoint(input) {
3105
3655
  const client = getClient();
3106
3656
  const row = await resolveTask(client, input.taskId);
@@ -3141,6 +3691,35 @@ function extractParentFromContext(contextBody) {
3141
3691
  function slugify(title) {
3142
3692
  return title.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3143
3693
  }
3694
+ function buildKeywordIndex() {
3695
+ const idx = /* @__PURE__ */ new Map();
3696
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
3697
+ for (const kw of keywords) {
3698
+ const existing = idx.get(kw) ?? [];
3699
+ existing.push(role);
3700
+ idx.set(kw, existing);
3701
+ }
3702
+ }
3703
+ return idx;
3704
+ }
3705
+ function checkLaneAffinity(title, context, assigneeName) {
3706
+ const employees = loadEmployeesSync();
3707
+ const employee = employees.find((e) => e.name === assigneeName);
3708
+ if (!employee) return void 0;
3709
+ const assigneeRole = employee.role;
3710
+ const text = `${title} ${context}`.toLowerCase();
3711
+ const matchedRoles = /* @__PURE__ */ new Set();
3712
+ for (const [keyword, roles] of KEYWORD_INDEX) {
3713
+ if (text.includes(keyword)) {
3714
+ for (const role of roles) matchedRoles.add(role);
3715
+ }
3716
+ }
3717
+ if (matchedRoles.size === 0) return void 0;
3718
+ if (matchedRoles.has(assigneeRole)) return void 0;
3719
+ if (assigneeRole === "COO") return void 0;
3720
+ const expectedRoles = Array.from(matchedRoles).join(" or ");
3721
+ return `\u26A0\uFE0F Lane mismatch: task content suggests ${expectedRoles}, but assigned to ${assigneeName} (${assigneeRole}).`;
3722
+ }
3144
3723
  async function resolveTask(client, identifier, scopeSession) {
3145
3724
  const scope = sessionScopeFilter(scopeSession);
3146
3725
  let result = await client.execute({
@@ -3190,7 +3769,14 @@ async function createTaskCore(input) {
3190
3769
  const id = crypto3.randomUUID();
3191
3770
  const now = (/* @__PURE__ */ new Date()).toISOString();
3192
3771
  const slug = slugify(input.title);
3193
- const taskFile = input.taskFile ?? `exe/${input.assignedTo}/${slug}.md`;
3772
+ let earlySessionScope = null;
3773
+ try {
3774
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3775
+ earlySessionScope = resolveExeSession2();
3776
+ } catch {
3777
+ }
3778
+ const scope = earlySessionScope ?? "default";
3779
+ const taskFile = input.taskFile ?? `tasks/${scope}/${input.assignedTo}/${slug}.md`;
3194
3780
  let blockedById = null;
3195
3781
  const initialStatus = input.blockedBy ? "blocked" : "open";
3196
3782
  if (input.blockedBy) {
@@ -3230,22 +3816,24 @@ async function createTaskCore(input) {
3230
3816
  if (dupCheck.rows.length > 0) {
3231
3817
  warning = `similar active task already exists (${String(dupCheck.rows[0].id)}). Created new task anyway.`;
3232
3818
  }
3819
+ if (!process.env.DISABLE_LANE_AFFINITY) {
3820
+ const laneWarning = checkLaneAffinity(input.title, input.context, input.assignedTo);
3821
+ if (laneWarning) {
3822
+ warning = warning ? `${warning}
3823
+ ${laneWarning}` : laneWarning;
3824
+ }
3825
+ }
3233
3826
  if (input.baseDir) {
3234
3827
  try {
3235
- await mkdir4(path10.join(input.baseDir, "exe", "output"), { recursive: true });
3236
- await mkdir4(path10.join(input.baseDir, "exe", "research"), { recursive: true });
3828
+ await mkdir4(path11.join(input.baseDir, "exe", "output"), { recursive: true });
3829
+ await mkdir4(path11.join(input.baseDir, "exe", "research"), { recursive: true });
3237
3830
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3238
3831
  await ensureGitignoreExe(input.baseDir);
3239
3832
  } catch {
3240
3833
  }
3241
3834
  }
3242
3835
  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
- }
3836
+ const sessionScope = earlySessionScope;
3249
3837
  await client.execute({
3250
3838
  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
3839
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -3272,6 +3860,39 @@ async function createTaskCore(input) {
3272
3860
  now
3273
3861
  ]
3274
3862
  });
3863
+ if (input.baseDir) {
3864
+ try {
3865
+ const EXE_OS_DIR = path11.join(os7.homedir(), ".exe-os");
3866
+ const mdPath = path11.join(EXE_OS_DIR, taskFile);
3867
+ const mdDir = path11.dirname(mdPath);
3868
+ if (!existsSync11(mdDir)) await mkdir4(mdDir, { recursive: true });
3869
+ const reviewer = input.reviewer ?? input.assignedBy;
3870
+ const mdContent = `# ${input.title}
3871
+
3872
+ **ID:** ${id}
3873
+ **Status:** ${initialStatus}
3874
+ **Priority:** ${input.priority}
3875
+ **Assigned by:** ${input.assignedBy}
3876
+ **Assigned to:** ${input.assignedTo}
3877
+ **Project:** ${input.projectName}
3878
+ **Created:** ${now.split("T")[0]}${parentTaskId ? `
3879
+ **Parent task:** ${parentTaskId}` : ""}
3880
+ **Reviewer:** ${reviewer}
3881
+
3882
+ ## Context
3883
+
3884
+ ${input.context}
3885
+
3886
+ ## MANDATORY: When done
3887
+
3888
+ You MUST call update_task with status "done" and a result summary when finished.
3889
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3890
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
3891
+ `;
3892
+ await writeFile4(mdPath, mdContent, "utf-8");
3893
+ } catch {
3894
+ }
3895
+ }
3275
3896
  return {
3276
3897
  id,
3277
3898
  title: input.title,
@@ -3464,7 +4085,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
3464
4085
  return { row, taskFile, now, taskId };
3465
4086
  }
3466
4087
  }
3467
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
4088
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
3468
4089
  process.stderr.write(
3469
4090
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
3470
4091
  `
@@ -3529,9 +4150,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3529
4150
  return { taskFile, assignedTo, assignedBy, taskSlug };
3530
4151
  }
3531
4152
  async function ensureArchitectureDoc(baseDir, projectName) {
3532
- const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
4153
+ const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
3533
4154
  try {
3534
- if (existsSync10(archPath)) return;
4155
+ if (existsSync11(archPath)) return;
3535
4156
  const template = [
3536
4157
  `# ${projectName} \u2014 System Architecture`,
3537
4158
  "",
@@ -3564,10 +4185,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3564
4185
  }
3565
4186
  }
3566
4187
  async function ensureGitignoreExe(baseDir) {
3567
- const gitignorePath = path10.join(baseDir, ".gitignore");
4188
+ const gitignorePath = path11.join(baseDir, ".gitignore");
3568
4189
  try {
3569
- if (existsSync10(gitignorePath)) {
3570
- const content = readFileSync8(gitignorePath, "utf-8");
4190
+ if (existsSync11(gitignorePath)) {
4191
+ const content = readFileSync9(gitignorePath, "utf-8");
3571
4192
  if (/^\/?exe\/?$/m.test(content)) return;
3572
4193
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3573
4194
  } else {
@@ -3576,20 +4197,30 @@ async function ensureGitignoreExe(baseDir) {
3576
4197
  } catch {
3577
4198
  }
3578
4199
  }
3579
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
4200
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
3580
4201
  var init_tasks_crud = __esm({
3581
4202
  "src/lib/tasks-crud.ts"() {
3582
4203
  "use strict";
3583
4204
  init_database();
3584
4205
  init_task_scope();
4206
+ init_employees();
4207
+ LANE_KEYWORDS = {
4208
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
4209
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
4210
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
4211
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
4212
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
4213
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
4214
+ };
4215
+ KEYWORD_INDEX = buildKeywordIndex();
3585
4216
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
3586
4217
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
3587
4218
  }
3588
4219
  });
3589
4220
 
3590
4221
  // src/lib/tasks-review.ts
3591
- import path11 from "path";
3592
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
4222
+ import path12 from "path";
4223
+ import { existsSync as existsSync12, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
3593
4224
  async function countPendingReviews(sessionScope) {
3594
4225
  const client = getClient();
3595
4226
  if (sessionScope) {
@@ -3611,7 +4242,7 @@ async function countNewPendingReviewsSince(sinceIso, sessionScope) {
3611
4242
  const result2 = await client.execute({
3612
4243
  sql: `SELECT COUNT(*) as cnt FROM tasks
3613
4244
  WHERE status = 'needs_review' AND updated_at > ?
3614
- AND (session_scope = ? OR session_scope IS NULL)`,
4245
+ AND session_scope = ?`,
3615
4246
  args: [sinceIso, sessionScope]
3616
4247
  });
3617
4248
  return Number(result2.rows[0]?.cnt) || 0;
@@ -3629,7 +4260,7 @@ async function listPendingReviews(limit, sessionScope) {
3629
4260
  const result2 = await client.execute({
3630
4261
  sql: `SELECT title, assigned_to, project_name FROM tasks
3631
4262
  WHERE status = 'needs_review'
3632
- AND (session_scope = ? OR session_scope IS NULL)
4263
+ AND session_scope = ?
3633
4264
  ORDER BY priority ASC, created_at DESC LIMIT ?`,
3634
4265
  args: [sessionScope, limit]
3635
4266
  });
@@ -3750,14 +4381,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3750
4381
  if (parts.length >= 3 && parts[0] === "review") {
3751
4382
  const agent = parts[1];
3752
4383
  const slug = parts.slice(2).join("-");
3753
- const originalTaskFile = `exe/${agent}/${slug}.md`;
4384
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
3754
4385
  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]
4386
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
4387
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
3757
4388
  });
3758
4389
  if (result.rowsAffected > 0) {
3759
4390
  process.stderr.write(
3760
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
4391
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
3761
4392
  `
3762
4393
  );
3763
4394
  }
@@ -3770,11 +4401,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3770
4401
  );
3771
4402
  }
3772
4403
  try {
3773
- const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
3774
- if (existsSync11(cacheDir)) {
4404
+ const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
4405
+ if (existsSync12(cacheDir)) {
3775
4406
  for (const f of readdirSync3(cacheDir)) {
3776
4407
  if (f.startsWith("review-notified-")) {
3777
- unlinkSync3(path11.join(cacheDir, f));
4408
+ unlinkSync4(path12.join(cacheDir, f));
3778
4409
  }
3779
4410
  }
3780
4411
  }
@@ -3795,7 +4426,7 @@ var init_tasks_review = __esm({
3795
4426
  });
3796
4427
 
3797
4428
  // src/lib/tasks-chain.ts
3798
- import path12 from "path";
4429
+ import path13 from "path";
3799
4430
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3800
4431
  async function cascadeUnblock(taskId, baseDir, now) {
3801
4432
  const client = getClient();
@@ -3812,7 +4443,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3812
4443
  });
3813
4444
  for (const ur of unblockedRows.rows) {
3814
4445
  try {
3815
- const ubFile = path12.join(baseDir, String(ur.task_file));
4446
+ const ubFile = path13.join(baseDir, String(ur.task_file));
3816
4447
  let ubContent = await readFile4(ubFile, "utf-8");
3817
4448
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3818
4449
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3886,7 +4517,7 @@ __export(project_name_exports, {
3886
4517
  getProjectName: () => getProjectName
3887
4518
  });
3888
4519
  import { execSync as execSync6 } from "child_process";
3889
- import path13 from "path";
4520
+ import path14 from "path";
3890
4521
  function getProjectName(cwd) {
3891
4522
  const dir = cwd ?? process.cwd();
3892
4523
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3899,7 +4530,7 @@ function getProjectName(cwd) {
3899
4530
  timeout: 2e3,
3900
4531
  stdio: ["pipe", "pipe", "pipe"]
3901
4532
  }).trim();
3902
- repoRoot = path13.dirname(gitCommonDir);
4533
+ repoRoot = path14.dirname(gitCommonDir);
3903
4534
  } catch {
3904
4535
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3905
4536
  cwd: dir,
@@ -3908,11 +4539,11 @@ function getProjectName(cwd) {
3908
4539
  stdio: ["pipe", "pipe", "pipe"]
3909
4540
  }).trim();
3910
4541
  }
3911
- _cached2 = path13.basename(repoRoot);
4542
+ _cached2 = path14.basename(repoRoot);
3912
4543
  _cachedCwd = dir;
3913
4544
  return _cached2;
3914
4545
  } catch {
3915
- _cached2 = path13.basename(dir);
4546
+ _cached2 = path14.basename(dir);
3916
4547
  _cachedCwd = dir;
3917
4548
  return _cached2;
3918
4549
  }
@@ -3948,7 +4579,7 @@ function findSessionForProject(projectName) {
3948
4579
  const sessions = listSessions();
3949
4580
  for (const s of sessions) {
3950
4581
  const proj = s.projectDir.split("/").filter(Boolean).pop();
3951
- if (proj === projectName && (s.agentId === "exe" || isCoordinatorName(s.agentId))) return s;
4582
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
3952
4583
  }
3953
4584
  return null;
3954
4585
  }
@@ -3994,7 +4625,7 @@ var init_session_scope = __esm({
3994
4625
 
3995
4626
  // src/lib/tasks-notify.ts
3996
4627
  async function dispatchTaskToEmployee(input) {
3997
- if (input.assignedTo === "exe" || isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
4628
+ if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
3998
4629
  let crossProject = false;
3999
4630
  if (input.projectName) {
4000
4631
  try {
@@ -4389,8 +5020,8 @@ __export(tasks_exports, {
4389
5020
  updateTaskStatus: () => updateTaskStatus,
4390
5021
  writeCheckpoint: () => writeCheckpoint
4391
5022
  });
4392
- import path14 from "path";
4393
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
5023
+ import path15 from "path";
5024
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
4394
5025
  async function createTask(input) {
4395
5026
  const result = await createTaskCore(input);
4396
5027
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -4409,14 +5040,14 @@ async function updateTask(input) {
4409
5040
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4410
5041
  try {
4411
5042
  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`);
5043
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
5044
+ const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
4414
5045
  if (input.status === "in_progress") {
4415
5046
  mkdirSync5(cacheDir, { recursive: true });
4416
5047
  writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4417
5048
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
4418
5049
  try {
4419
- unlinkSync4(cachePath);
5050
+ unlinkSync5(cachePath);
4420
5051
  } catch {
4421
5052
  }
4422
5053
  }
@@ -4473,7 +5104,7 @@ async function updateTask(input) {
4473
5104
  }
4474
5105
  const isTerminal = input.status === "done" || input.status === "needs_review";
4475
5106
  if (isTerminal) {
4476
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
5107
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
4477
5108
  if (!isCoordinator) {
4478
5109
  notifyTaskDone();
4479
5110
  }
@@ -4498,7 +5129,7 @@ async function updateTask(input) {
4498
5129
  }
4499
5130
  }
4500
5131
  }
4501
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
5132
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4502
5133
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
4503
5134
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
4504
5135
  taskId,
@@ -4514,7 +5145,7 @@ async function updateTask(input) {
4514
5145
  });
4515
5146
  }
4516
5147
  let nextTask;
4517
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
5148
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
4518
5149
  try {
4519
5150
  nextTask = await findNextTask(String(row.assigned_to));
4520
5151
  } catch {
@@ -4858,7 +5489,7 @@ var init_capacity_monitor = __esm({
4858
5489
  // src/lib/tmux-routing.ts
4859
5490
  var tmux_routing_exports = {};
4860
5491
  __export(tmux_routing_exports, {
4861
- acquireSpawnLock: () => acquireSpawnLock,
5492
+ acquireSpawnLock: () => acquireSpawnLock2,
4862
5493
  employeeSessionName: () => employeeSessionName,
4863
5494
  ensureEmployee: () => ensureEmployee,
4864
5495
  extractRootExe: () => extractRootExe,
@@ -4873,20 +5504,20 @@ __export(tmux_routing_exports, {
4873
5504
  notifyParentExe: () => notifyParentExe,
4874
5505
  parseParentExe: () => parseParentExe,
4875
5506
  registerParentExe: () => registerParentExe,
4876
- releaseSpawnLock: () => releaseSpawnLock,
5507
+ releaseSpawnLock: () => releaseSpawnLock2,
4877
5508
  resolveExeSession: () => resolveExeSession,
4878
5509
  sendIntercom: () => sendIntercom,
4879
5510
  spawnEmployee: () => spawnEmployee,
4880
5511
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4881
5512
  });
4882
5513
  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";
5514
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync } from "fs";
5515
+ import path16 from "path";
5516
+ import os8 from "os";
5517
+ import { fileURLToPath as fileURLToPath2 } from "url";
5518
+ import { unlinkSync as unlinkSync6 } from "fs";
4888
5519
  function spawnLockPath(sessionName) {
4889
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5520
+ return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4890
5521
  }
4891
5522
  function isProcessAlive(pid) {
4892
5523
  try {
@@ -4896,14 +5527,14 @@ function isProcessAlive(pid) {
4896
5527
  return false;
4897
5528
  }
4898
5529
  }
4899
- function acquireSpawnLock(sessionName) {
4900
- if (!existsSync12(SPAWN_LOCK_DIR)) {
5530
+ function acquireSpawnLock2(sessionName) {
5531
+ if (!existsSync13(SPAWN_LOCK_DIR)) {
4901
5532
  mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
4902
5533
  }
4903
5534
  const lockFile = spawnLockPath(sessionName);
4904
- if (existsSync12(lockFile)) {
5535
+ if (existsSync13(lockFile)) {
4905
5536
  try {
4906
- const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
5537
+ const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
4907
5538
  const age = Date.now() - lock.timestamp;
4908
5539
  if (isProcessAlive(lock.pid) && age < 6e4) {
4909
5540
  return false;
@@ -4914,22 +5545,22 @@ function acquireSpawnLock(sessionName) {
4914
5545
  writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4915
5546
  return true;
4916
5547
  }
4917
- function releaseSpawnLock(sessionName) {
5548
+ function releaseSpawnLock2(sessionName) {
4918
5549
  try {
4919
- unlinkSync5(spawnLockPath(sessionName));
5550
+ unlinkSync6(spawnLockPath(sessionName));
4920
5551
  } catch {
4921
5552
  }
4922
5553
  }
4923
5554
  function resolveBehaviorsExporterScript() {
4924
5555
  try {
4925
- const thisFile = fileURLToPath(import.meta.url);
4926
- const scriptPath = path15.join(
4927
- path15.dirname(thisFile),
5556
+ const thisFile = fileURLToPath2(import.meta.url);
5557
+ const scriptPath = path16.join(
5558
+ path16.dirname(thisFile),
4928
5559
  "..",
4929
5560
  "bin",
4930
5561
  "exe-export-behaviors.js"
4931
5562
  );
4932
- return existsSync12(scriptPath) ? scriptPath : null;
5563
+ return existsSync13(scriptPath) ? scriptPath : null;
4933
5564
  } catch {
4934
5565
  return null;
4935
5566
  }
@@ -4995,11 +5626,11 @@ function extractRootExe(name) {
4995
5626
  return parts.length > 0 ? parts[parts.length - 1] : null;
4996
5627
  }
4997
5628
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4998
- if (!existsSync12(SESSION_CACHE)) {
5629
+ if (!existsSync13(SESSION_CACHE)) {
4999
5630
  mkdirSync6(SESSION_CACHE, { recursive: true });
5000
5631
  }
5001
5632
  const rootExe = extractRootExe(parentExe) ?? parentExe;
5002
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5633
+ const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5003
5634
  writeFileSync6(filePath, JSON.stringify({
5004
5635
  parentExe: rootExe,
5005
5636
  dispatchedBy: dispatchedBy || rootExe,
@@ -5008,7 +5639,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5008
5639
  }
5009
5640
  function getParentExe(sessionKey) {
5010
5641
  try {
5011
- const data = JSON.parse(readFileSync9(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5642
+ const data = JSON.parse(readFileSync10(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5012
5643
  return data.parentExe || null;
5013
5644
  } catch {
5014
5645
  return null;
@@ -5016,8 +5647,8 @@ function getParentExe(sessionKey) {
5016
5647
  }
5017
5648
  function getDispatchedBy(sessionKey) {
5018
5649
  try {
5019
- const data = JSON.parse(readFileSync9(
5020
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5650
+ const data = JSON.parse(readFileSync10(
5651
+ path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5021
5652
  "utf8"
5022
5653
  ));
5023
5654
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5043,10 +5674,10 @@ function isEmployeeAlive(sessionName) {
5043
5674
  }
5044
5675
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
5045
5676
  const base = employeeSessionName(employeeName, exeSession);
5046
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
5677
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
5047
5678
  for (let i = 2; i <= maxInstances; i++) {
5048
5679
  const candidate = employeeSessionName(employeeName, exeSession, i);
5049
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
5680
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
5050
5681
  }
5051
5682
  return null;
5052
5683
  }
@@ -5078,15 +5709,15 @@ async function verifyPaneAtCapacity(sessionName) {
5078
5709
  }
5079
5710
  function readDebounceState() {
5080
5711
  try {
5081
- if (!existsSync12(DEBOUNCE_FILE)) return {};
5082
- return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
5712
+ if (!existsSync13(DEBOUNCE_FILE)) return {};
5713
+ return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
5083
5714
  } catch {
5084
5715
  return {};
5085
5716
  }
5086
5717
  }
5087
5718
  function writeDebounceState(state) {
5088
5719
  try {
5089
- if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5720
+ if (!existsSync13(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5090
5721
  writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
5091
5722
  } catch {
5092
5723
  }
@@ -5206,7 +5837,7 @@ function notifyParentExe(sessionKey) {
5206
5837
  return true;
5207
5838
  }
5208
5839
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
5209
- if (employeeName === "exe" || isCoordinatorName(employeeName)) {
5840
+ if (isCoordinatorName(employeeName)) {
5210
5841
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
5211
5842
  }
5212
5843
  try {
@@ -5278,26 +5909,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5278
5909
  const transport = getTransport();
5279
5910
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5280
5911
  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)) {
5912
+ const logDir = path16.join(os8.homedir(), ".exe-os", "session-logs");
5913
+ const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5914
+ if (!existsSync13(logDir)) {
5284
5915
  mkdirSync6(logDir, { recursive: true });
5285
5916
  }
5286
5917
  transport.kill(sessionName);
5287
5918
  let cleanupSuffix = "";
5288
5919
  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)) {
5920
+ const thisFile = fileURLToPath2(import.meta.url);
5921
+ const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5922
+ if (existsSync13(cleanupScript)) {
5292
5923
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5293
5924
  }
5294
5925
  } catch {
5295
5926
  }
5296
5927
  try {
5297
- const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
5928
+ const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
5298
5929
  let claudeJson = {};
5299
5930
  try {
5300
- claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
5931
+ claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
5301
5932
  } catch {
5302
5933
  }
5303
5934
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -5309,13 +5940,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5309
5940
  } catch {
5310
5941
  }
5311
5942
  try {
5312
- const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
5943
+ const settingsDir = path16.join(os8.homedir(), ".claude", "projects");
5313
5944
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5314
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
5315
- const settingsPath = path15.join(projSettingsDir, "settings.json");
5945
+ const projSettingsDir = path16.join(settingsDir, normalizedKey);
5946
+ const settingsPath = path16.join(projSettingsDir, "settings.json");
5316
5947
  let settings = {};
5317
5948
  try {
5318
- settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
5949
+ settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
5319
5950
  } catch {
5320
5951
  }
5321
5952
  const perms = settings.permissions ?? {};
@@ -5356,8 +5987,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5356
5987
  let behaviorsFlag = "";
5357
5988
  let legacyFallbackWarned = false;
5358
5989
  if (!useExeAgent && !useBinSymlink) {
5359
- const identityPath = path15.join(
5360
- os7.homedir(),
5990
+ const identityPath = path16.join(
5991
+ os8.homedir(),
5361
5992
  ".exe-os",
5362
5993
  "identity",
5363
5994
  `${employeeName}.md`
@@ -5366,13 +5997,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5366
5997
  const hasAgentFlag = claudeSupportsAgentFlag();
5367
5998
  if (hasAgentFlag) {
5368
5999
  identityFlag = ` --agent ${employeeName}`;
5369
- } else if (existsSync12(identityPath)) {
6000
+ } else if (existsSync13(identityPath)) {
5370
6001
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
5371
6002
  legacyFallbackWarned = true;
5372
6003
  }
5373
6004
  const behaviorsFile = exportBehaviorsSync(
5374
6005
  employeeName,
5375
- path15.basename(spawnCwd),
6006
+ path16.basename(spawnCwd),
5376
6007
  sessionName
5377
6008
  );
5378
6009
  if (behaviorsFile) {
@@ -5387,9 +6018,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5387
6018
  }
5388
6019
  let sessionContextFlag = "";
5389
6020
  try {
5390
- const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
6021
+ const ctxDir = path16.join(os8.homedir(), ".exe-os", "session-cache");
5391
6022
  mkdirSync6(ctxDir, { recursive: true });
5392
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
6023
+ const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
5393
6024
  const ctxContent = [
5394
6025
  `## Session Context`,
5395
6026
  `You are running in tmux session: ${sessionName}.`,
@@ -5428,13 +6059,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5428
6059
  command: spawnCommand
5429
6060
  });
5430
6061
  if (spawnResult.error) {
5431
- releaseSpawnLock(sessionName);
6062
+ releaseSpawnLock2(sessionName);
5432
6063
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
5433
6064
  }
5434
6065
  transport.pipeLog(sessionName, logFile);
5435
6066
  try {
5436
6067
  const mySession = getMySession();
5437
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6068
+ const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5438
6069
  writeFileSync6(dispatchInfo, JSON.stringify({
5439
6070
  dispatchedBy: mySession,
5440
6071
  rootExe: exeSession,
@@ -5466,7 +6097,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5466
6097
  }
5467
6098
  }
5468
6099
  if (!booted) {
5469
- releaseSpawnLock(sessionName);
6100
+ releaseSpawnLock2(sessionName);
5470
6101
  return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
5471
6102
  }
5472
6103
  if (!useExeAgent) {
@@ -5483,7 +6114,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5483
6114
  pid: 0,
5484
6115
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
5485
6116
  });
5486
- releaseSpawnLock(sessionName);
6117
+ releaseSpawnLock2(sessionName);
5487
6118
  return { sessionName };
5488
6119
  }
5489
6120
  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;
@@ -5499,14 +6130,14 @@ var init_tmux_routing = __esm({
5499
6130
  init_intercom_queue();
5500
6131
  init_plan_limits();
5501
6132
  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");
6133
+ SPAWN_LOCK_DIR = path16.join(os8.homedir(), ".exe-os", "spawn-locks");
6134
+ SESSION_CACHE = path16.join(os8.homedir(), ".exe-os", "session-cache");
5504
6135
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5505
6136
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5506
6137
  VERIFY_PANE_LINES = 200;
5507
6138
  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");
6139
+ INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
6140
+ DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
5510
6141
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5511
6142
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5512
6143
  }
@@ -5547,9 +6178,10 @@ __export(task_scanner_exports, {
5547
6178
  formatText: () => formatText,
5548
6179
  scanAgentTasks: () => scanAgentTasks
5549
6180
  });
5550
- import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync13, statSync } from "fs";
6181
+ import { readdirSync as readdirSync5, readFileSync as readFileSync12, existsSync as existsSync14, statSync as statSync2 } from "fs";
5551
6182
  import { execSync as execSync9 } from "child_process";
5552
- import path17 from "path";
6183
+ import path18 from "path";
6184
+ import os9 from "os";
5553
6185
  function getProjectRoot() {
5554
6186
  try {
5555
6187
  return execSync9("git rev-parse --show-toplevel", {
@@ -5561,19 +6193,31 @@ function getProjectRoot() {
5561
6193
  return process.cwd();
5562
6194
  }
5563
6195
  }
5564
- function scanAgentTasks(agentId) {
5565
- const taskDir = path17.join(getProjectRoot(), "exe", agentId);
6196
+ function getSessionScope() {
6197
+ try {
6198
+ const out = execSync9("tmux display-message -p '#{session_name}' 2>/dev/null", {
6199
+ encoding: "utf8",
6200
+ timeout: 1e3
6201
+ }).trim();
6202
+ if (!out) return "default";
6203
+ const dashIdx = out.lastIndexOf("-");
6204
+ return dashIdx > 0 ? out.slice(dashIdx + 1) : out;
6205
+ } catch {
6206
+ return "default";
6207
+ }
6208
+ }
6209
+ function scanDir(dirPath, seen) {
5566
6210
  const open = [];
5567
6211
  const inProgress = [];
5568
6212
  let done = 0;
5569
- let total = 0;
5570
- if (!existsSync13(taskDir)) return { open, inProgress, done, total };
6213
+ if (!existsSync14(dirPath)) return { open, inProgress, done };
5571
6214
  try {
5572
- const files = readdirSync5(taskDir).filter((f) => f.endsWith(".md"));
5573
- total = files.length;
6215
+ const files = readdirSync5(dirPath).filter((f) => f.endsWith(".md"));
5574
6216
  for (const f of files) {
6217
+ if (seen.has(f)) continue;
6218
+ seen.add(f);
5575
6219
  try {
5576
- const content = readFileSync11(path17.join(taskDir, f), "utf8");
6220
+ const content = readFileSync12(path18.join(dirPath, f), "utf8");
5577
6221
  const statusMatch = content.match(STATUS_RE);
5578
6222
  const status = statusMatch ? statusMatch[1].toLowerCase() : null;
5579
6223
  if (status === "done") {
@@ -5600,6 +6244,25 @@ function scanAgentTasks(agentId) {
5600
6244
  }
5601
6245
  } catch {
5602
6246
  }
6247
+ return { open, inProgress, done };
6248
+ }
6249
+ function scanAgentTasks(agentId) {
6250
+ const open = [];
6251
+ const inProgress = [];
6252
+ let done = 0;
6253
+ const seen = /* @__PURE__ */ new Set();
6254
+ const scope = getSessionScope();
6255
+ const centralDir = path18.join(os9.homedir(), ".exe-os", "tasks", scope, agentId);
6256
+ const central = scanDir(centralDir, seen);
6257
+ open.push(...central.open);
6258
+ inProgress.push(...central.inProgress);
6259
+ done += central.done;
6260
+ const legacyDir = path18.join(getProjectRoot(), "exe", agentId);
6261
+ const legacy = scanDir(legacyDir, seen);
6262
+ open.push(...legacy.open);
6263
+ inProgress.push(...legacy.inProgress);
6264
+ done += legacy.done;
6265
+ const total = open.length + inProgress.length + done;
5603
6266
  open.sort((a, b) => a.priority.localeCompare(b.priority));
5604
6267
  inProgress.sort((a, b) => a.priority.localeCompare(b.priority));
5605
6268
  return { open, inProgress, done, total };
@@ -5629,7 +6292,7 @@ function formatMandatory(agentId, result) {
5629
6292
  const current = inProgress[0];
5630
6293
  let stale = false;
5631
6294
  try {
5632
- const stat = statSync(path17.join(getProjectRoot(), "exe", agentId, current.file));
6295
+ const stat = statSync2(path18.join(getProjectRoot(), "exe", agentId, current.file));
5633
6296
  const ageMin = (Date.now() - stat.mtimeMs) / 6e4;
5634
6297
  if (ageMin > 30) stale = true;
5635
6298
  } catch {
@@ -5681,13 +6344,13 @@ __export(worker_gate_exports, {
5681
6344
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
5682
6345
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
5683
6346
  });
5684
- import { readdirSync as readdirSync6, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync14 } from "fs";
5685
- import path18 from "path";
6347
+ import { readdirSync as readdirSync6, writeFileSync as writeFileSync8, unlinkSync as unlinkSync8, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
6348
+ import path19 from "path";
5686
6349
  function tryAcquireWorkerSlot() {
5687
6350
  try {
5688
6351
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
5689
6352
  const reservationId = `res-${process.pid}-${Date.now()}`;
5690
- const reservationPath = path18.join(WORKER_PID_DIR, `${reservationId}.pid`);
6353
+ const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
5691
6354
  writeFileSync8(reservationPath, String(process.pid));
5692
6355
  const files = readdirSync6(WORKER_PID_DIR);
5693
6356
  let alive = 0;
@@ -5705,20 +6368,20 @@ function tryAcquireWorkerSlot() {
5705
6368
  alive++;
5706
6369
  } catch {
5707
6370
  try {
5708
- unlinkSync7(path18.join(WORKER_PID_DIR, f));
6371
+ unlinkSync8(path19.join(WORKER_PID_DIR, f));
5709
6372
  } catch {
5710
6373
  }
5711
6374
  }
5712
6375
  }
5713
6376
  if (alive > MAX_CONCURRENT_WORKERS) {
5714
6377
  try {
5715
- unlinkSync7(reservationPath);
6378
+ unlinkSync8(reservationPath);
5716
6379
  } catch {
5717
6380
  }
5718
6381
  return false;
5719
6382
  }
5720
6383
  try {
5721
- unlinkSync7(reservationPath);
6384
+ unlinkSync8(reservationPath);
5722
6385
  } catch {
5723
6386
  }
5724
6387
  return true;
@@ -5729,20 +6392,20 @@ function tryAcquireWorkerSlot() {
5729
6392
  function registerWorkerPid(pid) {
5730
6393
  try {
5731
6394
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
5732
- writeFileSync8(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
6395
+ writeFileSync8(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
5733
6396
  } catch {
5734
6397
  }
5735
6398
  }
5736
6399
  function cleanupWorkerPid() {
5737
6400
  try {
5738
- unlinkSync7(path18.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
6401
+ unlinkSync8(path19.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
5739
6402
  } catch {
5740
6403
  }
5741
6404
  }
5742
6405
  function tryAcquireBackfillLock() {
5743
6406
  try {
5744
6407
  mkdirSync8(WORKER_PID_DIR, { recursive: true });
5745
- if (existsSync14(BACKFILL_LOCK)) {
6408
+ if (existsSync15(BACKFILL_LOCK)) {
5746
6409
  try {
5747
6410
  const pid = parseInt(
5748
6411
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -5766,7 +6429,7 @@ function tryAcquireBackfillLock() {
5766
6429
  }
5767
6430
  function releaseBackfillLock() {
5768
6431
  try {
5769
- unlinkSync7(BACKFILL_LOCK);
6432
+ unlinkSync8(BACKFILL_LOCK);
5770
6433
  } catch {
5771
6434
  }
5772
6435
  }
@@ -5775,9 +6438,9 @@ var init_worker_gate = __esm({
5775
6438
  "src/lib/worker-gate.ts"() {
5776
6439
  "use strict";
5777
6440
  init_config();
5778
- WORKER_PID_DIR = path18.join(EXE_AI_DIR, "worker-pids");
6441
+ WORKER_PID_DIR = path19.join(EXE_AI_DIR, "worker-pids");
5779
6442
  MAX_CONCURRENT_WORKERS = 3;
5780
- BACKFILL_LOCK = path18.join(WORKER_PID_DIR, "backfill.lock");
6443
+ BACKFILL_LOCK = path19.join(WORKER_PID_DIR, "backfill.lock");
5781
6444
  }
5782
6445
  });
5783
6446
 
@@ -5860,6 +6523,232 @@ var init_compress = __esm({
5860
6523
  }
5861
6524
  });
5862
6525
 
6526
+ // src/lib/crdt-sync.ts
6527
+ var crdt_sync_exports = {};
6528
+ __export(crdt_sync_exports, {
6529
+ _setStatePath: () => _setStatePath,
6530
+ applyRemoteUpdate: () => applyRemoteUpdate,
6531
+ destroyCrdtDoc: () => destroyCrdtDoc,
6532
+ getDiffUpdate: () => getDiffUpdate,
6533
+ getFullState: () => getFullState,
6534
+ getStateVector: () => getStateVector,
6535
+ importExistingBehaviors: () => importExistingBehaviors,
6536
+ importExistingMemories: () => importExistingMemories,
6537
+ initCrdtDoc: () => initCrdtDoc,
6538
+ isCrdtSyncEnabled: () => isCrdtSyncEnabled,
6539
+ onUpdate: () => onUpdate,
6540
+ readAllBehaviors: () => readAllBehaviors,
6541
+ readAllMemories: () => readAllMemories,
6542
+ rebuildFromDb: () => rebuildFromDb
6543
+ });
6544
+ import * as Y from "yjs";
6545
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync16, mkdirSync as mkdirSync9, unlinkSync as unlinkSync9 } from "fs";
6546
+ import path20 from "path";
6547
+ import { homedir } from "os";
6548
+ function getStatePath() {
6549
+ return _statePathOverride ?? DEFAULT_STATE_PATH;
6550
+ }
6551
+ function _setStatePath(p) {
6552
+ _statePathOverride = p;
6553
+ }
6554
+ function initCrdtDoc() {
6555
+ if (doc) return doc;
6556
+ doc = new Y.Doc();
6557
+ const sp = getStatePath();
6558
+ if (existsSync16(sp)) {
6559
+ try {
6560
+ const state = readFileSync13(sp);
6561
+ Y.applyUpdate(doc, new Uint8Array(state));
6562
+ } catch {
6563
+ console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
6564
+ try {
6565
+ unlinkSync9(sp);
6566
+ } catch {
6567
+ }
6568
+ rebuildFromDb().catch((err) => {
6569
+ console.warn("[crdt-sync] rebuild from DB failed:", err);
6570
+ });
6571
+ }
6572
+ }
6573
+ doc.on("update", () => {
6574
+ persistState();
6575
+ });
6576
+ return doc;
6577
+ }
6578
+ function getMemoriesMap() {
6579
+ const d = initCrdtDoc();
6580
+ return d.getMap("memories");
6581
+ }
6582
+ function getBehaviorsMap() {
6583
+ const d = initCrdtDoc();
6584
+ return d.getMap("behaviors");
6585
+ }
6586
+ function applyRemoteUpdate(update) {
6587
+ const d = initCrdtDoc();
6588
+ Y.applyUpdate(d, update);
6589
+ }
6590
+ function getFullState() {
6591
+ const d = initCrdtDoc();
6592
+ return Y.encodeStateAsUpdate(d);
6593
+ }
6594
+ function getDiffUpdate(remoteStateVector) {
6595
+ const d = initCrdtDoc();
6596
+ return Y.encodeStateAsUpdate(d, remoteStateVector);
6597
+ }
6598
+ function getStateVector() {
6599
+ const d = initCrdtDoc();
6600
+ return Y.encodeStateVector(d);
6601
+ }
6602
+ function importExistingMemories(memories) {
6603
+ const map = getMemoriesMap();
6604
+ const d = initCrdtDoc();
6605
+ let imported = 0;
6606
+ d.transact(() => {
6607
+ for (const mem of memories) {
6608
+ if (!mem.id) continue;
6609
+ if (map.has(mem.id)) continue;
6610
+ const entry = new Y.Map();
6611
+ entry.set("id", mem.id);
6612
+ entry.set("agent_id", mem.agent_id ?? null);
6613
+ entry.set("agent_role", mem.agent_role ?? null);
6614
+ entry.set("session_id", mem.session_id ?? null);
6615
+ entry.set("timestamp", mem.timestamp ?? null);
6616
+ entry.set("tool_name", mem.tool_name ?? null);
6617
+ entry.set("project_name", mem.project_name ?? null);
6618
+ entry.set("has_error", mem.has_error ?? 0);
6619
+ entry.set("raw_text", mem.raw_text ?? "");
6620
+ entry.set("version", mem.version ?? 0);
6621
+ entry.set("author_device_id", mem.author_device_id ?? null);
6622
+ entry.set("scope", mem.scope ?? "business");
6623
+ map.set(mem.id, entry);
6624
+ imported++;
6625
+ }
6626
+ });
6627
+ return imported;
6628
+ }
6629
+ function importExistingBehaviors(behaviors) {
6630
+ const map = getBehaviorsMap();
6631
+ const d = initCrdtDoc();
6632
+ let imported = 0;
6633
+ d.transact(() => {
6634
+ for (const beh of behaviors) {
6635
+ if (!beh.id) continue;
6636
+ if (map.has(beh.id)) continue;
6637
+ const entry = new Y.Map();
6638
+ entry.set("id", beh.id);
6639
+ entry.set("agent_id", beh.agent_id ?? null);
6640
+ entry.set("project_name", beh.project_name ?? null);
6641
+ entry.set("domain", beh.domain ?? null);
6642
+ entry.set("content", beh.content ?? null);
6643
+ entry.set("active", beh.active ?? 1);
6644
+ entry.set("priority", beh.priority ?? "p1");
6645
+ entry.set("created_at", beh.created_at ?? null);
6646
+ entry.set("updated_at", beh.updated_at ?? null);
6647
+ map.set(beh.id, entry);
6648
+ imported++;
6649
+ }
6650
+ });
6651
+ return imported;
6652
+ }
6653
+ function readAllMemories() {
6654
+ const map = getMemoriesMap();
6655
+ const records = [];
6656
+ map.forEach((entry, id) => {
6657
+ records.push({
6658
+ id,
6659
+ agent_id: entry.get("agent_id"),
6660
+ agent_role: entry.get("agent_role"),
6661
+ session_id: entry.get("session_id"),
6662
+ timestamp: entry.get("timestamp"),
6663
+ tool_name: entry.get("tool_name"),
6664
+ project_name: entry.get("project_name"),
6665
+ has_error: entry.get("has_error"),
6666
+ raw_text: entry.get("raw_text"),
6667
+ version: entry.get("version"),
6668
+ author_device_id: entry.get("author_device_id"),
6669
+ scope: entry.get("scope")
6670
+ });
6671
+ });
6672
+ return records;
6673
+ }
6674
+ function readAllBehaviors() {
6675
+ const map = getBehaviorsMap();
6676
+ const records = [];
6677
+ map.forEach((entry, id) => {
6678
+ records.push({
6679
+ id,
6680
+ agent_id: entry.get("agent_id"),
6681
+ project_name: entry.get("project_name"),
6682
+ domain: entry.get("domain"),
6683
+ content: entry.get("content"),
6684
+ active: entry.get("active"),
6685
+ priority: entry.get("priority"),
6686
+ created_at: entry.get("created_at"),
6687
+ updated_at: entry.get("updated_at")
6688
+ });
6689
+ });
6690
+ return records;
6691
+ }
6692
+ function onUpdate(callback) {
6693
+ const d = initCrdtDoc();
6694
+ const handler = (update) => callback(update);
6695
+ d.on("update", handler);
6696
+ return () => d.off("update", handler);
6697
+ }
6698
+ function persistState() {
6699
+ if (!doc) return;
6700
+ try {
6701
+ const sp = getStatePath();
6702
+ const dir = path20.dirname(sp);
6703
+ if (!existsSync16(dir)) mkdirSync9(dir, { recursive: true });
6704
+ const state = Y.encodeStateAsUpdate(doc);
6705
+ writeFileSync9(sp, Buffer.from(state));
6706
+ } catch {
6707
+ }
6708
+ }
6709
+ function isCrdtSyncEnabled() {
6710
+ return process.env.EXE_CRDT_SYNC !== "0";
6711
+ }
6712
+ async function rebuildFromDb() {
6713
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6714
+ const client = getClient2();
6715
+ const result = await client.execute(
6716
+ "SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
6717
+ );
6718
+ const memories = result.rows.map((row) => ({
6719
+ id: String(row.id),
6720
+ agent_id: row.agent_id,
6721
+ agent_role: row.agent_role,
6722
+ session_id: row.session_id,
6723
+ timestamp: row.timestamp,
6724
+ tool_name: row.tool_name,
6725
+ project_name: row.project_name,
6726
+ has_error: row.has_error,
6727
+ raw_text: row.raw_text,
6728
+ version: row.version,
6729
+ author_device_id: row.author_device_id,
6730
+ scope: row.scope
6731
+ }));
6732
+ const count = importExistingMemories(memories);
6733
+ persistState();
6734
+ return count;
6735
+ }
6736
+ function destroyCrdtDoc() {
6737
+ if (doc) {
6738
+ doc.destroy();
6739
+ doc = null;
6740
+ }
6741
+ }
6742
+ var DEFAULT_STATE_PATH, _statePathOverride, doc;
6743
+ var init_crdt_sync = __esm({
6744
+ "src/lib/crdt-sync.ts"() {
6745
+ "use strict";
6746
+ DEFAULT_STATE_PATH = path20.join(homedir(), ".exe-os", "crdt-state.bin");
6747
+ _statePathOverride = null;
6748
+ doc = null;
6749
+ }
6750
+ });
6751
+
5863
6752
  // src/lib/cloud-sync.ts
5864
6753
  var cloud_sync_exports = {};
5865
6754
  __export(cloud_sync_exports, {
@@ -5888,16 +6777,16 @@ __export(cloud_sync_exports, {
5888
6777
  mergeRosterFromRemote: () => mergeRosterFromRemote,
5889
6778
  recordRosterDeletion: () => recordRosterDeletion
5890
6779
  });
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";
6780
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync17, readdirSync as readdirSync7, mkdirSync as mkdirSync10, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
5892
6781
  import crypto7 from "crypto";
5893
- import path19 from "path";
5894
- import { homedir } from "os";
6782
+ import path21 from "path";
6783
+ import { homedir as homedir2 } from "os";
5895
6784
  function sqlSafe(v) {
5896
6785
  return v === void 0 ? null : v;
5897
6786
  }
5898
6787
  function logError(msg) {
5899
6788
  try {
5900
- const logPath = path19.join(homedir(), ".exe-os", "workers.log");
6789
+ const logPath = path21.join(homedir2(), ".exe-os", "workers.log");
5901
6790
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
5902
6791
  `);
5903
6792
  } catch {
@@ -5905,20 +6794,20 @@ function logError(msg) {
5905
6794
  }
5906
6795
  async function withRosterLock(fn) {
5907
6796
  try {
5908
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
5909
- closeSync(fd);
5910
- writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
6797
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
6798
+ closeSync2(fd);
6799
+ writeFileSync10(ROSTER_LOCK_PATH, String(Date.now()));
5911
6800
  } catch (err) {
5912
6801
  if (err.code === "EEXIST") {
5913
6802
  try {
5914
- const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
6803
+ const ts = parseInt(readFileSync14(ROSTER_LOCK_PATH, "utf-8"), 10);
5915
6804
  if (Date.now() - ts < LOCK_STALE_MS) {
5916
6805
  throw new Error("Roster merge already in progress \u2014 another sync is running");
5917
6806
  }
5918
- unlinkSync8(ROSTER_LOCK_PATH);
5919
- const fd = openSync(ROSTER_LOCK_PATH, "wx");
5920
- closeSync(fd);
5921
- writeFileSync9(ROSTER_LOCK_PATH, String(Date.now()));
6807
+ unlinkSync10(ROSTER_LOCK_PATH);
6808
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
6809
+ closeSync2(fd);
6810
+ writeFileSync10(ROSTER_LOCK_PATH, String(Date.now()));
5922
6811
  } catch (retryErr) {
5923
6812
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
5924
6813
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -5931,7 +6820,7 @@ async function withRosterLock(fn) {
5931
6820
  return await fn();
5932
6821
  } finally {
5933
6822
  try {
5934
- unlinkSync8(ROSTER_LOCK_PATH);
6823
+ unlinkSync10(ROSTER_LOCK_PATH);
5935
6824
  } catch {
5936
6825
  }
5937
6826
  }
@@ -6076,29 +6965,75 @@ async function cloudSync(config) {
6076
6965
  const pullResult = await cloudPull(lastPullVersion, config);
6077
6966
  let pulled = 0;
6078
6967
  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;
6968
+ if (isCrdtSyncEnabled()) {
6969
+ const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
6970
+ initCrdtDoc2();
6971
+ importExistingMemories2(
6972
+ pullResult.records.map((rec) => ({
6973
+ id: String(rec.id ?? ""),
6974
+ agent_id: rec.agent_id,
6975
+ agent_role: rec.agent_role,
6976
+ session_id: rec.session_id,
6977
+ timestamp: rec.timestamp,
6978
+ tool_name: rec.tool_name,
6979
+ project_name: rec.project_name,
6980
+ has_error: rec.has_error ?? 0,
6981
+ raw_text: rec.raw_text ?? "",
6982
+ version: rec.version ?? 0,
6983
+ author_device_id: rec.author_device_id,
6984
+ scope: rec.scope ?? "business"
6985
+ }))
6986
+ );
6987
+ const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
6988
+ const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
6989
+ const stmts = merged.map((rec) => ({
6990
+ sql: `INSERT OR REPLACE INTO memories
6991
+ (id, agent_id, agent_role, session_id, timestamp,
6992
+ tool_name, project_name, has_error, raw_text, version,
6993
+ author_device_id, scope)
6994
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6995
+ args: [
6996
+ sqlSafe(rec.id),
6997
+ sqlSafe(rec.agent_id),
6998
+ sqlSafe(rec.agent_role),
6999
+ sqlSafe(rec.session_id),
7000
+ sqlSafe(rec.timestamp),
7001
+ sqlSafe(rec.tool_name),
7002
+ sqlSafe(rec.project_name),
7003
+ sqlSafe(rec.has_error ?? 0),
7004
+ sqlSafe(rec.raw_text ?? ""),
7005
+ sqlSafe(rec.version ?? 0),
7006
+ sqlSafe(rec.author_device_id),
7007
+ sqlSafe(rec.scope ?? "business")
7008
+ ]
7009
+ }));
7010
+ if (stmts.length > 0) await client.batch(stmts, "write");
7011
+ pulled = pullResult.records.length;
7012
+ } else {
7013
+ const stmts = pullResult.records.map((rec) => ({
7014
+ sql: `INSERT OR REPLACE INTO memories
7015
+ (id, agent_id, agent_role, session_id, timestamp,
7016
+ tool_name, project_name, has_error, raw_text, version,
7017
+ author_device_id, scope)
7018
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
7019
+ args: [
7020
+ sqlSafe(rec.id),
7021
+ sqlSafe(rec.agent_id),
7022
+ sqlSafe(rec.agent_role),
7023
+ sqlSafe(rec.session_id),
7024
+ sqlSafe(rec.timestamp),
7025
+ sqlSafe(rec.tool_name),
7026
+ sqlSafe(rec.project_name),
7027
+ sqlSafe(rec.has_error ?? 0),
7028
+ sqlSafe(rec.raw_text ?? ""),
7029
+ sqlSafe(rec.version ?? 0),
7030
+ sqlSafe(rec.author_device_id),
7031
+ sqlSafe(rec.scope ?? "business")
7032
+ ]
7033
+ }));
7034
+ await client.batch(stmts, "write");
7035
+ pulled = pullResult.records.length;
7036
+ }
6102
7037
  }
6103
7038
  if (pullResult.maxVersion > lastPullVersion) {
6104
7039
  await client.execute({
@@ -6246,8 +7181,8 @@ async function cloudSync(config) {
6246
7181
  try {
6247
7182
  const employees = await loadEmployees();
6248
7183
  rosterResult.employees = employees.length;
6249
- const idDir = path19.join(EXE_AI_DIR, "identity");
6250
- if (existsSync15(idDir)) {
7184
+ const idDir = path21.join(EXE_AI_DIR, "identity");
7185
+ if (existsSync17(idDir)) {
6251
7186
  rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
6252
7187
  }
6253
7188
  } catch {
@@ -6268,48 +7203,48 @@ async function cloudSync(config) {
6268
7203
  function recordRosterDeletion(name) {
6269
7204
  let deletions = [];
6270
7205
  try {
6271
- if (existsSync15(ROSTER_DELETIONS_PATH)) {
6272
- deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
7206
+ if (existsSync17(ROSTER_DELETIONS_PATH)) {
7207
+ deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
6273
7208
  }
6274
7209
  } catch {
6275
7210
  }
6276
7211
  if (!deletions.includes(name)) deletions.push(name);
6277
- writeFileSync9(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
7212
+ writeFileSync10(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
6278
7213
  }
6279
7214
  function consumeRosterDeletions() {
6280
7215
  try {
6281
- if (!existsSync15(ROSTER_DELETIONS_PATH)) return [];
6282
- const deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
6283
- writeFileSync9(ROSTER_DELETIONS_PATH, "[]");
7216
+ if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
7217
+ const deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
7218
+ writeFileSync10(ROSTER_DELETIONS_PATH, "[]");
6284
7219
  return deletions;
6285
7220
  } catch {
6286
7221
  return [];
6287
7222
  }
6288
7223
  }
6289
7224
  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");
7225
+ const rosterPath = paths?.rosterPath ?? path21.join(EXE_AI_DIR, "exe-employees.json");
7226
+ const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
7227
+ const configPath = paths?.configPath ?? path21.join(EXE_AI_DIR, "config.json");
6293
7228
  let roster = [];
6294
- if (existsSync15(rosterPath)) {
7229
+ if (existsSync17(rosterPath)) {
6295
7230
  try {
6296
- roster = JSON.parse(readFileSync12(rosterPath, "utf-8"));
7231
+ roster = JSON.parse(readFileSync14(rosterPath, "utf-8"));
6297
7232
  } catch {
6298
7233
  }
6299
7234
  }
6300
7235
  const identities = {};
6301
- if (existsSync15(identityDir)) {
7236
+ if (existsSync17(identityDir)) {
6302
7237
  for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
6303
7238
  try {
6304
- identities[file] = readFileSync12(path19.join(identityDir, file), "utf-8");
7239
+ identities[file] = readFileSync14(path21.join(identityDir, file), "utf-8");
6305
7240
  } catch {
6306
7241
  }
6307
7242
  }
6308
7243
  }
6309
7244
  let config;
6310
- if (existsSync15(configPath)) {
7245
+ if (existsSync17(configPath)) {
6311
7246
  try {
6312
- config = JSON.parse(readFileSync12(configPath, "utf-8"));
7247
+ config = JSON.parse(readFileSync14(configPath, "utf-8"));
6313
7248
  } catch {
6314
7249
  }
6315
7250
  }
@@ -6385,23 +7320,23 @@ async function cloudPullRoster(config) {
6385
7320
  }
6386
7321
  }
6387
7322
  function mergeConfig(remoteConfig, configPath) {
6388
- const cfgPath = configPath ?? path19.join(EXE_AI_DIR, "config.json");
7323
+ const cfgPath = configPath ?? path21.join(EXE_AI_DIR, "config.json");
6389
7324
  let local = {};
6390
- if (existsSync15(cfgPath)) {
7325
+ if (existsSync17(cfgPath)) {
6391
7326
  try {
6392
- local = JSON.parse(readFileSync12(cfgPath, "utf-8"));
7327
+ local = JSON.parse(readFileSync14(cfgPath, "utf-8"));
6393
7328
  } catch {
6394
7329
  }
6395
7330
  }
6396
7331
  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");
7332
+ const dir = path21.dirname(cfgPath);
7333
+ if (!existsSync17(dir)) mkdirSync10(dir, { recursive: true });
7334
+ writeFileSync10(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
6400
7335
  }
6401
7336
  async function mergeRosterFromRemote(remote, paths) {
6402
7337
  return withRosterLock(async () => {
6403
7338
  const rosterPath = paths?.rosterPath ?? void 0;
6404
- const identityDir = paths?.identityDir ?? path19.join(EXE_AI_DIR, "identity");
7339
+ const identityDir = paths?.identityDir ?? path21.join(EXE_AI_DIR, "identity");
6405
7340
  const localEmployees = await loadEmployees(rosterPath);
6406
7341
  const localNames = new Set(localEmployees.map((e) => e.name));
6407
7342
  let added = 0;
@@ -6422,15 +7357,15 @@ async function mergeRosterFromRemote(remote, paths) {
6422
7357
  ) ?? lookupKey;
6423
7358
  const remoteIdentity = remote.identities[matchedKey];
6424
7359
  if (remoteIdentity) {
6425
- if (!existsSync15(identityDir)) mkdirSync9(identityDir, { recursive: true });
6426
- const idPath = path19.join(identityDir, `${remoteEmp.name}.md`);
7360
+ if (!existsSync17(identityDir)) mkdirSync10(identityDir, { recursive: true });
7361
+ const idPath = path21.join(identityDir, `${remoteEmp.name}.md`);
6427
7362
  let localIdentity = null;
6428
7363
  try {
6429
- localIdentity = existsSync15(idPath) ? readFileSync12(idPath, "utf-8") : null;
7364
+ localIdentity = existsSync17(idPath) ? readFileSync14(idPath, "utf-8") : null;
6430
7365
  } catch {
6431
7366
  }
6432
7367
  if (localIdentity !== remoteIdentity) {
6433
- writeFileSync9(idPath, remoteIdentity, "utf-8");
7368
+ writeFileSync10(idPath, remoteIdentity, "utf-8");
6434
7369
  identitiesUpdated++;
6435
7370
  }
6436
7371
  }
@@ -6883,13 +7818,14 @@ var init_cloud_sync = __esm({
6883
7818
  init_compress();
6884
7819
  init_license();
6885
7820
  init_config();
7821
+ init_crdt_sync();
6886
7822
  init_employees();
6887
7823
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
6888
7824
  FETCH_TIMEOUT_MS = 3e4;
6889
7825
  PUSH_BATCH_SIZE = 5e3;
6890
- ROSTER_LOCK_PATH = path19.join(EXE_AI_DIR, "roster-merge.lock");
7826
+ ROSTER_LOCK_PATH = path21.join(EXE_AI_DIR, "roster-merge.lock");
6891
7827
  LOCK_STALE_MS = 3e4;
6892
- ROSTER_DELETIONS_PATH = path19.join(EXE_AI_DIR, "roster-deletions.json");
7828
+ ROSTER_DELETIONS_PATH = path21.join(EXE_AI_DIR, "roster-deletions.json");
6893
7829
  }
6894
7830
  });
6895
7831
 
@@ -7071,10 +8007,10 @@ var init_schedules = __esm({
7071
8007
 
7072
8008
  // src/bin/exe-boot.ts
7073
8009
  init_employees();
7074
- import path20 from "path";
8010
+ import path22 from "path";
7075
8011
  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";
8012
+ import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
8013
+ import os10 from "os";
7078
8014
 
7079
8015
  // src/lib/employee-templates.ts
7080
8016
  init_global_procedures();
@@ -7532,12 +8468,13 @@ init_task_scope();
7532
8468
 
7533
8469
  // src/lib/is-main.ts
7534
8470
  import { realpathSync } from "fs";
7535
- import { fileURLToPath as fileURLToPath2 } from "url";
8471
+ import { fileURLToPath as fileURLToPath3 } from "url";
7536
8472
  function isMainModule(importMetaUrl) {
7537
8473
  if (process.argv[1] == null) return false;
8474
+ if (process.argv[1].includes("mcp/server")) return false;
7538
8475
  try {
7539
8476
  const scriptPath = realpathSync(process.argv[1]);
7540
- const modulePath = realpathSync(fileURLToPath2(importMetaUrl));
8477
+ const modulePath = realpathSync(fileURLToPath3(importMetaUrl));
7541
8478
  return scriptPath === modulePath;
7542
8479
  } catch {
7543
8480
  return importMetaUrl === `file://${process.argv[1]}` || importMetaUrl === new URL(process.argv[1], "file://").href;
@@ -7549,19 +8486,19 @@ init_notifications();
7549
8486
 
7550
8487
  // src/adapters/claude/active-agent.ts
7551
8488
  init_config();
7552
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6, readdirSync as readdirSync4 } from "fs";
8489
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync7, readdirSync as readdirSync4 } from "fs";
7553
8490
  import { execSync as execSync8 } from "child_process";
7554
- import path16 from "path";
8491
+ import path17 from "path";
7555
8492
 
7556
8493
  // src/adapters/claude/session-key.ts
7557
8494
  init_session_key();
7558
8495
 
7559
8496
  // src/adapters/claude/active-agent.ts
7560
8497
  init_employees();
7561
- var CACHE_DIR = path16.join(EXE_AI_DIR, "session-cache");
8498
+ var CACHE_DIR = path17.join(EXE_AI_DIR, "session-cache");
7562
8499
  var STALE_MS = 24 * 60 * 60 * 1e3;
7563
8500
  function getMarkerPath() {
7564
- return path16.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
8501
+ return path17.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
7565
8502
  }
7566
8503
  function writeActiveAgent(agentId, agentRole) {
7567
8504
  try {
@@ -7576,11 +8513,11 @@ function writeActiveAgent(agentId, agentRole) {
7576
8513
  function cleanupSessionMarkers() {
7577
8514
  const key = getSessionKey();
7578
8515
  try {
7579
- unlinkSync6(path16.join(CACHE_DIR, `active-agent-${key}.json`));
8516
+ unlinkSync7(path17.join(CACHE_DIR, `active-agent-${key}.json`));
7580
8517
  } catch {
7581
8518
  }
7582
8519
  try {
7583
- unlinkSync6(path16.join(CACHE_DIR, "active-agent-undefined.json"));
8520
+ unlinkSync7(path17.join(CACHE_DIR, "active-agent-undefined.json"));
7584
8521
  } catch {
7585
8522
  }
7586
8523
  }
@@ -7670,7 +8607,7 @@ async function boot(options) {
7670
8607
  const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
7671
8608
  for (const dir of employeeDirs) {
7672
8609
  const employee = dir.name;
7673
- const taskDir = path20.join(exeDir, employee);
8610
+ const taskDir = path22.join(exeDir, employee);
7674
8611
  let files;
7675
8612
  try {
7676
8613
  files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
@@ -7681,7 +8618,7 @@ async function boot(options) {
7681
8618
  const taskFilePath = `exe/${employee}/${file}`;
7682
8619
  let content;
7683
8620
  try {
7684
- content = readFs(path20.join(taskDir, file), "utf8");
8621
+ content = readFs(path22.join(taskDir, file), "utf8");
7685
8622
  } catch {
7686
8623
  continue;
7687
8624
  }
@@ -7767,12 +8704,12 @@ async function boot(options) {
7767
8704
  }
7768
8705
  try {
7769
8706
  for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
7770
- const reviewDir = path20.join(process.cwd(), "exe", reviewDirName);
7771
- if (existsSync16(reviewDir)) {
8707
+ const reviewDir = path22.join(process.cwd(), "exe", reviewDirName);
8708
+ if (existsSync18(reviewDir)) {
7772
8709
  for (const f of readdirSync8(reviewDir)) {
7773
8710
  if (f.startsWith("review-") && f.endsWith(".md")) {
7774
8711
  try {
7775
- unlinkSync9(path20.join(reviewDir, f));
8712
+ unlinkSync11(path22.join(reviewDir, f));
7776
8713
  } catch {
7777
8714
  }
7778
8715
  }
@@ -7818,12 +8755,12 @@ async function boot(options) {
7818
8755
  });
7819
8756
  const taskFile = String(r.task_file);
7820
8757
  try {
7821
- const filePath = path20.join(process.cwd(), taskFile);
7822
- if (existsSync16(filePath)) {
7823
- let content = readFileSync13(filePath, "utf8");
8758
+ const filePath = path22.join(process.cwd(), taskFile);
8759
+ if (existsSync18(filePath)) {
8760
+ let content = readFileSync15(filePath, "utf8");
7824
8761
  content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
7825
- const { writeFileSync: writeFileSync10 } = await import("fs");
7826
- writeFileSync10(filePath, content);
8762
+ const { writeFileSync: writeFileSync11 } = await import("fs");
8763
+ writeFileSync11(filePath, content);
7827
8764
  }
7828
8765
  } catch {
7829
8766
  }
@@ -8154,13 +9091,13 @@ async function boot(options) {
8154
9091
  }));
8155
9092
  const assignedResult = await client.execute({
8156
9093
  sql: `SELECT COUNT(*) as cnt FROM tasks
8157
- WHERE project_name = ? AND (assigned_by = ? OR assigned_by = 'exe')
9094
+ WHERE project_name = ? AND assigned_by = ?
8158
9095
  AND created_at > datetime('now', '-7 days')${pScope.sql}`,
8159
9096
  args: [p.projectName, coordinatorName, ...pScope.args]
8160
9097
  });
8161
9098
  const tasksAssigned = Number(assignedResult.rows[0]?.cnt) || 0;
8162
9099
  if (tasksAssigned > 0) {
8163
- const exeEntry = activity.find((a) => a.name === coordinatorName || a.name === "exe");
9100
+ const exeEntry = activity.find((a) => a.name === coordinatorName || isCoordinatorName(a.name));
8164
9101
  if (exeEntry) {
8165
9102
  exeEntry.tasksAssigned = tasksAssigned;
8166
9103
  } else {
@@ -8184,7 +9121,7 @@ async function boot(options) {
8184
9121
  const projName = s.projectDir.split("/").pop() ?? "";
8185
9122
  const proj = projects.find((p) => p.projectName === projName);
8186
9123
  if (!proj || proj.sessionName) continue;
8187
- if (s.agentId === coordinatorName || s.agentId === "exe") {
9124
+ if (s.agentId === coordinatorName || isCoordinatorName(s.agentId)) {
8188
9125
  proj.sessionName = s.windowName;
8189
9126
  } else if (s.parentExe) {
8190
9127
  proj.sessionName = s.parentExe;
@@ -8292,19 +9229,19 @@ async function boot(options) {
8292
9229
  })()
8293
9230
  ]);
8294
9231
  try {
8295
- const configPath = path20.join(
8296
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path20.join(os8.homedir(), ".exe-os"),
9232
+ const configPath = path22.join(
9233
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os10.homedir(), ".exe-os"),
8297
9234
  "config.json"
8298
9235
  );
8299
- if (existsSync16(configPath)) {
8300
- const raw = JSON.parse(readFileSync13(configPath, "utf8"));
9236
+ if (existsSync18(configPath)) {
9237
+ const raw = JSON.parse(readFileSync15(configPath, "utf8"));
8301
9238
  briefData.cloudConnected = !!(raw.cloud || raw.turso);
8302
9239
  }
8303
9240
  } catch {
8304
9241
  }
8305
9242
  try {
8306
- const backfillFlagPath = path20.join(EXE_AI_DIR, "session-cache", "needs-backfill");
8307
- const isBackfillNeeded = () => existsSync16(backfillFlagPath);
9243
+ const backfillFlagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
9244
+ const isBackfillNeeded = () => existsSync18(backfillFlagPath);
8308
9245
  const coverageResult = await client.execute({
8309
9246
  sql: `SELECT COUNT(*) as total,
8310
9247
  SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
@@ -8326,16 +9263,16 @@ async function boot(options) {
8326
9263
  let daemonRunning = false;
8327
9264
  let daemonUptime;
8328
9265
  let daemonRequestsServed;
8329
- const socketPath = path20.join(EXE_AI_DIR, "exed.sock");
8330
- if (existsSync16(socketPath)) {
9266
+ const socketPath = path22.join(EXE_AI_DIR, "exed.sock");
9267
+ if (existsSync18(socketPath)) {
8331
9268
  try {
8332
- const net = await import("net");
9269
+ const net2 = await import("net");
8333
9270
  const health = await new Promise((resolve) => {
8334
9271
  const timer = setTimeout(() => {
8335
9272
  sock.destroy();
8336
9273
  resolve(null);
8337
9274
  }, 2e3);
8338
- const sock = net.createConnection({ path: socketPath });
9275
+ const sock = net2.createConnection({ path: socketPath });
8339
9276
  let buf = "";
8340
9277
  sock.on("connect", () => {
8341
9278
  sock.write(JSON.stringify({ id: "boot-health", type: "health" }) + "\n");
@@ -8369,10 +9306,10 @@ async function boot(options) {
8369
9306
  }
8370
9307
  }
8371
9308
  if (!daemonRunning) {
8372
- const pidPath = path20.join(EXE_AI_DIR, "exed.pid");
8373
- if (existsSync16(pidPath)) {
9309
+ const pidPath = path22.join(EXE_AI_DIR, "exed.pid");
9310
+ if (existsSync18(pidPath)) {
8374
9311
  try {
8375
- const pid = parseInt(readFileSync13(pidPath, "utf8").trim(), 10);
9312
+ const pid = parseInt(readFileSync15(pidPath, "utf8").trim(), 10);
8376
9313
  if (pid > 0) {
8377
9314
  process.kill(pid, 0);
8378
9315
  daemonRunning = true;
@@ -8383,10 +9320,10 @@ async function boot(options) {
8383
9320
  }
8384
9321
  if (nullCount === 0) {
8385
9322
  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);
9323
+ const flagPath = path22.join(EXE_AI_DIR, "session-cache", "needs-backfill");
9324
+ if (existsSync18(flagPath)) {
9325
+ const { unlinkSync: unlinkSync12 } = await import("fs");
9326
+ unlinkSync12(flagPath);
8390
9327
  }
8391
9328
  } catch {
8392
9329
  }
@@ -8407,26 +9344,26 @@ async function boot(options) {
8407
9344
  if (!tryAcquireWorkerSlot2()) {
8408
9345
  process.stderr.write("[exe-boot] Backfill needed but worker gate full \u2014 skipping\n");
8409
9346
  } 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");
9347
+ const { spawn: spawn2 } = await import("child_process");
9348
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
9349
+ const thisFile = fileURLToPath4(import.meta.url);
9350
+ const backfillPath = path22.resolve(path22.dirname(thisFile), "backfill-vectors.js");
9351
+ if (existsSync18(backfillPath)) {
9352
+ const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
9353
+ const workerLogPath = path22.join(EXE_AI_DIR, "workers.log");
8417
9354
  let stderrFd = "ignore";
8418
9355
  try {
8419
- stderrFd = openSync2(workerLogPath, "a");
9356
+ stderrFd = openSync3(workerLogPath, "a");
8420
9357
  } catch {
8421
9358
  }
8422
- const child = spawn(process.execPath, [backfillPath], {
9359
+ const child = spawn2(process.execPath, [backfillPath], {
8423
9360
  detached: true,
8424
9361
  stdio: ["ignore", "ignore", stderrFd]
8425
9362
  });
8426
9363
  child.unref();
8427
9364
  if (child.pid) registerWorkerPid2(child.pid);
8428
9365
  if (typeof stderrFd === "number") try {
8429
- closeSync2(stderrFd);
9366
+ closeSync3(stderrFd);
8430
9367
  } catch {
8431
9368
  }
8432
9369
  briefData.embedding.backfillRunning = true;
@@ -8438,13 +9375,13 @@ async function boot(options) {
8438
9375
  } catch {
8439
9376
  }
8440
9377
  try {
8441
- const { fileURLToPath: fileURLToPath3 } = await import("url");
8442
- const thisFile = fileURLToPath3(import.meta.url);
9378
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
9379
+ const thisFile = fileURLToPath4(import.meta.url);
8443
9380
  const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
8444
9381
  const missing = [];
8445
9382
  for (const bin of criticalBinaries) {
8446
- const binPath = path20.resolve(path20.dirname(thisFile), bin);
8447
- if (!existsSync16(binPath)) {
9383
+ const binPath = path22.resolve(path22.dirname(thisFile), bin);
9384
+ if (!existsSync18(binPath)) {
8448
9385
  missing.push(`dist/bin/${bin}`);
8449
9386
  }
8450
9387
  }
@@ -8473,7 +9410,7 @@ async function boot(options) {
8473
9410
  console.log(brief);
8474
9411
  return;
8475
9412
  }
8476
- const sessionDir = path20.join(EXE_AI_DIR, "sessions", coordinatorName);
9413
+ const sessionDir = path22.join(EXE_AI_DIR, "sessions", coordinatorName);
8477
9414
  await mkdir5(sessionDir, { recursive: true });
8478
9415
  const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
8479
9416
 
@@ -8482,7 +9419,7 @@ async function boot(options) {
8482
9419
  # Status Brief
8483
9420
 
8484
9421
  ${brief}`;
8485
- await writeFile6(path20.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
9422
+ await writeFile6(path22.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
8486
9423
  const unread = await readUnreadNotifications();
8487
9424
  if (unread.length > 0) {
8488
9425
  console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
@@ -8491,12 +9428,12 @@ ${brief}`;
8491
9428
  await cleanupOldNotifications();
8492
9429
  console.log(brief);
8493
9430
  try {
8494
- const configPath2 = path20.join(
8495
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path20.join(os8.homedir(), ".exe-os"),
9431
+ const configPath2 = path22.join(
9432
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os10.homedir(), ".exe-os"),
8496
9433
  "config.json"
8497
9434
  );
8498
- if (existsSync16(configPath2)) {
8499
- const rawCfg = JSON.parse(readFileSync13(configPath2, "utf8"));
9435
+ if (existsSync18(configPath2)) {
9436
+ const rawCfg = JSON.parse(readFileSync15(configPath2, "utf8"));
8500
9437
  const cloudCfg = rawCfg.cloud;
8501
9438
  if (cloudCfg?.apiKey) {
8502
9439
  const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));