@askexenow/exe-os 0.9.8 → 0.9.10

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1411 -953
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +913 -543
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +418 -262
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +793 -485
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +566 -357
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +530 -319
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +547 -336
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +649 -417
  44. package/dist/hooks/bug-report-worker.js +486 -316
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +528 -317
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3442 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +534 -323
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +614 -382
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +569 -347
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +664 -431
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +1049 -680
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +422 -357
  88. package/dist/lib/tmux-routing.js +314 -248
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1408 -672
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +448 -371
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1983 -315
  99. package/dist/runtime/index.js +567 -355
  100. package/dist/tui/App.js +887 -531
  101. package/package.json +4 -4
package/dist/bin/cli.js CHANGED
@@ -26,6 +26,44 @@ var __copyProps = (to, from, except, desc) => {
26
26
  };
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
+ // src/lib/secure-files.ts
30
+ import { chmodSync, existsSync, mkdirSync } from "fs";
31
+ import { chmod, mkdir } from "fs/promises";
32
+ async function ensurePrivateDir(dirPath) {
33
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
34
+ try {
35
+ await chmod(dirPath, PRIVATE_DIR_MODE);
36
+ } catch {
37
+ }
38
+ }
39
+ function ensurePrivateDirSync(dirPath) {
40
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
41
+ try {
42
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
43
+ } catch {
44
+ }
45
+ }
46
+ async function enforcePrivateFile(filePath) {
47
+ try {
48
+ await chmod(filePath, PRIVATE_FILE_MODE);
49
+ } catch {
50
+ }
51
+ }
52
+ function enforcePrivateFileSync(filePath) {
53
+ try {
54
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
55
+ } catch {
56
+ }
57
+ }
58
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
59
+ var init_secure_files = __esm({
60
+ "src/lib/secure-files.ts"() {
61
+ "use strict";
62
+ PRIVATE_DIR_MODE = 448;
63
+ PRIVATE_FILE_MODE = 384;
64
+ }
65
+ });
66
+
29
67
  // src/lib/config.ts
30
68
  var config_exports = {};
31
69
  __export(config_exports, {
@@ -42,8 +80,8 @@ __export(config_exports, {
42
80
  migrateConfig: () => migrateConfig,
43
81
  saveConfig: () => saveConfig
44
82
  });
45
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
46
- import { readFileSync, existsSync, renameSync } from "fs";
83
+ import { readFile, writeFile } from "fs/promises";
84
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
47
85
  import path from "path";
48
86
  import os from "os";
49
87
  function resolveDataDir() {
@@ -51,7 +89,7 @@ function resolveDataDir() {
51
89
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
52
90
  const newDir = path.join(os.homedir(), ".exe-os");
53
91
  const legacyDir = path.join(os.homedir(), ".exe-mem");
54
- if (!existsSync(newDir) && existsSync(legacyDir)) {
92
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
55
93
  try {
56
94
  renameSync(legacyDir, newDir);
57
95
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -114,9 +152,9 @@ function normalizeAutoUpdate(raw) {
114
152
  }
115
153
  async function loadConfig() {
116
154
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
117
- await mkdir(dir, { recursive: true });
155
+ await ensurePrivateDir(dir);
118
156
  const configPath = path.join(dir, "config.json");
119
- if (!existsSync(configPath)) {
157
+ if (!existsSync2(configPath)) {
120
158
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
121
159
  }
122
160
  const raw = await readFile(configPath, "utf-8");
@@ -129,6 +167,7 @@ async function loadConfig() {
129
167
  `);
130
168
  try {
131
169
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
170
+ await enforcePrivateFile(configPath);
132
171
  } catch {
133
172
  }
134
173
  }
@@ -147,7 +186,7 @@ async function loadConfig() {
147
186
  function loadConfigSync() {
148
187
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
149
188
  const configPath = path.join(dir, "config.json");
150
- if (!existsSync(configPath)) {
189
+ if (!existsSync2(configPath)) {
151
190
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
152
191
  }
153
192
  try {
@@ -165,12 +204,10 @@ function loadConfigSync() {
165
204
  }
166
205
  async function saveConfig(config) {
167
206
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
168
- await mkdir(dir, { recursive: true });
207
+ await ensurePrivateDir(dir);
169
208
  const configPath = path.join(dir, "config.json");
170
209
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
171
- if (config.cloud?.apiKey) {
172
- await chmod(configPath, 384);
173
- }
210
+ await enforcePrivateFile(configPath);
174
211
  }
175
212
  async function loadConfigFrom(configPath) {
176
213
  const raw = await readFile(configPath, "utf-8");
@@ -190,6 +227,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
190
227
  var init_config = __esm({
191
228
  "src/lib/config.ts"() {
192
229
  "use strict";
230
+ init_secure_files();
193
231
  EXE_AI_DIR = resolveDataDir();
194
232
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
195
233
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -306,10 +344,10 @@ __export(agent_config_exports, {
306
344
  saveAgentConfig: () => saveAgentConfig,
307
345
  setAgentRuntime: () => setAgentRuntime
308
346
  });
309
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
347
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
310
348
  import path2 from "path";
311
349
  function loadAgentConfig() {
312
- if (!existsSync2(AGENT_CONFIG_PATH)) return {};
350
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
313
351
  try {
314
352
  return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
315
353
  } catch {
@@ -318,8 +356,9 @@ function loadAgentConfig() {
318
356
  }
319
357
  function saveAgentConfig(config) {
320
358
  const dir = path2.dirname(AGENT_CONFIG_PATH);
321
- if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
359
+ ensurePrivateDirSync(dir);
322
360
  writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
361
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
323
362
  }
324
363
  function getAgentRuntime(agentId) {
325
364
  const config = loadAgentConfig();
@@ -359,6 +398,7 @@ var init_agent_config = __esm({
359
398
  "use strict";
360
399
  init_config();
361
400
  init_runtime_table();
401
+ init_secure_files();
362
402
  AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
363
403
  KNOWN_RUNTIMES = {
364
404
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
@@ -406,7 +446,7 @@ __export(employees_exports, {
406
446
  validateEmployeeName: () => validateEmployeeName
407
447
  });
408
448
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
409
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
449
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
410
450
  import { execSync } from "child_process";
411
451
  import path3 from "path";
412
452
  import os2 from "os";
@@ -445,7 +485,7 @@ function validateEmployeeName(name) {
445
485
  return { valid: true };
446
486
  }
447
487
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
448
- if (!existsSync3(employeesPath)) {
488
+ if (!existsSync4(employeesPath)) {
449
489
  return [];
450
490
  }
451
491
  const raw = await readFile2(employeesPath, "utf-8");
@@ -460,7 +500,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
460
500
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
461
501
  }
462
502
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
463
- if (!existsSync3(employeesPath)) return [];
503
+ if (!existsSync4(employeesPath)) return [];
464
504
  try {
465
505
  return JSON.parse(readFileSync3(employeesPath, "utf-8"));
466
506
  } catch {
@@ -508,7 +548,7 @@ function appendToCoordinatorTeam(employee) {
508
548
  const coordinator = getCoordinatorEmployee(loadEmployeesSync());
509
549
  if (!coordinator) return;
510
550
  const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
511
- if (!existsSync3(idPath)) return;
551
+ if (!existsSync4(idPath)) return;
512
552
  const content = readFileSync3(idPath, "utf-8");
513
553
  if (content.includes(`**${capitalize(employee.name)}`)) return;
514
554
  const teamMatch = content.match(TEAM_SECTION_RE);
@@ -562,9 +602,9 @@ async function normalizeRosterCase(rosterPath) {
562
602
  const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
563
603
  const oldPath = path3.join(identityDir, `${oldName}.md`);
564
604
  const newPath = path3.join(identityDir, `${emp.name}.md`);
565
- if (existsSync3(oldPath) && !existsSync3(newPath)) {
605
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
566
606
  renameSync2(oldPath, newPath);
567
- } else if (existsSync3(oldPath) && oldPath !== newPath) {
607
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
568
608
  const content = readFileSync3(oldPath, "utf-8");
569
609
  writeFileSync2(newPath, content, "utf-8");
570
610
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
@@ -607,7 +647,7 @@ function registerBinSymlinks(name) {
607
647
  for (const suffix of ["", "-opencode"]) {
608
648
  const linkName = `${name}${suffix}`;
609
649
  const linkPath = path3.join(binDir, linkName);
610
- if (existsSync3(linkPath)) {
650
+ if (existsSync4(linkPath)) {
611
651
  skipped.push(linkName);
612
652
  continue;
613
653
  }
@@ -638,7 +678,7 @@ var init_employees = __esm({
638
678
  import os3 from "os";
639
679
  import path4 from "path";
640
680
  import {
641
- existsSync as existsSync4,
681
+ existsSync as existsSync5,
642
682
  lstatSync,
643
683
  mkdirSync as mkdirSync2,
644
684
  readlinkSync as readlinkSync2,
@@ -657,7 +697,7 @@ function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
657
697
  const target = identitySourcePath(homeDir, agentId);
658
698
  const link = claudeAgentLinkPath(homeDir, agentId);
659
699
  mkdirSync2(claudeAgentsDir(homeDir), { recursive: true });
660
- if (existsSync4(link)) {
700
+ if (existsSync5(link)) {
661
701
  let stat2;
662
702
  try {
663
703
  stat2 = lstatSync(link);
@@ -731,12 +771,12 @@ __export(preferences_exports, {
731
771
  loadPreferences: () => loadPreferences,
732
772
  savePreferences: () => savePreferences
733
773
  });
734
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
774
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
735
775
  import path5 from "path";
736
776
  import os4 from "os";
737
777
  function loadPreferences(homeDir = os4.homedir()) {
738
778
  const configPath = path5.join(homeDir, ".exe-os", "config.json");
739
- if (!existsSync5(configPath)) return {};
779
+ if (!existsSync6(configPath)) return {};
740
780
  try {
741
781
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
742
782
  return config.preferences ?? {};
@@ -747,9 +787,9 @@ function loadPreferences(homeDir = os4.homedir()) {
747
787
  function savePreferences(prefs, homeDir = os4.homedir()) {
748
788
  const configDir = path5.join(homeDir, ".exe-os");
749
789
  const configPath = path5.join(configDir, "config.json");
750
- mkdirSync3(configDir, { recursive: true });
790
+ ensurePrivateDirSync(configDir);
751
791
  let config = {};
752
- if (existsSync5(configPath)) {
792
+ if (existsSync6(configPath)) {
753
793
  try {
754
794
  config = JSON.parse(readFileSync4(configPath, "utf-8"));
755
795
  } catch {
@@ -758,10 +798,12 @@ function savePreferences(prefs, homeDir = os4.homedir()) {
758
798
  }
759
799
  config.preferences = prefs;
760
800
  writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n");
801
+ enforcePrivateFileSync(configPath);
761
802
  }
762
803
  var init_preferences = __esm({
763
804
  "src/lib/preferences.ts"() {
764
805
  "use strict";
806
+ init_secure_files();
765
807
  }
766
808
  });
767
809
 
@@ -779,7 +821,7 @@ __export(installer_exports, {
779
821
  setupTmux: () => setupTmux
780
822
  });
781
823
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
782
- import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync4 } from "fs";
824
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync3 } from "fs";
783
825
  import path6 from "path";
784
826
  import os5 from "os";
785
827
  import { execSync as execSync2 } from "child_process";
@@ -790,7 +832,7 @@ function resolvePackageRoot() {
790
832
  const root = path6.parse(dir).root;
791
833
  while (dir !== root) {
792
834
  const pkgPath = path6.join(dir, "package.json");
793
- if (existsSync6(pkgPath)) {
835
+ if (existsSync7(pkgPath)) {
794
836
  try {
795
837
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
796
838
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
@@ -806,7 +848,7 @@ async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
806
848
  let skipped = 0;
807
849
  const skillsBase = path6.join(homeDir, ".claude", "skills");
808
850
  const exeDir = path6.join(packageRoot, "src", "commands", "exe");
809
- if (existsSync6(exeDir)) {
851
+ if (existsSync7(exeDir)) {
810
852
  const entries = await readdir(exeDir);
811
853
  const mdFiles = entries.filter((f) => f.endsWith(".md"));
812
854
  for (const file of mdFiles) {
@@ -821,7 +863,7 @@ async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
821
863
  }
822
864
  }
823
865
  const topLevelSrc = path6.join(packageRoot, "src", "commands", "exe.md");
824
- if (existsSync6(topLevelSrc)) {
866
+ if (existsSync7(topLevelSrc)) {
825
867
  const destDir = path6.join(skillsBase, "exe");
826
868
  await mkdir3(destDir, { recursive: true });
827
869
  const destPath = path6.join(destDir, "SKILL.md");
@@ -847,7 +889,7 @@ name: ${skillName}
847
889
  `);
848
890
  }
849
891
  }
850
- if (existsSync6(destPath)) {
892
+ if (existsSync7(destPath)) {
851
893
  const existing = await readFile3(destPath, "utf-8");
852
894
  if (existing === content) return false;
853
895
  }
@@ -857,7 +899,7 @@ name: ${skillName}
857
899
  async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
858
900
  const claudeJsonPath = path6.join(homeDir, ".claude.json");
859
901
  let claudeJson = {};
860
- if (existsSync6(claudeJsonPath)) {
902
+ if (existsSync7(claudeJsonPath)) {
861
903
  try {
862
904
  claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
863
905
  } catch {
@@ -874,21 +916,21 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
874
916
  env: {}
875
917
  };
876
918
  const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
877
- const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
878
919
  const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
879
- const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
880
- if (memMatches && osMatches) {
920
+ if (claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
921
+ delete claudeJson.mcpServers[MCP_PRIMARY_KEY];
922
+ }
923
+ if (memMatches && !claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
881
924
  await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
882
925
  return false;
883
926
  }
884
927
  claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
885
- claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
886
928
  await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
887
929
  await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
888
930
  return true;
889
931
  }
890
932
  async function cleanSettingsJsonMcp(settingsPath) {
891
- if (!existsSync6(settingsPath)) return;
933
+ if (!existsSync7(settingsPath)) return;
892
934
  try {
893
935
  const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
894
936
  const servers = settings.mcpServers;
@@ -915,7 +957,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
915
957
  const logSuffix = ` 2>> "${hookLogPath}"`;
916
958
  await mkdir3(logsDir, { recursive: true });
917
959
  let settings = {};
918
- if (existsSync6(settingsPath)) {
960
+ if (existsSync7(settingsPath)) {
919
961
  try {
920
962
  settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
921
963
  } catch {
@@ -1183,7 +1225,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1183
1225
  }
1184
1226
  async function cleanOldShellFunctions(homeDir = os5.homedir()) {
1185
1227
  const rosterPath = path6.join(homeDir, ".exe-os", "exe-employees.json");
1186
- if (!existsSync6(rosterPath)) return 0;
1228
+ if (!existsSync7(rosterPath)) return 0;
1187
1229
  let employees;
1188
1230
  try {
1189
1231
  employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
@@ -1204,7 +1246,7 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
1204
1246
  const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
1205
1247
  let totalRemoved = 0;
1206
1248
  for (const rcPath of rcFiles) {
1207
- if (!existsSync6(rcPath)) continue;
1249
+ if (!existsSync7(rcPath)) continue;
1208
1250
  let content;
1209
1251
  try {
1210
1252
  content = await readFile3(rcPath, "utf-8");
@@ -1337,13 +1379,13 @@ async function installStatusLine(packageRoot, homeDir = os5.homedir()) {
1337
1379
  const claudeDir = path6.join(homeDir, ".claude");
1338
1380
  await mkdir3(claudeDir, { recursive: true });
1339
1381
  const assetPath = path6.join(packageRoot, "dist", "assets", "statusline-command.sh");
1340
- if (!existsSync6(assetPath)) return "asset-missing";
1382
+ if (!existsSync7(assetPath)) return "asset-missing";
1341
1383
  const destScript = path6.join(claudeDir, "statusline-command.sh");
1342
1384
  const assetContent = await readFile3(assetPath, "utf-8");
1343
1385
  await writeFile3(destScript, assetContent, { mode: 493 });
1344
1386
  const settingsPath = path6.join(claudeDir, "settings.json");
1345
1387
  let settings = {};
1346
- if (existsSync6(settingsPath)) {
1388
+ if (existsSync7(settingsPath)) {
1347
1389
  try {
1348
1390
  settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
1349
1391
  } catch {
@@ -1382,7 +1424,7 @@ async function runInstaller(homeDir) {
1382
1424
  );
1383
1425
  const resolvedHome = homeDir ?? os5.homedir();
1384
1426
  const exeWorkspace = path6.join(resolvedHome, "exe");
1385
- if (!existsSync6(exeWorkspace)) {
1427
+ if (!existsSync7(exeWorkspace)) {
1386
1428
  try {
1387
1429
  await mkdir3(path6.join(exeWorkspace, "content"), { recursive: true });
1388
1430
  await mkdir3(path6.join(exeWorkspace, "operations"), { recursive: true });
@@ -1429,17 +1471,17 @@ function setupTmux(home) {
1429
1471
  const sourceLine = "source-file ~/.exe-os/tmux.conf";
1430
1472
  const pkgRoot = resolvePackageRoot();
1431
1473
  const assetPath = path6.join(pkgRoot, "dist", "assets", "tmux.conf");
1432
- if (!existsSync6(assetPath)) {
1474
+ if (!existsSync7(assetPath)) {
1433
1475
  process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
1434
1476
  `);
1435
1477
  return;
1436
1478
  }
1437
- mkdirSync4(exeDir, { recursive: true });
1479
+ mkdirSync3(exeDir, { recursive: true });
1438
1480
  copyFileSync(assetPath, exeTmuxConf);
1439
- if (existsSync6(userTmuxConf)) {
1481
+ if (existsSync7(userTmuxConf)) {
1440
1482
  const existing = readFileSync5(userTmuxConf, "utf8");
1441
1483
  if (!existing.includes(sourceLine)) {
1442
- if (!existsSync6(backupPath)) {
1484
+ if (!existsSync7(backupPath)) {
1443
1485
  copyFileSync(userTmuxConf, backupPath);
1444
1486
  process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
1445
1487
  `);
@@ -1462,7 +1504,7 @@ function setupGhostty(home) {
1462
1504
  const homeDir = home ?? os5.homedir();
1463
1505
  const xdgConfig = path6.join(homeDir, ".config", "ghostty");
1464
1506
  const macConfig = path6.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
1465
- const ghosttyInstalled = existsSync6(xdgConfig) || existsSync6(macConfig) || (() => {
1507
+ const ghosttyInstalled = existsSync7(xdgConfig) || existsSync7(macConfig) || (() => {
1466
1508
  try {
1467
1509
  execSync2("which ghostty 2>/dev/null");
1468
1510
  return true;
@@ -1475,28 +1517,28 @@ function setupGhostty(home) {
1475
1517
  }
1476
1518
  const pkgRoot = resolvePackageRoot();
1477
1519
  const assetPath = path6.join(pkgRoot, "dist", "assets", "ghostty.conf");
1478
- if (!existsSync6(assetPath)) {
1520
+ if (!existsSync7(assetPath)) {
1479
1521
  process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
1480
1522
  return;
1481
1523
  }
1482
1524
  const configDir = xdgConfig;
1483
1525
  const configPath = path6.join(configDir, "config");
1484
1526
  const backupPath = path6.join(configDir, "config.backup");
1485
- mkdirSync4(configDir, { recursive: true });
1527
+ mkdirSync3(configDir, { recursive: true });
1486
1528
  const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
1487
1529
  const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
1488
1530
  const assetContent = readFileSync5(assetPath, "utf8").trim();
1489
1531
  const markedSection = `${START_MARKER}
1490
1532
  ${assetContent}
1491
1533
  ${END_MARKER}`;
1492
- if (existsSync6(configPath)) {
1534
+ if (existsSync7(configPath)) {
1493
1535
  const existing = readFileSync5(configPath, "utf8");
1494
1536
  if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
1495
1537
  const before = existing.slice(0, existing.indexOf(START_MARKER));
1496
1538
  const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
1497
1539
  writeFileSync4(configPath, `${before}${markedSection}${after}`);
1498
1540
  } else if (existing.includes("Exe OS")) {
1499
- if (!existsSync6(backupPath)) {
1541
+ if (!existsSync7(backupPath)) {
1500
1542
  copyFileSync(configPath, backupPath);
1501
1543
  process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
1502
1544
  `);
@@ -1504,7 +1546,7 @@ ${END_MARKER}`;
1504
1546
  writeFileSync4(configPath, `${markedSection}
1505
1547
  `);
1506
1548
  } else {
1507
- if (!existsSync6(backupPath)) {
1549
+ if (!existsSync7(backupPath)) {
1508
1550
  copyFileSync(configPath, backupPath);
1509
1551
  process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
1510
1552
  `);
@@ -1564,7 +1606,7 @@ __export(keychain_exports, {
1564
1606
  setMasterKey: () => setMasterKey
1565
1607
  });
1566
1608
  import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
1567
- import { existsSync as existsSync7 } from "fs";
1609
+ import { existsSync as existsSync8 } from "fs";
1568
1610
  import path7 from "path";
1569
1611
  import os6 from "os";
1570
1612
  function getKeyDir() {
@@ -1592,7 +1634,7 @@ async function getMasterKey() {
1592
1634
  }
1593
1635
  }
1594
1636
  const keyPath = getKeyPath();
1595
- if (!existsSync7(keyPath)) {
1637
+ if (!existsSync8(keyPath)) {
1596
1638
  process.stderr.write(
1597
1639
  `[keychain] Key not found at ${keyPath} (HOME=${os6.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
1598
1640
  `
@@ -1635,7 +1677,7 @@ async function deleteMasterKey() {
1635
1677
  }
1636
1678
  }
1637
1679
  const keyPath = getKeyPath();
1638
- if (existsSync7(keyPath)) {
1680
+ if (existsSync8(keyPath)) {
1639
1681
  await unlink(keyPath);
1640
1682
  }
1641
1683
  }
@@ -2363,13 +2405,50 @@ var init_database_adapter = __esm({
2363
2405
  }
2364
2406
  });
2365
2407
 
2408
+ // src/lib/daemon-auth.ts
2409
+ import crypto2 from "crypto";
2410
+ import path9 from "path";
2411
+ import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
2412
+ function normalizeToken(token) {
2413
+ if (!token) return null;
2414
+ const trimmed = token.trim();
2415
+ return trimmed.length > 0 ? trimmed : null;
2416
+ }
2417
+ function readDaemonToken() {
2418
+ try {
2419
+ if (!existsSync9(DAEMON_TOKEN_PATH)) return null;
2420
+ return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
2421
+ } catch {
2422
+ return null;
2423
+ }
2424
+ }
2425
+ function ensureDaemonToken(seed) {
2426
+ const existing = readDaemonToken();
2427
+ if (existing) return existing;
2428
+ const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
2429
+ ensurePrivateDirSync(EXE_AI_DIR);
2430
+ writeFileSync5(DAEMON_TOKEN_PATH, `${token}
2431
+ `, "utf8");
2432
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
2433
+ return token;
2434
+ }
2435
+ var DAEMON_TOKEN_PATH;
2436
+ var init_daemon_auth = __esm({
2437
+ "src/lib/daemon-auth.ts"() {
2438
+ "use strict";
2439
+ init_config();
2440
+ init_secure_files();
2441
+ DAEMON_TOKEN_PATH = path9.join(EXE_AI_DIR, "exed.token");
2442
+ }
2443
+ });
2444
+
2366
2445
  // src/lib/exe-daemon-client.ts
2367
2446
  import net from "net";
2368
2447
  import os8 from "os";
2369
2448
  import { spawn } from "child_process";
2370
2449
  import { randomUUID } from "crypto";
2371
- import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
2372
- import path9 from "path";
2450
+ import { existsSync as existsSync10, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
2451
+ import path10 from "path";
2373
2452
  import { fileURLToPath as fileURLToPath3 } from "url";
2374
2453
  function handleData(chunk) {
2375
2454
  _buffer += chunk.toString();
@@ -2397,9 +2476,9 @@ function handleData(chunk) {
2397
2476
  }
2398
2477
  }
2399
2478
  function cleanupStaleFiles() {
2400
- if (existsSync8(PID_PATH)) {
2479
+ if (existsSync10(PID_PATH)) {
2401
2480
  try {
2402
- const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
2481
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
2403
2482
  if (pid > 0) {
2404
2483
  try {
2405
2484
  process.kill(pid, 0);
@@ -2420,11 +2499,11 @@ function cleanupStaleFiles() {
2420
2499
  }
2421
2500
  }
2422
2501
  function findPackageRoot() {
2423
- let dir = path9.dirname(fileURLToPath3(import.meta.url));
2424
- const { root } = path9.parse(dir);
2502
+ let dir = path10.dirname(fileURLToPath3(import.meta.url));
2503
+ const { root } = path10.parse(dir);
2425
2504
  while (dir !== root) {
2426
- if (existsSync8(path9.join(dir, "package.json"))) return dir;
2427
- dir = path9.dirname(dir);
2505
+ if (existsSync10(path10.join(dir, "package.json"))) return dir;
2506
+ dir = path10.dirname(dir);
2428
2507
  }
2429
2508
  return null;
2430
2509
  }
@@ -2450,16 +2529,17 @@ function spawnDaemon() {
2450
2529
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
2451
2530
  return;
2452
2531
  }
2453
- const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2454
- if (!existsSync8(daemonPath)) {
2532
+ const daemonPath = path10.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2533
+ if (!existsSync10(daemonPath)) {
2455
2534
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2456
2535
  `);
2457
2536
  return;
2458
2537
  }
2459
2538
  const resolvedPath = daemonPath;
2539
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
2460
2540
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2461
2541
  `);
2462
- const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
2542
+ const logPath = path10.join(path10.dirname(SOCKET_PATH), "exed.log");
2463
2543
  let stderrFd = "ignore";
2464
2544
  try {
2465
2545
  stderrFd = openSync(logPath, "a");
@@ -2477,7 +2557,8 @@ function spawnDaemon() {
2477
2557
  TMUX_PANE: void 0,
2478
2558
  // Prevents resolveExeSession() from scoping to one session
2479
2559
  EXE_DAEMON_SOCK: SOCKET_PATH,
2480
- EXE_DAEMON_PID: PID_PATH
2560
+ EXE_DAEMON_PID: PID_PATH,
2561
+ [DAEMON_TOKEN_ENV]: daemonToken
2481
2562
  }
2482
2563
  });
2483
2564
  child.unref();
@@ -2587,13 +2668,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
2587
2668
  return;
2588
2669
  }
2589
2670
  const id = randomUUID();
2671
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
2590
2672
  const timer = setTimeout(() => {
2591
2673
  _pending.delete(id);
2592
2674
  resolve({ error: "Request timeout" });
2593
2675
  }, timeoutMs);
2594
2676
  _pending.set(id, { resolve, timer });
2595
2677
  try {
2596
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
2678
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
2597
2679
  } catch {
2598
2680
  clearTimeout(timer);
2599
2681
  _pending.delete(id);
@@ -2622,9 +2704,9 @@ function killAndRespawnDaemon() {
2622
2704
  }
2623
2705
  try {
2624
2706
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
2625
- if (existsSync8(PID_PATH)) {
2707
+ if (existsSync10(PID_PATH)) {
2626
2708
  try {
2627
- const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
2709
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
2628
2710
  if (pid > 0) {
2629
2711
  try {
2630
2712
  process.kill(pid, "SIGKILL");
@@ -2744,17 +2826,19 @@ function disconnectClient() {
2744
2826
  function isClientConnected() {
2745
2827
  return _connected;
2746
2828
  }
2747
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2829
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2748
2830
  var init_exe_daemon_client = __esm({
2749
2831
  "src/lib/exe-daemon-client.ts"() {
2750
2832
  "use strict";
2751
2833
  init_config();
2752
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
2753
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
2754
- SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
2834
+ init_daemon_auth();
2835
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path10.join(EXE_AI_DIR, "exed.sock");
2836
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path10.join(EXE_AI_DIR, "exed.pid");
2837
+ SPAWN_LOCK_PATH = path10.join(EXE_AI_DIR, "exed-spawn.lock");
2755
2838
  SPAWN_LOCK_STALE_MS = 3e4;
2756
2839
  CONNECT_TIMEOUT_MS = 15e3;
2757
2840
  REQUEST_TIMEOUT_MS = 3e4;
2841
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
2758
2842
  _socket = null;
2759
2843
  _connected = false;
2760
2844
  _buffer = "";
@@ -3339,6 +3423,7 @@ async function ensureSchema() {
3339
3423
  project TEXT NOT NULL,
3340
3424
  summary TEXT NOT NULL,
3341
3425
  task_file TEXT,
3426
+ session_scope TEXT,
3342
3427
  read INTEGER NOT NULL DEFAULT 0,
3343
3428
  created_at TEXT NOT NULL
3344
3429
  );
@@ -3347,7 +3432,7 @@ async function ensureSchema() {
3347
3432
  ON notifications(read);
3348
3433
 
3349
3434
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
3350
- ON notifications(agent_id);
3435
+ ON notifications(agent_id, session_scope);
3351
3436
 
3352
3437
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
3353
3438
  ON notifications(task_file);
@@ -3385,6 +3470,7 @@ async function ensureSchema() {
3385
3470
  target_agent TEXT NOT NULL,
3386
3471
  target_project TEXT,
3387
3472
  target_device TEXT NOT NULL DEFAULT 'local',
3473
+ session_scope TEXT,
3388
3474
  content TEXT NOT NULL,
3389
3475
  priority TEXT DEFAULT 'normal',
3390
3476
  status TEXT DEFAULT 'pending',
@@ -3398,10 +3484,31 @@ async function ensureSchema() {
3398
3484
  );
3399
3485
 
3400
3486
  CREATE INDEX IF NOT EXISTS idx_messages_target
3401
- ON messages(target_agent, status);
3487
+ ON messages(target_agent, session_scope, status);
3402
3488
 
3403
3489
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
3404
- ON messages(target_agent, from_agent, server_seq);
3490
+ ON messages(target_agent, session_scope, from_agent, server_seq);
3491
+ `);
3492
+ try {
3493
+ await client.execute({
3494
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
3495
+ args: []
3496
+ });
3497
+ } catch {
3498
+ }
3499
+ try {
3500
+ await client.execute({
3501
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
3502
+ args: []
3503
+ });
3504
+ } catch {
3505
+ }
3506
+ await client.executeMultiple(`
3507
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
3508
+ ON notifications(agent_id, session_scope, read, created_at);
3509
+
3510
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
3511
+ ON messages(target_agent, session_scope, status, created_at);
3405
3512
  `);
3406
3513
  try {
3407
3514
  await client.execute({
@@ -3985,6 +4092,13 @@ async function ensureSchema() {
3985
4092
  } catch {
3986
4093
  }
3987
4094
  }
4095
+ try {
4096
+ await client.execute({
4097
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
4098
+ args: []
4099
+ });
4100
+ } catch {
4101
+ }
3988
4102
  }
3989
4103
  async function disposeDatabase() {
3990
4104
  if (_walCheckpointTimer) {
@@ -4030,13 +4144,13 @@ __export(crypto_exports, {
4030
4144
  initSyncCrypto: () => initSyncCrypto,
4031
4145
  isSyncCryptoInitialized: () => isSyncCryptoInitialized
4032
4146
  });
4033
- import crypto2 from "crypto";
4147
+ import crypto3 from "crypto";
4034
4148
  function initSyncCrypto(masterKey) {
4035
4149
  if (masterKey.length !== 32) {
4036
4150
  throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
4037
4151
  }
4038
4152
  _syncKey = Buffer.from(
4039
- crypto2.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
4153
+ crypto3.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
4040
4154
  );
4041
4155
  }
4042
4156
  function isSyncCryptoInitialized() {
@@ -4050,8 +4164,8 @@ function requireSyncKey() {
4050
4164
  }
4051
4165
  function encryptSyncBlob(data) {
4052
4166
  const key = requireSyncKey();
4053
- const iv = crypto2.randomBytes(IV_LENGTH);
4054
- const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
4167
+ const iv = crypto3.randomBytes(IV_LENGTH);
4168
+ const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
4055
4169
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
4056
4170
  const tag = cipher.getAuthTag();
4057
4171
  return Buffer.concat([iv, encrypted, tag]).toString("base64");
@@ -4065,7 +4179,7 @@ function decryptSyncBlob(ciphertext) {
4065
4179
  const iv = combined.subarray(0, IV_LENGTH);
4066
4180
  const tag = combined.subarray(combined.length - TAG_LENGTH);
4067
4181
  const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
4068
- const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
4182
+ const decipher = crypto3.createDecipheriv(ALGORITHM, key, iv);
4069
4183
  decipher.setAuthTag(tag);
4070
4184
  return Buffer.concat([decipher.update(encrypted), decipher.final()]);
4071
4185
  }
@@ -4118,9 +4232,12 @@ __export(license_exports, {
4118
4232
  stopLicenseRevalidation: () => stopLicenseRevalidation,
4119
4233
  validateLicense: () => validateLicense
4120
4234
  });
4121
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
4235
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
4122
4236
  import { randomUUID as randomUUID2 } from "crypto";
4123
- import path10 from "path";
4237
+ import { createRequire as createRequire2 } from "module";
4238
+ import { pathToFileURL as pathToFileURL2 } from "url";
4239
+ import os9 from "os";
4240
+ import path11 from "path";
4124
4241
  import { jwtVerify, importSPKI } from "jose";
4125
4242
  async function fetchRetry(url, init) {
4126
4243
  try {
@@ -4131,37 +4248,37 @@ async function fetchRetry(url, init) {
4131
4248
  }
4132
4249
  }
4133
4250
  function loadDeviceId() {
4134
- const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
4251
+ const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
4135
4252
  try {
4136
- if (existsSync9(deviceJsonPath)) {
4137
- const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
4253
+ if (existsSync11(deviceJsonPath)) {
4254
+ const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
4138
4255
  if (data.deviceId) return data.deviceId;
4139
4256
  }
4140
4257
  } catch {
4141
4258
  }
4142
4259
  try {
4143
- if (existsSync9(DEVICE_ID_PATH)) {
4144
- const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
4260
+ if (existsSync11(DEVICE_ID_PATH)) {
4261
+ const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
4145
4262
  if (id2) return id2;
4146
4263
  }
4147
4264
  } catch {
4148
4265
  }
4149
4266
  const id = randomUUID2();
4150
- mkdirSync5(EXE_AI_DIR, { recursive: true });
4151
- writeFileSync5(DEVICE_ID_PATH, id, "utf8");
4267
+ mkdirSync4(EXE_AI_DIR, { recursive: true });
4268
+ writeFileSync6(DEVICE_ID_PATH, id, "utf8");
4152
4269
  return id;
4153
4270
  }
4154
4271
  function loadLicense() {
4155
4272
  try {
4156
- if (!existsSync9(LICENSE_PATH)) return null;
4157
- return readFileSync7(LICENSE_PATH, "utf8").trim();
4273
+ if (!existsSync11(LICENSE_PATH)) return null;
4274
+ return readFileSync8(LICENSE_PATH, "utf8").trim();
4158
4275
  } catch {
4159
4276
  return null;
4160
4277
  }
4161
4278
  }
4162
4279
  function saveLicense(apiKey) {
4163
- mkdirSync5(EXE_AI_DIR, { recursive: true });
4164
- writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
4280
+ mkdirSync4(EXE_AI_DIR, { recursive: true });
4281
+ writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
4165
4282
  }
4166
4283
  async function verifyLicenseJwt(token) {
4167
4284
  try {
@@ -4187,8 +4304,8 @@ async function verifyLicenseJwt(token) {
4187
4304
  }
4188
4305
  async function getCachedLicense() {
4189
4306
  try {
4190
- if (!existsSync9(CACHE_PATH)) return null;
4191
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
4307
+ if (!existsSync11(CACHE_PATH)) return null;
4308
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
4192
4309
  if (!raw.token || typeof raw.token !== "string") return null;
4193
4310
  return await verifyLicenseJwt(raw.token);
4194
4311
  } catch {
@@ -4197,8 +4314,8 @@ async function getCachedLicense() {
4197
4314
  }
4198
4315
  function readCachedToken() {
4199
4316
  try {
4200
- if (!existsSync9(CACHE_PATH)) return null;
4201
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
4317
+ if (!existsSync11(CACHE_PATH)) return null;
4318
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
4202
4319
  return typeof raw.token === "string" ? raw.token : null;
4203
4320
  } catch {
4204
4321
  return null;
@@ -4232,56 +4349,130 @@ function getRawCachedPlan() {
4232
4349
  }
4233
4350
  function cacheResponse(token) {
4234
4351
  try {
4235
- writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
4352
+ writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
4236
4353
  } catch {
4237
4354
  }
4238
4355
  }
4239
- async function validateLicense(apiKey, deviceId) {
4240
- const did = deviceId ?? loadDeviceId();
4356
+ function loadPrismaForLicense() {
4357
+ if (_prismaFailed) return null;
4358
+ const dbUrl = process.env.DATABASE_URL;
4359
+ if (!dbUrl) {
4360
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os9.homedir(), "exe-db");
4361
+ if (!existsSync11(path11.join(exeDbRoot, "package.json"))) {
4362
+ _prismaFailed = true;
4363
+ return null;
4364
+ }
4365
+ }
4366
+ if (!_prismaPromise) {
4367
+ _prismaPromise = (async () => {
4368
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4369
+ if (explicitPath) {
4370
+ const mod2 = await import(pathToFileURL2(explicitPath).href);
4371
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4372
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4373
+ return new Ctor2();
4374
+ }
4375
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os9.homedir(), "exe-db");
4376
+ const req = createRequire2(path11.join(exeDbRoot, "package.json"));
4377
+ const entry = req.resolve("@prisma/client");
4378
+ const mod = await import(pathToFileURL2(entry).href);
4379
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4380
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
4381
+ return new Ctor();
4382
+ })().catch((err) => {
4383
+ _prismaFailed = true;
4384
+ _prismaPromise = null;
4385
+ throw err;
4386
+ });
4387
+ }
4388
+ return _prismaPromise;
4389
+ }
4390
+ async function validateViaPostgres(apiKey) {
4391
+ const loader = loadPrismaForLicense();
4392
+ if (!loader) return null;
4393
+ try {
4394
+ const prisma = await loader;
4395
+ const rows = await prisma.$queryRawUnsafe(
4396
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
4397
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
4398
+ apiKey
4399
+ );
4400
+ if (!rows || rows.length === 0) return null;
4401
+ const row = rows[0];
4402
+ if (row.status !== "active") return null;
4403
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
4404
+ const plan = row.plan;
4405
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
4406
+ return {
4407
+ valid: true,
4408
+ plan,
4409
+ email: row.email,
4410
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
4411
+ deviceLimit: row.device_limit ?? limits.devices,
4412
+ employeeLimit: row.employee_limit ?? limits.employees,
4413
+ memoryLimit: row.memory_limit ?? limits.memories
4414
+ };
4415
+ } catch {
4416
+ return null;
4417
+ }
4418
+ }
4419
+ async function validateViaCFWorker(apiKey, deviceId) {
4241
4420
  try {
4242
4421
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
4243
4422
  method: "POST",
4244
4423
  headers: { "Content-Type": "application/json" },
4245
- body: JSON.stringify({ apiKey, deviceId: did }),
4424
+ body: JSON.stringify({ apiKey, deviceId }),
4246
4425
  signal: AbortSignal.timeout(1e4)
4247
4426
  });
4248
- if (res.ok) {
4249
- const data = await res.json();
4250
- if (data.error === "device_limit_exceeded") {
4251
- const cached2 = await getCachedLicense();
4252
- if (cached2) return cached2;
4253
- const raw2 = getRawCachedPlan();
4254
- if (raw2) return { ...raw2, valid: false };
4255
- return { ...FREE_LICENSE, valid: false, plan: "free" };
4256
- }
4257
- if (data.token) {
4258
- cacheResponse(data.token);
4259
- const verified = await verifyLicenseJwt(data.token);
4260
- if (verified) return verified;
4261
- }
4262
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
4263
- return {
4264
- valid: data.valid,
4265
- plan: data.plan,
4266
- email: data.email,
4267
- expiresAt: data.expiresAt,
4268
- deviceLimit: limits.devices,
4269
- employeeLimit: limits.employees,
4270
- memoryLimit: limits.memories
4271
- };
4427
+ if (!res.ok) return null;
4428
+ const data = await res.json();
4429
+ if (data.error === "device_limit_exceeded") return null;
4430
+ if (!data.valid) return null;
4431
+ if (data.token) {
4432
+ cacheResponse(data.token);
4433
+ const verified = await verifyLicenseJwt(data.token);
4434
+ if (verified) return verified;
4435
+ }
4436
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
4437
+ return {
4438
+ valid: data.valid,
4439
+ plan: data.plan,
4440
+ email: data.email,
4441
+ expiresAt: data.expiresAt,
4442
+ deviceLimit: limits.devices,
4443
+ employeeLimit: limits.employees,
4444
+ memoryLimit: limits.memories
4445
+ };
4446
+ } catch {
4447
+ return null;
4448
+ }
4449
+ }
4450
+ async function validateLicense(apiKey, deviceId) {
4451
+ const did = deviceId ?? loadDeviceId();
4452
+ const pgResult = await validateViaPostgres(apiKey);
4453
+ if (pgResult) {
4454
+ try {
4455
+ writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
4456
+ } catch {
4457
+ }
4458
+ return pgResult;
4459
+ }
4460
+ const cfResult = await validateViaCFWorker(apiKey, did);
4461
+ if (cfResult) return cfResult;
4462
+ const cached = await getCachedLicense();
4463
+ if (cached) return cached;
4464
+ try {
4465
+ if (existsSync11(CACHE_PATH)) {
4466
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
4467
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
4468
+ return raw.pgLicense;
4469
+ }
4272
4470
  }
4273
- const cached = await getCachedLicense();
4274
- if (cached) return cached;
4275
- const raw = getRawCachedPlan();
4276
- if (raw) return raw;
4277
- return { ...FREE_LICENSE, valid: false, plan: "free" };
4278
4471
  } catch {
4279
- const cached = await getCachedLicense();
4280
- if (cached) return cached;
4281
- const rawFallback = getRawCachedPlan();
4282
- if (rawFallback) return rawFallback;
4283
- return { ...FREE_LICENSE, valid: false, error: "offline" };
4284
4472
  }
4473
+ const rawFallback = getRawCachedPlan();
4474
+ if (rawFallback) return rawFallback;
4475
+ return { ...FREE_LICENSE, valid: false };
4285
4476
  }
4286
4477
  function getCacheAgeMs() {
4287
4478
  try {
@@ -4296,9 +4487,9 @@ async function checkLicense() {
4296
4487
  let key = loadLicense();
4297
4488
  if (!key) {
4298
4489
  try {
4299
- const configPath = path10.join(EXE_AI_DIR, "config.json");
4300
- if (existsSync9(configPath)) {
4301
- const raw = JSON.parse(readFileSync7(configPath, "utf8"));
4490
+ const configPath = path11.join(EXE_AI_DIR, "config.json");
4491
+ if (existsSync11(configPath)) {
4492
+ const raw = JSON.parse(readFileSync8(configPath, "utf8"));
4302
4493
  const cloud = raw.cloud;
4303
4494
  if (cloud?.apiKey) {
4304
4495
  key = cloud.apiKey;
@@ -4452,14 +4643,14 @@ function stopLicenseRevalidation() {
4452
4643
  _revalTimer = null;
4453
4644
  }
4454
4645
  }
4455
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
4646
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
4456
4647
  var init_license = __esm({
4457
4648
  "src/lib/license.ts"() {
4458
4649
  "use strict";
4459
4650
  init_config();
4460
- LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
4461
- CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
4462
- DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
4651
+ LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
4652
+ CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
4653
+ DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
4463
4654
  API_BASE = "https://askexe.com/cloud";
4464
4655
  RETRY_DELAY_MS = 500;
4465
4656
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -4483,6 +4674,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
4483
4674
  employeeLimit: 1,
4484
4675
  memoryLimit: 5e3
4485
4676
  };
4677
+ _prismaPromise = null;
4678
+ _prismaFailed = false;
4486
4679
  CACHE_MAX_AGE_MS = 36e5;
4487
4680
  _revalTimer = null;
4488
4681
  }
@@ -4507,8 +4700,8 @@ __export(crdt_sync_exports, {
4507
4700
  rebuildFromDb: () => rebuildFromDb
4508
4701
  });
4509
4702
  import * as Y from "yjs";
4510
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync6, unlinkSync as unlinkSync3 } from "fs";
4511
- import path11 from "path";
4703
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5, unlinkSync as unlinkSync3 } from "fs";
4704
+ import path12 from "path";
4512
4705
  import { homedir } from "os";
4513
4706
  function getStatePath() {
4514
4707
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -4520,9 +4713,9 @@ function initCrdtDoc() {
4520
4713
  if (doc) return doc;
4521
4714
  doc = new Y.Doc();
4522
4715
  const sp = getStatePath();
4523
- if (existsSync10(sp)) {
4716
+ if (existsSync12(sp)) {
4524
4717
  try {
4525
- const state = readFileSync8(sp);
4718
+ const state = readFileSync9(sp);
4526
4719
  Y.applyUpdate(doc, new Uint8Array(state));
4527
4720
  } catch {
4528
4721
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -4664,10 +4857,10 @@ function persistState() {
4664
4857
  if (!doc) return;
4665
4858
  try {
4666
4859
  const sp = getStatePath();
4667
- const dir = path11.dirname(sp);
4668
- if (!existsSync10(dir)) mkdirSync6(dir, { recursive: true });
4860
+ const dir = path12.dirname(sp);
4861
+ if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
4669
4862
  const state = Y.encodeStateAsUpdate(doc);
4670
- writeFileSync6(sp, Buffer.from(state));
4863
+ writeFileSync7(sp, Buffer.from(state));
4671
4864
  } catch {
4672
4865
  }
4673
4866
  }
@@ -4708,7 +4901,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
4708
4901
  var init_crdt_sync = __esm({
4709
4902
  "src/lib/crdt-sync.ts"() {
4710
4903
  "use strict";
4711
- DEFAULT_STATE_PATH = path11.join(homedir(), ".exe-os", "crdt-state.bin");
4904
+ DEFAULT_STATE_PATH = path12.join(homedir(), ".exe-os", "crdt-state.bin");
4712
4905
  _statePathOverride = null;
4713
4906
  doc = null;
4714
4907
  }
@@ -4740,39 +4933,107 @@ __export(cloud_sync_exports, {
4740
4933
  cloudSync: () => cloudSync,
4741
4934
  mergeConfig: () => mergeConfig,
4742
4935
  mergeRosterFromRemote: () => mergeRosterFromRemote,
4936
+ pushToPostgres: () => pushToPostgres,
4743
4937
  recordRosterDeletion: () => recordRosterDeletion
4744
4938
  });
4745
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync11, readdirSync, mkdirSync as mkdirSync7, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
4746
- import crypto3 from "crypto";
4747
- import path12 from "path";
4939
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync13, readdirSync, mkdirSync as mkdirSync6, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
4940
+ import crypto4 from "crypto";
4941
+ import path13 from "path";
4748
4942
  import { homedir as homedir2 } from "os";
4749
4943
  function sqlSafe(v) {
4750
4944
  return v === void 0 ? null : v;
4751
4945
  }
4752
4946
  function logError(msg) {
4753
4947
  try {
4754
- const logPath = path12.join(homedir2(), ".exe-os", "workers.log");
4948
+ const logPath = path13.join(homedir2(), ".exe-os", "workers.log");
4755
4949
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
4756
4950
  `);
4757
4951
  } catch {
4758
4952
  }
4759
4953
  }
4954
+ function loadPgClient() {
4955
+ if (_pgFailed) return null;
4956
+ const postgresUrl = process.env.DATABASE_URL;
4957
+ const configPath = path13.join(EXE_AI_DIR, "config.json");
4958
+ let cloudPostgresUrl;
4959
+ try {
4960
+ if (existsSync13(configPath)) {
4961
+ const cfg = JSON.parse(readFileSync10(configPath, "utf8"));
4962
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
4963
+ if (cfg.cloud?.syncToPostgres === false) {
4964
+ _pgFailed = true;
4965
+ return null;
4966
+ }
4967
+ }
4968
+ } catch {
4969
+ }
4970
+ const url = postgresUrl || cloudPostgresUrl;
4971
+ if (!url) {
4972
+ _pgFailed = true;
4973
+ return null;
4974
+ }
4975
+ if (!_pgPromise) {
4976
+ _pgPromise = (async () => {
4977
+ const { createRequire: createRequire3 } = await import("module");
4978
+ const { pathToFileURL: pathToFileURL3 } = await import("url");
4979
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(homedir2(), "exe-db");
4980
+ const req = createRequire3(path13.join(exeDbRoot, "package.json"));
4981
+ const entry = req.resolve("@prisma/client");
4982
+ const mod = await import(pathToFileURL3(entry).href);
4983
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4984
+ if (!Ctor) throw new Error("No PrismaClient");
4985
+ return new Ctor();
4986
+ })().catch(() => {
4987
+ _pgFailed = true;
4988
+ _pgPromise = null;
4989
+ throw new Error("pg_unavailable");
4990
+ });
4991
+ }
4992
+ return _pgPromise;
4993
+ }
4994
+ async function pushToPostgres(records) {
4995
+ const loader = loadPgClient();
4996
+ if (!loader) return 0;
4997
+ let prisma;
4998
+ try {
4999
+ prisma = await loader;
5000
+ } catch {
5001
+ return 0;
5002
+ }
5003
+ let inserted = 0;
5004
+ for (const rec of records) {
5005
+ try {
5006
+ await prisma.$executeRawUnsafe(
5007
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
5008
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
5009
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
5010
+ String(rec.id ?? ""),
5011
+ JSON.stringify(rec),
5012
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
5013
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
5014
+ );
5015
+ inserted++;
5016
+ } catch {
5017
+ }
5018
+ }
5019
+ return inserted;
5020
+ }
4760
5021
  async function withRosterLock(fn) {
4761
5022
  try {
4762
5023
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
4763
5024
  closeSync2(fd);
4764
- writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
5025
+ writeFileSync8(ROSTER_LOCK_PATH, String(Date.now()));
4765
5026
  } catch (err) {
4766
5027
  if (err.code === "EEXIST") {
4767
5028
  try {
4768
- const ts = parseInt(readFileSync9(ROSTER_LOCK_PATH, "utf-8"), 10);
5029
+ const ts = parseInt(readFileSync10(ROSTER_LOCK_PATH, "utf-8"), 10);
4769
5030
  if (Date.now() - ts < LOCK_STALE_MS) {
4770
5031
  throw new Error("Roster merge already in progress \u2014 another sync is running");
4771
5032
  }
4772
5033
  unlinkSync4(ROSTER_LOCK_PATH);
4773
5034
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
4774
5035
  closeSync2(fd);
4775
- writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
5036
+ writeFileSync8(ROSTER_LOCK_PATH, String(Date.now()));
4776
5037
  } catch (retryErr) {
4777
5038
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
4778
5039
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -5042,6 +5303,10 @@ async function cloudSync(config) {
5042
5303
  const maxVersion = Number(records[records.length - 1].version);
5043
5304
  const pushOk = await cloudPush(records, maxVersion, config);
5044
5305
  if (!pushOk) break;
5306
+ try {
5307
+ await pushToPostgres(records);
5308
+ } catch {
5309
+ }
5045
5310
  await client.execute({
5046
5311
  sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
5047
5312
  args: [String(maxVersion)]
@@ -5146,8 +5411,8 @@ async function cloudSync(config) {
5146
5411
  try {
5147
5412
  const employees = await loadEmployees();
5148
5413
  rosterResult.employees = employees.length;
5149
- const idDir = path12.join(EXE_AI_DIR, "identity");
5150
- if (existsSync11(idDir)) {
5414
+ const idDir = path13.join(EXE_AI_DIR, "identity");
5415
+ if (existsSync13(idDir)) {
5151
5416
  rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
5152
5417
  }
5153
5418
  } catch {
@@ -5168,62 +5433,62 @@ async function cloudSync(config) {
5168
5433
  function recordRosterDeletion(name) {
5169
5434
  let deletions = [];
5170
5435
  try {
5171
- if (existsSync11(ROSTER_DELETIONS_PATH)) {
5172
- deletions = JSON.parse(readFileSync9(ROSTER_DELETIONS_PATH, "utf-8"));
5436
+ if (existsSync13(ROSTER_DELETIONS_PATH)) {
5437
+ deletions = JSON.parse(readFileSync10(ROSTER_DELETIONS_PATH, "utf-8"));
5173
5438
  }
5174
5439
  } catch {
5175
5440
  }
5176
5441
  if (!deletions.includes(name)) deletions.push(name);
5177
- writeFileSync7(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
5442
+ writeFileSync8(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
5178
5443
  }
5179
5444
  function consumeRosterDeletions() {
5180
5445
  try {
5181
- if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
5182
- const deletions = JSON.parse(readFileSync9(ROSTER_DELETIONS_PATH, "utf-8"));
5183
- writeFileSync7(ROSTER_DELETIONS_PATH, "[]");
5446
+ if (!existsSync13(ROSTER_DELETIONS_PATH)) return [];
5447
+ const deletions = JSON.parse(readFileSync10(ROSTER_DELETIONS_PATH, "utf-8"));
5448
+ writeFileSync8(ROSTER_DELETIONS_PATH, "[]");
5184
5449
  return deletions;
5185
5450
  } catch {
5186
5451
  return [];
5187
5452
  }
5188
5453
  }
5189
5454
  function buildRosterBlob(paths) {
5190
- const rosterPath = paths?.rosterPath ?? path12.join(EXE_AI_DIR, "exe-employees.json");
5191
- const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
5192
- const configPath = paths?.configPath ?? path12.join(EXE_AI_DIR, "config.json");
5455
+ const rosterPath = paths?.rosterPath ?? path13.join(EXE_AI_DIR, "exe-employees.json");
5456
+ const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
5457
+ const configPath = paths?.configPath ?? path13.join(EXE_AI_DIR, "config.json");
5193
5458
  let roster = [];
5194
- if (existsSync11(rosterPath)) {
5459
+ if (existsSync13(rosterPath)) {
5195
5460
  try {
5196
- roster = JSON.parse(readFileSync9(rosterPath, "utf-8"));
5461
+ roster = JSON.parse(readFileSync10(rosterPath, "utf-8"));
5197
5462
  } catch {
5198
5463
  }
5199
5464
  }
5200
5465
  const identities = {};
5201
- if (existsSync11(identityDir)) {
5466
+ if (existsSync13(identityDir)) {
5202
5467
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
5203
5468
  try {
5204
- identities[file] = readFileSync9(path12.join(identityDir, file), "utf-8");
5469
+ identities[file] = readFileSync10(path13.join(identityDir, file), "utf-8");
5205
5470
  } catch {
5206
5471
  }
5207
5472
  }
5208
5473
  }
5209
5474
  let config;
5210
- if (existsSync11(configPath)) {
5475
+ if (existsSync13(configPath)) {
5211
5476
  try {
5212
- config = JSON.parse(readFileSync9(configPath, "utf-8"));
5477
+ config = JSON.parse(readFileSync10(configPath, "utf-8"));
5213
5478
  } catch {
5214
5479
  }
5215
5480
  }
5216
5481
  let agentConfig;
5217
- const agentConfigPath = path12.join(EXE_AI_DIR, "agent-config.json");
5218
- if (existsSync11(agentConfigPath)) {
5482
+ const agentConfigPath = path13.join(EXE_AI_DIR, "agent-config.json");
5483
+ if (existsSync13(agentConfigPath)) {
5219
5484
  try {
5220
- agentConfig = JSON.parse(readFileSync9(agentConfigPath, "utf-8"));
5485
+ agentConfig = JSON.parse(readFileSync10(agentConfigPath, "utf-8"));
5221
5486
  } catch {
5222
5487
  }
5223
5488
  }
5224
5489
  const deletedNames = consumeRosterDeletions();
5225
5490
  const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
5226
- const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
5491
+ const hash = crypto4.createHash("sha256").update(content).digest("hex").slice(0, 16);
5227
5492
  return { roster, identities, config, agentConfig, deletedNames, version: hash };
5228
5493
  }
5229
5494
  async function cloudPushRoster(config) {
@@ -5293,23 +5558,24 @@ async function cloudPullRoster(config) {
5293
5558
  }
5294
5559
  }
5295
5560
  function mergeConfig(remoteConfig, configPath) {
5296
- const cfgPath = configPath ?? path12.join(EXE_AI_DIR, "config.json");
5561
+ const cfgPath = configPath ?? path13.join(EXE_AI_DIR, "config.json");
5297
5562
  let local = {};
5298
- if (existsSync11(cfgPath)) {
5563
+ if (existsSync13(cfgPath)) {
5299
5564
  try {
5300
- local = JSON.parse(readFileSync9(cfgPath, "utf-8"));
5565
+ local = JSON.parse(readFileSync10(cfgPath, "utf-8"));
5301
5566
  } catch {
5302
5567
  }
5303
5568
  }
5304
5569
  const merged = { ...remoteConfig, ...local };
5305
- const dir = path12.dirname(cfgPath);
5306
- if (!existsSync11(dir)) mkdirSync7(dir, { recursive: true });
5307
- writeFileSync7(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
5570
+ const dir = path13.dirname(cfgPath);
5571
+ ensurePrivateDirSync(dir);
5572
+ writeFileSync8(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
5573
+ enforcePrivateFileSync(cfgPath);
5308
5574
  }
5309
5575
  async function mergeRosterFromRemote(remote, paths) {
5310
5576
  return withRosterLock(async () => {
5311
5577
  const rosterPath = paths?.rosterPath ?? void 0;
5312
- const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
5578
+ const identityDir = paths?.identityDir ?? path13.join(EXE_AI_DIR, "identity");
5313
5579
  const localEmployees = await loadEmployees(rosterPath);
5314
5580
  const localNames = new Set(localEmployees.map((e) => e.name));
5315
5581
  let added = 0;
@@ -5330,15 +5596,15 @@ async function mergeRosterFromRemote(remote, paths) {
5330
5596
  ) ?? lookupKey;
5331
5597
  const remoteIdentity = remote.identities[matchedKey];
5332
5598
  if (remoteIdentity) {
5333
- if (!existsSync11(identityDir)) mkdirSync7(identityDir, { recursive: true });
5334
- const idPath = path12.join(identityDir, `${remoteEmp.name}.md`);
5599
+ if (!existsSync13(identityDir)) mkdirSync6(identityDir, { recursive: true });
5600
+ const idPath = path13.join(identityDir, `${remoteEmp.name}.md`);
5335
5601
  let localIdentity = null;
5336
5602
  try {
5337
- localIdentity = existsSync11(idPath) ? readFileSync9(idPath, "utf-8") : null;
5603
+ localIdentity = existsSync13(idPath) ? readFileSync10(idPath, "utf-8") : null;
5338
5604
  } catch {
5339
5605
  }
5340
5606
  if (localIdentity !== remoteIdentity) {
5341
- writeFileSync7(idPath, remoteIdentity, "utf-8");
5607
+ writeFileSync8(idPath, remoteIdentity, "utf-8");
5342
5608
  identitiesUpdated++;
5343
5609
  }
5344
5610
  }
@@ -5364,16 +5630,18 @@ async function mergeRosterFromRemote(remote, paths) {
5364
5630
  }
5365
5631
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
5366
5632
  try {
5367
- const agentConfigPath = path12.join(EXE_AI_DIR, "agent-config.json");
5633
+ const agentConfigPath = path13.join(EXE_AI_DIR, "agent-config.json");
5368
5634
  let local = {};
5369
- if (existsSync11(agentConfigPath)) {
5635
+ if (existsSync13(agentConfigPath)) {
5370
5636
  try {
5371
- local = JSON.parse(readFileSync9(agentConfigPath, "utf-8"));
5637
+ local = JSON.parse(readFileSync10(agentConfigPath, "utf-8"));
5372
5638
  } catch {
5373
5639
  }
5374
5640
  }
5375
5641
  const merged = { ...remote.agentConfig, ...local };
5376
- writeFileSync7(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
5642
+ ensurePrivateDirSync(path13.dirname(agentConfigPath));
5643
+ writeFileSync8(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
5644
+ enforcePrivateFileSync(agentConfigPath);
5377
5645
  } catch {
5378
5646
  }
5379
5647
  }
@@ -5797,7 +6065,7 @@ async function cloudPullDocuments(config) {
5797
6065
  }
5798
6066
  return { pulled };
5799
6067
  }
5800
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
6068
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
5801
6069
  var init_cloud_sync = __esm({
5802
6070
  "src/lib/cloud-sync.ts"() {
5803
6071
  "use strict";
@@ -5808,12 +6076,15 @@ var init_cloud_sync = __esm({
5808
6076
  init_config();
5809
6077
  init_crdt_sync();
5810
6078
  init_employees();
6079
+ init_secure_files();
5811
6080
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
5812
6081
  FETCH_TIMEOUT_MS = 3e4;
5813
6082
  PUSH_BATCH_SIZE = 5e3;
5814
- ROSTER_LOCK_PATH = path12.join(EXE_AI_DIR, "roster-merge.lock");
6083
+ ROSTER_LOCK_PATH = path13.join(EXE_AI_DIR, "roster-merge.lock");
5815
6084
  LOCK_STALE_MS = 3e4;
5816
- ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
6085
+ _pgPromise = null;
6086
+ _pgFailed = false;
6087
+ ROSTER_DELETIONS_PATH = path13.join(EXE_AI_DIR, "roster-deletions.json");
5817
6088
  }
5818
6089
  });
5819
6090
 
@@ -6111,6 +6382,7 @@ var shard_manager_exports = {};
6111
6382
  __export(shard_manager_exports, {
6112
6383
  disposeShards: () => disposeShards,
6113
6384
  ensureShardSchema: () => ensureShardSchema,
6385
+ getOpenShardCount: () => getOpenShardCount,
6114
6386
  getReadyShardClient: () => getReadyShardClient,
6115
6387
  getShardClient: () => getShardClient,
6116
6388
  getShardsDir: () => getShardsDir,
@@ -6119,15 +6391,18 @@ __export(shard_manager_exports, {
6119
6391
  listShards: () => listShards,
6120
6392
  shardExists: () => shardExists
6121
6393
  });
6122
- import path13 from "path";
6123
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync2 } from "fs";
6394
+ import path14 from "path";
6395
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync2 } from "fs";
6124
6396
  import { createClient as createClient2 } from "@libsql/client";
6125
6397
  function initShardManager(encryptionKey) {
6126
6398
  _encryptionKey = encryptionKey;
6127
- if (!existsSync12(SHARDS_DIR)) {
6128
- mkdirSync8(SHARDS_DIR, { recursive: true });
6399
+ if (!existsSync14(SHARDS_DIR)) {
6400
+ mkdirSync7(SHARDS_DIR, { recursive: true });
6129
6401
  }
6130
6402
  _shardingEnabled = true;
6403
+ if (_evictionTimer) clearInterval(_evictionTimer);
6404
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
6405
+ _evictionTimer.unref();
6131
6406
  }
6132
6407
  function isShardingEnabled() {
6133
6408
  return _shardingEnabled;
@@ -6144,21 +6419,28 @@ function getShardClient(projectName) {
6144
6419
  throw new Error(`Invalid project name for shard: "${projectName}"`);
6145
6420
  }
6146
6421
  const cached = _shards.get(safeName);
6147
- if (cached) return cached;
6148
- const dbPath = path13.join(SHARDS_DIR, `${safeName}.db`);
6422
+ if (cached) {
6423
+ _shardLastAccess.set(safeName, Date.now());
6424
+ return cached;
6425
+ }
6426
+ while (_shards.size >= MAX_OPEN_SHARDS) {
6427
+ evictLRU();
6428
+ }
6429
+ const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
6149
6430
  const client = createClient2({
6150
6431
  url: `file:${dbPath}`,
6151
6432
  encryptionKey: _encryptionKey
6152
6433
  });
6153
6434
  _shards.set(safeName, client);
6435
+ _shardLastAccess.set(safeName, Date.now());
6154
6436
  return client;
6155
6437
  }
6156
6438
  function shardExists(projectName) {
6157
6439
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
6158
- return existsSync12(path13.join(SHARDS_DIR, `${safeName}.db`));
6440
+ return existsSync14(path14.join(SHARDS_DIR, `${safeName}.db`));
6159
6441
  }
6160
6442
  function listShards() {
6161
- if (!existsSync12(SHARDS_DIR)) return [];
6443
+ if (!existsSync14(SHARDS_DIR)) return [];
6162
6444
  return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
6163
6445
  }
6164
6446
  async function ensureShardSchema(client) {
@@ -6210,6 +6492,8 @@ async function ensureShardSchema(client) {
6210
6492
  for (const col of [
6211
6493
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
6212
6494
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
6495
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
6496
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
6213
6497
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
6214
6498
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
6215
6499
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -6347,21 +6631,69 @@ async function getReadyShardClient(projectName) {
6347
6631
  await ensureShardSchema(client);
6348
6632
  return client;
6349
6633
  }
6634
+ function evictLRU() {
6635
+ let oldest = null;
6636
+ let oldestTime = Infinity;
6637
+ for (const [name, time] of _shardLastAccess) {
6638
+ if (time < oldestTime) {
6639
+ oldestTime = time;
6640
+ oldest = name;
6641
+ }
6642
+ }
6643
+ if (oldest) {
6644
+ const client = _shards.get(oldest);
6645
+ if (client) {
6646
+ client.close();
6647
+ }
6648
+ _shards.delete(oldest);
6649
+ _shardLastAccess.delete(oldest);
6650
+ }
6651
+ }
6652
+ function evictIdleShards() {
6653
+ const now = Date.now();
6654
+ const toEvict = [];
6655
+ for (const [name, lastAccess] of _shardLastAccess) {
6656
+ if (now - lastAccess > SHARD_IDLE_MS) {
6657
+ toEvict.push(name);
6658
+ }
6659
+ }
6660
+ for (const name of toEvict) {
6661
+ const client = _shards.get(name);
6662
+ if (client) {
6663
+ client.close();
6664
+ }
6665
+ _shards.delete(name);
6666
+ _shardLastAccess.delete(name);
6667
+ }
6668
+ }
6669
+ function getOpenShardCount() {
6670
+ return _shards.size;
6671
+ }
6350
6672
  function disposeShards() {
6673
+ if (_evictionTimer) {
6674
+ clearInterval(_evictionTimer);
6675
+ _evictionTimer = null;
6676
+ }
6351
6677
  for (const [, client] of _shards) {
6352
6678
  client.close();
6353
6679
  }
6354
6680
  _shards.clear();
6681
+ _shardLastAccess.clear();
6355
6682
  _shardingEnabled = false;
6356
6683
  _encryptionKey = null;
6357
6684
  }
6358
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
6685
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
6359
6686
  var init_shard_manager = __esm({
6360
6687
  "src/lib/shard-manager.ts"() {
6361
6688
  "use strict";
6362
6689
  init_config();
6363
- SHARDS_DIR = path13.join(EXE_AI_DIR, "shards");
6690
+ SHARDS_DIR = path14.join(EXE_AI_DIR, "shards");
6691
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
6692
+ MAX_OPEN_SHARDS = 10;
6693
+ EVICTION_INTERVAL_MS = 60 * 1e3;
6364
6694
  _shards = /* @__PURE__ */ new Map();
6695
+ _shardLastAccess = /* @__PURE__ */ new Map();
6696
+ _evictionTimer = null;
6365
6697
  _encryptionKey = null;
6366
6698
  _shardingEnabled = false;
6367
6699
  }
@@ -7129,15 +7461,15 @@ var backfill_conversations_exports = {};
7129
7461
  __export(backfill_conversations_exports, {
7130
7462
  backfillConversations: () => backfillConversations
7131
7463
  });
7132
- import crypto4 from "crypto";
7464
+ import crypto5 from "crypto";
7133
7465
  import { createReadStream } from "fs";
7134
7466
  import { readdir as readdir2, stat } from "fs/promises";
7135
- import path14 from "path";
7467
+ import path15 from "path";
7136
7468
  import { createInterface as createInterface2 } from "readline";
7137
7469
  import { homedir as homedir3 } from "os";
7138
7470
  import { parseArgs } from "util";
7139
7471
  async function findJsonlFiles(sinceDate, projectFilter) {
7140
- const projectsDir = path14.join(homedir3(), ".claude", "projects");
7472
+ const projectsDir = path15.join(homedir3(), ".claude", "projects");
7141
7473
  const files = [];
7142
7474
  async function walk(dir, depth = 0) {
7143
7475
  if (depth > MAX_WALK_DEPTH) return;
@@ -7148,7 +7480,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
7148
7480
  return;
7149
7481
  }
7150
7482
  for (const entry of entries) {
7151
- const full = path14.join(dir, entry.name);
7483
+ const full = path15.join(dir, entry.name);
7152
7484
  if (entry.isDirectory()) {
7153
7485
  if (entry.name === "subagents" || entry.name === "tool-results") continue;
7154
7486
  await walk(full, depth + 1);
@@ -7173,7 +7505,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
7173
7505
  if (!entry.isDirectory()) continue;
7174
7506
  const decoded = decodeProjectDir(entry.name);
7175
7507
  if (decoded.toLowerCase().includes(projectFilter.toLowerCase())) {
7176
- await walk(path14.join(projectsDir, entry.name));
7508
+ await walk(path15.join(projectsDir, entry.name));
7177
7509
  }
7178
7510
  }
7179
7511
  } else {
@@ -7190,14 +7522,14 @@ function decodeProjectDir(dirName) {
7190
7522
  return dirName;
7191
7523
  }
7192
7524
  function projectNameFromPath(filePath) {
7193
- const projectsDir = path14.join(homedir3(), ".claude", "projects");
7194
- const relative = path14.relative(projectsDir, filePath);
7195
- const projectDir = relative.split(path14.sep)[0] ?? "unknown";
7525
+ const projectsDir = path15.join(homedir3(), ".claude", "projects");
7526
+ const relative = path15.relative(projectsDir, filePath);
7527
+ const projectDir = relative.split(path15.sep)[0] ?? "unknown";
7196
7528
  return decodeProjectDir(projectDir);
7197
7529
  }
7198
7530
  async function parseConversation(filePath) {
7199
7531
  const conv = {
7200
- sessionId: path14.basename(filePath, ".jsonl"),
7532
+ sessionId: path15.basename(filePath, ".jsonl"),
7201
7533
  projectName: projectNameFromPath(filePath),
7202
7534
  cwd: void 0,
7203
7535
  startTime: void 0,
@@ -7261,7 +7593,7 @@ async function parseConversation(filePath) {
7261
7593
  }
7262
7594
  }
7263
7595
  if (conv.cwd) {
7264
- conv.projectName = path14.basename(conv.cwd);
7596
+ conv.projectName = path15.basename(conv.cwd);
7265
7597
  const worktreeMatch = conv.cwd.match(/\.worktrees\/([^/]+)/);
7266
7598
  if (worktreeMatch?.[1]) {
7267
7599
  conv.agentId = worktreeMatch[1];
@@ -7434,7 +7766,7 @@ async function backfillConversations(options) {
7434
7766
  }
7435
7767
  }
7436
7768
  await writeMemory({
7437
- id: crypto4.randomUUID(),
7769
+ id: crypto5.randomUUID(),
7438
7770
  agent_id: conv.agentId,
7439
7771
  agent_role: isCoordinatorName(conv.agentId) ? "COO" : "specialist",
7440
7772
  session_id: conv.sessionId,
@@ -7913,9 +8245,9 @@ Unclassified: ${unclassified}
7913
8245
  }
7914
8246
  async function exportBatches(options) {
7915
8247
  const fs8 = await import("fs");
7916
- const path45 = await import("path");
8248
+ const path46 = await import("path");
7917
8249
  const client = getClient();
7918
- const outDir = path45.join(process.cwd(), "exe/output/classifications/input");
8250
+ const outDir = path46.join(process.cwd(), "exe/output/classifications/input");
7919
8251
  fs8.mkdirSync(outDir, { recursive: true });
7920
8252
  const countResult = await client.execute({
7921
8253
  sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
@@ -7939,7 +8271,7 @@ async function exportBatches(options) {
7939
8271
  const text = String(row.text || "").replace(/\n/g, " ");
7940
8272
  return JSON.stringify({ id: row.id, text });
7941
8273
  });
7942
- const batchFile = path45.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
8274
+ const batchFile = path46.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
7943
8275
  fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
7944
8276
  exported += batch.rows.length;
7945
8277
  offset += options.batchSize;
@@ -7955,7 +8287,7 @@ async function exportBatches(options) {
7955
8287
  }
7956
8288
  async function importClassifications(importDir) {
7957
8289
  const fs8 = await import("fs");
7958
- const path45 = await import("path");
8290
+ const path46 = await import("path");
7959
8291
  const client = getClient();
7960
8292
  const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
7961
8293
  process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
@@ -7963,7 +8295,7 @@ async function importClassifications(importDir) {
7963
8295
  let imported = 0;
7964
8296
  let invalid = 0;
7965
8297
  for (const file of files) {
7966
- const lines = fs8.readFileSync(path45.join(importDir, file), "utf-8").split("\n").filter(Boolean);
8298
+ const lines = fs8.readFileSync(path46.join(importDir, file), "utf-8").split("\n").filter(Boolean);
7967
8299
  for (const line of lines) {
7968
8300
  try {
7969
8301
  const rec = JSON.parse(line);
@@ -8104,17 +8436,17 @@ __export(identity_exports, {
8104
8436
  listIdentities: () => listIdentities,
8105
8437
  updateIdentity: () => updateIdentity
8106
8438
  });
8107
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
8439
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
8108
8440
  import { readdirSync as readdirSync3 } from "fs";
8109
- import path15 from "path";
8441
+ import path16 from "path";
8110
8442
  import { createHash as createHash2 } from "crypto";
8111
8443
  function ensureDir() {
8112
- if (!existsSync13(IDENTITY_DIR2)) {
8113
- mkdirSync9(IDENTITY_DIR2, { recursive: true });
8444
+ if (!existsSync15(IDENTITY_DIR2)) {
8445
+ mkdirSync8(IDENTITY_DIR2, { recursive: true });
8114
8446
  }
8115
8447
  }
8116
8448
  function identityPath(agentId) {
8117
- return path15.join(IDENTITY_DIR2, `${agentId}.md`);
8449
+ return path16.join(IDENTITY_DIR2, `${agentId}.md`);
8118
8450
  }
8119
8451
  function parseFrontmatter(raw) {
8120
8452
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -8155,8 +8487,8 @@ function contentHash(content) {
8155
8487
  }
8156
8488
  function getIdentity(agentId) {
8157
8489
  const filePath = identityPath(agentId);
8158
- if (!existsSync13(filePath)) return null;
8159
- const raw = readFileSync10(filePath, "utf-8");
8490
+ if (!existsSync15(filePath)) return null;
8491
+ const raw = readFileSync11(filePath, "utf-8");
8160
8492
  const { frontmatter, body } = parseFrontmatter(raw);
8161
8493
  return {
8162
8494
  agentId,
@@ -8170,7 +8502,7 @@ async function updateIdentity(agentId, content, updatedBy) {
8170
8502
  ensureDir();
8171
8503
  const filePath = identityPath(agentId);
8172
8504
  const hash = contentHash(content);
8173
- writeFileSync8(filePath, content, "utf-8");
8505
+ writeFileSync9(filePath, content, "utf-8");
8174
8506
  try {
8175
8507
  const client = getClient();
8176
8508
  await client.execute({
@@ -8226,15 +8558,15 @@ var init_identity = __esm({
8226
8558
  "use strict";
8227
8559
  init_config();
8228
8560
  init_database();
8229
- IDENTITY_DIR2 = path15.join(EXE_AI_DIR, "identity");
8561
+ IDENTITY_DIR2 = path16.join(EXE_AI_DIR, "identity");
8230
8562
  }
8231
8563
  });
8232
8564
 
8233
8565
  // src/lib/orchestration-package.ts
8234
8566
  import { randomUUID as randomUUID4 } from "crypto";
8235
- import { copyFileSync as copyFileSync2, existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
8236
- import os9 from "os";
8237
- import path16 from "path";
8567
+ import { copyFileSync as copyFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
8568
+ import os10 from "os";
8569
+ import path17 from "path";
8238
8570
  function ensureObject(value, label) {
8239
8571
  if (value == null || Array.isArray(value) || typeof value !== "object") {
8240
8572
  throw new Error(`${label} must be an object`);
@@ -8293,15 +8625,15 @@ function validateProcedureEntry(value, index) {
8293
8625
  };
8294
8626
  }
8295
8627
  function getRosterPath() {
8296
- return path16.join(os9.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
8628
+ return path17.join(os10.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
8297
8629
  }
8298
8630
  function getBackupPath() {
8299
- return path16.join(os9.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
8631
+ return path17.join(os10.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
8300
8632
  }
8301
8633
  function readRosterFile() {
8302
8634
  const rosterPath = getRosterPath();
8303
- if (!existsSync14(rosterPath)) return [];
8304
- const raw = readFileSync11(rosterPath, "utf-8");
8635
+ if (!existsSync16(rosterPath)) return [];
8636
+ const raw = readFileSync12(rosterPath, "utf-8");
8305
8637
  const parsed = JSON.parse(raw);
8306
8638
  if (!Array.isArray(parsed)) {
8307
8639
  throw new Error("Roster file must contain a JSON array");
@@ -8313,8 +8645,8 @@ function writeRosterFile(roster) {
8313
8645
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
8314
8646
  }
8315
8647
  const rosterPath = getRosterPath();
8316
- mkdirSync10(path16.dirname(rosterPath), { recursive: true });
8317
- if (existsSync14(rosterPath)) {
8648
+ mkdirSync9(path17.dirname(rosterPath), { recursive: true });
8649
+ if (existsSync16(rosterPath)) {
8318
8650
  const currentRoster = readRosterFile();
8319
8651
  if (roster.length < currentRoster.length) {
8320
8652
  throw new Error(
@@ -8323,7 +8655,7 @@ function writeRosterFile(roster) {
8323
8655
  }
8324
8656
  copyFileSync2(rosterPath, getBackupPath());
8325
8657
  }
8326
- writeFileSync9(rosterPath, `${JSON.stringify(roster, null, 2)}
8658
+ writeFileSync10(rosterPath, `${JSON.stringify(roster, null, 2)}
8327
8659
  `, "utf-8");
8328
8660
  }
8329
8661
  function buildImportedRosterEntries(roster, timestamp) {
@@ -8590,8 +8922,8 @@ var exe_export_exports = {};
8590
8922
  __export(exe_export_exports, {
8591
8923
  runExeExport: () => runExeExport
8592
8924
  });
8593
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
8594
- import path17 from "path";
8925
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
8926
+ import path18 from "path";
8595
8927
  function printUsage() {
8596
8928
  process.stdout.write("Usage: exe-os export --output <path>\n");
8597
8929
  }
@@ -8612,8 +8944,8 @@ async function runExeExport(argv = process.argv.slice(2)) {
8612
8944
  await initStore();
8613
8945
  try {
8614
8946
  const pkg = await exportOrchestration("cli");
8615
- mkdirSync11(path17.dirname(outputPath), { recursive: true });
8616
- writeFileSync10(outputPath, `${JSON.stringify(pkg, null, 2)}
8947
+ mkdirSync10(path18.dirname(outputPath), { recursive: true });
8948
+ writeFileSync11(outputPath, `${JSON.stringify(pkg, null, 2)}
8617
8949
  `, "utf-8");
8618
8950
  process.stdout.write(
8619
8951
  `Exported ${pkg.roster.length} roster entries, ${Object.keys(pkg.identities).length} identities, ${pkg.behaviors.length} behaviors, ${pkg.procedures.length} procedures to ${outputPath}
@@ -8649,7 +8981,7 @@ var exe_import_exports = {};
8649
8981
  __export(exe_import_exports, {
8650
8982
  runExeImport: () => runExeImport
8651
8983
  });
8652
- import { readFileSync as readFileSync12 } from "fs";
8984
+ import { readFileSync as readFileSync13 } from "fs";
8653
8985
  function printUsage2() {
8654
8986
  process.stdout.write("Usage: exe-os import --from <path> [--merge]\n");
8655
8987
  }
@@ -8672,7 +9004,7 @@ async function runExeImport(argv = process.argv.slice(2)) {
8672
9004
  if (parsed == null) return;
8673
9005
  await initStore();
8674
9006
  try {
8675
- const raw = readFileSync12(parsed.packagePath, "utf-8");
9007
+ const raw = readFileSync13(parsed.packagePath, "utf-8");
8676
9008
  const pkg = validatePackage(JSON.parse(raw));
8677
9009
  const result = await importOrchestration(pkg, parsed.strategy);
8678
9010
  process.stdout.write(
@@ -8712,14 +9044,14 @@ __export(session_registry_exports, {
8712
9044
  pruneStaleSessions: () => pruneStaleSessions,
8713
9045
  registerSession: () => registerSession
8714
9046
  });
8715
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, existsSync as existsSync15 } from "fs";
9047
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, mkdirSync as mkdirSync11, existsSync as existsSync17 } from "fs";
8716
9048
  import { execSync as execSync3 } from "child_process";
8717
- import path18 from "path";
8718
- import os10 from "os";
9049
+ import path19 from "path";
9050
+ import os11 from "os";
8719
9051
  function registerSession(entry) {
8720
- const dir = path18.dirname(REGISTRY_PATH);
8721
- if (!existsSync15(dir)) {
8722
- mkdirSync12(dir, { recursive: true });
9052
+ const dir = path19.dirname(REGISTRY_PATH);
9053
+ if (!existsSync17(dir)) {
9054
+ mkdirSync11(dir, { recursive: true });
8723
9055
  }
8724
9056
  const sessions = listSessions();
8725
9057
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -8728,11 +9060,11 @@ function registerSession(entry) {
8728
9060
  } else {
8729
9061
  sessions.push(entry);
8730
9062
  }
8731
- writeFileSync11(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
9063
+ writeFileSync12(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
8732
9064
  }
8733
9065
  function listSessions() {
8734
9066
  try {
8735
- const raw = readFileSync13(REGISTRY_PATH, "utf8");
9067
+ const raw = readFileSync14(REGISTRY_PATH, "utf8");
8736
9068
  return JSON.parse(raw);
8737
9069
  } catch {
8738
9070
  return [];
@@ -8753,7 +9085,7 @@ function pruneStaleSessions() {
8753
9085
  const alive = sessions.filter((s) => liveSet.has(s.windowName));
8754
9086
  const pruned = sessions.length - alive.length;
8755
9087
  if (pruned > 0) {
8756
- writeFileSync11(REGISTRY_PATH, JSON.stringify(alive, null, 2));
9088
+ writeFileSync12(REGISTRY_PATH, JSON.stringify(alive, null, 2));
8757
9089
  }
8758
9090
  return pruned;
8759
9091
  }
@@ -8761,7 +9093,7 @@ var REGISTRY_PATH;
8761
9093
  var init_session_registry = __esm({
8762
9094
  "src/lib/session-registry.ts"() {
8763
9095
  "use strict";
8764
- REGISTRY_PATH = path18.join(os10.homedir(), ".exe-os", "session-registry.json");
9096
+ REGISTRY_PATH = path19.join(os11.homedir(), ".exe-os", "session-registry.json");
8765
9097
  }
8766
9098
  });
8767
9099
 
@@ -9007,17 +9339,17 @@ __export(intercom_queue_exports, {
9007
9339
  queueIntercom: () => queueIntercom,
9008
9340
  readQueue: () => readQueue
9009
9341
  });
9010
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, renameSync as renameSync3, existsSync as existsSync16, mkdirSync as mkdirSync13 } from "fs";
9011
- import path19 from "path";
9012
- import os11 from "os";
9342
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, renameSync as renameSync3, existsSync as existsSync18, mkdirSync as mkdirSync12 } from "fs";
9343
+ import path20 from "path";
9344
+ import os12 from "os";
9013
9345
  function ensureDir2() {
9014
- const dir = path19.dirname(QUEUE_PATH);
9015
- if (!existsSync16(dir)) mkdirSync13(dir, { recursive: true });
9346
+ const dir = path20.dirname(QUEUE_PATH);
9347
+ if (!existsSync18(dir)) mkdirSync12(dir, { recursive: true });
9016
9348
  }
9017
9349
  function readQueue() {
9018
9350
  try {
9019
- if (!existsSync16(QUEUE_PATH)) return [];
9020
- return JSON.parse(readFileSync14(QUEUE_PATH, "utf8"));
9351
+ if (!existsSync18(QUEUE_PATH)) return [];
9352
+ return JSON.parse(readFileSync15(QUEUE_PATH, "utf8"));
9021
9353
  } catch {
9022
9354
  return [];
9023
9355
  }
@@ -9025,7 +9357,7 @@ function readQueue() {
9025
9357
  function writeQueue(queue) {
9026
9358
  ensureDir2();
9027
9359
  const tmp = `${QUEUE_PATH}.tmp`;
9028
- writeFileSync12(tmp, JSON.stringify(queue, null, 2));
9360
+ writeFileSync13(tmp, JSON.stringify(queue, null, 2));
9029
9361
  renameSync3(tmp, QUEUE_PATH);
9030
9362
  }
9031
9363
  function queueIntercom(targetSession, reason) {
@@ -9117,20 +9449,20 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
9117
9449
  var init_intercom_queue = __esm({
9118
9450
  "src/lib/intercom-queue.ts"() {
9119
9451
  "use strict";
9120
- QUEUE_PATH = path19.join(os11.homedir(), ".exe-os", "intercom-queue.json");
9452
+ QUEUE_PATH = path20.join(os12.homedir(), ".exe-os", "intercom-queue.json");
9121
9453
  MAX_RETRIES2 = 5;
9122
9454
  TTL_MS = 60 * 60 * 1e3;
9123
- INTERCOM_LOG = path19.join(os11.homedir(), ".exe-os", "intercom.log");
9455
+ INTERCOM_LOG = path20.join(os12.homedir(), ".exe-os", "intercom.log");
9124
9456
  }
9125
9457
  });
9126
9458
 
9127
9459
  // src/lib/plan-limits.ts
9128
- import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
9129
- import path20 from "path";
9460
+ import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
9461
+ import path21 from "path";
9130
9462
  function getLicenseSync() {
9131
9463
  try {
9132
- if (!existsSync17(CACHE_PATH2)) return freeLicense();
9133
- const raw = JSON.parse(readFileSync15(CACHE_PATH2, "utf8"));
9464
+ if (!existsSync19(CACHE_PATH2)) return freeLicense();
9465
+ const raw = JSON.parse(readFileSync16(CACHE_PATH2, "utf8"));
9134
9466
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
9135
9467
  const parts = raw.token.split(".");
9136
9468
  if (parts.length !== 3) return freeLicense();
@@ -9168,8 +9500,8 @@ function assertEmployeeLimitSync(rosterPath) {
9168
9500
  const filePath = rosterPath ?? EMPLOYEES_PATH;
9169
9501
  let count = 0;
9170
9502
  try {
9171
- if (existsSync17(filePath)) {
9172
- const raw = readFileSync15(filePath, "utf8");
9503
+ if (existsSync19(filePath)) {
9504
+ const raw = readFileSync16(filePath, "utf8");
9173
9505
  const employees = JSON.parse(raw);
9174
9506
  count = Array.isArray(employees) ? employees.length : 0;
9175
9507
  }
@@ -9198,29 +9530,69 @@ var init_plan_limits = __esm({
9198
9530
  this.name = "PlanLimitError";
9199
9531
  }
9200
9532
  };
9201
- CACHE_PATH2 = path20.join(EXE_AI_DIR, "license-cache.json");
9533
+ CACHE_PATH2 = path21.join(EXE_AI_DIR, "license-cache.json");
9534
+ }
9535
+ });
9536
+
9537
+ // src/lib/task-scope.ts
9538
+ var task_scope_exports = {};
9539
+ __export(task_scope_exports, {
9540
+ getCurrentSessionScope: () => getCurrentSessionScope,
9541
+ sessionScopeFilter: () => sessionScopeFilter,
9542
+ strictSessionScopeFilter: () => strictSessionScopeFilter
9543
+ });
9544
+ function getCurrentSessionScope() {
9545
+ try {
9546
+ return resolveExeSession();
9547
+ } catch {
9548
+ return null;
9549
+ }
9550
+ }
9551
+ function sessionScopeFilter(sessionScope, tableAlias) {
9552
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
9553
+ if (!scope) return { sql: "", args: [] };
9554
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
9555
+ return {
9556
+ sql: ` AND (${col} IS NULL OR ${col} = ?)`,
9557
+ args: [scope]
9558
+ };
9559
+ }
9560
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
9561
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
9562
+ if (!scope) return { sql: "", args: [] };
9563
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
9564
+ return {
9565
+ sql: ` AND ${col} = ?`,
9566
+ args: [scope]
9567
+ };
9568
+ }
9569
+ var init_task_scope = __esm({
9570
+ "src/lib/task-scope.ts"() {
9571
+ "use strict";
9572
+ init_tmux_routing();
9202
9573
  }
9203
9574
  });
9204
9575
 
9205
9576
  // src/lib/notifications.ts
9206
- import crypto5 from "crypto";
9207
- import path21 from "path";
9208
- import os12 from "os";
9577
+ import crypto6 from "crypto";
9578
+ import path22 from "path";
9579
+ import os13 from "os";
9209
9580
  import {
9210
- readFileSync as readFileSync16,
9581
+ readFileSync as readFileSync17,
9211
9582
  readdirSync as readdirSync4,
9212
9583
  unlinkSync as unlinkSync5,
9213
- existsSync as existsSync18,
9584
+ existsSync as existsSync20,
9214
9585
  rmdirSync
9215
9586
  } from "fs";
9216
9587
  async function writeNotification(notification) {
9217
9588
  try {
9218
9589
  const client = getClient();
9219
- const id = crypto5.randomUUID();
9590
+ const id = crypto6.randomUUID();
9220
9591
  const now = (/* @__PURE__ */ new Date()).toISOString();
9592
+ const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
9221
9593
  await client.execute({
9222
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
9223
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
9594
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
9595
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
9224
9596
  args: [
9225
9597
  id,
9226
9598
  notification.agentId,
@@ -9229,6 +9601,7 @@ async function writeNotification(notification) {
9229
9601
  notification.project,
9230
9602
  notification.summary,
9231
9603
  notification.taskFile ?? null,
9604
+ sessionScope,
9232
9605
  now
9233
9606
  ]
9234
9607
  });
@@ -9237,12 +9610,14 @@ async function writeNotification(notification) {
9237
9610
  `);
9238
9611
  }
9239
9612
  }
9240
- async function markAsReadByTaskFile(taskFile) {
9613
+ async function markAsReadByTaskFile(taskFile, sessionScope) {
9241
9614
  try {
9242
9615
  const client = getClient();
9616
+ const scope = strictSessionScopeFilter(sessionScope);
9243
9617
  await client.execute({
9244
- sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
9245
- args: [taskFile]
9618
+ sql: `UPDATE notifications SET read = 1
9619
+ WHERE task_file = ? AND read = 0${scope.sql}`,
9620
+ args: [taskFile, ...scope.args]
9246
9621
  });
9247
9622
  } catch {
9248
9623
  }
@@ -9251,11 +9626,12 @@ var init_notifications = __esm({
9251
9626
  "src/lib/notifications.ts"() {
9252
9627
  "use strict";
9253
9628
  init_database();
9629
+ init_task_scope();
9254
9630
  }
9255
9631
  });
9256
9632
 
9257
9633
  // src/lib/session-kill-telemetry.ts
9258
- import crypto6 from "crypto";
9634
+ import crypto7 from "crypto";
9259
9635
  async function recordSessionKill(input) {
9260
9636
  try {
9261
9637
  const client = getClient();
@@ -9265,7 +9641,7 @@ async function recordSessionKill(input) {
9265
9641
  ticks_idle, estimated_tokens_saved)
9266
9642
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
9267
9643
  args: [
9268
- crypto6.randomUUID(),
9644
+ crypto7.randomUUID(),
9269
9645
  input.sessionName,
9270
9646
  input.agentId,
9271
9647
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -9288,32 +9664,107 @@ var init_session_kill_telemetry = __esm({
9288
9664
  }
9289
9665
  });
9290
9666
 
9291
- // src/lib/task-scope.ts
9292
- var task_scope_exports = {};
9293
- __export(task_scope_exports, {
9294
- getCurrentSessionScope: () => getCurrentSessionScope,
9295
- sessionScopeFilter: () => sessionScopeFilter
9296
- });
9297
- function getCurrentSessionScope() {
9667
+ // src/lib/project-name.ts
9668
+ import { execSync as execSync6 } from "child_process";
9669
+ import path23 from "path";
9670
+ function getProjectName(cwd2) {
9671
+ const dir = cwd2 ?? process.cwd();
9672
+ if (_cached2 && _cachedCwd === dir) return _cached2;
9298
9673
  try {
9299
- return resolveExeSession();
9674
+ let repoRoot;
9675
+ try {
9676
+ const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
9677
+ cwd: dir,
9678
+ encoding: "utf8",
9679
+ timeout: 2e3,
9680
+ stdio: ["pipe", "pipe", "pipe"]
9681
+ }).trim();
9682
+ repoRoot = path23.dirname(gitCommonDir);
9683
+ } catch {
9684
+ repoRoot = execSync6("git rev-parse --show-toplevel", {
9685
+ cwd: dir,
9686
+ encoding: "utf8",
9687
+ timeout: 2e3,
9688
+ stdio: ["pipe", "pipe", "pipe"]
9689
+ }).trim();
9690
+ }
9691
+ _cached2 = path23.basename(repoRoot);
9692
+ _cachedCwd = dir;
9693
+ return _cached2;
9300
9694
  } catch {
9301
- return null;
9695
+ _cached2 = path23.basename(dir);
9696
+ _cachedCwd = dir;
9697
+ return _cached2;
9302
9698
  }
9303
9699
  }
9304
- function sessionScopeFilter(sessionScope, tableAlias) {
9305
- const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
9306
- if (!scope) return { sql: "", args: [] };
9307
- const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
9308
- return {
9309
- sql: ` AND (${col} IS NULL OR ${col} = ?)`,
9310
- args: [scope]
9311
- };
9700
+ var _cached2, _cachedCwd;
9701
+ var init_project_name = __esm({
9702
+ "src/lib/project-name.ts"() {
9703
+ "use strict";
9704
+ _cached2 = null;
9705
+ _cachedCwd = null;
9706
+ }
9707
+ });
9708
+
9709
+ // src/lib/session-scope.ts
9710
+ var session_scope_exports = {};
9711
+ __export(session_scope_exports, {
9712
+ assertSessionScope: () => assertSessionScope,
9713
+ findSessionForProject: () => findSessionForProject,
9714
+ getSessionProject: () => getSessionProject
9715
+ });
9716
+ function getSessionProject(sessionName) {
9717
+ const sessions = listSessions();
9718
+ const entry = sessions.find((s) => s.windowName === sessionName);
9719
+ if (!entry) return null;
9720
+ const parts = entry.projectDir.split("/").filter(Boolean);
9721
+ return parts[parts.length - 1] ?? null;
9312
9722
  }
9313
- var init_task_scope = __esm({
9314
- "src/lib/task-scope.ts"() {
9723
+ function findSessionForProject(projectName) {
9724
+ const sessions = listSessions();
9725
+ for (const s of sessions) {
9726
+ const proj = s.projectDir.split("/").filter(Boolean).pop();
9727
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
9728
+ }
9729
+ return null;
9730
+ }
9731
+ function assertSessionScope(actionType, targetProject) {
9732
+ try {
9733
+ const currentProject = getProjectName();
9734
+ const exeSession = resolveExeSession();
9735
+ if (!exeSession) {
9736
+ return { allowed: true, reason: "no_session" };
9737
+ }
9738
+ if (currentProject === targetProject) {
9739
+ return {
9740
+ allowed: true,
9741
+ reason: "same_session",
9742
+ currentProject,
9743
+ targetProject
9744
+ };
9745
+ }
9746
+ process.stderr.write(
9747
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
9748
+ `
9749
+ );
9750
+ return {
9751
+ allowed: false,
9752
+ reason: "cross_session_denied",
9753
+ currentProject,
9754
+ targetProject,
9755
+ targetSession: findSessionForProject(targetProject)?.windowName
9756
+ };
9757
+ } catch {
9758
+ return { allowed: true, reason: "no_session" };
9759
+ }
9760
+ }
9761
+ var init_session_scope = __esm({
9762
+ "src/lib/session-scope.ts"() {
9315
9763
  "use strict";
9764
+ init_session_registry();
9765
+ init_project_name();
9316
9766
  init_tmux_routing();
9767
+ init_employees();
9317
9768
  }
9318
9769
  });
9319
9770
 
@@ -9334,12 +9785,12 @@ __export(tasks_crud_exports, {
9334
9785
  updateTaskStatus: () => updateTaskStatus,
9335
9786
  writeCheckpoint: () => writeCheckpoint
9336
9787
  });
9337
- import crypto7 from "crypto";
9338
- import path22 from "path";
9339
- import os13 from "os";
9340
- import { execSync as execSync6 } from "child_process";
9788
+ import crypto8 from "crypto";
9789
+ import path24 from "path";
9790
+ import os14 from "os";
9791
+ import { execSync as execSync7 } from "child_process";
9341
9792
  import { mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
9342
- import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
9793
+ import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
9343
9794
  async function writeCheckpoint(input) {
9344
9795
  const client = getClient();
9345
9796
  const row = await resolveTask(client, input.taskId);
@@ -9455,13 +9906,28 @@ async function resolveTask(client, identifier, scopeSession) {
9455
9906
  }
9456
9907
  async function createTaskCore(input) {
9457
9908
  const client = getClient();
9458
- const id = crypto7.randomUUID();
9909
+ const id = crypto8.randomUUID();
9459
9910
  const now = (/* @__PURE__ */ new Date()).toISOString();
9460
9911
  const slug = slugify(input.title);
9461
9912
  let earlySessionScope = null;
9913
+ let scopeMismatchWarning;
9462
9914
  try {
9463
9915
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
9464
- earlySessionScope = resolveExeSession2();
9916
+ const resolved = resolveExeSession2();
9917
+ if (resolved && input.projectName) {
9918
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
9919
+ const sessionProject = getSessionProject2(resolved);
9920
+ if (sessionProject && sessionProject !== input.projectName) {
9921
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
9922
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
9923
+ `);
9924
+ earlySessionScope = null;
9925
+ } else {
9926
+ earlySessionScope = resolved;
9927
+ }
9928
+ } else {
9929
+ earlySessionScope = resolved;
9930
+ }
9465
9931
  } catch {
9466
9932
  }
9467
9933
  const scope = earlySessionScope ?? "default";
@@ -9512,10 +9978,14 @@ async function createTaskCore(input) {
9512
9978
  ${laneWarning}` : laneWarning;
9513
9979
  }
9514
9980
  }
9981
+ if (scopeMismatchWarning) {
9982
+ warning = warning ? `${warning}
9983
+ ${scopeMismatchWarning}` : scopeMismatchWarning;
9984
+ }
9515
9985
  if (input.baseDir) {
9516
9986
  try {
9517
- await mkdir5(path22.join(input.baseDir, "exe", "output"), { recursive: true });
9518
- await mkdir5(path22.join(input.baseDir, "exe", "research"), { recursive: true });
9987
+ await mkdir5(path24.join(input.baseDir, "exe", "output"), { recursive: true });
9988
+ await mkdir5(path24.join(input.baseDir, "exe", "research"), { recursive: true });
9519
9989
  await ensureArchitectureDoc(input.baseDir, input.projectName);
9520
9990
  await ensureGitignoreExe(input.baseDir);
9521
9991
  } catch {
@@ -9551,13 +10021,19 @@ ${laneWarning}` : laneWarning;
9551
10021
  });
9552
10022
  if (input.baseDir) {
9553
10023
  try {
9554
- const EXE_OS_DIR = path22.join(os13.homedir(), ".exe-os");
9555
- const mdPath = path22.join(EXE_OS_DIR, taskFile);
9556
- const mdDir = path22.dirname(mdPath);
9557
- if (!existsSync19(mdDir)) await mkdir5(mdDir, { recursive: true });
10024
+ const EXE_OS_DIR = path24.join(os14.homedir(), ".exe-os");
10025
+ const mdPath = path24.join(EXE_OS_DIR, taskFile);
10026
+ const mdDir = path24.dirname(mdPath);
10027
+ if (!existsSync21(mdDir)) await mkdir5(mdDir, { recursive: true });
9558
10028
  const reviewer = input.reviewer ?? input.assignedBy;
9559
10029
  const mdContent = `# ${input.title}
9560
10030
 
10031
+ ## MANDATORY: When done
10032
+
10033
+ You MUST call update_task with status "done" and a result summary when finished.
10034
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
10035
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
10036
+
9561
10037
  **ID:** ${id}
9562
10038
  **Status:** ${initialStatus}
9563
10039
  **Priority:** ${input.priority}
@@ -9571,12 +10047,6 @@ ${laneWarning}` : laneWarning;
9571
10047
  ## Context
9572
10048
 
9573
10049
  ${input.context}
9574
-
9575
- ## MANDATORY: When done
9576
-
9577
- You MUST call update_task with status "done" and a result summary when finished.
9578
- If you skip this, your reviewer will not know you're done and your work won't be reviewed.
9579
- Do NOT let a failed commit or any error prevent you from calling update_task(done).
9580
10050
  `;
9581
10051
  await writeFile5(mdPath, mdContent, "utf-8");
9582
10052
  } catch (err) {
@@ -9658,14 +10128,14 @@ function isTmuxSessionAlive(identifier) {
9658
10128
  if (!identifier || identifier === "unknown") return true;
9659
10129
  try {
9660
10130
  if (identifier.startsWith("%")) {
9661
- const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
10131
+ const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
9662
10132
  timeout: 2e3,
9663
10133
  encoding: "utf8",
9664
10134
  stdio: ["pipe", "pipe", "pipe"]
9665
10135
  });
9666
10136
  return output.split("\n").some((l) => l.trim() === identifier);
9667
10137
  } else {
9668
- execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
10138
+ execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
9669
10139
  timeout: 2e3,
9670
10140
  stdio: ["pipe", "pipe", "pipe"]
9671
10141
  });
@@ -9674,7 +10144,7 @@ function isTmuxSessionAlive(identifier) {
9674
10144
  } catch {
9675
10145
  if (identifier.startsWith("%")) return true;
9676
10146
  try {
9677
- execSync6("tmux list-sessions", {
10147
+ execSync7("tmux list-sessions", {
9678
10148
  timeout: 2e3,
9679
10149
  stdio: ["pipe", "pipe", "pipe"]
9680
10150
  });
@@ -9689,12 +10159,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
9689
10159
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
9690
10160
  try {
9691
10161
  const since = new Date(taskCreatedAt).toISOString();
9692
- const branch = execSync6(
10162
+ const branch = execSync7(
9693
10163
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
9694
10164
  { encoding: "utf8", timeout: 3e3 }
9695
10165
  ).trim();
9696
10166
  const branchArg = branch && branch !== "HEAD" ? branch : "";
9697
- const commitCount = execSync6(
10167
+ const commitCount = execSync7(
9698
10168
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
9699
10169
  { encoding: "utf8", timeout: 5e3 }
9700
10170
  ).trim();
@@ -9825,7 +10295,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
9825
10295
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
9826
10296
  } catch {
9827
10297
  }
9828
- if (input.status === "done" || input.status === "cancelled") {
10298
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
9829
10299
  try {
9830
10300
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
9831
10301
  clearQueueForAgent2(String(row.assigned_to));
@@ -9854,9 +10324,9 @@ async function deleteTaskCore(taskId, _baseDir) {
9854
10324
  return { taskFile, assignedTo, assignedBy, taskSlug };
9855
10325
  }
9856
10326
  async function ensureArchitectureDoc(baseDir, projectName) {
9857
- const archPath = path22.join(baseDir, "exe", "ARCHITECTURE.md");
10327
+ const archPath = path24.join(baseDir, "exe", "ARCHITECTURE.md");
9858
10328
  try {
9859
- if (existsSync19(archPath)) return;
10329
+ if (existsSync21(archPath)) return;
9860
10330
  const template = [
9861
10331
  `# ${projectName} \u2014 System Architecture`,
9862
10332
  "",
@@ -9889,10 +10359,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
9889
10359
  }
9890
10360
  }
9891
10361
  async function ensureGitignoreExe(baseDir) {
9892
- const gitignorePath = path22.join(baseDir, ".gitignore");
10362
+ const gitignorePath = path24.join(baseDir, ".gitignore");
9893
10363
  try {
9894
- if (existsSync19(gitignorePath)) {
9895
- const content = readFileSync17(gitignorePath, "utf-8");
10364
+ if (existsSync21(gitignorePath)) {
10365
+ const content = readFileSync18(gitignorePath, "utf-8");
9896
10366
  if (/^\/?exe\/?$/m.test(content)) return;
9897
10367
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
9898
10368
  } else {
@@ -9923,58 +10393,42 @@ var init_tasks_crud = __esm({
9923
10393
  });
9924
10394
 
9925
10395
  // src/lib/tasks-review.ts
9926
- import path23 from "path";
9927
- import { existsSync as existsSync20, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
10396
+ import path25 from "path";
10397
+ import { existsSync as existsSync22, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
9928
10398
  async function countPendingReviews(sessionScope) {
9929
10399
  const client = getClient();
9930
- if (sessionScope) {
9931
- const result2 = await client.execute({
9932
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
9933
- args: [sessionScope]
9934
- });
9935
- return Number(result2.rows[0]?.cnt) || 0;
9936
- }
10400
+ const scope = strictSessionScopeFilter(
10401
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
10402
+ );
9937
10403
  const result = await client.execute({
9938
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
9939
- args: []
10404
+ sql: `SELECT COUNT(*) as cnt FROM tasks
10405
+ WHERE status = 'needs_review'${scope.sql}`,
10406
+ args: [...scope.args]
9940
10407
  });
9941
10408
  return Number(result.rows[0]?.cnt) || 0;
9942
10409
  }
9943
10410
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
9944
10411
  const client = getClient();
9945
- if (sessionScope) {
9946
- const result2 = await client.execute({
9947
- sql: `SELECT COUNT(*) as cnt FROM tasks
9948
- WHERE status = 'needs_review' AND updated_at > ?
9949
- AND session_scope = ?`,
9950
- args: [sinceIso, sessionScope]
9951
- });
9952
- return Number(result2.rows[0]?.cnt) || 0;
9953
- }
10412
+ const scope = strictSessionScopeFilter(
10413
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
10414
+ );
9954
10415
  const result = await client.execute({
9955
10416
  sql: `SELECT COUNT(*) as cnt FROM tasks
9956
- WHERE status = 'needs_review' AND updated_at > ?`,
9957
- args: [sinceIso]
10417
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
10418
+ args: [sinceIso, ...scope.args]
9958
10419
  });
9959
10420
  return Number(result.rows[0]?.cnt) || 0;
9960
10421
  }
9961
10422
  async function listPendingReviews(limit, sessionScope) {
9962
10423
  const client = getClient();
9963
- if (sessionScope) {
9964
- const result2 = await client.execute({
9965
- sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
9966
- WHERE status = 'needs_review'
9967
- AND session_scope = ?
9968
- ORDER BY updated_at ASC LIMIT ?`,
9969
- args: [sessionScope, limit]
9970
- });
9971
- return result2.rows;
9972
- }
10424
+ const scope = strictSessionScopeFilter(
10425
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
10426
+ );
9973
10427
  const result = await client.execute({
9974
10428
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
9975
- WHERE status = 'needs_review'
10429
+ WHERE status = 'needs_review'${scope.sql}
9976
10430
  ORDER BY updated_at ASC LIMIT ?`,
9977
- args: [limit]
10431
+ args: [...scope.args, limit]
9978
10432
  });
9979
10433
  return result.rows;
9980
10434
  }
@@ -9986,7 +10440,7 @@ async function cleanupOrphanedReviews() {
9986
10440
  WHERE status IN ('open', 'needs_review', 'in_progress')
9987
10441
  AND assigned_by = 'system'
9988
10442
  AND title LIKE 'Review:%'
9989
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
10443
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
9990
10444
  args: [now]
9991
10445
  });
9992
10446
  const r1b = await client.execute({
@@ -10105,11 +10559,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
10105
10559
  );
10106
10560
  }
10107
10561
  try {
10108
- const cacheDir = path23.join(EXE_AI_DIR, "session-cache");
10109
- if (existsSync20(cacheDir)) {
10562
+ const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
10563
+ if (existsSync22(cacheDir)) {
10110
10564
  for (const f of readdirSync5(cacheDir)) {
10111
10565
  if (f.startsWith("review-notified-")) {
10112
- unlinkSync6(path23.join(cacheDir, f));
10566
+ unlinkSync6(path25.join(cacheDir, f));
10113
10567
  }
10114
10568
  }
10115
10569
  }
@@ -10126,11 +10580,12 @@ var init_tasks_review = __esm({
10126
10580
  init_tmux_routing();
10127
10581
  init_session_key();
10128
10582
  init_state_bus();
10583
+ init_task_scope();
10129
10584
  }
10130
10585
  });
10131
10586
 
10132
10587
  // src/lib/tasks-chain.ts
10133
- import path24 from "path";
10588
+ import path26 from "path";
10134
10589
  import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
10135
10590
  async function cascadeUnblock(taskId, baseDir, now) {
10136
10591
  const client = getClient();
@@ -10147,7 +10602,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
10147
10602
  });
10148
10603
  for (const ur of unblockedRows.rows) {
10149
10604
  try {
10150
- const ubFile = path24.join(baseDir, String(ur.task_file));
10605
+ const ubFile = path26.join(baseDir, String(ur.task_file));
10151
10606
  let ubContent = await readFile5(ubFile, "utf-8");
10152
10607
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
10153
10608
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -10182,7 +10637,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
10182
10637
  const scScope = sessionScopeFilter();
10183
10638
  const remaining = await client.execute({
10184
10639
  sql: `SELECT COUNT(*) as cnt FROM tasks
10185
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
10640
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
10186
10641
  args: [parentTaskId, ...scScope.args]
10187
10642
  });
10188
10643
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -10214,110 +10669,6 @@ var init_tasks_chain = __esm({
10214
10669
  }
10215
10670
  });
10216
10671
 
10217
- // src/lib/project-name.ts
10218
- import { execSync as execSync7 } from "child_process";
10219
- import path25 from "path";
10220
- function getProjectName(cwd2) {
10221
- const dir = cwd2 ?? process.cwd();
10222
- if (_cached2 && _cachedCwd === dir) return _cached2;
10223
- try {
10224
- let repoRoot;
10225
- try {
10226
- const gitCommonDir = execSync7("git rev-parse --path-format=absolute --git-common-dir", {
10227
- cwd: dir,
10228
- encoding: "utf8",
10229
- timeout: 2e3,
10230
- stdio: ["pipe", "pipe", "pipe"]
10231
- }).trim();
10232
- repoRoot = path25.dirname(gitCommonDir);
10233
- } catch {
10234
- repoRoot = execSync7("git rev-parse --show-toplevel", {
10235
- cwd: dir,
10236
- encoding: "utf8",
10237
- timeout: 2e3,
10238
- stdio: ["pipe", "pipe", "pipe"]
10239
- }).trim();
10240
- }
10241
- _cached2 = path25.basename(repoRoot);
10242
- _cachedCwd = dir;
10243
- return _cached2;
10244
- } catch {
10245
- _cached2 = path25.basename(dir);
10246
- _cachedCwd = dir;
10247
- return _cached2;
10248
- }
10249
- }
10250
- var _cached2, _cachedCwd;
10251
- var init_project_name = __esm({
10252
- "src/lib/project-name.ts"() {
10253
- "use strict";
10254
- _cached2 = null;
10255
- _cachedCwd = null;
10256
- }
10257
- });
10258
-
10259
- // src/lib/session-scope.ts
10260
- var session_scope_exports = {};
10261
- __export(session_scope_exports, {
10262
- assertSessionScope: () => assertSessionScope,
10263
- findSessionForProject: () => findSessionForProject,
10264
- getSessionProject: () => getSessionProject
10265
- });
10266
- function getSessionProject(sessionName) {
10267
- const sessions = listSessions();
10268
- const entry = sessions.find((s) => s.windowName === sessionName);
10269
- if (!entry) return null;
10270
- const parts = entry.projectDir.split("/").filter(Boolean);
10271
- return parts[parts.length - 1] ?? null;
10272
- }
10273
- function findSessionForProject(projectName) {
10274
- const sessions = listSessions();
10275
- for (const s of sessions) {
10276
- const proj = s.projectDir.split("/").filter(Boolean).pop();
10277
- if (proj === projectName && isCoordinatorName(s.agentId)) return s;
10278
- }
10279
- return null;
10280
- }
10281
- function assertSessionScope(actionType, targetProject) {
10282
- try {
10283
- const currentProject = getProjectName();
10284
- const exeSession = resolveExeSession();
10285
- if (!exeSession) {
10286
- return { allowed: true, reason: "no_session" };
10287
- }
10288
- if (currentProject === targetProject) {
10289
- return {
10290
- allowed: true,
10291
- reason: "same_session",
10292
- currentProject,
10293
- targetProject
10294
- };
10295
- }
10296
- process.stderr.write(
10297
- `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
10298
- `
10299
- );
10300
- return {
10301
- allowed: false,
10302
- reason: "cross_session_denied",
10303
- currentProject,
10304
- targetProject,
10305
- targetSession: findSessionForProject(targetProject)?.windowName
10306
- };
10307
- } catch {
10308
- return { allowed: true, reason: "no_session" };
10309
- }
10310
- }
10311
- var init_session_scope = __esm({
10312
- "src/lib/session-scope.ts"() {
10313
- "use strict";
10314
- init_session_registry();
10315
- init_project_name();
10316
- init_tmux_routing();
10317
- init_employees();
10318
- }
10319
- });
10320
-
10321
10672
  // src/lib/tasks-notify.ts
10322
10673
  async function dispatchTaskToEmployee(input) {
10323
10674
  if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
@@ -10385,10 +10736,10 @@ var init_tasks_notify = __esm({
10385
10736
  });
10386
10737
 
10387
10738
  // src/lib/behaviors.ts
10388
- import crypto8 from "crypto";
10739
+ import crypto9 from "crypto";
10389
10740
  async function storeBehavior(opts) {
10390
10741
  const client = getClient();
10391
- const id = crypto8.randomUUID();
10742
+ const id = crypto9.randomUUID();
10392
10743
  const now = (/* @__PURE__ */ new Date()).toISOString();
10393
10744
  await client.execute({
10394
10745
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -10417,7 +10768,7 @@ __export(skill_learning_exports, {
10417
10768
  storeTrajectory: () => storeTrajectory,
10418
10769
  sweepTrajectories: () => sweepTrajectories
10419
10770
  });
10420
- import crypto9 from "crypto";
10771
+ import crypto10 from "crypto";
10421
10772
  async function extractTrajectory(taskId, agentId) {
10422
10773
  const client = getClient();
10423
10774
  const result = await client.execute({
@@ -10446,11 +10797,11 @@ async function extractTrajectory(taskId, agentId) {
10446
10797
  return signature;
10447
10798
  }
10448
10799
  function hashSignature(signature) {
10449
- return crypto9.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
10800
+ return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
10450
10801
  }
10451
10802
  async function storeTrajectory(opts) {
10452
10803
  const client = getClient();
10453
- const id = crypto9.randomUUID();
10804
+ const id = crypto10.randomUUID();
10454
10805
  const now = (/* @__PURE__ */ new Date()).toISOString();
10455
10806
  const signatureHash = hashSignature(opts.signature);
10456
10807
  await client.execute({
@@ -10715,8 +11066,8 @@ __export(tasks_exports, {
10715
11066
  updateTaskStatus: () => updateTaskStatus,
10716
11067
  writeCheckpoint: () => writeCheckpoint
10717
11068
  });
10718
- import path26 from "path";
10719
- import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, unlinkSync as unlinkSync7 } from "fs";
11069
+ import path27 from "path";
11070
+ import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, unlinkSync as unlinkSync7 } from "fs";
10720
11071
  async function createTask(input) {
10721
11072
  const result = await createTaskCore(input);
10722
11073
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -10735,12 +11086,12 @@ async function updateTask(input) {
10735
11086
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
10736
11087
  try {
10737
11088
  const agent = String(row.assigned_to);
10738
- const cacheDir = path26.join(EXE_AI_DIR, "session-cache");
10739
- const cachePath = path26.join(cacheDir, `current-task-${agent}.json`);
11089
+ const cacheDir = path27.join(EXE_AI_DIR, "session-cache");
11090
+ const cachePath = path27.join(cacheDir, `current-task-${agent}.json`);
10740
11091
  if (input.status === "in_progress") {
10741
- mkdirSync14(cacheDir, { recursive: true });
10742
- writeFileSync13(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
10743
- } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
11092
+ mkdirSync13(cacheDir, { recursive: true });
11093
+ writeFileSync14(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
11094
+ } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
10744
11095
  try {
10745
11096
  unlinkSync7(cachePath);
10746
11097
  } catch {
@@ -10748,10 +11099,10 @@ async function updateTask(input) {
10748
11099
  }
10749
11100
  } catch {
10750
11101
  }
10751
- if (input.status === "done") {
11102
+ if (input.status === "done" || input.status === "closed") {
10752
11103
  await cleanupReviewFile(row, taskFile, input.baseDir);
10753
11104
  }
10754
- if (input.status === "done" || input.status === "cancelled") {
11105
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
10755
11106
  try {
10756
11107
  const client = getClient();
10757
11108
  const taskTitle = String(row.title);
@@ -10767,7 +11118,7 @@ async function updateTask(input) {
10767
11118
  if (!isCoordinatorName(assignedAgent)) {
10768
11119
  try {
10769
11120
  const draftClient = getClient();
10770
- if (input.status === "done") {
11121
+ if (input.status === "done" || input.status === "closed") {
10771
11122
  await draftClient.execute({
10772
11123
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
10773
11124
  args: [assignedAgent]
@@ -10784,7 +11135,7 @@ async function updateTask(input) {
10784
11135
  try {
10785
11136
  const client = getClient();
10786
11137
  const cascaded = await client.execute({
10787
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
11138
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
10788
11139
  WHERE parent_task_id = ? AND status = 'needs_review'`,
10789
11140
  args: [now, taskId]
10790
11141
  });
@@ -10797,14 +11148,14 @@ async function updateTask(input) {
10797
11148
  } catch {
10798
11149
  }
10799
11150
  }
10800
- const isTerminal = input.status === "done" || input.status === "needs_review";
11151
+ const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
10801
11152
  if (isTerminal) {
10802
11153
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
10803
11154
  if (!isCoordinator) {
10804
11155
  notifyTaskDone();
10805
11156
  }
10806
11157
  await markTaskNotificationsRead(taskFile);
10807
- if (input.status === "done") {
11158
+ if (input.status === "done" || input.status === "closed") {
10808
11159
  try {
10809
11160
  await cascadeUnblock(taskId, input.baseDir, now);
10810
11161
  } catch {
@@ -10824,7 +11175,7 @@ async function updateTask(input) {
10824
11175
  }
10825
11176
  }
10826
11177
  }
10827
- if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
11178
+ if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
10828
11179
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
10829
11180
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
10830
11181
  taskId,
@@ -11196,6 +11547,7 @@ __export(tmux_routing_exports, {
11196
11547
  isEmployeeAlive: () => isEmployeeAlive,
11197
11548
  isExeSession: () => isExeSession,
11198
11549
  isSessionBusy: () => isSessionBusy,
11550
+ notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
11199
11551
  notifyParentExe: () => notifyParentExe,
11200
11552
  parseParentExe: () => parseParentExe,
11201
11553
  registerParentExe: () => registerParentExe,
@@ -11206,13 +11558,13 @@ __export(tmux_routing_exports, {
11206
11558
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
11207
11559
  });
11208
11560
  import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
11209
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15, existsSync as existsSync21, appendFileSync as appendFileSync2, readdirSync as readdirSync6 } from "fs";
11210
- import path27 from "path";
11211
- import os14 from "os";
11561
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14, existsSync as existsSync23, appendFileSync as appendFileSync2, readdirSync as readdirSync6 } from "fs";
11562
+ import path28 from "path";
11563
+ import os15 from "os";
11212
11564
  import { fileURLToPath as fileURLToPath4 } from "url";
11213
11565
  import { unlinkSync as unlinkSync8 } from "fs";
11214
11566
  function spawnLockPath(sessionName) {
11215
- return path27.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
11567
+ return path28.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
11216
11568
  }
11217
11569
  function isProcessAlive(pid) {
11218
11570
  try {
@@ -11223,13 +11575,13 @@ function isProcessAlive(pid) {
11223
11575
  }
11224
11576
  }
11225
11577
  function acquireSpawnLock2(sessionName) {
11226
- if (!existsSync21(SPAWN_LOCK_DIR)) {
11227
- mkdirSync15(SPAWN_LOCK_DIR, { recursive: true });
11578
+ if (!existsSync23(SPAWN_LOCK_DIR)) {
11579
+ mkdirSync14(SPAWN_LOCK_DIR, { recursive: true });
11228
11580
  }
11229
11581
  const lockFile = spawnLockPath(sessionName);
11230
- if (existsSync21(lockFile)) {
11582
+ if (existsSync23(lockFile)) {
11231
11583
  try {
11232
- const lock = JSON.parse(readFileSync18(lockFile, "utf8"));
11584
+ const lock = JSON.parse(readFileSync19(lockFile, "utf8"));
11233
11585
  const age = Date.now() - lock.timestamp;
11234
11586
  if (isProcessAlive(lock.pid) && age < 6e4) {
11235
11587
  return false;
@@ -11237,7 +11589,7 @@ function acquireSpawnLock2(sessionName) {
11237
11589
  } catch {
11238
11590
  }
11239
11591
  }
11240
- writeFileSync14(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
11592
+ writeFileSync15(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
11241
11593
  return true;
11242
11594
  }
11243
11595
  function releaseSpawnLock2(sessionName) {
@@ -11249,13 +11601,13 @@ function releaseSpawnLock2(sessionName) {
11249
11601
  function resolveBehaviorsExporterScript() {
11250
11602
  try {
11251
11603
  const thisFile = fileURLToPath4(import.meta.url);
11252
- const scriptPath = path27.join(
11253
- path27.dirname(thisFile),
11604
+ const scriptPath = path28.join(
11605
+ path28.dirname(thisFile),
11254
11606
  "..",
11255
11607
  "bin",
11256
11608
  "exe-export-behaviors.js"
11257
11609
  );
11258
- return existsSync21(scriptPath) ? scriptPath : null;
11610
+ return existsSync23(scriptPath) ? scriptPath : null;
11259
11611
  } catch {
11260
11612
  return null;
11261
11613
  }
@@ -11321,12 +11673,12 @@ function extractRootExe(name) {
11321
11673
  return parts.length > 0 ? parts[parts.length - 1] : null;
11322
11674
  }
11323
11675
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
11324
- if (!existsSync21(SESSION_CACHE)) {
11325
- mkdirSync15(SESSION_CACHE, { recursive: true });
11676
+ if (!existsSync23(SESSION_CACHE)) {
11677
+ mkdirSync14(SESSION_CACHE, { recursive: true });
11326
11678
  }
11327
11679
  const rootExe = extractRootExe(parentExe) ?? parentExe;
11328
- const filePath = path27.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
11329
- writeFileSync14(filePath, JSON.stringify({
11680
+ const filePath = path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
11681
+ writeFileSync15(filePath, JSON.stringify({
11330
11682
  parentExe: rootExe,
11331
11683
  dispatchedBy: dispatchedBy || rootExe,
11332
11684
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -11334,7 +11686,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
11334
11686
  }
11335
11687
  function getParentExe(sessionKey) {
11336
11688
  try {
11337
- const data = JSON.parse(readFileSync18(path27.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
11689
+ const data = JSON.parse(readFileSync19(path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
11338
11690
  return data.parentExe || null;
11339
11691
  } catch {
11340
11692
  return null;
@@ -11342,8 +11694,8 @@ function getParentExe(sessionKey) {
11342
11694
  }
11343
11695
  function getDispatchedBy(sessionKey) {
11344
11696
  try {
11345
- const data = JSON.parse(readFileSync18(
11346
- path27.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
11697
+ const data = JSON.parse(readFileSync19(
11698
+ path28.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
11347
11699
  "utf8"
11348
11700
  ));
11349
11701
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -11413,8 +11765,8 @@ async function verifyPaneAtCapacity(sessionName) {
11413
11765
  }
11414
11766
  function readDebounceState() {
11415
11767
  try {
11416
- if (!existsSync21(DEBOUNCE_FILE)) return {};
11417
- const raw = JSON.parse(readFileSync18(DEBOUNCE_FILE, "utf8"));
11768
+ if (!existsSync23(DEBOUNCE_FILE)) return {};
11769
+ const raw = JSON.parse(readFileSync19(DEBOUNCE_FILE, "utf8"));
11418
11770
  const state = {};
11419
11771
  for (const [key, val] of Object.entries(raw)) {
11420
11772
  if (typeof val === "number") {
@@ -11430,8 +11782,8 @@ function readDebounceState() {
11430
11782
  }
11431
11783
  function writeDebounceState(state) {
11432
11784
  try {
11433
- if (!existsSync21(SESSION_CACHE)) mkdirSync15(SESSION_CACHE, { recursive: true });
11434
- writeFileSync14(DEBOUNCE_FILE, JSON.stringify(state));
11785
+ if (!existsSync23(SESSION_CACHE)) mkdirSync14(SESSION_CACHE, { recursive: true });
11786
+ writeFileSync15(DEBOUNCE_FILE, JSON.stringify(state));
11435
11787
  } catch {
11436
11788
  }
11437
11789
  }
@@ -11529,8 +11881,8 @@ function sendIntercom(targetSession) {
11529
11881
  try {
11530
11882
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
11531
11883
  const agent = baseAgentName(rawAgent);
11532
- const markerPath = path27.join(SESSION_CACHE, `current-task-${agent}.json`);
11533
- if (existsSync21(markerPath)) {
11884
+ const markerPath = path28.join(SESSION_CACHE, `current-task-${agent}.json`);
11885
+ if (existsSync23(markerPath)) {
11534
11886
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
11535
11887
  return "debounced";
11536
11888
  }
@@ -11539,8 +11891,8 @@ function sendIntercom(targetSession) {
11539
11891
  try {
11540
11892
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
11541
11893
  const agent = baseAgentName(rawAgent);
11542
- const taskDir = path27.join(process.cwd(), "exe", agent);
11543
- if (existsSync21(taskDir)) {
11894
+ const taskDir = path28.join(process.cwd(), "exe", agent);
11895
+ if (existsSync23(taskDir)) {
11544
11896
  const files = readdirSync6(taskDir).filter(
11545
11897
  (f) => f.endsWith(".md") && f !== "DONE.txt"
11546
11898
  );
@@ -11600,6 +11952,21 @@ function notifyParentExe(sessionKey) {
11600
11952
  }
11601
11953
  return true;
11602
11954
  }
11955
+ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
11956
+ const transport = getTransport();
11957
+ try {
11958
+ const sessions = transport.listSessions();
11959
+ if (!sessions.includes(coordinatorSession)) return false;
11960
+ execSync8(
11961
+ `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
11962
+ { timeout: 3e3 }
11963
+ );
11964
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
11965
+ return true;
11966
+ } catch {
11967
+ return false;
11968
+ }
11969
+ }
11603
11970
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
11604
11971
  if (isCoordinatorName(employeeName)) {
11605
11972
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
@@ -11673,26 +12040,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11673
12040
  const transport = getTransport();
11674
12041
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
11675
12042
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
11676
- const logDir = path27.join(os14.homedir(), ".exe-os", "session-logs");
11677
- const logFile = path27.join(logDir, `${instanceLabel}-${Date.now()}.log`);
11678
- if (!existsSync21(logDir)) {
11679
- mkdirSync15(logDir, { recursive: true });
12043
+ const logDir = path28.join(os15.homedir(), ".exe-os", "session-logs");
12044
+ const logFile = path28.join(logDir, `${instanceLabel}-${Date.now()}.log`);
12045
+ if (!existsSync23(logDir)) {
12046
+ mkdirSync14(logDir, { recursive: true });
11680
12047
  }
11681
12048
  transport.kill(sessionName);
11682
12049
  let cleanupSuffix = "";
11683
12050
  try {
11684
12051
  const thisFile = fileURLToPath4(import.meta.url);
11685
- const cleanupScript = path27.join(path27.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
11686
- if (existsSync21(cleanupScript)) {
12052
+ const cleanupScript = path28.join(path28.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
12053
+ if (existsSync23(cleanupScript)) {
11687
12054
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
11688
12055
  }
11689
12056
  } catch {
11690
12057
  }
11691
12058
  try {
11692
- const claudeJsonPath = path27.join(os14.homedir(), ".claude.json");
12059
+ const claudeJsonPath = path28.join(os15.homedir(), ".claude.json");
11693
12060
  let claudeJson = {};
11694
12061
  try {
11695
- claudeJson = JSON.parse(readFileSync18(claudeJsonPath, "utf8"));
12062
+ claudeJson = JSON.parse(readFileSync19(claudeJsonPath, "utf8"));
11696
12063
  } catch {
11697
12064
  }
11698
12065
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -11700,17 +12067,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11700
12067
  const trustDir = opts?.cwd ?? projectDir;
11701
12068
  if (!projects[trustDir]) projects[trustDir] = {};
11702
12069
  projects[trustDir].hasTrustDialogAccepted = true;
11703
- writeFileSync14(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
12070
+ writeFileSync15(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
11704
12071
  } catch {
11705
12072
  }
11706
12073
  try {
11707
- const settingsDir = path27.join(os14.homedir(), ".claude", "projects");
12074
+ const settingsDir = path28.join(os15.homedir(), ".claude", "projects");
11708
12075
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
11709
- const projSettingsDir = path27.join(settingsDir, normalizedKey);
11710
- const settingsPath = path27.join(projSettingsDir, "settings.json");
12076
+ const projSettingsDir = path28.join(settingsDir, normalizedKey);
12077
+ const settingsPath = path28.join(projSettingsDir, "settings.json");
11711
12078
  let settings = {};
11712
12079
  try {
11713
- settings = JSON.parse(readFileSync18(settingsPath, "utf8"));
12080
+ settings = JSON.parse(readFileSync19(settingsPath, "utf8"));
11714
12081
  } catch {
11715
12082
  }
11716
12083
  const perms = settings.permissions ?? {};
@@ -11738,8 +12105,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11738
12105
  if (changed) {
11739
12106
  perms.allow = allow;
11740
12107
  settings.permissions = perms;
11741
- mkdirSync15(projSettingsDir, { recursive: true });
11742
- writeFileSync14(settingsPath, JSON.stringify(settings, null, 2) + "\n");
12108
+ mkdirSync14(projSettingsDir, { recursive: true });
12109
+ writeFileSync15(settingsPath, JSON.stringify(settings, null, 2) + "\n");
11743
12110
  }
11744
12111
  } catch {
11745
12112
  }
@@ -11754,8 +12121,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11754
12121
  let behaviorsFlag = "";
11755
12122
  let legacyFallbackWarned = false;
11756
12123
  if (!useExeAgent && !useBinSymlink) {
11757
- const identityPath2 = path27.join(
11758
- os14.homedir(),
12124
+ const identityPath2 = path28.join(
12125
+ os15.homedir(),
11759
12126
  ".exe-os",
11760
12127
  "identity",
11761
12128
  `${employeeName}.md`
@@ -11764,13 +12131,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11764
12131
  const hasAgentFlag = claudeSupportsAgentFlag();
11765
12132
  if (hasAgentFlag) {
11766
12133
  identityFlag = ` --agent ${employeeName}`;
11767
- } else if (existsSync21(identityPath2)) {
12134
+ } else if (existsSync23(identityPath2)) {
11768
12135
  identityFlag = ` --append-system-prompt-file ${identityPath2}`;
11769
12136
  legacyFallbackWarned = true;
11770
12137
  }
11771
12138
  const behaviorsFile = exportBehaviorsSync(
11772
12139
  employeeName,
11773
- path27.basename(spawnCwd),
12140
+ path28.basename(spawnCwd),
11774
12141
  sessionName
11775
12142
  );
11776
12143
  if (behaviorsFile) {
@@ -11785,16 +12152,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11785
12152
  }
11786
12153
  let sessionContextFlag = "";
11787
12154
  try {
11788
- const ctxDir = path27.join(os14.homedir(), ".exe-os", "session-cache");
11789
- mkdirSync15(ctxDir, { recursive: true });
11790
- const ctxFile = path27.join(ctxDir, `session-context-${sessionName}.md`);
12155
+ const ctxDir = path28.join(os15.homedir(), ".exe-os", "session-cache");
12156
+ mkdirSync14(ctxDir, { recursive: true });
12157
+ const ctxFile = path28.join(ctxDir, `session-context-${sessionName}.md`);
11791
12158
  const ctxContent = [
11792
12159
  `## Session Context`,
11793
12160
  `You are running in tmux session: ${sessionName}.`,
11794
12161
  `Your parent coordinator session is ${exeSession}.`,
11795
12162
  `Your employees (if any) use the -${exeSession} suffix.`
11796
12163
  ].join("\n");
11797
- writeFileSync14(ctxFile, ctxContent);
12164
+ writeFileSync15(ctxFile, ctxContent);
11798
12165
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
11799
12166
  } catch {
11800
12167
  }
@@ -11871,8 +12238,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
11871
12238
  transport.pipeLog(sessionName, logFile);
11872
12239
  try {
11873
12240
  const mySession = getMySession();
11874
- const dispatchInfo = path27.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
11875
- writeFileSync14(dispatchInfo, JSON.stringify({
12241
+ const dispatchInfo = path28.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
12242
+ writeFileSync15(dispatchInfo, JSON.stringify({
11876
12243
  dispatchedBy: mySession,
11877
12244
  rootExe: exeSession,
11878
12245
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -11946,15 +12313,15 @@ var init_tmux_routing = __esm({
11946
12313
  init_intercom_queue();
11947
12314
  init_plan_limits();
11948
12315
  init_employees();
11949
- SPAWN_LOCK_DIR = path27.join(os14.homedir(), ".exe-os", "spawn-locks");
11950
- SESSION_CACHE = path27.join(os14.homedir(), ".exe-os", "session-cache");
12316
+ SPAWN_LOCK_DIR = path28.join(os15.homedir(), ".exe-os", "spawn-locks");
12317
+ SESSION_CACHE = path28.join(os15.homedir(), ".exe-os", "session-cache");
11951
12318
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
11952
12319
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
11953
12320
  VERIFY_PANE_LINES = 200;
11954
12321
  INTERCOM_DEBOUNCE_MS = 3e4;
11955
12322
  CODEX_DEBOUNCE_MS = 12e4;
11956
- INTERCOM_LOG2 = path27.join(os14.homedir(), ".exe-os", "intercom.log");
11957
- DEBOUNCE_FILE = path27.join(SESSION_CACHE, "intercom-debounce.json");
12323
+ INTERCOM_LOG2 = path28.join(os15.homedir(), ".exe-os", "intercom.log");
12324
+ DEBOUNCE_FILE = path28.join(SESSION_CACHE, "intercom-debounce.json");
11958
12325
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
11959
12326
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
11960
12327
  }
@@ -11977,10 +12344,10 @@ __export(messaging_exports, {
11977
12344
  sendMessage: () => sendMessage,
11978
12345
  setWsClientSend: () => setWsClientSend
11979
12346
  });
11980
- import crypto10 from "crypto";
12347
+ import crypto11 from "crypto";
11981
12348
  function generateUlid() {
11982
12349
  const timestamp = Date.now().toString(36).padStart(10, "0");
11983
- const random = crypto10.randomBytes(10).toString("hex").slice(0, 16);
12350
+ const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
11984
12351
  return (timestamp + random).toUpperCase();
11985
12352
  }
11986
12353
  function rowToMessage(row) {
@@ -11991,6 +12358,7 @@ function rowToMessage(row) {
11991
12358
  targetAgent: row.target_agent,
11992
12359
  targetProject: row.target_project ?? null,
11993
12360
  targetDevice: row.target_device,
12361
+ sessionScope: row.session_scope ?? null,
11994
12362
  content: row.content,
11995
12363
  priority: row.priority ?? "normal",
11996
12364
  status: row.status ?? "pending",
@@ -12008,15 +12376,17 @@ async function sendMessage(input) {
12008
12376
  const id = generateUlid();
12009
12377
  const now = (/* @__PURE__ */ new Date()).toISOString();
12010
12378
  const targetDevice = input.targetDevice ?? "local";
12379
+ const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
12011
12380
  await client.execute({
12012
- sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
12013
- VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
12381
+ sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
12382
+ VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
12014
12383
  args: [
12015
12384
  id,
12016
12385
  input.fromAgent,
12017
12386
  input.targetAgent,
12018
12387
  input.targetProject ?? null,
12019
12388
  targetDevice,
12389
+ sessionScope,
12020
12390
  input.content,
12021
12391
  input.priority ?? "normal",
12022
12392
  now
@@ -12030,9 +12400,10 @@ async function sendMessage(input) {
12030
12400
  }
12031
12401
  } catch {
12032
12402
  }
12403
+ const sentScope = strictSessionScopeFilter(sessionScope);
12033
12404
  const result = await client.execute({
12034
- sql: "SELECT * FROM messages WHERE id = ?",
12035
- args: [id]
12405
+ sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
12406
+ args: [id, ...sentScope.args]
12036
12407
  });
12037
12408
  return rowToMessage(result.rows[0]);
12038
12409
  }
@@ -12056,6 +12427,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
12056
12427
  fromAgent: msg.fromAgent,
12057
12428
  targetAgent: msg.targetAgent,
12058
12429
  targetProject: msg.targetProject,
12430
+ sessionScope: msg.sessionScope,
12059
12431
  content: msg.content,
12060
12432
  priority: msg.priority,
12061
12433
  createdAt: msg.createdAt
@@ -12099,7 +12471,7 @@ async function deliverLocalMessage(messageId) {
12099
12471
  } catch {
12100
12472
  const newRetryCount = msg.retryCount + 1;
12101
12473
  if (newRetryCount >= MAX_RETRIES3) {
12102
- await markFailed(messageId, "session unavailable after 10 retries");
12474
+ await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
12103
12475
  } else {
12104
12476
  await client.execute({
12105
12477
  sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
@@ -12109,85 +12481,101 @@ async function deliverLocalMessage(messageId) {
12109
12481
  return false;
12110
12482
  }
12111
12483
  }
12112
- async function getPendingMessages(targetAgent) {
12484
+ async function getPendingMessages(targetAgent, sessionScope) {
12113
12485
  const client = getClient();
12486
+ const scope = strictSessionScopeFilter(sessionScope);
12114
12487
  const result = await client.execute({
12115
12488
  sql: `SELECT * FROM messages
12116
- WHERE target_agent = ? AND status IN ('pending', 'delivered')
12489
+ WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
12117
12490
  ORDER BY id`,
12118
- args: [targetAgent]
12491
+ args: [targetAgent, ...scope.args]
12119
12492
  });
12120
12493
  return result.rows.map((row) => rowToMessage(row));
12121
12494
  }
12122
- async function markRead(messageId) {
12495
+ async function markRead(messageId, sessionScope) {
12123
12496
  const client = getClient();
12497
+ const scope = strictSessionScopeFilter(sessionScope);
12124
12498
  await client.execute({
12125
- sql: "UPDATE messages SET status = 'read' WHERE id = ? AND status IN ('pending', 'delivered')",
12126
- args: [messageId]
12499
+ sql: `UPDATE messages SET status = 'read'
12500
+ WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
12501
+ args: [messageId, ...scope.args]
12127
12502
  });
12128
12503
  }
12129
- async function markAcknowledged(messageId) {
12504
+ async function markAcknowledged(messageId, sessionScope) {
12130
12505
  const client = getClient();
12506
+ const scope = strictSessionScopeFilter(sessionScope);
12131
12507
  await client.execute({
12132
- sql: "UPDATE messages SET status = 'acknowledged', processed_at = ? WHERE id = ? AND status = 'read'",
12133
- args: [(/* @__PURE__ */ new Date()).toISOString(), messageId]
12508
+ sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
12509
+ WHERE id = ? AND status = 'read'${scope.sql}`,
12510
+ args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
12134
12511
  });
12135
12512
  }
12136
- async function markProcessed(messageId) {
12513
+ async function markProcessed(messageId, sessionScope) {
12137
12514
  const client = getClient();
12515
+ const scope = strictSessionScopeFilter(sessionScope);
12138
12516
  await client.execute({
12139
- sql: "UPDATE messages SET status = 'processed', processed_at = ? WHERE id = ?",
12140
- args: [(/* @__PURE__ */ new Date()).toISOString(), messageId]
12517
+ sql: `UPDATE messages SET status = 'processed', processed_at = ?
12518
+ WHERE id = ?${scope.sql}`,
12519
+ args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
12141
12520
  });
12142
12521
  }
12143
- async function getMessageStatus(messageId) {
12522
+ async function getMessageStatus(messageId, sessionScope) {
12144
12523
  const client = getClient();
12524
+ const scope = strictSessionScopeFilter(sessionScope);
12145
12525
  const result = await client.execute({
12146
- sql: "SELECT status FROM messages WHERE id = ?",
12147
- args: [messageId]
12526
+ sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
12527
+ args: [messageId, ...scope.args]
12148
12528
  });
12149
12529
  return result.rows[0]?.status ?? null;
12150
12530
  }
12151
- async function getUnacknowledgedMessages(targetAgent) {
12531
+ async function getUnacknowledgedMessages(targetAgent, sessionScope) {
12152
12532
  const client = getClient();
12533
+ const scope = strictSessionScopeFilter(sessionScope);
12153
12534
  const result = await client.execute({
12154
12535
  sql: `SELECT * FROM messages
12155
- WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
12536
+ WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
12156
12537
  ORDER BY id`,
12157
- args: [targetAgent]
12538
+ args: [targetAgent, ...scope.args]
12158
12539
  });
12159
12540
  return result.rows.map((row) => rowToMessage(row));
12160
12541
  }
12161
- async function getReadMessages(targetAgent) {
12542
+ async function getReadMessages(targetAgent, sessionScope) {
12162
12543
  const client = getClient();
12544
+ const scope = strictSessionScopeFilter(sessionScope);
12163
12545
  const result = await client.execute({
12164
- sql: "SELECT * FROM messages WHERE target_agent = ? AND status = 'read' ORDER BY id",
12165
- args: [targetAgent]
12546
+ sql: `SELECT * FROM messages
12547
+ WHERE target_agent = ? AND status = 'read'${scope.sql}
12548
+ ORDER BY id`,
12549
+ args: [targetAgent, ...scope.args]
12166
12550
  });
12167
12551
  return result.rows.map((row) => rowToMessage(row));
12168
12552
  }
12169
- async function markFailed(messageId, reason) {
12553
+ async function markFailed(messageId, reason, sessionScope) {
12170
12554
  const client = getClient();
12555
+ const scope = strictSessionScopeFilter(sessionScope);
12171
12556
  await client.execute({
12172
- sql: "UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ? WHERE id = ?",
12173
- args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId]
12557
+ sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
12558
+ WHERE id = ?${scope.sql}`,
12559
+ args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
12174
12560
  });
12175
12561
  }
12176
- async function getFailedMessages() {
12562
+ async function getFailedMessages(sessionScope) {
12177
12563
  const client = getClient();
12564
+ const scope = strictSessionScopeFilter(sessionScope);
12178
12565
  const result = await client.execute({
12179
- sql: "SELECT * FROM messages WHERE status = 'failed' ORDER BY created_at DESC",
12180
- args: []
12566
+ sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
12567
+ args: [...scope.args]
12181
12568
  });
12182
12569
  return result.rows.map((row) => rowToMessage(row));
12183
12570
  }
12184
- async function retryPendingMessages() {
12571
+ async function retryPendingMessages(sessionScope) {
12185
12572
  const client = getClient();
12573
+ const scope = strictSessionScopeFilter(sessionScope);
12186
12574
  const result = await client.execute({
12187
12575
  sql: `SELECT * FROM messages
12188
- WHERE status = 'pending' AND retry_count < ?
12576
+ WHERE status = 'pending' AND retry_count < ?${scope.sql}
12189
12577
  ORDER BY id`,
12190
- args: [MAX_RETRIES3]
12578
+ args: [MAX_RETRIES3, ...scope.args]
12191
12579
  });
12192
12580
  let delivered = 0;
12193
12581
  for (const row of result.rows) {
@@ -12206,6 +12594,7 @@ var init_messaging = __esm({
12206
12594
  "use strict";
12207
12595
  init_database();
12208
12596
  init_tmux_routing();
12597
+ init_task_scope();
12209
12598
  MAX_RETRIES3 = 10;
12210
12599
  _wsClientSend = null;
12211
12600
  }
@@ -12221,9 +12610,9 @@ __export(active_agent_exports, {
12221
12610
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
12222
12611
  writeActiveAgent: () => writeActiveAgent
12223
12612
  });
12224
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16, unlinkSync as unlinkSync9, readdirSync as readdirSync7 } from "fs";
12613
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9, readdirSync as readdirSync7 } from "fs";
12225
12614
  import { execSync as execSync9 } from "child_process";
12226
- import path28 from "path";
12615
+ import path29 from "path";
12227
12616
  function isNameWithOptionalInstance(candidate, baseName) {
12228
12617
  if (candidate === baseName) return true;
12229
12618
  if (!candidate.startsWith(baseName)) return false;
@@ -12267,12 +12656,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
12267
12656
  return null;
12268
12657
  }
12269
12658
  function getMarkerPath() {
12270
- return path28.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
12659
+ return path29.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
12271
12660
  }
12272
12661
  function writeActiveAgent(agentId, agentRole) {
12273
12662
  try {
12274
- mkdirSync16(CACHE_DIR, { recursive: true });
12275
- writeFileSync15(
12663
+ mkdirSync15(CACHE_DIR, { recursive: true });
12664
+ writeFileSync16(
12276
12665
  getMarkerPath(),
12277
12666
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
12278
12667
  );
@@ -12288,7 +12677,7 @@ function clearActiveAgent() {
12288
12677
  function getActiveAgent() {
12289
12678
  try {
12290
12679
  const markerPath = getMarkerPath();
12291
- const raw = readFileSync19(markerPath, "utf8");
12680
+ const raw = readFileSync20(markerPath, "utf8");
12292
12681
  const data = JSON.parse(raw);
12293
12682
  if (data.agentId) {
12294
12683
  if (data.startedAt) {
@@ -12336,14 +12725,14 @@ function getAllActiveAgents() {
12336
12725
  const key = file.slice("active-agent-".length, -".json".length);
12337
12726
  if (key === "undefined") continue;
12338
12727
  try {
12339
- const raw = readFileSync19(path28.join(CACHE_DIR, file), "utf8");
12728
+ const raw = readFileSync20(path29.join(CACHE_DIR, file), "utf8");
12340
12729
  const data = JSON.parse(raw);
12341
12730
  if (!data.agentId) continue;
12342
12731
  if (data.startedAt) {
12343
12732
  const age = Date.now() - new Date(data.startedAt).getTime();
12344
12733
  if (age > STALE_MS) {
12345
12734
  try {
12346
- unlinkSync9(path28.join(CACHE_DIR, file));
12735
+ unlinkSync9(path29.join(CACHE_DIR, file));
12347
12736
  } catch {
12348
12737
  }
12349
12738
  continue;
@@ -12366,11 +12755,11 @@ function getAllActiveAgents() {
12366
12755
  function cleanupSessionMarkers() {
12367
12756
  const key = getSessionKey();
12368
12757
  try {
12369
- unlinkSync9(path28.join(CACHE_DIR, `active-agent-${key}.json`));
12758
+ unlinkSync9(path29.join(CACHE_DIR, `active-agent-${key}.json`));
12370
12759
  } catch {
12371
12760
  }
12372
12761
  try {
12373
- unlinkSync9(path28.join(CACHE_DIR, "active-agent-undefined.json"));
12762
+ unlinkSync9(path29.join(CACHE_DIR, "active-agent-undefined.json"));
12374
12763
  } catch {
12375
12764
  }
12376
12765
  }
@@ -12381,7 +12770,7 @@ var init_active_agent = __esm({
12381
12770
  init_config();
12382
12771
  init_session_key();
12383
12772
  init_employees();
12384
- CACHE_DIR = path28.join(EXE_AI_DIR, "session-cache");
12773
+ CACHE_DIR = path29.join(EXE_AI_DIR, "session-cache");
12385
12774
  STALE_MS = 24 * 60 * 60 * 1e3;
12386
12775
  }
12387
12776
  });
@@ -13009,14 +13398,14 @@ __export(exe_rename_exports, {
13009
13398
  main: () => main2,
13010
13399
  renameEmployee: () => renameEmployee
13011
13400
  });
13012
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, renameSync as renameSync4, unlinkSync as unlinkSync10, existsSync as existsSync22 } from "fs";
13401
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as renameSync4, unlinkSync as unlinkSync10, existsSync as existsSync24 } from "fs";
13013
13402
  import { execSync as execSync10 } from "child_process";
13014
- import path29 from "path";
13403
+ import path30 from "path";
13015
13404
  import { homedir as homedir4 } from "os";
13016
13405
  async function renameEmployee(oldName, newName, opts = {}) {
13017
- const rosterPath = opts.rosterPath ?? path29.join(homedir4(), ".exe-os", "exe-employees.json");
13018
- const identityDir = opts.identityDir ?? path29.join(homedir4(), ".exe-os", "identity");
13019
- const agentsDir = opts.agentsDir ?? path29.join(homedir4(), ".claude", "agents");
13406
+ const rosterPath = opts.rosterPath ?? path30.join(homedir4(), ".exe-os", "exe-employees.json");
13407
+ const identityDir = opts.identityDir ?? path30.join(homedir4(), ".exe-os", "identity");
13408
+ const agentsDir = opts.agentsDir ?? path30.join(homedir4(), ".claude", "agents");
13020
13409
  const validation = validateEmployeeName(newName);
13021
13410
  if (!validation.valid) {
13022
13411
  return { success: false, error: validation.error };
@@ -13045,40 +13434,40 @@ async function renameEmployee(oldName, newName, opts = {}) {
13045
13434
  undo: () => {
13046
13435
  employee.name = originalName;
13047
13436
  employee.systemPrompt = originalPrompt;
13048
- writeFileSync16(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
13437
+ writeFileSync17(rosterPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
13049
13438
  }
13050
13439
  });
13051
- const oldIdentityPath = path29.join(identityDir, `${rosterOldName}.md`);
13052
- const newIdentityPath = path29.join(identityDir, `${newName}.md`);
13053
- if (existsSync22(oldIdentityPath)) {
13054
- const content = readFileSync20(oldIdentityPath, "utf-8");
13440
+ const oldIdentityPath = path30.join(identityDir, `${rosterOldName}.md`);
13441
+ const newIdentityPath = path30.join(identityDir, `${newName}.md`);
13442
+ if (existsSync24(oldIdentityPath)) {
13443
+ const content = readFileSync21(oldIdentityPath, "utf-8");
13055
13444
  const updatedContent = content.replace(
13056
13445
  /^(agent_id:\s*)\S+/m,
13057
13446
  `$1${newName}`
13058
13447
  );
13059
13448
  renameSync4(oldIdentityPath, newIdentityPath);
13060
- writeFileSync16(newIdentityPath, updatedContent, "utf-8");
13449
+ writeFileSync17(newIdentityPath, updatedContent, "utf-8");
13061
13450
  rollbackStack.push({
13062
13451
  description: "restore identity file",
13063
13452
  undo: () => {
13064
- if (existsSync22(newIdentityPath)) {
13065
- writeFileSync16(newIdentityPath, content, "utf-8");
13453
+ if (existsSync24(newIdentityPath)) {
13454
+ writeFileSync17(newIdentityPath, content, "utf-8");
13066
13455
  renameSync4(newIdentityPath, oldIdentityPath);
13067
13456
  }
13068
13457
  }
13069
13458
  });
13070
13459
  }
13071
- const oldAgentPath = path29.join(agentsDir, `${rosterOldName}.md`);
13072
- const newAgentPath = path29.join(agentsDir, `${newName}.md`);
13073
- if (existsSync22(oldAgentPath)) {
13074
- const agentContent = readFileSync20(oldAgentPath, "utf-8");
13460
+ const oldAgentPath = path30.join(agentsDir, `${rosterOldName}.md`);
13461
+ const newAgentPath = path30.join(agentsDir, `${newName}.md`);
13462
+ if (existsSync24(oldAgentPath)) {
13463
+ const agentContent = readFileSync21(oldAgentPath, "utf-8");
13075
13464
  renameSync4(oldAgentPath, newAgentPath);
13076
13465
  rollbackStack.push({
13077
13466
  description: "restore agent file",
13078
13467
  undo: () => {
13079
- if (existsSync22(newAgentPath)) {
13468
+ if (existsSync24(newAgentPath)) {
13080
13469
  renameSync4(newAgentPath, oldAgentPath);
13081
- writeFileSync16(oldAgentPath, agentContent, "utf-8");
13470
+ writeFileSync17(oldAgentPath, agentContent, "utf-8");
13082
13471
  }
13083
13472
  }
13084
13473
  });
@@ -13156,10 +13545,10 @@ function removeOldSymlinks(name) {
13156
13545
  try {
13157
13546
  const exeBinPath = findExeBin2();
13158
13547
  if (!exeBinPath) return;
13159
- const binDir = path29.dirname(exeBinPath);
13548
+ const binDir = path30.dirname(exeBinPath);
13160
13549
  for (const suffix of ["", "-opencode"]) {
13161
- const linkPath = path29.join(binDir, `${name}${suffix}`);
13162
- if (existsSync22(linkPath)) {
13550
+ const linkPath = path30.join(binDir, `${name}${suffix}`);
13551
+ if (existsSync24(linkPath)) {
13163
13552
  try {
13164
13553
  unlinkSync10(linkPath);
13165
13554
  } catch {
@@ -13204,16 +13593,16 @@ var init_exe_rename = __esm({
13204
13593
  });
13205
13594
 
13206
13595
  // src/lib/model-downloader.ts
13207
- import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync23, unlinkSync as unlinkSync11, renameSync as renameSync5 } from "fs";
13596
+ import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync25, unlinkSync as unlinkSync11, renameSync as renameSync5 } from "fs";
13208
13597
  import { mkdir as mkdir6 } from "fs/promises";
13209
13598
  import { createHash as createHash3 } from "crypto";
13210
- import path30 from "path";
13599
+ import path31 from "path";
13211
13600
  async function downloadModel(opts) {
13212
13601
  const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
13213
- const destPath = path30.join(destDir, LOCAL_FILENAME);
13602
+ const destPath = path31.join(destDir, LOCAL_FILENAME);
13214
13603
  const tmpPath = destPath + ".tmp";
13215
13604
  await mkdir6(destDir, { recursive: true });
13216
- if (existsSync23(destPath)) {
13605
+ if (existsSync25(destPath)) {
13217
13606
  const hash = await fileHash(destPath);
13218
13607
  if (hash === EXPECTED_SHA256) {
13219
13608
  return destPath;
@@ -13225,7 +13614,7 @@ async function downloadModel(opts) {
13225
13614
  let downloaded = 0;
13226
13615
  for (let attempt = 1; attempt <= MAX_RETRIES4; attempt++) {
13227
13616
  try {
13228
- if (existsSync23(tmpPath)) unlinkSync11(tmpPath);
13617
+ if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
13229
13618
  const response = await fetchFn(GGUF_URL, {
13230
13619
  redirect: "follow",
13231
13620
  signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
@@ -13270,7 +13659,7 @@ async function downloadModel(opts) {
13270
13659
  process.stderr.write(`
13271
13660
  Download attempt ${attempt} failed, retrying...
13272
13661
  `);
13273
- if (existsSync23(tmpPath)) unlinkSync11(tmpPath);
13662
+ if (existsSync25(tmpPath)) unlinkSync11(tmpPath);
13274
13663
  }
13275
13664
  }
13276
13665
  }
@@ -13333,10 +13722,10 @@ async function disposeEmbedder() {
13333
13722
  async function embedDirect(text) {
13334
13723
  const llamaCpp = await import("node-llama-cpp");
13335
13724
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
13336
- const { existsSync: existsSync30 } = await import("fs");
13337
- const path45 = await import("path");
13338
- const modelPath = path45.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
13339
- if (!existsSync30(modelPath)) {
13725
+ const { existsSync: existsSync32 } = await import("fs");
13726
+ const path46 = await import("path");
13727
+ const modelPath = path46.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
13728
+ if (!existsSync32(modelPath)) {
13340
13729
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
13341
13730
  }
13342
13731
  const llama = await llamaCpp.getLlama();
@@ -13911,36 +14300,36 @@ __export(session_wrappers_exports, {
13911
14300
  generateSessionWrappers: () => generateSessionWrappers
13912
14301
  });
13913
14302
  import {
13914
- existsSync as existsSync24,
13915
- readFileSync as readFileSync21,
13916
- writeFileSync as writeFileSync17,
13917
- mkdirSync as mkdirSync17,
13918
- chmodSync,
14303
+ existsSync as existsSync26,
14304
+ readFileSync as readFileSync22,
14305
+ writeFileSync as writeFileSync18,
14306
+ mkdirSync as mkdirSync16,
14307
+ chmodSync as chmodSync2,
13919
14308
  readdirSync as readdirSync8,
13920
14309
  unlinkSync as unlinkSync12
13921
14310
  } from "fs";
13922
- import path31 from "path";
14311
+ import path32 from "path";
13923
14312
  import { homedir as homedir5 } from "os";
13924
14313
  function generateSessionWrappers(packageRoot, homeDir) {
13925
14314
  const home = homeDir ?? homedir5();
13926
- const binDir = path31.join(home, ".exe-os", "bin");
13927
- const rosterPath = path31.join(home, ".exe-os", "exe-employees.json");
13928
- mkdirSync17(binDir, { recursive: true });
13929
- const exeStartDst = path31.join(binDir, "exe-start");
14315
+ const binDir = path32.join(home, ".exe-os", "bin");
14316
+ const rosterPath = path32.join(home, ".exe-os", "exe-employees.json");
14317
+ mkdirSync16(binDir, { recursive: true });
14318
+ const exeStartDst = path32.join(binDir, "exe-start");
13930
14319
  const candidates = [
13931
- path31.join(packageRoot, "dist", "bin", "exe-start.sh"),
13932
- path31.join(packageRoot, "src", "bin", "exe-start.sh")
14320
+ path32.join(packageRoot, "dist", "bin", "exe-start.sh"),
14321
+ path32.join(packageRoot, "src", "bin", "exe-start.sh")
13933
14322
  ];
13934
14323
  for (const src of candidates) {
13935
- if (existsSync24(src)) {
13936
- writeFileSync17(exeStartDst, readFileSync21(src));
13937
- chmodSync(exeStartDst, 493);
14324
+ if (existsSync26(src)) {
14325
+ writeFileSync18(exeStartDst, readFileSync22(src));
14326
+ chmodSync2(exeStartDst, 493);
13938
14327
  break;
13939
14328
  }
13940
14329
  }
13941
14330
  let employees = [];
13942
14331
  try {
13943
- employees = JSON.parse(readFileSync21(rosterPath, "utf8"));
14332
+ employees = JSON.parse(readFileSync22(rosterPath, "utf8"));
13944
14333
  } catch {
13945
14334
  return { created: 0, pathConfigured: false };
13946
14335
  }
@@ -13950,9 +14339,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
13950
14339
  try {
13951
14340
  for (const f of readdirSync8(binDir)) {
13952
14341
  if (f === "exe-start") continue;
13953
- const fPath = path31.join(binDir, f);
14342
+ const fPath = path32.join(binDir, f);
13954
14343
  try {
13955
- const content = readFileSync21(fPath, "utf8");
14344
+ const content = readFileSync22(fPath, "utf8");
13956
14345
  if (content.includes("exe-start")) {
13957
14346
  unlinkSync12(fPath);
13958
14347
  }
@@ -13967,31 +14356,31 @@ exec "${exeStartDst}" "$0" "$@"
13967
14356
  `;
13968
14357
  for (const emp of employees) {
13969
14358
  for (let n = 1; n <= MAX_N; n++) {
13970
- const wrapperPath = path31.join(binDir, `${emp.name}${n}`);
13971
- writeFileSync17(wrapperPath, wrapperContent);
13972
- chmodSync(wrapperPath, 493);
14359
+ const wrapperPath = path32.join(binDir, `${emp.name}${n}`);
14360
+ writeFileSync18(wrapperPath, wrapperContent);
14361
+ chmodSync2(wrapperPath, 493);
13973
14362
  created++;
13974
14363
  }
13975
14364
  }
13976
14365
  const codexLauncherCandidates = [
13977
- path31.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
13978
- path31.join(packageRoot, "src", "bin", "exe-start-codex.ts")
14366
+ path32.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
14367
+ path32.join(packageRoot, "src", "bin", "exe-start-codex.ts")
13979
14368
  ];
13980
14369
  let codexLauncher = null;
13981
14370
  for (const c of codexLauncherCandidates) {
13982
- if (existsSync24(c)) {
14371
+ if (existsSync26(c)) {
13983
14372
  codexLauncher = c;
13984
14373
  break;
13985
14374
  }
13986
14375
  }
13987
14376
  if (codexLauncher) {
13988
14377
  for (const emp of employees) {
13989
- const wrapperPath = path31.join(binDir, `${emp.name}-codex`);
14378
+ const wrapperPath = path32.join(binDir, `${emp.name}-codex`);
13990
14379
  const content = `#!/bin/bash
13991
14380
  exec node "${codexLauncher}" --agent ${emp.name} "$@"
13992
14381
  `;
13993
- writeFileSync17(wrapperPath, content);
13994
- chmodSync(wrapperPath, 493);
14382
+ writeFileSync18(wrapperPath, content);
14383
+ chmodSync2(wrapperPath, 493);
13995
14384
  created++;
13996
14385
  }
13997
14386
  }
@@ -14009,24 +14398,24 @@ export PATH="${binDir}:$PATH"
14009
14398
  const shell = process.env.SHELL ?? "/bin/bash";
14010
14399
  const profilePaths = [];
14011
14400
  if (shell.includes("zsh")) {
14012
- profilePaths.push(path31.join(home, ".zshrc"));
14401
+ profilePaths.push(path32.join(home, ".zshrc"));
14013
14402
  } else if (shell.includes("bash")) {
14014
- profilePaths.push(path31.join(home, ".bashrc"));
14015
- profilePaths.push(path31.join(home, ".bash_profile"));
14403
+ profilePaths.push(path32.join(home, ".bashrc"));
14404
+ profilePaths.push(path32.join(home, ".bash_profile"));
14016
14405
  } else {
14017
- profilePaths.push(path31.join(home, ".profile"));
14406
+ profilePaths.push(path32.join(home, ".profile"));
14018
14407
  }
14019
14408
  for (const profilePath of profilePaths) {
14020
14409
  try {
14021
14410
  let content = "";
14022
14411
  try {
14023
- content = readFileSync21(profilePath, "utf8");
14412
+ content = readFileSync22(profilePath, "utf8");
14024
14413
  } catch {
14025
14414
  }
14026
14415
  if (content.includes(".exe-os/bin")) {
14027
14416
  return false;
14028
14417
  }
14029
- writeFileSync17(profilePath, content + exportLine);
14418
+ writeFileSync18(profilePath, content + exportLine);
14030
14419
  return true;
14031
14420
  } catch {
14032
14421
  continue;
@@ -14048,37 +14437,37 @@ __export(setup_wizard_exports, {
14048
14437
  runSetupWizard: () => runSetupWizard,
14049
14438
  validateModel: () => validateModel
14050
14439
  });
14051
- import crypto11 from "crypto";
14052
- import { existsSync as existsSync25, mkdirSync as mkdirSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync18, unlinkSync as unlinkSync13 } from "fs";
14053
- import os15 from "os";
14054
- import path32 from "path";
14440
+ import crypto12 from "crypto";
14441
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync19, unlinkSync as unlinkSync13 } from "fs";
14442
+ import os16 from "os";
14443
+ import path33 from "path";
14055
14444
  import { createInterface as createInterface3 } from "readline";
14056
14445
  function findPackageRoot2() {
14057
- let dir = path32.dirname(new URL(import.meta.url).pathname);
14058
- const root = path32.parse(dir).root;
14446
+ let dir = path33.dirname(new URL(import.meta.url).pathname);
14447
+ const root = path33.parse(dir).root;
14059
14448
  while (dir !== root) {
14060
- const pkgPath = path32.join(dir, "package.json");
14061
- if (existsSync25(pkgPath)) {
14449
+ const pkgPath = path33.join(dir, "package.json");
14450
+ if (existsSync27(pkgPath)) {
14062
14451
  try {
14063
- const pkg = JSON.parse(readFileSync22(pkgPath, "utf-8"));
14452
+ const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
14064
14453
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
14065
14454
  } catch {
14066
14455
  }
14067
14456
  }
14068
- dir = path32.dirname(dir);
14457
+ dir = path33.dirname(dir);
14069
14458
  }
14070
14459
  return null;
14071
14460
  }
14072
14461
  function loadSetupState() {
14073
14462
  try {
14074
- return JSON.parse(readFileSync22(SETUP_STATE_PATH, "utf8"));
14463
+ return JSON.parse(readFileSync23(SETUP_STATE_PATH, "utf8"));
14075
14464
  } catch {
14076
14465
  return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
14077
14466
  }
14078
14467
  }
14079
14468
  function saveSetupState(state) {
14080
- mkdirSync18(path32.dirname(SETUP_STATE_PATH), { recursive: true });
14081
- writeFileSync18(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
14469
+ mkdirSync17(path33.dirname(SETUP_STATE_PATH), { recursive: true });
14470
+ writeFileSync19(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
14082
14471
  }
14083
14472
  function clearSetupState() {
14084
14473
  try {
@@ -14097,10 +14486,10 @@ function ask2(rl, prompt) {
14097
14486
  });
14098
14487
  }
14099
14488
  function getAvailableMemoryGB() {
14100
- return os15.freemem() / (1024 * 1024 * 1024);
14489
+ return os16.freemem() / (1024 * 1024 * 1024);
14101
14490
  }
14102
14491
  function getTotalMemoryGB() {
14103
- return os15.totalmem() / (1024 * 1024 * 1024);
14492
+ return os16.totalmem() / (1024 * 1024 * 1024);
14104
14493
  }
14105
14494
  function isLowMemory() {
14106
14495
  return getAvailableMemoryGB() < 2;
@@ -14111,8 +14500,8 @@ async function validateModel(log) {
14111
14500
  if (totalGB <= 8 || isLowMemory()) {
14112
14501
  log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
14113
14502
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
14114
- const modelPath = path32.join(MODELS_DIR, LOCAL_FILENAME);
14115
- if (existsSync25(modelPath)) {
14503
+ const modelPath = path33.join(MODELS_DIR, LOCAL_FILENAME);
14504
+ if (existsSync27(modelPath)) {
14116
14505
  const { statSync: statSync2 } = await import("fs");
14117
14506
  const size = statSync2(modelPath).size;
14118
14507
  if (size > 300 * 1e6) {
@@ -14203,7 +14592,7 @@ async function runSetupWizard(opts = {}) {
14203
14592
  if (state.completedSteps.length > 0) {
14204
14593
  log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
14205
14594
  }
14206
- if (existsSync25(LEGACY_LANCE_PATH)) {
14595
+ if (existsSync27(LEGACY_LANCE_PATH)) {
14207
14596
  log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
14208
14597
  log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
14209
14598
  log(" The old directory will not be modified or deleted.");
@@ -14215,7 +14604,7 @@ async function runSetupWizard(opts = {}) {
14215
14604
  log("Encryption key already exists \u2014 skipping generation.");
14216
14605
  } else {
14217
14606
  log("Generating 256-bit encryption key...");
14218
- const key = crypto11.randomBytes(32);
14607
+ const key = crypto12.randomBytes(32);
14219
14608
  await setMasterKey(key);
14220
14609
  log("Encryption key generated and stored securely.");
14221
14610
  }
@@ -14367,19 +14756,19 @@ async function runSetupWizard(opts = {}) {
14367
14756
  await saveConfig(config);
14368
14757
  log("");
14369
14758
  try {
14370
- const claudeJsonPath = path32.join(os15.homedir(), ".claude.json");
14759
+ const claudeJsonPath = path33.join(os16.homedir(), ".claude.json");
14371
14760
  let claudeJson = {};
14372
14761
  try {
14373
- claudeJson = JSON.parse(readFileSync22(claudeJsonPath, "utf8"));
14762
+ claudeJson = JSON.parse(readFileSync23(claudeJsonPath, "utf8"));
14374
14763
  } catch {
14375
14764
  }
14376
14765
  if (!claudeJson.projects) claudeJson.projects = {};
14377
14766
  const projects = claudeJson.projects;
14378
- for (const dir of [process.cwd(), os15.homedir()]) {
14767
+ for (const dir of [process.cwd(), os16.homedir()]) {
14379
14768
  if (!projects[dir]) projects[dir] = {};
14380
14769
  projects[dir].hasTrustDialogAccepted = true;
14381
14770
  }
14382
- writeFileSync18(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
14771
+ writeFileSync19(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
14383
14772
  } catch {
14384
14773
  }
14385
14774
  state.completedSteps.push(5);
@@ -14393,7 +14782,7 @@ async function runSetupWizard(opts = {}) {
14393
14782
  const prefs = { ...existingPrefs };
14394
14783
  log("=== Config Defaults ===");
14395
14784
  log("");
14396
- const ghosttyDetected = existsSync25(path32.join(os15.homedir(), ".config", "ghostty")) || existsSync25(path32.join(os15.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
14785
+ const ghosttyDetected = existsSync27(path33.join(os16.homedir(), ".config", "ghostty")) || existsSync27(path33.join(os16.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
14397
14786
  if (ghosttyDetected) {
14398
14787
  const ghosttyAnswer = await ask2(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
14399
14788
  prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
@@ -14440,7 +14829,7 @@ async function runSetupWizard(opts = {}) {
14440
14829
  let missingIdentities = [];
14441
14830
  for (const emp of roster) {
14442
14831
  const idPath = identityPath2(emp.name);
14443
- if (!existsSync25(idPath)) {
14832
+ if (!existsSync27(idPath)) {
14444
14833
  missingIdentities.push(emp.name);
14445
14834
  }
14446
14835
  }
@@ -14472,7 +14861,7 @@ async function runSetupWizard(opts = {}) {
14472
14861
  }
14473
14862
  missingIdentities = [];
14474
14863
  for (const emp of roster) {
14475
- if (!existsSync25(identityPath2(emp.name))) {
14864
+ if (!existsSync27(identityPath2(emp.name))) {
14476
14865
  missingIdentities.push(emp.name);
14477
14866
  }
14478
14867
  }
@@ -14537,9 +14926,9 @@ async function runSetupWizard(opts = {}) {
14537
14926
  const cooIdentityContent = getIdentityTemplate("coo");
14538
14927
  if (cooIdentityContent) {
14539
14928
  const cooIdPath = identityPath2(cooName);
14540
- mkdirSync18(path32.dirname(cooIdPath), { recursive: true });
14929
+ mkdirSync17(path33.dirname(cooIdPath), { recursive: true });
14541
14930
  const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
14542
- writeFileSync18(cooIdPath, replaced, "utf-8");
14931
+ writeFileSync19(cooIdPath, replaced, "utf-8");
14543
14932
  }
14544
14933
  registerBinSymlinks2(cooName);
14545
14934
  createdEmployees.push({ name: cooName, role: "COO" });
@@ -14633,9 +15022,9 @@ async function runSetupWizard(opts = {}) {
14633
15022
  const ctoIdentityContent = getIdentityTemplate("cto");
14634
15023
  if (ctoIdentityContent) {
14635
15024
  const ctoIdPath = identityPath2(ctoName);
14636
- mkdirSync18(path32.dirname(ctoIdPath), { recursive: true });
15025
+ mkdirSync17(path33.dirname(ctoIdPath), { recursive: true });
14637
15026
  const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
14638
- writeFileSync18(ctoIdPath, replaced, "utf-8");
15027
+ writeFileSync19(ctoIdPath, replaced, "utf-8");
14639
15028
  }
14640
15029
  registerBinSymlinks2(ctoName);
14641
15030
  createdEmployees.push({ name: ctoName, role: "CTO" });
@@ -14656,9 +15045,9 @@ async function runSetupWizard(opts = {}) {
14656
15045
  const cmoIdentityContent = getIdentityTemplate("cmo");
14657
15046
  if (cmoIdentityContent) {
14658
15047
  const cmoIdPath = identityPath2(cmoName);
14659
- mkdirSync18(path32.dirname(cmoIdPath), { recursive: true });
15048
+ mkdirSync17(path33.dirname(cmoIdPath), { recursive: true });
14660
15049
  const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
14661
- writeFileSync18(cmoIdPath, replaced, "utf-8");
15050
+ writeFileSync19(cmoIdPath, replaced, "utf-8");
14662
15051
  }
14663
15052
  registerBinSymlinks2(cmoName);
14664
15053
  createdEmployees.push({ name: cmoName, role: "CMO" });
@@ -14680,7 +15069,7 @@ async function runSetupWizard(opts = {}) {
14680
15069
  log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
14681
15070
  }
14682
15071
  if (wrapResult.pathConfigured) {
14683
- const binDir = path32.join(os15.homedir(), ".exe-os", "bin");
15072
+ const binDir = path33.join(os16.homedir(), ".exe-os", "bin");
14684
15073
  process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
14685
15074
  pathJustConfigured = true;
14686
15075
  }
@@ -14723,7 +15112,7 @@ async function runSetupWizard(opts = {}) {
14723
15112
  const pkgRoot2 = findPackageRoot2();
14724
15113
  if (pkgRoot2) {
14725
15114
  try {
14726
- version = JSON.parse(readFileSync22(path32.join(pkgRoot2, "package.json"), "utf-8")).version;
15115
+ version = JSON.parse(readFileSync23(path33.join(pkgRoot2, "package.json"), "utf-8")).version;
14727
15116
  } catch {
14728
15117
  }
14729
15118
  }
@@ -14757,17 +15146,17 @@ var init_setup_wizard = __esm({
14757
15146
  init_config();
14758
15147
  init_keychain();
14759
15148
  init_model_downloader();
14760
- SETUP_STATE_PATH = path32.join(os15.homedir(), ".exe-os", "setup-state.json");
15149
+ SETUP_STATE_PATH = path33.join(os16.homedir(), ".exe-os", "setup-state.json");
14761
15150
  }
14762
15151
  });
14763
15152
 
14764
15153
  // src/lib/update-check.ts
14765
15154
  import { execSync as execSync11 } from "child_process";
14766
- import { readFileSync as readFileSync23 } from "fs";
14767
- import path33 from "path";
15155
+ import { readFileSync as readFileSync24 } from "fs";
15156
+ import path34 from "path";
14768
15157
  function getLocalVersion(packageRoot) {
14769
- const pkgPath = path33.join(packageRoot, "package.json");
14770
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
15158
+ const pkgPath = path34.join(packageRoot, "package.json");
15159
+ const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
14771
15160
  return pkg.version;
14772
15161
  }
14773
15162
  function getRemoteVersion() {
@@ -19291,8 +19680,8 @@ var init_ErrorOverview = __esm({
19291
19680
  "use strict";
19292
19681
  init_Box();
19293
19682
  init_Text();
19294
- cleanupPath = (path45) => {
19295
- return path45?.replace(`file://${cwd()}/`, "");
19683
+ cleanupPath = (path46) => {
19684
+ return path46?.replace(`file://${cwd()}/`, "");
19296
19685
  };
19297
19686
  stackUtils = new StackUtils({
19298
19687
  cwd: cwd(),
@@ -21700,11 +22089,11 @@ function Footer() {
21700
22089
  } catch {
21701
22090
  }
21702
22091
  try {
21703
- const { existsSync: existsSync30 } = await import("fs");
22092
+ const { existsSync: existsSync32 } = await import("fs");
21704
22093
  const { join } = await import("path");
21705
22094
  const home = process.env.HOME ?? "";
21706
22095
  const pidPath = join(home, ".exe-os", "exed.pid");
21707
- setDaemon(existsSync30(pidPath) ? "running" : "stopped");
22096
+ setDaemon(existsSync32(pidPath) ? "running" : "stopped");
21708
22097
  } catch {
21709
22098
  setDaemon("unknown");
21710
22099
  }
@@ -23746,10 +24135,10 @@ var init_hooks = __esm({
23746
24135
  });
23747
24136
 
23748
24137
  // src/runtime/safety-checks.ts
23749
- import path34 from "path";
23750
- import os16 from "os";
24138
+ import path35 from "path";
24139
+ import os17 from "os";
23751
24140
  function checkPathSafety(filePath) {
23752
- const resolved = path34.resolve(filePath);
24141
+ const resolved = path35.resolve(filePath);
23753
24142
  for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
23754
24143
  const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
23755
24144
  if (matches) {
@@ -23759,7 +24148,7 @@ function checkPathSafety(filePath) {
23759
24148
  return { safe: true, bypassImmune: true };
23760
24149
  }
23761
24150
  function checkReadPathSafety(filePath) {
23762
- const resolved = path34.resolve(filePath);
24151
+ const resolved = path35.resolve(filePath);
23763
24152
  const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
23764
24153
  (p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
23765
24154
  );
@@ -23774,7 +24163,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
23774
24163
  var init_safety_checks = __esm({
23775
24164
  "src/runtime/safety-checks.ts"() {
23776
24165
  "use strict";
23777
- HOME = os16.homedir();
24166
+ HOME = os17.homedir();
23778
24167
  BYPASS_IMMUNE_PATTERNS = [
23779
24168
  {
23780
24169
  pattern: /\/\.git\/hooks\//,
@@ -23785,11 +24174,11 @@ var init_safety_checks = __esm({
23785
24174
  reason: "Git config can set hooks and command execution"
23786
24175
  },
23787
24176
  {
23788
- pattern: (p) => p.startsWith(path34.join(HOME, ".claude")),
24177
+ pattern: (p) => p.startsWith(path35.join(HOME, ".claude")),
23789
24178
  reason: "Claude configuration files are protected"
23790
24179
  },
23791
24180
  {
23792
- pattern: (p) => p.startsWith(path34.join(HOME, ".exe-os")),
24181
+ pattern: (p) => p.startsWith(path35.join(HOME, ".exe-os")),
23793
24182
  reason: "exe-os configuration files are protected"
23794
24183
  },
23795
24184
  {
@@ -23806,7 +24195,7 @@ var init_safety_checks = __esm({
23806
24195
  },
23807
24196
  {
23808
24197
  pattern: (p) => {
23809
- const name = path34.basename(p);
24198
+ const name = path35.basename(p);
23810
24199
  return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
23811
24200
  },
23812
24201
  reason: "Shell configuration files can execute arbitrary code on login"
@@ -23833,7 +24222,7 @@ __export(file_read_exports, {
23833
24222
  FileReadTool: () => FileReadTool
23834
24223
  });
23835
24224
  import fs3 from "fs/promises";
23836
- import path35 from "path";
24225
+ import path36 from "path";
23837
24226
  import { z } from "zod";
23838
24227
  function isBinary(buf) {
23839
24228
  for (let i = 0; i < buf.length; i++) {
@@ -23869,7 +24258,7 @@ var init_file_read = __esm({
23869
24258
  return { behavior: "allow" };
23870
24259
  },
23871
24260
  async call(input, context) {
23872
- const filePath = path35.isAbsolute(input.file_path) ? input.file_path : path35.resolve(context.cwd, input.file_path);
24261
+ const filePath = path36.isAbsolute(input.file_path) ? input.file_path : path36.resolve(context.cwd, input.file_path);
23873
24262
  let stat2;
23874
24263
  try {
23875
24264
  stat2 = await fs3.stat(filePath);
@@ -23909,7 +24298,7 @@ __export(glob_exports, {
23909
24298
  GlobTool: () => GlobTool
23910
24299
  });
23911
24300
  import fs4 from "fs/promises";
23912
- import path36 from "path";
24301
+ import path37 from "path";
23913
24302
  import { z as z2 } from "zod";
23914
24303
  async function walkDir(dir, maxDepth = 10) {
23915
24304
  const results = [];
@@ -23925,7 +24314,7 @@ async function walkDir(dir, maxDepth = 10) {
23925
24314
  if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
23926
24315
  continue;
23927
24316
  }
23928
- const fullPath = path36.join(current, entry.name);
24317
+ const fullPath = path37.join(current, entry.name);
23929
24318
  if (entry.isDirectory()) {
23930
24319
  await walk(fullPath, depth + 1);
23931
24320
  } else {
@@ -23959,11 +24348,11 @@ var init_glob = __esm({
23959
24348
  inputSchema: inputSchema2,
23960
24349
  isReadOnly: true,
23961
24350
  async call(input, context) {
23962
- const baseDir = input.path ? path36.isAbsolute(input.path) ? input.path : path36.resolve(context.cwd, input.path) : context.cwd;
24351
+ const baseDir = input.path ? path37.isAbsolute(input.path) ? input.path : path37.resolve(context.cwd, input.path) : context.cwd;
23963
24352
  try {
23964
24353
  const entries = await walkDir(baseDir);
23965
24354
  const matched = entries.filter(
23966
- (e) => simpleGlobMatch(path36.relative(baseDir, e.path), input.pattern)
24355
+ (e) => simpleGlobMatch(path37.relative(baseDir, e.path), input.pattern)
23967
24356
  );
23968
24357
  matched.sort((a, b) => b.mtime - a.mtime);
23969
24358
  if (matched.length === 0) {
@@ -23989,7 +24378,7 @@ __export(grep_exports, {
23989
24378
  });
23990
24379
  import { spawn as spawn2 } from "child_process";
23991
24380
  import fs5 from "fs/promises";
23992
- import path37 from "path";
24381
+ import path38 from "path";
23993
24382
  import { z as z3 } from "zod";
23994
24383
  function runRipgrep(input, searchPath, context) {
23995
24384
  return new Promise((resolve, reject) => {
@@ -24043,7 +24432,7 @@ async function nodeGrep(input, searchPath) {
24043
24432
  }
24044
24433
  for (const entry of entries) {
24045
24434
  if (entry.name === "node_modules" || entry.name === ".git") continue;
24046
- const fullPath = path37.join(dir, entry.name);
24435
+ const fullPath = path38.join(dir, entry.name);
24047
24436
  if (entry.isDirectory()) {
24048
24437
  await walk(fullPath);
24049
24438
  } else {
@@ -24089,7 +24478,7 @@ var init_grep = __esm({
24089
24478
  inputSchema: inputSchema3,
24090
24479
  isReadOnly: true,
24091
24480
  async call(input, context) {
24092
- const searchPath = input.path ? path37.isAbsolute(input.path) ? input.path : path37.resolve(context.cwd, input.path) : context.cwd;
24481
+ const searchPath = input.path ? path38.isAbsolute(input.path) ? input.path : path38.resolve(context.cwd, input.path) : context.cwd;
24093
24482
  try {
24094
24483
  const result = await runRipgrep(input, searchPath, context);
24095
24484
  return result;
@@ -24114,7 +24503,7 @@ __export(file_write_exports, {
24114
24503
  FileWriteTool: () => FileWriteTool
24115
24504
  });
24116
24505
  import fs6 from "fs/promises";
24117
- import path38 from "path";
24506
+ import path39 from "path";
24118
24507
  import { z as z4 } from "zod";
24119
24508
  var inputSchema4, FileWriteTool;
24120
24509
  var init_file_write = __esm({
@@ -24142,8 +24531,8 @@ var init_file_write = __esm({
24142
24531
  return { behavior: "allow" };
24143
24532
  },
24144
24533
  async call(input, context) {
24145
- const filePath = path38.isAbsolute(input.file_path) ? input.file_path : path38.resolve(context.cwd, input.file_path);
24146
- const dir = path38.dirname(filePath);
24534
+ const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
24535
+ const dir = path39.dirname(filePath);
24147
24536
  await fs6.mkdir(dir, { recursive: true });
24148
24537
  await fs6.writeFile(filePath, input.content, "utf-8");
24149
24538
  return {
@@ -24161,7 +24550,7 @@ __export(file_edit_exports, {
24161
24550
  FileEditTool: () => FileEditTool
24162
24551
  });
24163
24552
  import fs7 from "fs/promises";
24164
- import path39 from "path";
24553
+ import path40 from "path";
24165
24554
  import { z as z5 } from "zod";
24166
24555
  function countOccurrences(haystack, needle) {
24167
24556
  let count = 0;
@@ -24202,7 +24591,7 @@ var init_file_edit = __esm({
24202
24591
  return { behavior: "allow" };
24203
24592
  },
24204
24593
  async call(input, context) {
24205
- const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
24594
+ const filePath = path40.isAbsolute(input.file_path) ? input.file_path : path40.resolve(context.cwd, input.file_path);
24206
24595
  let content;
24207
24596
  try {
24208
24597
  content = await fs7.readFile(filePath, "utf-8");
@@ -24444,7 +24833,7 @@ var init_bash = __esm({
24444
24833
  // src/tui/views/CommandCenter.tsx
24445
24834
  import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
24446
24835
  import TextInput from "ink-text-input";
24447
- import path40 from "path";
24836
+ import path41 from "path";
24448
24837
  import { homedir as homedir6 } from "os";
24449
24838
  import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
24450
24839
  function CommandCenterView({
@@ -24479,15 +24868,15 @@ function CommandCenterView({
24479
24868
  const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
24480
24869
  const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
24481
24870
  const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
24482
- const { readFileSync: readFileSync27, existsSync: existsSync30 } = await import("fs");
24871
+ const { readFileSync: readFileSync28, existsSync: existsSync32 } = await import("fs");
24483
24872
  const { join } = await import("path");
24484
24873
  const { homedir: homedir8 } = await import("os");
24485
24874
  const configPath = join(homedir8(), ".exe-os", "config.json");
24486
24875
  let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
24487
24876
  let providerConfigs = {};
24488
- if (existsSync30(configPath)) {
24877
+ if (existsSync32(configPath)) {
24489
24878
  try {
24490
- const raw = JSON.parse(readFileSync27(configPath, "utf8"));
24879
+ const raw = JSON.parse(readFileSync28(configPath, "utf8"));
24491
24880
  if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
24492
24881
  if (raw.providers && typeof raw.providers === "object") {
24493
24882
  providerConfigs = raw.providers;
@@ -24548,7 +24937,7 @@ function CommandCenterView({
24548
24937
  const markerDir = join(homedir8(), ".exe-os", "session-cache");
24549
24938
  const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
24550
24939
  for (const f of agentFiles) {
24551
- const data = JSON.parse(readFileSync27(join(markerDir, f), "utf8"));
24940
+ const data = JSON.parse(readFileSync28(join(markerDir, f), "utf8"));
24552
24941
  if (data.agentRole) {
24553
24942
  agentRole = data.agentRole;
24554
24943
  break;
@@ -24693,7 +25082,7 @@ function CommandCenterView({
24693
25082
  const demoEntries = DEMO_PROJECTS.map((p) => ({
24694
25083
  projectName: p.projectName,
24695
25084
  exeSession: p.exeSession,
24696
- projectDir: path40.join(homedir6(), p.projectName),
25085
+ projectDir: path41.join(homedir6(), p.projectName),
24697
25086
  employeeCount: p.employees.length,
24698
25087
  activeCount: p.employees.filter((e) => e.status === "active").length,
24699
25088
  memoryCount: p.employees.length * 4e3,
@@ -24731,7 +25120,7 @@ function CommandCenterView({
24731
25120
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
24732
25121
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
24733
25122
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
24734
- const { existsSync: existsSync30 } = await import("fs");
25123
+ const { existsSync: existsSync32 } = await import("fs");
24735
25124
  const { join } = await import("path");
24736
25125
  const client = getClient2();
24737
25126
  if (!client) {
@@ -24802,7 +25191,7 @@ function CommandCenterView({
24802
25191
  }
24803
25192
  const memoryCount = memoryCounts.get(name) ?? 0;
24804
25193
  const openTaskCount = openTaskCounts.get(name) ?? 0;
24805
- const hasGit = projectDir ? existsSync30(join(projectDir, ".git")) : false;
25194
+ const hasGit = projectDir ? existsSync32(join(projectDir, ".git")) : false;
24806
25195
  const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
24807
25196
  projectList.push({
24808
25197
  projectName: name,
@@ -24827,7 +25216,7 @@ function CommandCenterView({
24827
25216
  setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
24828
25217
  try {
24829
25218
  const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
24830
- setHealth((h) => ({ ...h, daemon: existsSync30(pidPath) ? "running" : "stopped" }));
25219
+ setHealth((h) => ({ ...h, daemon: existsSync32(pidPath) ? "running" : "stopped" }));
24831
25220
  } catch {
24832
25221
  }
24833
25222
  const activityResult = await client.execute(
@@ -25697,7 +26086,7 @@ var init_useOrchestrator = __esm({
25697
26086
 
25698
26087
  // src/tui/views/Sessions.tsx
25699
26088
  import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
25700
- import path41 from "path";
26089
+ import path42 from "path";
25701
26090
  import { homedir as homedir7 } from "os";
25702
26091
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
25703
26092
  function isCoordinatorEntry(entry) {
@@ -25735,7 +26124,7 @@ function SessionsView({
25735
26124
  if (demo) {
25736
26125
  setProjects(DEMO_PROJECTS.map((p) => ({
25737
26126
  ...p,
25738
- projectDir: path41.join(homedir7(), p.projectName),
26127
+ projectDir: path42.join(homedir7(), p.projectName),
25739
26128
  employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
25740
26129
  })));
25741
26130
  return;
@@ -26145,6 +26534,133 @@ var init_Sessions = __esm({
26145
26534
  }
26146
26535
  });
26147
26536
 
26537
+ // src/lib/tui-data.ts
26538
+ var tui_data_exports = {};
26539
+ __export(tui_data_exports, {
26540
+ loadMemoryDashboard: () => loadMemoryDashboard,
26541
+ loadTaskList: () => loadTaskList,
26542
+ loadTeamMetrics: () => loadTeamMetrics,
26543
+ searchWikiMemoryRows: () => searchWikiMemoryRows
26544
+ });
26545
+ async function loadMemoryDashboard(limit) {
26546
+ const client = getClient();
26547
+ const [countResult, recentResult, agentResult] = await Promise.all([
26548
+ client.execute("SELECT COUNT(*) as cnt FROM memories"),
26549
+ client.execute({
26550
+ sql: "SELECT agent_id, tool_name, project_name, raw_text, timestamp FROM memories ORDER BY timestamp DESC LIMIT ?",
26551
+ args: [limit]
26552
+ }),
26553
+ client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id ORDER BY cnt DESC")
26554
+ ]);
26555
+ return {
26556
+ total: Number(countResult.rows[0]?.cnt ?? 0),
26557
+ recent: recentResult.rows.map((row) => ({
26558
+ agentId: String(row.agent_id ?? "unknown"),
26559
+ toolName: String(row.tool_name ?? ""),
26560
+ projectName: String(row.project_name ?? ""),
26561
+ rawText: String(row.raw_text ?? ""),
26562
+ timestamp: String(row.timestamp ?? "")
26563
+ })),
26564
+ byAgent: agentResult.rows.map((row) => ({
26565
+ agentId: String(row.agent_id ?? "unknown"),
26566
+ count: Number(row.cnt ?? 0)
26567
+ }))
26568
+ };
26569
+ }
26570
+ async function loadTeamMetrics(employeeNames) {
26571
+ const client = getClient();
26572
+ const memoryCounts = /* @__PURE__ */ new Map();
26573
+ const projectsByEmployee = /* @__PURE__ */ new Map();
26574
+ const currentTaskByEmployee = /* @__PURE__ */ new Map();
26575
+ const scope = sessionScopeFilter();
26576
+ const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
26577
+ for (const row of memResult.rows) {
26578
+ memoryCounts.set(String(row.agent_id), Number(row.cnt));
26579
+ }
26580
+ for (const employeeName of employeeNames) {
26581
+ const [projectResult, taskResult] = await Promise.all([
26582
+ client.execute({
26583
+ sql: `SELECT DISTINCT project_name,
26584
+ MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
26585
+ FROM tasks
26586
+ WHERE assigned_to = ? AND status IN ('open','in_progress','done')${scope.sql}
26587
+ GROUP BY project_name
26588
+ ORDER BY urgency ASC
26589
+ LIMIT 5`,
26590
+ args: [employeeName, ...scope.args]
26591
+ }),
26592
+ client.execute({
26593
+ sql: `SELECT title FROM tasks
26594
+ WHERE assigned_to = ? AND status = 'in_progress'${scope.sql}
26595
+ ORDER BY updated_at DESC
26596
+ LIMIT 1`,
26597
+ args: [employeeName, ...scope.args]
26598
+ })
26599
+ ]);
26600
+ const projects = projectResult.rows.map((row) => {
26601
+ const urgency = Number(row.urgency);
26602
+ return {
26603
+ name: String(row.project_name),
26604
+ status: urgency === 1 ? "active" : urgency === 2 ? "has_tasks" : "idle"
26605
+ };
26606
+ });
26607
+ if (projects.length > 0) projectsByEmployee.set(employeeName, projects);
26608
+ if (taskResult.rows.length > 0) {
26609
+ currentTaskByEmployee.set(employeeName, String(taskResult.rows[0].title));
26610
+ }
26611
+ }
26612
+ return { memoryCounts, projectsByEmployee, currentTaskByEmployee };
26613
+ }
26614
+ async function loadTaskList() {
26615
+ const client = getClient();
26616
+ const scope = sessionScopeFilter();
26617
+ const result = await client.execute({
26618
+ sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
26619
+ FROM tasks
26620
+ WHERE 1=1${scope.sql}
26621
+ ORDER BY
26622
+ CASE status WHEN 'in_progress' THEN 0 WHEN 'open' THEN 1 WHEN 'blocked' THEN 2 WHEN 'needs_review' THEN 3 WHEN 'done' THEN 4 ELSE 5 END,
26623
+ CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
26624
+ created_at DESC`,
26625
+ args: [...scope.args]
26626
+ });
26627
+ return result.rows.map((row) => ({
26628
+ id: String(row.id ?? ""),
26629
+ title: String(row.title ?? ""),
26630
+ priority: String(row.priority ?? "p2").toUpperCase(),
26631
+ assignedTo: String(row.assigned_to ?? ""),
26632
+ assignedBy: String(row.assigned_by ?? ""),
26633
+ status: String(row.status ?? "open"),
26634
+ projectName: String(row.project_name ?? ""),
26635
+ createdAt: String(row.created_at ?? ""),
26636
+ result: String(row.result ?? "")
26637
+ }));
26638
+ }
26639
+ async function searchWikiMemoryRows(query) {
26640
+ const client = getClient();
26641
+ const result = await client.execute({
26642
+ sql: `SELECT id, agent_id, raw_text, timestamp, project_name
26643
+ FROM memories
26644
+ WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
26645
+ ORDER BY timestamp DESC LIMIT 20`,
26646
+ args: [`%${query}%`]
26647
+ });
26648
+ return result.rows.map((row) => ({
26649
+ id: String(row.id),
26650
+ agentId: String(row.agent_id),
26651
+ rawText: String(row.raw_text),
26652
+ timestamp: String(row.timestamp),
26653
+ projectName: String(row.project_name ?? "")
26654
+ }));
26655
+ }
26656
+ var init_tui_data = __esm({
26657
+ "src/lib/tui-data.ts"() {
26658
+ "use strict";
26659
+ init_database();
26660
+ init_task_scope();
26661
+ }
26662
+ });
26663
+
26148
26664
  // src/tui/views/Tasks.tsx
26149
26665
  import React20, { useState as useState10, useEffect as useEffect12, useMemo as useMemo5 } from "react";
26150
26666
  import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
@@ -26264,37 +26780,22 @@ function TasksView({ onBack }) {
26264
26780
  const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
26265
26781
  return withTrace2("tui.tasks.loadTasks", async () => {
26266
26782
  try {
26267
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
26268
- const client = getClient2();
26269
- if (client) {
26270
- const tScope = sessionScopeFilter();
26271
- const result = await client.execute({
26272
- sql: `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
26273
- FROM tasks
26274
- WHERE 1=1${tScope.sql}
26275
- ORDER BY
26276
- CASE status WHEN 'in_progress' THEN 0 WHEN 'open' THEN 1 WHEN 'blocked' THEN 2 WHEN 'needs_review' THEN 3 WHEN 'done' THEN 4 ELSE 5 END,
26277
- CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
26278
- created_at DESC`,
26279
- args: [...tScope.args]
26280
- });
26281
- setAllTasks(
26282
- result.rows.map((r) => ({
26283
- id: String(r.id ?? ""),
26284
- priority: String(r.priority ?? "p2").toUpperCase(),
26285
- title: String(r.title ?? ""),
26286
- assignee: String(r.assigned_to ?? ""),
26287
- assignedBy: String(r.assigned_by ?? ""),
26288
- status: String(r.status ?? "open"),
26289
- project: String(r.project_name ?? ""),
26290
- createdAt: String(r.created_at ?? ""),
26291
- result: String(r.result ?? "")
26292
- }))
26293
- );
26294
- setDbError(null);
26295
- } else {
26296
- setDbError("Database client not initialized. Run exe-os setup.");
26297
- }
26783
+ const { loadTaskList: loadTaskList2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
26784
+ const tasks = await loadTaskList2();
26785
+ setAllTasks(
26786
+ tasks.map((task) => ({
26787
+ id: task.id,
26788
+ priority: task.priority,
26789
+ title: task.title,
26790
+ assignee: task.assignedTo,
26791
+ assignedBy: task.assignedBy,
26792
+ status: task.status,
26793
+ project: task.projectName,
26794
+ createdAt: task.createdAt,
26795
+ result: task.result
26796
+ }))
26797
+ );
26798
+ setDbError(null);
26298
26799
  } catch (err) {
26299
26800
  setDbError(err instanceof Error ? err.message : "Unknown error");
26300
26801
  } finally {
@@ -26423,7 +26924,6 @@ var init_Tasks = __esm({
26423
26924
  await init_ink2();
26424
26925
  init_DemoContext();
26425
26926
  init_demo_data();
26426
- init_task_scope();
26427
26927
  STATUS_FILTERS = ["all", "open", "in_progress", "done", "blocked", "needs_review"];
26428
26928
  PRIORITY_COLORS = {
26429
26929
  P0: "red",
@@ -26839,12 +27339,12 @@ async function loadGatewayConfig() {
26839
27339
  state.running = false;
26840
27340
  }
26841
27341
  try {
26842
- const { existsSync: existsSync30, readFileSync: readFileSync27 } = await import("fs");
27342
+ const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
26843
27343
  const { join } = await import("path");
26844
27344
  const home = process.env.HOME ?? "";
26845
27345
  const configPath = join(home, ".exe-os", "gateway.json");
26846
- if (existsSync30(configPath)) {
26847
- const raw = JSON.parse(readFileSync27(configPath, "utf8"));
27346
+ if (existsSync32(configPath)) {
27347
+ const raw = JSON.parse(readFileSync28(configPath, "utf8"));
26848
27348
  state.port = raw.port ?? 3100;
26849
27349
  state.gatewayUrl = raw.gatewayUrl ?? "";
26850
27350
  if (raw.adapters) {
@@ -27414,41 +27914,16 @@ function TeamView({ onBack, onViewSessions }) {
27414
27914
  let projectsByEmployee = /* @__PURE__ */ new Map();
27415
27915
  let currentTaskByEmployee = /* @__PURE__ */ new Map();
27416
27916
  try {
27417
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
27418
- const client = getClient2();
27419
- if (client) {
27420
- const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
27421
- for (const row of memResult.rows) {
27422
- memoryCounts.set(String(row.agent_id), Number(row.cnt));
27423
- }
27424
- const tmScope = sessionScopeFilter();
27425
- for (const emp of roster) {
27426
- const projResult = await client.execute({
27427
- sql: `SELECT DISTINCT project_name,
27428
- MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
27429
- FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')${tmScope.sql}
27430
- GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
27431
- args: [emp.name, ...tmScope.args]
27432
- });
27433
- const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
27434
- const urgency = Number(r.urgency);
27435
- let pStatus = "idle";
27436
- if (urgency === 1) pStatus = "active";
27437
- else if (urgency === 2) pStatus = "has_tasks";
27438
- return { name: String(r.project_name), status: pStatus };
27439
- });
27440
- if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
27441
- }
27442
- for (const emp of roster) {
27443
- const taskResult = await client.execute({
27444
- sql: `SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress'${tmScope.sql} ORDER BY updated_at DESC LIMIT 1`,
27445
- args: [emp.name, ...tmScope.args]
27446
- });
27447
- if (taskResult.rows.length > 0) {
27448
- currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
27449
- }
27450
- }
27451
- }
27917
+ const { loadTeamMetrics: loadTeamMetrics2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
27918
+ const teamMetrics = await loadTeamMetrics2(roster.map((emp) => emp.name));
27919
+ memoryCounts = teamMetrics.memoryCounts;
27920
+ projectsByEmployee = new Map(
27921
+ Array.from(teamMetrics.projectsByEmployee.entries()).map(([name, projects]) => [
27922
+ name,
27923
+ projects.filter((project) => !DEPRECATED_PROJECTS.has(project.name))
27924
+ ])
27925
+ );
27926
+ currentTaskByEmployee = teamMetrics.currentTaskByEmployee;
27452
27927
  } catch {
27453
27928
  }
27454
27929
  const teamData = roster.map((emp) => {
@@ -27467,12 +27942,12 @@ function TeamView({ onBack, onViewSessions }) {
27467
27942
  setMembers(teamData);
27468
27943
  setDbError(null);
27469
27944
  try {
27470
- const { existsSync: existsSync30, readFileSync: readFileSync27 } = await import("fs");
27945
+ const { existsSync: existsSync32, readFileSync: readFileSync28 } = await import("fs");
27471
27946
  const { join } = await import("path");
27472
27947
  const home = process.env.HOME ?? "";
27473
27948
  const gatewayConfig = join(home, ".exe-os", "gateway.json");
27474
- if (existsSync30(gatewayConfig)) {
27475
- const raw = JSON.parse(readFileSync27(gatewayConfig, "utf8"));
27949
+ if (existsSync32(gatewayConfig)) {
27950
+ const raw = JSON.parse(readFileSync28(gatewayConfig, "utf8"));
27476
27951
  if (raw.agents && raw.agents.length > 0) {
27477
27952
  setExternals(raw.agents.map((a) => ({
27478
27953
  name: a.name,
@@ -27639,7 +28114,6 @@ var init_Team = __esm({
27639
28114
  init_demo_data();
27640
28115
  init_useOrchestrator();
27641
28116
  init_agent_status();
27642
- init_task_scope();
27643
28117
  DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
27644
28118
  }
27645
28119
  });
@@ -27653,8 +28127,8 @@ __export(wiki_client_exports, {
27653
28127
  listDocuments: () => listDocuments,
27654
28128
  listWorkspaces: () => listWorkspaces
27655
28129
  });
27656
- async function wikiFetch(config, path45, method = "GET", body) {
27657
- const url = `${config.baseUrl}/api/v1${path45}`;
28130
+ async function wikiFetch(config, path46, method = "GET", body) {
28131
+ const url = `${config.baseUrl}/api/v1${path46}`;
27658
28132
  const headers = {
27659
28133
  Authorization: `Bearer ${config.apiKey}`,
27660
28134
  "Content-Type": "application/json"
@@ -27687,7 +28161,7 @@ async function wikiFetch(config, path45, method = "GET", body) {
27687
28161
  }
27688
28162
  }
27689
28163
  if (!response.ok) {
27690
- throw new Error(`Wiki API ${method} ${path45}: ${response.status} ${response.statusText}`);
28164
+ throw new Error(`Wiki API ${method} ${path46}: ${response.status} ${response.statusText}`);
27691
28165
  }
27692
28166
  return response.json();
27693
28167
  } finally {
@@ -27933,24 +28407,8 @@ function WikiView({ onBack }) {
27933
28407
  }
27934
28408
  setSearchLoading(true);
27935
28409
  try {
27936
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
27937
- const client = getClient2();
27938
- const result = await client.execute({
27939
- sql: `SELECT id, agent_id, raw_text, timestamp, project_name
27940
- FROM memories
27941
- WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
27942
- ORDER BY timestamp DESC LIMIT 20`,
27943
- args: [`%${query}%`]
27944
- });
27945
- setSearchResults(
27946
- result.rows.map((r) => ({
27947
- id: String(r.id),
27948
- agentId: String(r.agent_id),
27949
- rawText: String(r.raw_text),
27950
- timestamp: String(r.timestamp),
27951
- projectName: String(r.project_name ?? "")
27952
- }))
27953
- );
28410
+ const { searchWikiMemoryRows: searchWikiMemoryRows2 } = await Promise.resolve().then(() => (init_tui_data(), tui_data_exports));
28411
+ setSearchResults(await searchWikiMemoryRows2(query));
27954
28412
  } catch {
27955
28413
  setSearchResults([]);
27956
28414
  } finally {
@@ -28297,12 +28755,12 @@ function SettingsView({ onBack }) {
28297
28755
  }
28298
28756
  setProviders(providerList);
28299
28757
  try {
28300
- const { existsSync: existsSync30 } = await import("fs");
28758
+ const { existsSync: existsSync32 } = await import("fs");
28301
28759
  const { join } = await import("path");
28302
28760
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
28303
28761
  const cfg = await loadConfig2();
28304
28762
  const home = process.env.HOME ?? "";
28305
- const hasKey = existsSync30(join(home, ".exe-os", "master.key"));
28763
+ const hasKey = existsSync32(join(home, ".exe-os", "master.key"));
28306
28764
  if (cfg.cloud) {
28307
28765
  setCloud({
28308
28766
  configured: true,
@@ -28315,22 +28773,22 @@ function SettingsView({ onBack }) {
28315
28773
  const pidPath = join(home, ".exe-os", "exed.pid");
28316
28774
  let daemon = "unknown";
28317
28775
  try {
28318
- daemon = existsSync30(pidPath) ? "running" : "stopped";
28776
+ daemon = existsSync32(pidPath) ? "running" : "stopped";
28319
28777
  } catch {
28320
28778
  }
28321
28779
  let version = "unknown";
28322
28780
  try {
28323
- const { readFileSync: readFileSync27 } = await import("fs");
28324
- const { createRequire: createRequire2 } = await import("module");
28325
- const require2 = createRequire2(import.meta.url);
28781
+ const { readFileSync: readFileSync28 } = await import("fs");
28782
+ const { createRequire: createRequire3 } = await import("module");
28783
+ const require2 = createRequire3(import.meta.url);
28326
28784
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
28327
- const pkg = JSON.parse(readFileSync27(pkgPath, "utf8"));
28785
+ const pkg = JSON.parse(readFileSync28(pkgPath, "utf8"));
28328
28786
  version = pkg.version;
28329
28787
  } catch {
28330
28788
  try {
28331
- const { readFileSync: readFileSync27 } = await import("fs");
28789
+ const { readFileSync: readFileSync28 } = await import("fs");
28332
28790
  const { join: joinPath } = await import("path");
28333
- const pkg = JSON.parse(readFileSync27(joinPath(process.cwd(), "package.json"), "utf8"));
28791
+ const pkg = JSON.parse(readFileSync28(joinPath(process.cwd(), "package.json"), "utf8"));
28334
28792
  version = pkg.version;
28335
28793
  } catch {
28336
28794
  }
@@ -29131,15 +29589,15 @@ __export(installer_exports2, {
29131
29589
  verifyOpenCodeHooks: () => verifyOpenCodeHooks
29132
29590
  });
29133
29591
  import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
29134
- import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
29135
- import path42 from "path";
29136
- import os17 from "os";
29137
- async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
29138
- const configDir = path42.join(homeDir, ".config", "opencode");
29139
- const configPath = path42.join(configDir, "opencode.json");
29592
+ import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
29593
+ import path43 from "path";
29594
+ import os18 from "os";
29595
+ async function registerOpenCodeMcp(packageRoot, homeDir = os18.homedir()) {
29596
+ const configDir = path43.join(homeDir, ".config", "opencode");
29597
+ const configPath = path43.join(configDir, "opencode.json");
29140
29598
  await mkdir7(configDir, { recursive: true });
29141
29599
  let config = {};
29142
- if (existsSync27(configPath)) {
29600
+ if (existsSync29(configPath)) {
29143
29601
  try {
29144
29602
  config = JSON.parse(await readFile6(configPath, "utf-8"));
29145
29603
  } catch {
@@ -29151,7 +29609,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
29151
29609
  }
29152
29610
  const newEntry = {
29153
29611
  type: "local",
29154
- command: ["node", path42.join(packageRoot, "dist", "mcp", "server.js")],
29612
+ command: ["node", path43.join(packageRoot, "dist", "mcp", "server.js")],
29155
29613
  enabled: true
29156
29614
  };
29157
29615
  const current = config.mcp["exe-os"];
@@ -29165,15 +29623,15 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os17.homedir()) {
29165
29623
  await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n");
29166
29624
  return true;
29167
29625
  }
29168
- async function installOpenCodePlugin(packageRoot, homeDir = os17.homedir()) {
29169
- const pluginDir = path42.join(homeDir, ".config", "opencode", "plugins");
29170
- const pluginPath = path42.join(pluginDir, "exe-os.mjs");
29626
+ async function installOpenCodePlugin(packageRoot, homeDir = os18.homedir()) {
29627
+ const pluginDir = path43.join(homeDir, ".config", "opencode", "plugins");
29628
+ const pluginPath = path43.join(pluginDir, "exe-os.mjs");
29171
29629
  await mkdir7(pluginDir, { recursive: true });
29172
29630
  const pluginContent = PLUGIN_TEMPLATE.replace(
29173
29631
  /__PACKAGE_ROOT__/g,
29174
29632
  packageRoot.replace(/\\/g, "\\\\")
29175
29633
  );
29176
- if (existsSync27(pluginPath)) {
29634
+ if (existsSync29(pluginPath)) {
29177
29635
  const existing = await readFile6(pluginPath, "utf-8");
29178
29636
  if (existing === pluginContent) {
29179
29637
  return false;
@@ -29182,17 +29640,17 @@ async function installOpenCodePlugin(packageRoot, homeDir = os17.homedir()) {
29182
29640
  await writeFile7(pluginPath, pluginContent);
29183
29641
  return true;
29184
29642
  }
29185
- function verifyOpenCodeHooks(homeDir = os17.homedir()) {
29186
- const configPath = path42.join(homeDir, ".config", "opencode", "opencode.json");
29187
- const pluginPath = path42.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
29188
- if (!existsSync27(configPath)) return false;
29643
+ function verifyOpenCodeHooks(homeDir = os18.homedir()) {
29644
+ const configPath = path43.join(homeDir, ".config", "opencode", "opencode.json");
29645
+ const pluginPath = path43.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
29646
+ if (!existsSync29(configPath)) return false;
29189
29647
  try {
29190
- const config = JSON.parse(readFileSync25(configPath, "utf-8"));
29648
+ const config = JSON.parse(readFileSync26(configPath, "utf-8"));
29191
29649
  if (!config.mcp?.["exe-os"]?.enabled) return false;
29192
29650
  } catch {
29193
29651
  return false;
29194
29652
  }
29195
- if (!existsSync27(pluginPath)) return false;
29653
+ if (!existsSync29(pluginPath)) return false;
29196
29654
  return true;
29197
29655
  }
29198
29656
  async function runOpenCodeInstaller(homeDir) {
@@ -29225,19 +29683,19 @@ __export(installer_exports3, {
29225
29683
  verifyCodexHooks: () => verifyCodexHooks
29226
29684
  });
29227
29685
  import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
29228
- import { existsSync as existsSync28 } from "fs";
29229
- import path43 from "path";
29230
- import os18 from "os";
29231
- async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29232
- const codexDir = path43.join(homeDir, ".codex");
29233
- const hooksPath = path43.join(codexDir, "hooks.json");
29234
- const logsDir = path43.join(homeDir, ".exe-os", "logs");
29235
- const hookLogPath = path43.join(logsDir, "hooks.log");
29686
+ import { existsSync as existsSync30 } from "fs";
29687
+ import path44 from "path";
29688
+ import os19 from "os";
29689
+ async function mergeCodexHooks(packageRoot, homeDir = os19.homedir()) {
29690
+ const codexDir = path44.join(homeDir, ".codex");
29691
+ const hooksPath = path44.join(codexDir, "hooks.json");
29692
+ const logsDir = path44.join(homeDir, ".exe-os", "logs");
29693
+ const hookLogPath = path44.join(logsDir, "hooks.log");
29236
29694
  const logSuffix = ` 2>> "${hookLogPath}"`;
29237
29695
  await mkdir8(codexDir, { recursive: true });
29238
29696
  await mkdir8(logsDir, { recursive: true });
29239
29697
  let hooksJson = {};
29240
- if (existsSync28(hooksPath)) {
29698
+ if (existsSync30(hooksPath)) {
29241
29699
  try {
29242
29700
  hooksJson = JSON.parse(await readFile7(hooksPath, "utf-8"));
29243
29701
  } catch {
@@ -29254,7 +29712,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29254
29712
  hooks: [
29255
29713
  {
29256
29714
  type: "command",
29257
- command: `node "${path43.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
29715
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
29258
29716
  timeout: 30,
29259
29717
  statusMessage: "exe-os: loading memory brief"
29260
29718
  }
@@ -29269,11 +29727,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29269
29727
  hooks: [
29270
29728
  {
29271
29729
  type: "command",
29272
- command: `node "${path43.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
29730
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
29273
29731
  },
29274
29732
  {
29275
29733
  type: "command",
29276
- command: `node "${path43.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
29734
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
29277
29735
  }
29278
29736
  ]
29279
29737
  },
@@ -29285,11 +29743,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29285
29743
  hooks: [
29286
29744
  {
29287
29745
  type: "command",
29288
- command: `node "${path43.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
29746
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
29289
29747
  },
29290
29748
  {
29291
29749
  type: "command",
29292
- command: `node "${path43.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
29750
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
29293
29751
  timeout: 5
29294
29752
  }
29295
29753
  ]
@@ -29302,7 +29760,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29302
29760
  hooks: [
29303
29761
  {
29304
29762
  type: "command",
29305
- command: `node "${path43.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
29763
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
29306
29764
  }
29307
29765
  ]
29308
29766
  },
@@ -29315,7 +29773,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29315
29773
  hooks: [
29316
29774
  {
29317
29775
  type: "command",
29318
- command: `node "${path43.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
29776
+ command: `node "${path44.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
29319
29777
  }
29320
29778
  ]
29321
29779
  },
@@ -29346,9 +29804,9 @@ async function mergeCodexHooks(packageRoot, homeDir = os18.homedir()) {
29346
29804
  await writeFile8(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
29347
29805
  return { added, skipped };
29348
29806
  }
29349
- function verifyCodexHooks(homeDir = os18.homedir()) {
29350
- const hooksPath = path43.join(homeDir, ".codex", "hooks.json");
29351
- if (!existsSync28(hooksPath)) return false;
29807
+ function verifyCodexHooks(homeDir = os19.homedir()) {
29808
+ const hooksPath = path44.join(homeDir, ".codex", "hooks.json");
29809
+ if (!existsSync30(hooksPath)) return false;
29352
29810
  try {
29353
29811
  const hooksJson = JSON.parse(
29354
29812
  __require("fs").readFileSync(hooksPath, "utf-8")
@@ -29368,14 +29826,14 @@ function verifyCodexHooks(homeDir = os18.homedir()) {
29368
29826
  return false;
29369
29827
  }
29370
29828
  }
29371
- async function installCodexStatusLine(homeDir = os18.homedir()) {
29829
+ async function installCodexStatusLine(homeDir = os19.homedir()) {
29372
29830
  const prefs = loadPreferences(homeDir);
29373
29831
  if (prefs.codexStatusLine === false) return "opted-out";
29374
- const codexDir = path43.join(homeDir, ".codex");
29375
- const configPath = path43.join(codexDir, "config.toml");
29832
+ const codexDir = path44.join(homeDir, ".codex");
29833
+ const configPath = path44.join(codexDir, "config.toml");
29376
29834
  await mkdir8(codexDir, { recursive: true });
29377
29835
  let content = "";
29378
- if (existsSync28(configPath)) {
29836
+ if (existsSync30(configPath)) {
29379
29837
  content = await readFile7(configPath, "utf-8");
29380
29838
  if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
29381
29839
  return "already-configured";
@@ -29423,14 +29881,14 @@ var init_installer3 = __esm({
29423
29881
  });
29424
29882
 
29425
29883
  // src/bin/cli.ts
29426
- import { existsSync as existsSync29, readFileSync as readFileSync26, writeFileSync as writeFileSync19, readdirSync as readdirSync9, rmSync } from "fs";
29427
- import path44 from "path";
29428
- import os19 from "os";
29884
+ import { existsSync as existsSync31, readFileSync as readFileSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync9, rmSync } from "fs";
29885
+ import path45 from "path";
29886
+ import os20 from "os";
29429
29887
  var args = process.argv.slice(2);
29430
29888
  if (args.includes("--version") || args.includes("-v")) {
29431
29889
  try {
29432
- const pkgPath = path44.join(path44.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
29433
- const pkg = JSON.parse(readFileSync26(pkgPath, "utf8"));
29890
+ const pkgPath = path45.join(path45.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
29891
+ const pkg = JSON.parse(readFileSync27(pkgPath, "utf8"));
29434
29892
  console.log(pkg.version);
29435
29893
  } catch {
29436
29894
  console.log("unknown");
@@ -29594,11 +30052,11 @@ ID: ${result.id}`);
29594
30052
  });
29595
30053
  await init_App2().then(() => App_exports);
29596
30054
  } else {
29597
- const claudeDir = path44.join(os19.homedir(), ".claude");
29598
- const settingsPath = path44.join(claudeDir, "settings.json");
29599
- const hasClaudeCode = existsSync29(settingsPath) && (() => {
30055
+ const claudeDir = path45.join(os20.homedir(), ".claude");
30056
+ const settingsPath = path45.join(claudeDir, "settings.json");
30057
+ const hasClaudeCode = existsSync31(settingsPath) && (() => {
29600
30058
  try {
29601
- const raw = readFileSync26(settingsPath, "utf8");
30059
+ const raw = readFileSync27(settingsPath, "utf8");
29602
30060
  return raw.includes("exe-os") || raw.includes("exe-mem");
29603
30061
  } catch {
29604
30062
  return false;
@@ -29608,9 +30066,9 @@ ID: ${result.id}`);
29608
30066
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
29609
30067
  let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
29610
30068
  try {
29611
- const rosterPath = path44.join(os19.homedir(), ".exe-os", "exe-employees.json");
29612
- if (existsSync29(rosterPath)) {
29613
- const roster = JSON.parse(readFileSync26(rosterPath, "utf8"));
30069
+ const rosterPath = path45.join(os20.homedir(), ".exe-os", "exe-employees.json");
30070
+ if (existsSync31(rosterPath)) {
30071
+ const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
29614
30072
  const coo = roster.find((e) => e.role === "COO");
29615
30073
  if (coo) cooName = coo.name;
29616
30074
  }
@@ -29674,14 +30132,14 @@ async function runCodexInstall() {
29674
30132
  }
29675
30133
  }
29676
30134
  async function runClaudeCheck() {
29677
- const claudeDir = path44.join(os19.homedir(), ".claude");
29678
- const settingsPath = path44.join(claudeDir, "settings.json");
29679
- const claudeJsonPath = path44.join(os19.homedir(), ".claude.json");
30135
+ const claudeDir = path45.join(os20.homedir(), ".claude");
30136
+ const settingsPath = path45.join(claudeDir, "settings.json");
30137
+ const claudeJsonPath = path45.join(os20.homedir(), ".claude.json");
29680
30138
  let ok = true;
29681
- if (existsSync29(settingsPath)) {
30139
+ if (existsSync31(settingsPath)) {
29682
30140
  let settings;
29683
30141
  try {
29684
- settings = JSON.parse(readFileSync26(settingsPath, "utf8"));
30142
+ settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
29685
30143
  } catch {
29686
30144
  console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
29687
30145
  ok = false;
@@ -29707,10 +30165,10 @@ async function runClaudeCheck() {
29707
30165
  console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
29708
30166
  ok = false;
29709
30167
  }
29710
- if (existsSync29(claudeJsonPath)) {
30168
+ if (existsSync31(claudeJsonPath)) {
29711
30169
  let claudeJson;
29712
30170
  try {
29713
- claudeJson = JSON.parse(readFileSync26(claudeJsonPath, "utf8"));
30171
+ claudeJson = JSON.parse(readFileSync27(claudeJsonPath, "utf8"));
29714
30172
  } catch {
29715
30173
  console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
29716
30174
  ok = false;
@@ -29729,8 +30187,8 @@ async function runClaudeCheck() {
29729
30187
  console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
29730
30188
  ok = false;
29731
30189
  }
29732
- const skillsDir = path44.join(claudeDir, "skills");
29733
- if (existsSync29(skillsDir)) {
30190
+ const skillsDir = path45.join(claudeDir, "skills");
30191
+ if (existsSync31(skillsDir)) {
29734
30192
  console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
29735
30193
  } else {
29736
30194
  console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
@@ -29746,17 +30204,17 @@ async function runClaudeCheck() {
29746
30204
  async function runClaudeUninstall(flags = []) {
29747
30205
  const dryRun = flags.includes("--dry-run");
29748
30206
  const purge = flags.includes("--purge");
29749
- const homeDir = os19.homedir();
29750
- const claudeDir = path44.join(homeDir, ".claude");
29751
- const settingsPath = path44.join(claudeDir, "settings.json");
29752
- const claudeJsonPath = path44.join(homeDir, ".claude.json");
29753
- const exeOsDir = path44.join(homeDir, ".exe-os");
30207
+ const homeDir = os20.homedir();
30208
+ const claudeDir = path45.join(homeDir, ".claude");
30209
+ const settingsPath = path45.join(claudeDir, "settings.json");
30210
+ const claudeJsonPath = path45.join(homeDir, ".claude.json");
30211
+ const exeOsDir = path45.join(homeDir, ".exe-os");
29754
30212
  let removed = 0;
29755
30213
  const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
29756
30214
  let settings = {};
29757
- if (existsSync29(settingsPath)) {
30215
+ if (existsSync31(settingsPath)) {
29758
30216
  try {
29759
- settings = JSON.parse(readFileSync26(settingsPath, "utf8"));
30217
+ settings = JSON.parse(readFileSync27(settingsPath, "utf8"));
29760
30218
  } catch {
29761
30219
  console.error("Your ~/.claude/settings.json appears malformed.");
29762
30220
  if (purge) {
@@ -29794,15 +30252,15 @@ async function runClaudeUninstall(flags = []) {
29794
30252
  permCount = before - settings.permissions.allow.length;
29795
30253
  }
29796
30254
  if (!dryRun) {
29797
- writeFileSync19(settingsPath, JSON.stringify(settings, null, 2) + "\n");
30255
+ writeFileSync20(settingsPath, JSON.stringify(settings, null, 2) + "\n");
29798
30256
  }
29799
30257
  log("\u2713 Removed exe-os hooks from settings.json");
29800
30258
  if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
29801
30259
  removed++;
29802
30260
  }
29803
30261
  }
29804
- if (existsSync29(claudeJsonPath)) {
29805
- const raw = readFileSync26(claudeJsonPath, "utf8");
30262
+ if (existsSync31(claudeJsonPath)) {
30263
+ const raw = readFileSync27(claudeJsonPath, "utf8");
29806
30264
  if (raw.length > 1e6) {
29807
30265
  console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
29808
30266
  } else {
@@ -29823,7 +30281,7 @@ async function runClaudeUninstall(flags = []) {
29823
30281
  }
29824
30282
  if (removedMcp) {
29825
30283
  if (!dryRun) {
29826
- writeFileSync19(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
30284
+ writeFileSync20(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
29827
30285
  }
29828
30286
  log("\u2713 Removed exe-os MCP server from claude.json");
29829
30287
  removed++;
@@ -29831,14 +30289,14 @@ async function runClaudeUninstall(flags = []) {
29831
30289
  }
29832
30290
  }
29833
30291
  }
29834
- const skillsDir = path44.join(claudeDir, "skills");
29835
- if (existsSync29(skillsDir)) {
30292
+ const skillsDir = path45.join(claudeDir, "skills");
30293
+ if (existsSync31(skillsDir)) {
29836
30294
  let skillCount = 0;
29837
30295
  try {
29838
30296
  const entries = readdirSync9(skillsDir);
29839
30297
  for (const entry of entries) {
29840
30298
  if (entry.startsWith("exe")) {
29841
- const fullPath = path44.join(skillsDir, entry);
30299
+ const fullPath = path45.join(skillsDir, entry);
29842
30300
  if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
29843
30301
  skillCount++;
29844
30302
  }
@@ -29850,30 +30308,30 @@ async function runClaudeUninstall(flags = []) {
29850
30308
  removed++;
29851
30309
  }
29852
30310
  }
29853
- const claudeMdPath = path44.join(claudeDir, "CLAUDE.md");
29854
- if (existsSync29(claudeMdPath)) {
29855
- const content = readFileSync26(claudeMdPath, "utf8");
30311
+ const claudeMdPath = path45.join(claudeDir, "CLAUDE.md");
30312
+ if (existsSync31(claudeMdPath)) {
30313
+ const content = readFileSync27(claudeMdPath, "utf8");
29856
30314
  const startMarker = "<!-- exe-os:orchestration-start -->";
29857
30315
  const endMarker = "<!-- exe-os:orchestration-end -->";
29858
30316
  const startIdx = content.indexOf(startMarker);
29859
30317
  const endIdx = content.indexOf(endMarker);
29860
30318
  if (startIdx !== -1 && endIdx !== -1) {
29861
30319
  const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
29862
- if (!dryRun) writeFileSync19(claudeMdPath, cleaned);
30320
+ if (!dryRun) writeFileSync20(claudeMdPath, cleaned);
29863
30321
  log("\u2713 Removed orchestration block from CLAUDE.md");
29864
30322
  removed++;
29865
30323
  }
29866
30324
  }
29867
- const agentsDir = path44.join(claudeDir, "agents");
29868
- if (existsSync29(agentsDir)) {
30325
+ const agentsDir = path45.join(claudeDir, "agents");
30326
+ if (existsSync31(agentsDir)) {
29869
30327
  let agentCount = 0;
29870
30328
  try {
29871
30329
  const entries = readdirSync9(agentsDir).filter((f) => f.endsWith(".md"));
29872
30330
  let knownNames = /* @__PURE__ */ new Set();
29873
- const rosterPath = path44.join(exeOsDir, "exe-employees.json");
29874
- if (existsSync29(rosterPath)) {
30331
+ const rosterPath = path45.join(exeOsDir, "exe-employees.json");
30332
+ if (existsSync31(rosterPath)) {
29875
30333
  try {
29876
- const roster = JSON.parse(readFileSync26(rosterPath, "utf8"));
30334
+ const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
29877
30335
  knownNames = new Set(roster.map((e) => e.name));
29878
30336
  } catch {
29879
30337
  }
@@ -29881,7 +30339,7 @@ async function runClaudeUninstall(flags = []) {
29881
30339
  for (const entry of entries) {
29882
30340
  const name = entry.replace(/\.md$/, "");
29883
30341
  if (knownNames.has(name)) {
29884
- if (!dryRun) rmSync(path44.join(agentsDir, entry), { force: true });
30342
+ if (!dryRun) rmSync(path45.join(agentsDir, entry), { force: true });
29885
30343
  agentCount++;
29886
30344
  }
29887
30345
  }
@@ -29892,16 +30350,16 @@ async function runClaudeUninstall(flags = []) {
29892
30350
  removed++;
29893
30351
  }
29894
30352
  }
29895
- const projectsDir = path44.join(claudeDir, "projects");
29896
- if (existsSync29(projectsDir)) {
30353
+ const projectsDir = path45.join(claudeDir, "projects");
30354
+ if (existsSync31(projectsDir)) {
29897
30355
  let projectCount = 0;
29898
30356
  try {
29899
30357
  const projects = readdirSync9(projectsDir);
29900
30358
  for (const proj of projects) {
29901
- const projSettings = path44.join(projectsDir, proj, "settings.json");
29902
- if (!existsSync29(projSettings)) continue;
30359
+ const projSettings = path45.join(projectsDir, proj, "settings.json");
30360
+ if (!existsSync31(projSettings)) continue;
29903
30361
  try {
29904
- const pSettings = JSON.parse(readFileSync26(projSettings, "utf8"));
30362
+ const pSettings = JSON.parse(readFileSync27(projSettings, "utf8"));
29905
30363
  let changed = false;
29906
30364
  if (Array.isArray(pSettings.permissions?.allow)) {
29907
30365
  const before = pSettings.permissions.allow.length;
@@ -29911,7 +30369,7 @@ async function runClaudeUninstall(flags = []) {
29911
30369
  if (pSettings.permissions.allow.length < before) changed = true;
29912
30370
  }
29913
30371
  if (changed && !dryRun) {
29914
- writeFileSync19(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
30372
+ writeFileSync20(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
29915
30373
  }
29916
30374
  if (changed) projectCount++;
29917
30375
  } catch {
@@ -29935,18 +30393,18 @@ async function runClaudeUninstall(flags = []) {
29935
30393
  };
29936
30394
  const exeBinPath = findExeBin3();
29937
30395
  if (!exeBinPath) throw new Error("exe-os not found in PATH");
29938
- const binDir = path44.dirname(exeBinPath);
30396
+ const binDir = path45.dirname(exeBinPath);
29939
30397
  let symlinkCount = 0;
29940
- const rosterPath = path44.join(exeOsDir, "exe-employees.json");
29941
- if (existsSync29(rosterPath)) {
29942
- const roster = JSON.parse(readFileSync26(rosterPath, "utf8"));
30398
+ const rosterPath = path45.join(exeOsDir, "exe-employees.json");
30399
+ if (existsSync31(rosterPath)) {
30400
+ const roster = JSON.parse(readFileSync27(rosterPath, "utf8"));
29943
30401
  const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
29944
30402
  const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
29945
30403
  for (const emp of roster) {
29946
30404
  if (emp.name === coordinatorName) continue;
29947
30405
  for (const suffix of ["", "-opencode"]) {
29948
- const linkPath = path44.join(binDir, `${emp.name}${suffix}`);
29949
- if (existsSync29(linkPath)) {
30406
+ const linkPath = path45.join(binDir, `${emp.name}${suffix}`);
30407
+ if (existsSync31(linkPath)) {
29950
30408
  if (!dryRun) rmSync(linkPath, { force: true });
29951
30409
  symlinkCount++;
29952
30410
  }
@@ -29959,7 +30417,7 @@ async function runClaudeUninstall(flags = []) {
29959
30417
  }
29960
30418
  } catch {
29961
30419
  }
29962
- if (purge && existsSync29(exeOsDir)) {
30420
+ if (purge && existsSync31(exeOsDir)) {
29963
30421
  if (!dryRun) {
29964
30422
  process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
29965
30423
  process.stdout.write(" Removing ~/.exe-os...\n");
@@ -29984,7 +30442,7 @@ async function checkForUpdateOnBoot() {
29984
30442
  const config = await loadConfig2();
29985
30443
  if (!config.autoUpdate.checkOnBoot) return;
29986
30444
  const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
29987
- const packageRoot = path44.resolve(
30445
+ const packageRoot = path45.resolve(
29988
30446
  new URL("../..", import.meta.url).pathname
29989
30447
  );
29990
30448
  const result = checkForUpdate2(packageRoot);
@@ -30044,7 +30502,7 @@ async function runActivate(key) {
30044
30502
  const idTemplate = getIdentityTemplate(identityKey);
30045
30503
  if (idTemplate) {
30046
30504
  const idPath = identityPath2(name);
30047
- const dir = path44.dirname(idPath);
30505
+ const dir = path45.dirname(idPath);
30048
30506
  if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
30049
30507
  fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
30050
30508
  }