@askexenow/exe-os 0.9.7 → 0.9.9

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 +953 -105
  2. package/dist/bin/backfill-responses.js +952 -104
  3. package/dist/bin/backfill-vectors.js +956 -108
  4. package/dist/bin/cleanup-stale-review-tasks.js +802 -58
  5. package/dist/bin/cli.js +2292 -1070
  6. package/dist/bin/exe-agent-config.js +157 -101
  7. package/dist/bin/exe-agent.js +55 -29
  8. package/dist/bin/exe-assign.js +940 -92
  9. package/dist/bin/exe-boot.js +1424 -442
  10. package/dist/bin/exe-call.js +240 -141
  11. package/dist/bin/exe-cloud.js +198 -70
  12. package/dist/bin/exe-dispatch.js +951 -192
  13. package/dist/bin/exe-doctor.js +791 -51
  14. package/dist/bin/exe-export-behaviors.js +790 -42
  15. package/dist/bin/exe-forget.js +771 -31
  16. package/dist/bin/exe-gateway.js +1592 -521
  17. package/dist/bin/exe-heartbeat.js +850 -109
  18. package/dist/bin/exe-kill.js +783 -35
  19. package/dist/bin/exe-launch-agent.js +1030 -107
  20. package/dist/bin/exe-link.js +916 -110
  21. package/dist/bin/exe-new-employee.js +526 -217
  22. package/dist/bin/exe-pending-messages.js +1046 -62
  23. package/dist/bin/exe-pending-notifications.js +1318 -111
  24. package/dist/bin/exe-pending-reviews.js +1040 -72
  25. package/dist/bin/exe-rename.js +772 -59
  26. package/dist/bin/exe-review.js +772 -32
  27. package/dist/bin/exe-search.js +982 -128
  28. package/dist/bin/exe-session-cleanup.js +1180 -306
  29. package/dist/bin/exe-settings.js +185 -105
  30. package/dist/bin/exe-start-codex.js +886 -132
  31. package/dist/bin/exe-start-opencode.js +873 -119
  32. package/dist/bin/exe-status.js +803 -59
  33. package/dist/bin/exe-team.js +772 -32
  34. package/dist/bin/git-sweep.js +1046 -223
  35. package/dist/bin/graph-backfill.js +779 -31
  36. package/dist/bin/graph-export.js +785 -37
  37. package/dist/bin/install.js +632 -200
  38. package/dist/bin/scan-tasks.js +1055 -232
  39. package/dist/bin/setup.js +1419 -320
  40. package/dist/bin/shard-migrate.js +783 -35
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +782 -34
  43. package/dist/gateway/index.js +1444 -449
  44. package/dist/hooks/bug-report-worker.js +1141 -269
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +1044 -221
  47. package/dist/hooks/error-recall.js +989 -135
  48. package/dist/hooks/exe-heartbeat-hook.js +99 -75
  49. package/dist/hooks/ingest-worker.js +4176 -3226
  50. package/dist/hooks/ingest.js +920 -168
  51. package/dist/hooks/instructions-loaded.js +874 -70
  52. package/dist/hooks/notification.js +860 -56
  53. package/dist/hooks/post-compact.js +881 -73
  54. package/dist/hooks/pre-compact.js +1050 -227
  55. package/dist/hooks/pre-tool-use.js +1084 -159
  56. package/dist/hooks/prompt-ingest-worker.js +1089 -164
  57. package/dist/hooks/prompt-submit.js +1469 -515
  58. package/dist/hooks/response-ingest-worker.js +1104 -179
  59. package/dist/hooks/session-end.js +1085 -251
  60. package/dist/hooks/session-start.js +1241 -231
  61. package/dist/hooks/stop.js +935 -109
  62. package/dist/hooks/subagent-stop.js +881 -73
  63. package/dist/hooks/summary-worker.js +1323 -307
  64. package/dist/index.js +1449 -452
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +909 -115
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +42 -9
  69. package/dist/lib/database.js +739 -33
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +2359 -0
  72. package/dist/lib/device-registry.js +760 -47
  73. package/dist/lib/embedder.js +201 -73
  74. package/dist/lib/employee-templates.js +30 -4
  75. package/dist/lib/employees.js +290 -86
  76. package/dist/lib/exe-daemon-client.js +187 -83
  77. package/dist/lib/exe-daemon.js +1696 -616
  78. package/dist/lib/hybrid-search.js +982 -128
  79. package/dist/lib/identity.js +43 -13
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +167 -80
  82. package/dist/lib/reminders.js +35 -5
  83. package/dist/lib/schedules.js +772 -32
  84. package/dist/lib/skill-learning.js +54 -7
  85. package/dist/lib/store.js +779 -31
  86. package/dist/lib/task-router.js +94 -73
  87. package/dist/lib/tasks.js +298 -225
  88. package/dist/lib/tmux-routing.js +246 -172
  89. package/dist/lib/token-spend.js +52 -14
  90. package/dist/mcp/server.js +2893 -850
  91. package/dist/mcp/tools/complete-reminder.js +35 -5
  92. package/dist/mcp/tools/create-reminder.js +35 -5
  93. package/dist/mcp/tools/create-task.js +507 -323
  94. package/dist/mcp/tools/deactivate-behavior.js +40 -10
  95. package/dist/mcp/tools/list-reminders.js +35 -5
  96. package/dist/mcp/tools/list-tasks.js +277 -104
  97. package/dist/mcp/tools/send-message.js +129 -56
  98. package/dist/mcp/tools/update-task.js +1864 -188
  99. package/dist/runtime/index.js +1083 -259
  100. package/dist/tui/App.js +1501 -434
  101. package/package.json +3 -2
@@ -70,9 +70,47 @@ var init_db_retry = __esm({
70
70
  }
71
71
  });
72
72
 
73
+ // src/lib/secure-files.ts
74
+ import { chmodSync, existsSync, mkdirSync } from "fs";
75
+ import { chmod, mkdir } from "fs/promises";
76
+ async function ensurePrivateDir(dirPath) {
77
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
78
+ try {
79
+ await chmod(dirPath, PRIVATE_DIR_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ function ensurePrivateDirSync(dirPath) {
84
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
85
+ try {
86
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
87
+ } catch {
88
+ }
89
+ }
90
+ async function enforcePrivateFile(filePath) {
91
+ try {
92
+ await chmod(filePath, PRIVATE_FILE_MODE);
93
+ } catch {
94
+ }
95
+ }
96
+ function enforcePrivateFileSync(filePath) {
97
+ try {
98
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
99
+ } catch {
100
+ }
101
+ }
102
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
103
+ var init_secure_files = __esm({
104
+ "src/lib/secure-files.ts"() {
105
+ "use strict";
106
+ PRIVATE_DIR_MODE = 448;
107
+ PRIVATE_FILE_MODE = 384;
108
+ }
109
+ });
110
+
73
111
  // src/lib/config.ts
74
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
75
- import { readFileSync, existsSync, renameSync } from "fs";
112
+ import { readFile, writeFile } from "fs/promises";
113
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
76
114
  import path from "path";
77
115
  import os from "os";
78
116
  function resolveDataDir() {
@@ -80,7 +118,7 @@ function resolveDataDir() {
80
118
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
81
119
  const newDir = path.join(os.homedir(), ".exe-os");
82
120
  const legacyDir = path.join(os.homedir(), ".exe-mem");
83
- if (!existsSync(newDir) && existsSync(legacyDir)) {
121
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
84
122
  try {
85
123
  renameSync(legacyDir, newDir);
86
124
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -143,9 +181,9 @@ function normalizeAutoUpdate(raw) {
143
181
  }
144
182
  async function loadConfig() {
145
183
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
146
- await mkdir(dir, { recursive: true });
184
+ await ensurePrivateDir(dir);
147
185
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
186
+ if (!existsSync2(configPath)) {
149
187
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
188
  }
151
189
  const raw = await readFile(configPath, "utf-8");
@@ -158,6 +196,7 @@ async function loadConfig() {
158
196
  `);
159
197
  try {
160
198
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
199
+ await enforcePrivateFile(configPath);
161
200
  } catch {
162
201
  }
163
202
  }
@@ -177,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
177
216
  var init_config = __esm({
178
217
  "src/lib/config.ts"() {
179
218
  "use strict";
219
+ init_secure_files();
180
220
  EXE_AI_DIR = resolveDataDir();
181
221
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
182
222
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -253,6 +293,120 @@ var init_config = __esm({
253
293
  }
254
294
  });
255
295
 
296
+ // src/lib/runtime-table.ts
297
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
298
+ var init_runtime_table = __esm({
299
+ "src/lib/runtime-table.ts"() {
300
+ "use strict";
301
+ RUNTIME_TABLE = {
302
+ codex: {
303
+ binary: "codex",
304
+ launchMode: "interactive",
305
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
306
+ inlineFlag: "--no-alt-screen",
307
+ apiKeyEnv: "OPENAI_API_KEY",
308
+ defaultModel: "gpt-5.4"
309
+ },
310
+ opencode: {
311
+ binary: "opencode",
312
+ launchMode: "exec",
313
+ autoApproveFlag: "--dangerously-skip-permissions",
314
+ inlineFlag: "",
315
+ apiKeyEnv: "ANTHROPIC_API_KEY",
316
+ defaultModel: "anthropic/claude-sonnet-4-6"
317
+ }
318
+ };
319
+ DEFAULT_RUNTIME = "claude";
320
+ }
321
+ });
322
+
323
+ // src/lib/agent-config.ts
324
+ var agent_config_exports = {};
325
+ __export(agent_config_exports, {
326
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
327
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
328
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
329
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
330
+ clearAgentRuntime: () => clearAgentRuntime,
331
+ getAgentRuntime: () => getAgentRuntime,
332
+ loadAgentConfig: () => loadAgentConfig,
333
+ saveAgentConfig: () => saveAgentConfig,
334
+ setAgentRuntime: () => setAgentRuntime
335
+ });
336
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
337
+ import path2 from "path";
338
+ function loadAgentConfig() {
339
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
340
+ try {
341
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
342
+ } catch {
343
+ return {};
344
+ }
345
+ }
346
+ function saveAgentConfig(config) {
347
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
348
+ ensurePrivateDirSync(dir);
349
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
350
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
351
+ }
352
+ function getAgentRuntime(agentId) {
353
+ const config = loadAgentConfig();
354
+ const entry = config[agentId];
355
+ if (entry) return entry;
356
+ const orgDefault = config["default"];
357
+ if (orgDefault) return orgDefault;
358
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
359
+ }
360
+ function setAgentRuntime(agentId, runtime, model) {
361
+ const knownModels = KNOWN_RUNTIMES[runtime];
362
+ if (!knownModels) {
363
+ return {
364
+ ok: false,
365
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
366
+ };
367
+ }
368
+ if (!knownModels.includes(model)) {
369
+ return {
370
+ ok: false,
371
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
372
+ };
373
+ }
374
+ const config = loadAgentConfig();
375
+ config[agentId] = { runtime, model };
376
+ saveAgentConfig(config);
377
+ return { ok: true };
378
+ }
379
+ function clearAgentRuntime(agentId) {
380
+ const config = loadAgentConfig();
381
+ delete config[agentId];
382
+ saveAgentConfig(config);
383
+ }
384
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
385
+ var init_agent_config = __esm({
386
+ "src/lib/agent-config.ts"() {
387
+ "use strict";
388
+ init_config();
389
+ init_runtime_table();
390
+ init_secure_files();
391
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
392
+ KNOWN_RUNTIMES = {
393
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
394
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
395
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
396
+ };
397
+ RUNTIME_LABELS = {
398
+ claude: "Claude Code (Anthropic)",
399
+ codex: "Codex (OpenAI)",
400
+ opencode: "OpenCode (open source)"
401
+ };
402
+ DEFAULT_MODELS = {
403
+ claude: "claude-opus-4",
404
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
405
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
406
+ };
407
+ }
408
+ });
409
+
256
410
  // src/lib/employees.ts
257
411
  var employees_exports = {};
258
412
  __export(employees_exports, {
@@ -268,6 +422,7 @@ __export(employees_exports, {
268
422
  getEmployeeByRole: () => getEmployeeByRole,
269
423
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
270
424
  hasRole: () => hasRole,
425
+ hireEmployee: () => hireEmployee,
271
426
  isCoordinatorName: () => isCoordinatorName,
272
427
  isCoordinatorRole: () => isCoordinatorRole,
273
428
  isMultiInstance: () => isMultiInstance,
@@ -280,9 +435,9 @@ __export(employees_exports, {
280
435
  validateEmployeeName: () => validateEmployeeName
281
436
  });
282
437
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
283
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
438
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
284
439
  import { execSync } from "child_process";
285
- import path2 from "path";
440
+ import path3 from "path";
286
441
  import os2 from "os";
287
442
  function normalizeRole(role) {
288
443
  return (role ?? "").trim().toLowerCase();
@@ -319,7 +474,7 @@ function validateEmployeeName(name) {
319
474
  return { valid: true };
320
475
  }
321
476
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
322
- if (!existsSync2(employeesPath)) {
477
+ if (!existsSync4(employeesPath)) {
323
478
  return [];
324
479
  }
325
480
  const raw = await readFile2(employeesPath, "utf-8");
@@ -330,13 +485,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
330
485
  }
331
486
  }
332
487
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
333
- await mkdir2(path2.dirname(employeesPath), { recursive: true });
488
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
334
489
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
335
490
  }
336
491
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
337
- if (!existsSync2(employeesPath)) return [];
492
+ if (!existsSync4(employeesPath)) return [];
338
493
  try {
339
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
494
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
340
495
  } catch {
341
496
  return [];
342
497
  }
@@ -378,6 +533,52 @@ function addEmployee(employees, employee) {
378
533
  }
379
534
  return [...employees, normalized];
380
535
  }
536
+ function appendToCoordinatorTeam(employee) {
537
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
538
+ if (!coordinator) return;
539
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
540
+ if (!existsSync4(idPath)) return;
541
+ const content = readFileSync3(idPath, "utf-8");
542
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
543
+ const teamMatch = content.match(TEAM_SECTION_RE);
544
+ if (!teamMatch || teamMatch.index === void 0) return;
545
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
546
+ const nextHeading = afterTeam.match(/\n## /);
547
+ const entry = `
548
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
549
+ `;
550
+ let updated;
551
+ if (nextHeading && nextHeading.index !== void 0) {
552
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
553
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
554
+ } else {
555
+ updated = content.trimEnd() + "\n" + entry;
556
+ }
557
+ writeFileSync2(idPath, updated, "utf-8");
558
+ }
559
+ function capitalize(s) {
560
+ return s.charAt(0).toUpperCase() + s.slice(1);
561
+ }
562
+ async function hireEmployee(employee) {
563
+ const employees = await loadEmployees();
564
+ const updated = addEmployee(employees, employee);
565
+ await saveEmployees(updated);
566
+ try {
567
+ appendToCoordinatorTeam(employee);
568
+ } catch {
569
+ }
570
+ try {
571
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
572
+ const config = loadAgentConfig2();
573
+ const name = employee.name.toLowerCase();
574
+ if (!config[name] && config["default"]) {
575
+ config[name] = { ...config["default"] };
576
+ saveAgentConfig2(config);
577
+ }
578
+ } catch {
579
+ }
580
+ return updated;
581
+ }
381
582
  async function normalizeRosterCase(rosterPath) {
382
583
  const employees = await loadEmployees(rosterPath);
383
584
  let changed = false;
@@ -387,14 +588,14 @@ async function normalizeRosterCase(rosterPath) {
387
588
  emp.name = emp.name.toLowerCase();
388
589
  changed = true;
389
590
  try {
390
- const identityDir = path2.join(os2.homedir(), ".exe-os", "identity");
391
- const oldPath = path2.join(identityDir, `${oldName}.md`);
392
- const newPath = path2.join(identityDir, `${emp.name}.md`);
393
- if (existsSync2(oldPath) && !existsSync2(newPath)) {
591
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
592
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
593
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
594
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
394
595
  renameSync2(oldPath, newPath);
395
- } else if (existsSync2(oldPath) && oldPath !== newPath) {
396
- const content = readFileSync2(oldPath, "utf-8");
397
- writeFileSync(newPath, content, "utf-8");
596
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
597
+ const content = readFileSync3(oldPath, "utf-8");
598
+ writeFileSync2(newPath, content, "utf-8");
398
599
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
399
600
  unlinkSync(oldPath);
400
601
  }
@@ -424,7 +625,7 @@ function registerBinSymlinks(name) {
424
625
  errors.push("Could not find 'exe-os' in PATH");
425
626
  return { created, skipped, errors };
426
627
  }
427
- const binDir = path2.dirname(exeBinPath);
628
+ const binDir = path3.dirname(exeBinPath);
428
629
  let target;
429
630
  try {
430
631
  target = readlinkSync(exeBinPath);
@@ -434,8 +635,8 @@ function registerBinSymlinks(name) {
434
635
  }
435
636
  for (const suffix of ["", "-opencode"]) {
436
637
  const linkName = `${name}${suffix}`;
437
- const linkPath = path2.join(binDir, linkName);
438
- if (existsSync2(linkPath)) {
638
+ const linkPath = path3.join(binDir, linkName);
639
+ if (existsSync4(linkPath)) {
439
640
  skipped.push(linkName);
440
641
  continue;
441
642
  }
@@ -448,21 +649,619 @@ function registerBinSymlinks(name) {
448
649
  }
449
650
  return { created, skipped, errors };
450
651
  }
451
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
652
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
452
653
  var init_employees = __esm({
453
654
  "src/lib/employees.ts"() {
454
655
  "use strict";
455
656
  init_config();
456
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
657
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
457
658
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
458
659
  COORDINATOR_ROLE = "COO";
459
660
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
661
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
662
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
663
+ }
664
+ });
665
+
666
+ // src/lib/database-adapter.ts
667
+ import os3 from "os";
668
+ import path4 from "path";
669
+ import { createRequire } from "module";
670
+ import { pathToFileURL } from "url";
671
+ function quotedIdentifier(identifier) {
672
+ return `"${identifier.replace(/"/g, '""')}"`;
673
+ }
674
+ function unqualifiedTableName(name) {
675
+ const raw = name.trim().replace(/^"|"$/g, "");
676
+ const parts = raw.split(".");
677
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
678
+ }
679
+ function stripTrailingSemicolon(sql) {
680
+ return sql.trim().replace(/;+\s*$/u, "");
681
+ }
682
+ function appendClause(sql, clause) {
683
+ const trimmed = stripTrailingSemicolon(sql);
684
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
685
+ if (!returningMatch) {
686
+ return `${trimmed}${clause}`;
687
+ }
688
+ const idx = returningMatch.index;
689
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
690
+ }
691
+ function normalizeStatement(stmt) {
692
+ if (typeof stmt === "string") {
693
+ return { kind: "positional", sql: stmt, args: [] };
694
+ }
695
+ const sql = stmt.sql;
696
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
697
+ return { kind: "positional", sql, args: stmt.args ?? [] };
698
+ }
699
+ return { kind: "named", sql, args: stmt.args };
700
+ }
701
+ function rewriteBooleanLiterals(sql) {
702
+ let out = sql;
703
+ for (const column of BOOLEAN_COLUMN_NAMES) {
704
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
705
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
706
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
707
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
708
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
709
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
710
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
711
+ }
712
+ return out;
713
+ }
714
+ function rewriteInsertOrIgnore(sql) {
715
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
716
+ return sql;
717
+ }
718
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
719
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
720
+ }
721
+ function rewriteInsertOrReplace(sql) {
722
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
723
+ if (!match) {
724
+ return sql;
725
+ }
726
+ const rawTable = match[1];
727
+ const rawColumns = match[2];
728
+ const remainder = match[3];
729
+ const tableName = unqualifiedTableName(rawTable);
730
+ const conflictKeys = UPSERT_KEYS[tableName];
731
+ if (!conflictKeys?.length) {
732
+ return sql;
733
+ }
734
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
735
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
736
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
737
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
738
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
739
+ }
740
+ function rewriteSql(sql) {
741
+ let out = sql;
742
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
743
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
744
+ out = rewriteBooleanLiterals(out);
745
+ out = rewriteInsertOrReplace(out);
746
+ out = rewriteInsertOrIgnore(out);
747
+ return stripTrailingSemicolon(out);
748
+ }
749
+ function toBoolean(value) {
750
+ if (value === null || value === void 0) return value;
751
+ if (typeof value === "boolean") return value;
752
+ if (typeof value === "number") return value !== 0;
753
+ if (typeof value === "bigint") return value !== 0n;
754
+ if (typeof value === "string") {
755
+ const normalized = value.trim().toLowerCase();
756
+ if (normalized === "0" || normalized === "false") return false;
757
+ if (normalized === "1" || normalized === "true") return true;
758
+ }
759
+ return Boolean(value);
760
+ }
761
+ function countQuestionMarks(sql, end) {
762
+ let count = 0;
763
+ let inSingle = false;
764
+ let inDouble = false;
765
+ let inLineComment = false;
766
+ let inBlockComment = false;
767
+ for (let i = 0; i < end; i++) {
768
+ const ch = sql[i];
769
+ const next = sql[i + 1];
770
+ if (inLineComment) {
771
+ if (ch === "\n") inLineComment = false;
772
+ continue;
773
+ }
774
+ if (inBlockComment) {
775
+ if (ch === "*" && next === "/") {
776
+ inBlockComment = false;
777
+ i += 1;
778
+ }
779
+ continue;
780
+ }
781
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
782
+ inLineComment = true;
783
+ i += 1;
784
+ continue;
785
+ }
786
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
787
+ inBlockComment = true;
788
+ i += 1;
789
+ continue;
790
+ }
791
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
792
+ inSingle = !inSingle;
793
+ continue;
794
+ }
795
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
796
+ inDouble = !inDouble;
797
+ continue;
798
+ }
799
+ if (!inSingle && !inDouble && ch === "?") {
800
+ count += 1;
801
+ }
802
+ }
803
+ return count;
804
+ }
805
+ function findBooleanPlaceholderIndexes(sql) {
806
+ const indexes = /* @__PURE__ */ new Set();
807
+ for (const column of BOOLEAN_COLUMN_NAMES) {
808
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
809
+ for (const match of sql.matchAll(pattern)) {
810
+ const matchText = match[0];
811
+ const qIndex = match.index + matchText.lastIndexOf("?");
812
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
813
+ }
814
+ }
815
+ return indexes;
816
+ }
817
+ function coerceInsertBooleanArgs(sql, args) {
818
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
819
+ if (!match) return;
820
+ const rawTable = match[1];
821
+ const rawColumns = match[2];
822
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
823
+ if (!boolColumns?.size) return;
824
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
825
+ for (const [index, column] of columns.entries()) {
826
+ if (boolColumns.has(column) && index < args.length) {
827
+ args[index] = toBoolean(args[index]);
828
+ }
829
+ }
830
+ }
831
+ function coerceUpdateBooleanArgs(sql, args) {
832
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
833
+ if (!match) return;
834
+ const rawTable = match[1];
835
+ const setClause = match[2];
836
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
837
+ if (!boolColumns?.size) return;
838
+ const assignments = setClause.split(",");
839
+ let placeholderIndex = 0;
840
+ for (const assignment of assignments) {
841
+ if (!assignment.includes("?")) continue;
842
+ placeholderIndex += 1;
843
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
844
+ if (colMatch && boolColumns.has(colMatch[1])) {
845
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
846
+ }
847
+ }
848
+ }
849
+ function coerceBooleanArgs(sql, args) {
850
+ const nextArgs = [...args];
851
+ coerceInsertBooleanArgs(sql, nextArgs);
852
+ coerceUpdateBooleanArgs(sql, nextArgs);
853
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
854
+ for (const index of placeholderIndexes) {
855
+ if (index > 0 && index <= nextArgs.length) {
856
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
857
+ }
858
+ }
859
+ return nextArgs;
860
+ }
861
+ function convertQuestionMarksToDollarParams(sql) {
862
+ let out = "";
863
+ let placeholder = 0;
864
+ let inSingle = false;
865
+ let inDouble = false;
866
+ let inLineComment = false;
867
+ let inBlockComment = false;
868
+ for (let i = 0; i < sql.length; i++) {
869
+ const ch = sql[i];
870
+ const next = sql[i + 1];
871
+ if (inLineComment) {
872
+ out += ch;
873
+ if (ch === "\n") inLineComment = false;
874
+ continue;
875
+ }
876
+ if (inBlockComment) {
877
+ out += ch;
878
+ if (ch === "*" && next === "/") {
879
+ out += next;
880
+ inBlockComment = false;
881
+ i += 1;
882
+ }
883
+ continue;
884
+ }
885
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
886
+ out += ch + next;
887
+ inLineComment = true;
888
+ i += 1;
889
+ continue;
890
+ }
891
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
892
+ out += ch + next;
893
+ inBlockComment = true;
894
+ i += 1;
895
+ continue;
896
+ }
897
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
898
+ inSingle = !inSingle;
899
+ out += ch;
900
+ continue;
901
+ }
902
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
903
+ inDouble = !inDouble;
904
+ out += ch;
905
+ continue;
906
+ }
907
+ if (!inSingle && !inDouble && ch === "?") {
908
+ placeholder += 1;
909
+ out += `$${placeholder}`;
910
+ continue;
911
+ }
912
+ out += ch;
913
+ }
914
+ return out;
915
+ }
916
+ function translateStatementForPostgres(stmt) {
917
+ const normalized = normalizeStatement(stmt);
918
+ if (normalized.kind === "named") {
919
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
920
+ }
921
+ const rewrittenSql = rewriteSql(normalized.sql);
922
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
923
+ return {
924
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
925
+ args: coercedArgs
926
+ };
927
+ }
928
+ function shouldBypassPostgres(stmt) {
929
+ const normalized = normalizeStatement(stmt);
930
+ if (normalized.kind === "named") {
931
+ return true;
932
+ }
933
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
934
+ }
935
+ function shouldFallbackOnError(error) {
936
+ const message = error instanceof Error ? error.message : String(error);
937
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
938
+ }
939
+ function isReadQuery(sql) {
940
+ const trimmed = sql.trimStart();
941
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
942
+ }
943
+ function buildRow(row, columns) {
944
+ const values = columns.map((column) => row[column]);
945
+ return Object.assign(values, row);
946
+ }
947
+ function buildResultSet(rows, rowsAffected = 0) {
948
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
949
+ const resultRows = rows.map((row) => buildRow(row, columns));
950
+ return {
951
+ columns,
952
+ columnTypes: columns.map(() => ""),
953
+ rows: resultRows,
954
+ rowsAffected,
955
+ lastInsertRowid: void 0,
956
+ toJSON() {
957
+ return {
958
+ columns,
959
+ columnTypes: columns.map(() => ""),
960
+ rows,
961
+ rowsAffected,
962
+ lastInsertRowid: void 0
963
+ };
964
+ }
965
+ };
966
+ }
967
+ async function loadPrismaClient() {
968
+ if (!prismaClientPromise) {
969
+ prismaClientPromise = (async () => {
970
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
971
+ if (explicitPath) {
972
+ const module2 = await import(pathToFileURL(explicitPath).href);
973
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
974
+ if (!PrismaClient2) {
975
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
976
+ }
977
+ return new PrismaClient2();
978
+ }
979
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
980
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
981
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
982
+ const module = await import(pathToFileURL(prismaEntry).href);
983
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
984
+ if (!PrismaClient) {
985
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
986
+ }
987
+ return new PrismaClient();
988
+ })();
989
+ }
990
+ return prismaClientPromise;
991
+ }
992
+ async function ensureCompatibilityViews(prisma) {
993
+ if (!compatibilityBootstrapPromise) {
994
+ compatibilityBootstrapPromise = (async () => {
995
+ for (const mapping of VIEW_MAPPINGS) {
996
+ const relation = mapping.source.replace(/"/g, "");
997
+ const rows = await prisma.$queryRawUnsafe(
998
+ "SELECT to_regclass($1) AS regclass",
999
+ relation
1000
+ );
1001
+ if (!rows[0]?.regclass) {
1002
+ continue;
1003
+ }
1004
+ await prisma.$executeRawUnsafe(
1005
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1006
+ );
1007
+ }
1008
+ })();
1009
+ }
1010
+ return compatibilityBootstrapPromise;
1011
+ }
1012
+ async function executeOnPrisma(executor, stmt) {
1013
+ const translated = translateStatementForPostgres(stmt);
1014
+ if (isReadQuery(translated.sql)) {
1015
+ const rows = await executor.$queryRawUnsafe(
1016
+ translated.sql,
1017
+ ...translated.args
1018
+ );
1019
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1020
+ }
1021
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1022
+ return buildResultSet([], rowsAffected);
1023
+ }
1024
+ function splitSqlStatements(sql) {
1025
+ const parts = [];
1026
+ let current = "";
1027
+ let inSingle = false;
1028
+ let inDouble = false;
1029
+ let inLineComment = false;
1030
+ let inBlockComment = false;
1031
+ for (let i = 0; i < sql.length; i++) {
1032
+ const ch = sql[i];
1033
+ const next = sql[i + 1];
1034
+ if (inLineComment) {
1035
+ current += ch;
1036
+ if (ch === "\n") inLineComment = false;
1037
+ continue;
1038
+ }
1039
+ if (inBlockComment) {
1040
+ current += ch;
1041
+ if (ch === "*" && next === "/") {
1042
+ current += next;
1043
+ inBlockComment = false;
1044
+ i += 1;
1045
+ }
1046
+ continue;
1047
+ }
1048
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1049
+ current += ch + next;
1050
+ inLineComment = true;
1051
+ i += 1;
1052
+ continue;
1053
+ }
1054
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1055
+ current += ch + next;
1056
+ inBlockComment = true;
1057
+ i += 1;
1058
+ continue;
1059
+ }
1060
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1061
+ inSingle = !inSingle;
1062
+ current += ch;
1063
+ continue;
1064
+ }
1065
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1066
+ inDouble = !inDouble;
1067
+ current += ch;
1068
+ continue;
1069
+ }
1070
+ if (!inSingle && !inDouble && ch === ";") {
1071
+ if (current.trim()) {
1072
+ parts.push(current.trim());
1073
+ }
1074
+ current = "";
1075
+ continue;
1076
+ }
1077
+ current += ch;
1078
+ }
1079
+ if (current.trim()) {
1080
+ parts.push(current.trim());
1081
+ }
1082
+ return parts;
1083
+ }
1084
+ async function createPrismaDbAdapter(fallbackClient) {
1085
+ const prisma = await loadPrismaClient();
1086
+ await ensureCompatibilityViews(prisma);
1087
+ let closed = false;
1088
+ let adapter;
1089
+ const fallbackExecute = async (stmt, error) => {
1090
+ if (!fallbackClient) {
1091
+ if (error) throw error;
1092
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1093
+ }
1094
+ if (error) {
1095
+ process.stderr.write(
1096
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1097
+ `
1098
+ );
1099
+ }
1100
+ return fallbackClient.execute(stmt);
1101
+ };
1102
+ adapter = {
1103
+ async execute(stmt) {
1104
+ if (shouldBypassPostgres(stmt)) {
1105
+ return fallbackExecute(stmt);
1106
+ }
1107
+ try {
1108
+ return await executeOnPrisma(prisma, stmt);
1109
+ } catch (error) {
1110
+ if (shouldFallbackOnError(error)) {
1111
+ return fallbackExecute(stmt, error);
1112
+ }
1113
+ throw error;
1114
+ }
1115
+ },
1116
+ async batch(stmts, mode) {
1117
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1118
+ if (!fallbackClient) {
1119
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1120
+ }
1121
+ return fallbackClient.batch(stmts, mode);
1122
+ }
1123
+ try {
1124
+ if (prisma.$transaction) {
1125
+ return await prisma.$transaction(async (tx) => {
1126
+ const results2 = [];
1127
+ for (const stmt of stmts) {
1128
+ results2.push(await executeOnPrisma(tx, stmt));
1129
+ }
1130
+ return results2;
1131
+ });
1132
+ }
1133
+ const results = [];
1134
+ for (const stmt of stmts) {
1135
+ results.push(await executeOnPrisma(prisma, stmt));
1136
+ }
1137
+ return results;
1138
+ } catch (error) {
1139
+ if (fallbackClient && shouldFallbackOnError(error)) {
1140
+ process.stderr.write(
1141
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1142
+ `
1143
+ );
1144
+ return fallbackClient.batch(stmts, mode);
1145
+ }
1146
+ throw error;
1147
+ }
1148
+ },
1149
+ async migrate(stmts) {
1150
+ if (fallbackClient) {
1151
+ return fallbackClient.migrate(stmts);
1152
+ }
1153
+ return adapter.batch(stmts, "deferred");
1154
+ },
1155
+ async transaction(mode) {
1156
+ if (!fallbackClient) {
1157
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1158
+ }
1159
+ return fallbackClient.transaction(mode);
1160
+ },
1161
+ async executeMultiple(sql) {
1162
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1163
+ return fallbackClient.executeMultiple(sql);
1164
+ }
1165
+ for (const statement of splitSqlStatements(sql)) {
1166
+ await adapter.execute(statement);
1167
+ }
1168
+ },
1169
+ async sync() {
1170
+ if (fallbackClient) {
1171
+ return fallbackClient.sync();
1172
+ }
1173
+ return { frame_no: 0, frames_synced: 0 };
1174
+ },
1175
+ close() {
1176
+ closed = true;
1177
+ prismaClientPromise = null;
1178
+ compatibilityBootstrapPromise = null;
1179
+ void prisma.$disconnect?.();
1180
+ },
1181
+ get closed() {
1182
+ return closed;
1183
+ },
1184
+ get protocol() {
1185
+ return "prisma-postgres";
1186
+ }
1187
+ };
1188
+ return adapter;
1189
+ }
1190
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1191
+ var init_database_adapter = __esm({
1192
+ "src/lib/database-adapter.ts"() {
1193
+ "use strict";
1194
+ VIEW_MAPPINGS = [
1195
+ { view: "memories", source: "memory.memory_records" },
1196
+ { view: "tasks", source: "memory.tasks" },
1197
+ { view: "behaviors", source: "memory.behaviors" },
1198
+ { view: "entities", source: "memory.entities" },
1199
+ { view: "relationships", source: "memory.relationships" },
1200
+ { view: "entity_memories", source: "memory.entity_memories" },
1201
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1202
+ { view: "notifications", source: "memory.notifications" },
1203
+ { view: "messages", source: "memory.messages" },
1204
+ { view: "users", source: "wiki.users" },
1205
+ { view: "workspaces", source: "wiki.workspaces" },
1206
+ { view: "workspace_users", source: "wiki.workspace_users" },
1207
+ { view: "documents", source: "wiki.workspace_documents" },
1208
+ { view: "chats", source: "wiki.workspace_chats" }
1209
+ ];
1210
+ UPSERT_KEYS = {
1211
+ memories: ["id"],
1212
+ tasks: ["id"],
1213
+ behaviors: ["id"],
1214
+ entities: ["id"],
1215
+ relationships: ["id"],
1216
+ entity_aliases: ["alias"],
1217
+ notifications: ["id"],
1218
+ messages: ["id"],
1219
+ users: ["id"],
1220
+ workspaces: ["id"],
1221
+ workspace_users: ["id"],
1222
+ documents: ["id"],
1223
+ chats: ["id"]
1224
+ };
1225
+ BOOLEAN_COLUMNS_BY_TABLE = {
1226
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1227
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1228
+ notifications: /* @__PURE__ */ new Set(["read"]),
1229
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1230
+ };
1231
+ BOOLEAN_COLUMN_NAMES = new Set(
1232
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1233
+ );
1234
+ IMMEDIATE_FALLBACK_PATTERNS = [
1235
+ /\bPRAGMA\b/i,
1236
+ /\bsqlite_master\b/i,
1237
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1238
+ /\bMATCH\b/i,
1239
+ /\bvector_distance_cos\s*\(/i,
1240
+ /\bjson_extract\s*\(/i,
1241
+ /\bjulianday\s*\(/i,
1242
+ /\bstrftime\s*\(/i,
1243
+ /\blast_insert_rowid\s*\(/i
1244
+ ];
1245
+ prismaClientPromise = null;
1246
+ compatibilityBootstrapPromise = null;
460
1247
  }
461
1248
  });
462
1249
 
463
1250
  // src/lib/database.ts
464
1251
  import { createClient } from "@libsql/client";
465
1252
  async function initDatabase(config) {
1253
+ if (_walCheckpointTimer) {
1254
+ clearInterval(_walCheckpointTimer);
1255
+ _walCheckpointTimer = null;
1256
+ }
1257
+ if (_daemonClient) {
1258
+ _daemonClient.close();
1259
+ _daemonClient = null;
1260
+ }
1261
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1262
+ _adapterClient.close();
1263
+ }
1264
+ _adapterClient = null;
466
1265
  if (_client) {
467
1266
  _client.close();
468
1267
  _client = null;
@@ -476,6 +1275,7 @@ async function initDatabase(config) {
476
1275
  }
477
1276
  _client = createClient(opts);
478
1277
  _resilientClient = wrapWithRetry(_client);
1278
+ _adapterClient = _resilientClient;
479
1279
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
480
1280
  });
481
1281
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -486,11 +1286,17 @@ async function initDatabase(config) {
486
1286
  });
487
1287
  }, 3e4);
488
1288
  _walCheckpointTimer.unref();
1289
+ if (process.env.DATABASE_URL) {
1290
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1291
+ }
489
1292
  }
490
1293
  function getClient() {
491
- if (!_resilientClient) {
1294
+ if (!_adapterClient) {
492
1295
  throw new Error("Database client not initialized. Call initDatabase() first.");
493
1296
  }
1297
+ if (process.env.DATABASE_URL) {
1298
+ return _adapterClient;
1299
+ }
494
1300
  if (process.env.EXE_IS_DAEMON === "1") {
495
1301
  return _resilientClient;
496
1302
  }
@@ -783,6 +1589,7 @@ async function ensureSchema() {
783
1589
  project TEXT NOT NULL,
784
1590
  summary TEXT NOT NULL,
785
1591
  task_file TEXT,
1592
+ session_scope TEXT,
786
1593
  read INTEGER NOT NULL DEFAULT 0,
787
1594
  created_at TEXT NOT NULL
788
1595
  );
@@ -791,7 +1598,7 @@ async function ensureSchema() {
791
1598
  ON notifications(read);
792
1599
 
793
1600
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
794
- ON notifications(agent_id);
1601
+ ON notifications(agent_id, session_scope);
795
1602
 
796
1603
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
797
1604
  ON notifications(task_file);
@@ -829,6 +1636,7 @@ async function ensureSchema() {
829
1636
  target_agent TEXT NOT NULL,
830
1637
  target_project TEXT,
831
1638
  target_device TEXT NOT NULL DEFAULT 'local',
1639
+ session_scope TEXT,
832
1640
  content TEXT NOT NULL,
833
1641
  priority TEXT DEFAULT 'normal',
834
1642
  status TEXT DEFAULT 'pending',
@@ -842,10 +1650,31 @@ async function ensureSchema() {
842
1650
  );
843
1651
 
844
1652
  CREATE INDEX IF NOT EXISTS idx_messages_target
845
- ON messages(target_agent, status);
1653
+ ON messages(target_agent, session_scope, status);
846
1654
 
847
1655
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
848
- ON messages(target_agent, from_agent, server_seq);
1656
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1657
+ `);
1658
+ try {
1659
+ await client.execute({
1660
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1661
+ args: []
1662
+ });
1663
+ } catch {
1664
+ }
1665
+ try {
1666
+ await client.execute({
1667
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1668
+ args: []
1669
+ });
1670
+ } catch {
1671
+ }
1672
+ await client.executeMultiple(`
1673
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1674
+ ON notifications(agent_id, session_scope, read, created_at);
1675
+
1676
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1677
+ ON messages(target_agent, session_scope, status, created_at);
849
1678
  `);
850
1679
  try {
851
1680
  await client.execute({
@@ -1429,28 +2258,45 @@ async function ensureSchema() {
1429
2258
  } catch {
1430
2259
  }
1431
2260
  }
2261
+ try {
2262
+ await client.execute({
2263
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2264
+ args: []
2265
+ });
2266
+ } catch {
2267
+ }
1432
2268
  }
1433
2269
  async function disposeDatabase() {
2270
+ if (_walCheckpointTimer) {
2271
+ clearInterval(_walCheckpointTimer);
2272
+ _walCheckpointTimer = null;
2273
+ }
1434
2274
  if (_daemonClient) {
1435
2275
  _daemonClient.close();
1436
2276
  _daemonClient = null;
1437
2277
  }
2278
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2279
+ _adapterClient.close();
2280
+ }
2281
+ _adapterClient = null;
1438
2282
  if (_client) {
1439
2283
  _client.close();
1440
2284
  _client = null;
1441
2285
  _resilientClient = null;
1442
2286
  }
1443
2287
  }
1444
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2288
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1445
2289
  var init_database = __esm({
1446
2290
  "src/lib/database.ts"() {
1447
2291
  "use strict";
1448
2292
  init_db_retry();
1449
2293
  init_employees();
2294
+ init_database_adapter();
1450
2295
  _client = null;
1451
2296
  _resilientClient = null;
1452
2297
  _walCheckpointTimer = null;
1453
2298
  _daemonClient = null;
2299
+ _adapterClient = null;
1454
2300
  initTurso = initDatabase;
1455
2301
  disposeTurso = disposeDatabase;
1456
2302
  }
@@ -1461,6 +2307,7 @@ var shard_manager_exports = {};
1461
2307
  __export(shard_manager_exports, {
1462
2308
  disposeShards: () => disposeShards,
1463
2309
  ensureShardSchema: () => ensureShardSchema,
2310
+ getOpenShardCount: () => getOpenShardCount,
1464
2311
  getReadyShardClient: () => getReadyShardClient,
1465
2312
  getShardClient: () => getShardClient,
1466
2313
  getShardsDir: () => getShardsDir,
@@ -1469,15 +2316,18 @@ __export(shard_manager_exports, {
1469
2316
  listShards: () => listShards,
1470
2317
  shardExists: () => shardExists
1471
2318
  });
1472
- import path4 from "path";
1473
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
2319
+ import path6 from "path";
2320
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
1474
2321
  import { createClient as createClient2 } from "@libsql/client";
1475
2322
  function initShardManager(encryptionKey) {
1476
2323
  _encryptionKey = encryptionKey;
1477
- if (!existsSync4(SHARDS_DIR)) {
1478
- mkdirSync(SHARDS_DIR, { recursive: true });
2324
+ if (!existsSync6(SHARDS_DIR)) {
2325
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1479
2326
  }
1480
2327
  _shardingEnabled = true;
2328
+ if (_evictionTimer) clearInterval(_evictionTimer);
2329
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
2330
+ _evictionTimer.unref();
1481
2331
  }
1482
2332
  function isShardingEnabled() {
1483
2333
  return _shardingEnabled;
@@ -1494,21 +2344,28 @@ function getShardClient(projectName) {
1494
2344
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1495
2345
  }
1496
2346
  const cached = _shards.get(safeName);
1497
- if (cached) return cached;
1498
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
2347
+ if (cached) {
2348
+ _shardLastAccess.set(safeName, Date.now());
2349
+ return cached;
2350
+ }
2351
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2352
+ evictLRU();
2353
+ }
2354
+ const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
1499
2355
  const client = createClient2({
1500
2356
  url: `file:${dbPath}`,
1501
2357
  encryptionKey: _encryptionKey
1502
2358
  });
1503
2359
  _shards.set(safeName, client);
2360
+ _shardLastAccess.set(safeName, Date.now());
1504
2361
  return client;
1505
2362
  }
1506
2363
  function shardExists(projectName) {
1507
2364
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1508
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
2365
+ return existsSync6(path6.join(SHARDS_DIR, `${safeName}.db`));
1509
2366
  }
1510
2367
  function listShards() {
1511
- if (!existsSync4(SHARDS_DIR)) return [];
2368
+ if (!existsSync6(SHARDS_DIR)) return [];
1512
2369
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1513
2370
  }
1514
2371
  async function ensureShardSchema(client) {
@@ -1560,6 +2417,8 @@ async function ensureShardSchema(client) {
1560
2417
  for (const col of [
1561
2418
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
1562
2419
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2420
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2421
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
1563
2422
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
1564
2423
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
1565
2424
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -1582,7 +2441,23 @@ async function ensureShardSchema(client) {
1582
2441
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1583
2442
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1584
2443
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1585
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2444
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2445
+ // Metadata enrichment columns (must match database.ts)
2446
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2447
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2448
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2449
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2450
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2451
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2452
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2453
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2454
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2455
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2456
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2457
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2458
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2459
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2460
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1586
2461
  ]) {
1587
2462
  try {
1588
2463
  await client.execute(col);
@@ -1681,21 +2556,69 @@ async function getReadyShardClient(projectName) {
1681
2556
  await ensureShardSchema(client);
1682
2557
  return client;
1683
2558
  }
2559
+ function evictLRU() {
2560
+ let oldest = null;
2561
+ let oldestTime = Infinity;
2562
+ for (const [name, time] of _shardLastAccess) {
2563
+ if (time < oldestTime) {
2564
+ oldestTime = time;
2565
+ oldest = name;
2566
+ }
2567
+ }
2568
+ if (oldest) {
2569
+ const client = _shards.get(oldest);
2570
+ if (client) {
2571
+ client.close();
2572
+ }
2573
+ _shards.delete(oldest);
2574
+ _shardLastAccess.delete(oldest);
2575
+ }
2576
+ }
2577
+ function evictIdleShards() {
2578
+ const now = Date.now();
2579
+ const toEvict = [];
2580
+ for (const [name, lastAccess] of _shardLastAccess) {
2581
+ if (now - lastAccess > SHARD_IDLE_MS) {
2582
+ toEvict.push(name);
2583
+ }
2584
+ }
2585
+ for (const name of toEvict) {
2586
+ const client = _shards.get(name);
2587
+ if (client) {
2588
+ client.close();
2589
+ }
2590
+ _shards.delete(name);
2591
+ _shardLastAccess.delete(name);
2592
+ }
2593
+ }
2594
+ function getOpenShardCount() {
2595
+ return _shards.size;
2596
+ }
1684
2597
  function disposeShards() {
2598
+ if (_evictionTimer) {
2599
+ clearInterval(_evictionTimer);
2600
+ _evictionTimer = null;
2601
+ }
1685
2602
  for (const [, client] of _shards) {
1686
2603
  client.close();
1687
2604
  }
1688
2605
  _shards.clear();
2606
+ _shardLastAccess.clear();
1689
2607
  _shardingEnabled = false;
1690
2608
  _encryptionKey = null;
1691
2609
  }
1692
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2610
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
1693
2611
  var init_shard_manager = __esm({
1694
2612
  "src/lib/shard-manager.ts"() {
1695
2613
  "use strict";
1696
2614
  init_config();
1697
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2615
+ SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
2616
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2617
+ MAX_OPEN_SHARDS = 10;
2618
+ EVICTION_INTERVAL_MS = 60 * 1e3;
1698
2619
  _shards = /* @__PURE__ */ new Map();
2620
+ _shardLastAccess = /* @__PURE__ */ new Map();
2621
+ _evictionTimer = null;
1699
2622
  _encryptionKey = null;
1700
2623
  _shardingEnabled = false;
1701
2624
  }
@@ -2583,9 +3506,9 @@ __export(active_agent_exports, {
2583
3506
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
2584
3507
  writeActiveAgent: () => writeActiveAgent
2585
3508
  });
2586
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
3509
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2587
3510
  import { execSync as execSync4 } from "child_process";
2588
- import path6 from "path";
3511
+ import path8 from "path";
2589
3512
  function isNameWithOptionalInstance(candidate, baseName) {
2590
3513
  if (candidate === baseName) return true;
2591
3514
  if (!candidate.startsWith(baseName)) return false;
@@ -2629,12 +3552,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
2629
3552
  return null;
2630
3553
  }
2631
3554
  function getMarkerPath() {
2632
- return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
3555
+ return path8.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
2633
3556
  }
2634
3557
  function writeActiveAgent(agentId, agentRole) {
2635
3558
  try {
2636
- mkdirSync3(CACHE_DIR, { recursive: true });
2637
- writeFileSync3(
3559
+ mkdirSync4(CACHE_DIR, { recursive: true });
3560
+ writeFileSync4(
2638
3561
  getMarkerPath(),
2639
3562
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
2640
3563
  );
@@ -2650,7 +3573,7 @@ function clearActiveAgent() {
2650
3573
  function getActiveAgent() {
2651
3574
  try {
2652
3575
  const markerPath = getMarkerPath();
2653
- const raw = readFileSync3(markerPath, "utf8");
3576
+ const raw = readFileSync4(markerPath, "utf8");
2654
3577
  const data = JSON.parse(raw);
2655
3578
  if (data.agentId) {
2656
3579
  if (data.startedAt) {
@@ -2698,14 +3621,14 @@ function getAllActiveAgents() {
2698
3621
  const key = file.slice("active-agent-".length, -".json".length);
2699
3622
  if (key === "undefined") continue;
2700
3623
  try {
2701
- const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
3624
+ const raw = readFileSync4(path8.join(CACHE_DIR, file), "utf8");
2702
3625
  const data = JSON.parse(raw);
2703
3626
  if (!data.agentId) continue;
2704
3627
  if (data.startedAt) {
2705
3628
  const age = Date.now() - new Date(data.startedAt).getTime();
2706
3629
  if (age > STALE_MS) {
2707
3630
  try {
2708
- unlinkSync3(path6.join(CACHE_DIR, file));
3631
+ unlinkSync3(path8.join(CACHE_DIR, file));
2709
3632
  } catch {
2710
3633
  }
2711
3634
  continue;
@@ -2728,11 +3651,11 @@ function getAllActiveAgents() {
2728
3651
  function cleanupSessionMarkers() {
2729
3652
  const key = getSessionKey();
2730
3653
  try {
2731
- unlinkSync3(path6.join(CACHE_DIR, `active-agent-${key}.json`));
3654
+ unlinkSync3(path8.join(CACHE_DIR, `active-agent-${key}.json`));
2732
3655
  } catch {
2733
3656
  }
2734
3657
  try {
2735
- unlinkSync3(path6.join(CACHE_DIR, "active-agent-undefined.json"));
3658
+ unlinkSync3(path8.join(CACHE_DIR, "active-agent-undefined.json"));
2736
3659
  } catch {
2737
3660
  }
2738
3661
  }
@@ -2743,15 +3666,15 @@ var init_active_agent = __esm({
2743
3666
  init_config();
2744
3667
  init_session_key();
2745
3668
  init_employees();
2746
- CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
3669
+ CACHE_DIR = path8.join(EXE_AI_DIR, "session-cache");
2747
3670
  STALE_MS = 24 * 60 * 60 * 1e3;
2748
3671
  }
2749
3672
  });
2750
3673
 
2751
3674
  // src/bin/exe-launch-agent.ts
2752
- import os5 from "os";
2753
- import path7 from "path";
2754
- import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
3675
+ import os6 from "os";
3676
+ import path9 from "path";
3677
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
2755
3678
  import { spawnSync } from "child_process";
2756
3679
 
2757
3680
  // src/lib/store.ts
@@ -2760,16 +3683,16 @@ init_database();
2760
3683
 
2761
3684
  // src/lib/keychain.ts
2762
3685
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2763
- import { existsSync as existsSync3 } from "fs";
2764
- import path3 from "path";
2765
- import os3 from "os";
3686
+ import { existsSync as existsSync5 } from "fs";
3687
+ import path5 from "path";
3688
+ import os4 from "os";
2766
3689
  var SERVICE = "exe-mem";
2767
3690
  var ACCOUNT = "master-key";
2768
3691
  function getKeyDir() {
2769
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
3692
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
2770
3693
  }
2771
3694
  function getKeyPath() {
2772
- return path3.join(getKeyDir(), "master.key");
3695
+ return path5.join(getKeyDir(), "master.key");
2773
3696
  }
2774
3697
  async function tryKeytar() {
2775
3698
  try {
@@ -2790,9 +3713,9 @@ async function getMasterKey() {
2790
3713
  }
2791
3714
  }
2792
3715
  const keyPath = getKeyPath();
2793
- if (!existsSync3(keyPath)) {
3716
+ if (!existsSync5(keyPath)) {
2794
3717
  process.stderr.write(
2795
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3718
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2796
3719
  `
2797
3720
  );
2798
3721
  return null;
@@ -3103,15 +4026,15 @@ function vectorToBlob(vector) {
3103
4026
  }
3104
4027
 
3105
4028
  // src/lib/behaviors-export.ts
3106
- import os4 from "os";
3107
- import path5 from "path";
4029
+ import os5 from "os";
4030
+ import path7 from "path";
3108
4031
  import {
3109
- existsSync as existsSync5,
3110
- mkdirSync as mkdirSync2,
4032
+ existsSync as existsSync7,
4033
+ mkdirSync as mkdirSync3,
3111
4034
  readdirSync as readdirSync2,
3112
4035
  statSync,
3113
4036
  unlinkSync as unlinkSync2,
3114
- writeFileSync as writeFileSync2
4037
+ writeFileSync as writeFileSync3
3115
4038
  } from "fs";
3116
4039
 
3117
4040
  // src/lib/behaviors.ts
@@ -3146,15 +4069,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
3146
4069
  }
3147
4070
 
3148
4071
  // src/lib/behaviors-export.ts
3149
- var BEHAVIORS_EXPORT_DIR = path5.join(
3150
- os4.homedir(),
4072
+ var BEHAVIORS_EXPORT_DIR = path7.join(
4073
+ os5.homedir(),
3151
4074
  ".exe-os",
3152
4075
  "behaviors-export"
3153
4076
  );
3154
4077
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
3155
4078
  var EXPORT_BEHAVIOR_LIMIT = 30;
3156
4079
  function sweepStaleBehaviorExports(now = Date.now()) {
3157
- if (!existsSync5(BEHAVIORS_EXPORT_DIR)) return;
4080
+ if (!existsSync7(BEHAVIORS_EXPORT_DIR)) return;
3158
4081
  let entries;
3159
4082
  try {
3160
4083
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -3162,7 +4085,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
3162
4085
  return;
3163
4086
  }
3164
4087
  for (const entry of entries) {
3165
- const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
4088
+ const filePath = path7.join(BEHAVIORS_EXPORT_DIR, entry);
3166
4089
  try {
3167
4090
  const stat = statSync(filePath);
3168
4091
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -3195,22 +4118,22 @@ function renderBehaviorExport(behaviors) {
3195
4118
  }
3196
4119
  function exportFilePath(agentId, projectName, sessionKey) {
3197
4120
  if (!sessionKey) {
3198
- return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
4121
+ return path7.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
3199
4122
  }
3200
4123
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3201
- return path5.join(
4124
+ return path7.join(
3202
4125
  BEHAVIORS_EXPORT_DIR,
3203
4126
  `${agentId}-${safeProject}-${sessionKey}.md`
3204
4127
  );
3205
4128
  }
3206
4129
  async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
3207
- mkdirSync2(BEHAVIORS_EXPORT_DIR, { recursive: true });
4130
+ mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
3208
4131
  sweepStaleBehaviorExports();
3209
4132
  const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
3210
4133
  if (behaviors.length === 0) return null;
3211
4134
  const body = renderBehaviorExport(behaviors);
3212
4135
  const target = exportFilePath(agentId, projectName, sessionKey);
3213
- writeFileSync2(target, body, "utf-8");
4136
+ writeFileSync3(target, body, "utf-8");
3214
4137
  return target;
3215
4138
  }
3216
4139
 
@@ -3269,7 +4192,7 @@ function parseBasename(basename) {
3269
4192
  return { agent, provider };
3270
4193
  }
3271
4194
  function resolveAgent(argv) {
3272
- const invokedAs = path7.basename(argv[1] ?? "");
4195
+ const invokedAs = path9.basename(argv[1] ?? "");
3273
4196
  if (invokedAs && invokedAs !== "exe-launch-agent" && !invokedAs.endsWith(".js")) {
3274
4197
  const { agent: agent2, provider } = parseBasename(invokedAs.toLowerCase());
3275
4198
  return { agent: agent2, provider, passthrough: argv.slice(2) };
@@ -3298,20 +4221,20 @@ async function isKnownAgent(agent) {
3298
4221
  }
3299
4222
  }
3300
4223
  function identityPathFor(agent) {
3301
- const dir = path7.join(os5.homedir(), ".exe-os", "identity");
3302
- const exactPath = path7.join(dir, `${agent}.md`);
3303
- if (existsSync6(exactPath)) return exactPath;
4224
+ const dir = path9.join(os6.homedir(), ".exe-os", "identity");
4225
+ const exactPath = path9.join(dir, `${agent}.md`);
4226
+ if (existsSync8(exactPath)) return exactPath;
3304
4227
  try {
3305
4228
  const files = readdirSync4(dir);
3306
4229
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
3307
- if (match) return path7.join(dir, match);
4230
+ if (match) return path9.join(dir, match);
3308
4231
  } catch {
3309
4232
  }
3310
4233
  return exactPath;
3311
4234
  }
3312
4235
  function leanMcpConfigFor(agent) {
3313
- const p = path7.join(os5.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
3314
- return existsSync6(p) ? p : null;
4236
+ const p = path9.join(os6.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
4237
+ return existsSync8(p) ? p : null;
3315
4238
  }
3316
4239
  var _ccHelpOutput = null;
3317
4240
  function getCcHelpOutput() {
@@ -3334,39 +4257,39 @@ function _resetCcHelpCache() {
3334
4257
  function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _provider) {
3335
4258
  const args = ["--dangerously-skip-permissions"];
3336
4259
  const idPath = identityPathFor(agent);
3337
- const ccAgentPath = path7.join(os5.homedir(), ".claude", "agents", `${agent}.md`);
4260
+ const ccAgentPath = path9.join(os6.homedir(), ".claude", "agents", `${agent}.md`);
3338
4261
  let effectiveCcPath = null;
3339
- if (existsSync6(ccAgentPath)) {
4262
+ if (existsSync8(ccAgentPath)) {
3340
4263
  effectiveCcPath = ccAgentPath;
3341
4264
  } else {
3342
4265
  try {
3343
- const ccAgentDir = path7.join(os5.homedir(), ".claude", "agents");
4266
+ const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
3344
4267
  const ccFiles = readdirSync4(ccAgentDir);
3345
4268
  const ccMatch = ccFiles.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
3346
- if (ccMatch) effectiveCcPath = path7.join(ccAgentDir, ccMatch);
4269
+ if (ccMatch) effectiveCcPath = path9.join(ccAgentDir, ccMatch);
3347
4270
  } catch {
3348
4271
  }
3349
4272
  }
3350
- const effectiveIdPath = existsSync6(idPath) ? idPath : effectiveCcPath;
4273
+ const effectiveIdPath = existsSync8(idPath) ? idPath : effectiveCcPath;
3351
4274
  let identityContent = null;
3352
- if (effectiveIdPath && existsSync6(effectiveIdPath)) {
4275
+ if (effectiveIdPath && existsSync8(effectiveIdPath)) {
3353
4276
  try {
3354
- const content = readFileSync4(effectiveIdPath, "utf-8");
4277
+ const content = readFileSync5(effectiveIdPath, "utf-8");
3355
4278
  if (content.trim().length > 0) identityContent = content;
3356
4279
  } catch {
3357
4280
  }
3358
4281
  }
3359
4282
  if (!identityContent) {
3360
4283
  try {
3361
- const rosterPath = path7.join(os5.homedir(), ".exe-os", "exe-employees.json");
3362
- if (existsSync6(rosterPath)) {
3363
- const roster = JSON.parse(readFileSync4(rosterPath, "utf8"));
4284
+ const rosterPath = path9.join(os6.homedir(), ".exe-os", "exe-employees.json");
4285
+ if (existsSync8(rosterPath)) {
4286
+ const roster = JSON.parse(readFileSync5(rosterPath, "utf8"));
3364
4287
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
3365
4288
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
3366
4289
  identityContent = emp.systemPrompt;
3367
4290
  try {
3368
- const dir = path7.dirname(idPath);
3369
- if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
4291
+ const dir = path9.dirname(idPath);
4292
+ if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
3370
4293
  const hasFrontmatter = identityContent.trimStart().startsWith("---");
3371
4294
  const fileContent = hasFrontmatter ? identityContent : `---
3372
4295
  role: ${(emp.role ?? "employee").toLowerCase()}
@@ -3378,7 +4301,7 @@ updated_at: ${(/* @__PURE__ */ new Date()).toISOString()}
3378
4301
  ---
3379
4302
 
3380
4303
  ${identityContent}`;
3381
- writeFileSync4(idPath, fileContent, "utf-8");
4304
+ writeFileSync5(idPath, fileContent, "utf-8");
3382
4305
  identityContent = fileContent;
3383
4306
  process.stderr.write(`[exe-launch-agent] self-healed missing identity file: ${idPath}
3384
4307
  `);
@@ -3406,15 +4329,15 @@ ${identityContent}`;
3406
4329
  args.push("--system-prompt", getSessionPrompt(identityContent));
3407
4330
  } else {
3408
4331
  try {
3409
- const tmpPath = path7.join(os5.homedir(), ".exe-os", "session-cache", `${agent}-identity.md`);
3410
- mkdirSync4(path7.dirname(tmpPath), { recursive: true });
3411
- writeFileSync4(tmpPath, identityContent, "utf-8");
4332
+ const tmpPath = path9.join(os6.homedir(), ".exe-os", "session-cache", `${agent}-identity.md`);
4333
+ mkdirSync5(path9.dirname(tmpPath), { recursive: true });
4334
+ writeFileSync5(tmpPath, identityContent, "utf-8");
3412
4335
  args.push("--append-system-prompt-file", tmpPath);
3413
4336
  } catch {
3414
4337
  }
3415
4338
  }
3416
4339
  }
3417
- if (behaviorsPath && existsSync6(behaviorsPath)) {
4340
+ if (behaviorsPath && existsSync8(behaviorsPath)) {
3418
4341
  args.push("--append-system-prompt-file", behaviorsPath);
3419
4342
  }
3420
4343
  const leanMcp = leanMcpConfigFor(agent);
@@ -3535,28 +4458,28 @@ async function main() {
3535
4458
  _resetCcAgentSupportCache();
3536
4459
  const hasAgentFlag = claudeSupportsAgentFlag();
3537
4460
  if (hasAgentFlag) {
3538
- const ccAgentDir = path7.join(os5.homedir(), ".claude", "agents");
3539
- const ccAgentFile = path7.join(ccAgentDir, `${agent}.md`);
3540
- if (!existsSync6(ccAgentFile)) {
4461
+ const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
4462
+ const ccAgentFile = path9.join(ccAgentDir, `${agent}.md`);
4463
+ if (!existsSync8(ccAgentFile)) {
3541
4464
  const exeIdentity = identityPathFor(agent);
3542
4465
  let sourceFile = null;
3543
- if (existsSync6(exeIdentity)) {
4466
+ if (existsSync8(exeIdentity)) {
3544
4467
  sourceFile = exeIdentity;
3545
4468
  } else {
3546
4469
  try {
3547
- const identityDir = path7.dirname(exeIdentity);
4470
+ const identityDir = path9.dirname(exeIdentity);
3548
4471
  const files = readdirSync4(identityDir);
3549
4472
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
3550
- if (match) sourceFile = path7.join(identityDir, match);
4473
+ if (match) sourceFile = path9.join(identityDir, match);
3551
4474
  } catch {
3552
4475
  }
3553
4476
  }
3554
4477
  if (sourceFile) {
3555
4478
  try {
3556
- mkdirSync4(ccAgentDir, { recursive: true });
3557
- let content = readFileSync4(sourceFile, "utf-8");
4479
+ mkdirSync5(ccAgentDir, { recursive: true });
4480
+ let content = readFileSync5(sourceFile, "utf-8");
3558
4481
  content = content.replace(/\$\{agent_id\}/g, baseAgentName(agent));
3559
- writeFileSync4(ccAgentFile, content, "utf-8");
4482
+ writeFileSync5(ccAgentFile, content, "utf-8");
3560
4483
  process.stderr.write(
3561
4484
  `[exe-launch-agent] auto-provisioned ${ccAgentFile} from ${sourceFile}
3562
4485
  `
@@ -3576,7 +4499,7 @@ async function main() {
3576
4499
  const empRole = (() => {
3577
4500
  try {
3578
4501
  const emps = __require("fs").readFileSync(
3579
- path7.join(os5.homedir(), ".exe-os", "exe-employees.json"),
4502
+ path9.join(os6.homedir(), ".exe-os", "exe-employees.json"),
3580
4503
  "utf-8"
3581
4504
  );
3582
4505
  const found = JSON.parse(emps).find(