@askexenow/exe-os 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/bin/backfill-conversations.js +754 -79
  2. package/dist/bin/backfill-responses.js +752 -77
  3. package/dist/bin/backfill-vectors.js +752 -77
  4. package/dist/bin/cleanup-stale-review-tasks.js +657 -35
  5. package/dist/bin/cli.js +1388 -605
  6. package/dist/bin/exe-agent-config.js +123 -95
  7. package/dist/bin/exe-agent.js +41 -25
  8. package/dist/bin/exe-assign.js +732 -57
  9. package/dist/bin/exe-boot.js +784 -153
  10. package/dist/bin/exe-call.js +209 -138
  11. package/dist/bin/exe-cloud.js +35 -12
  12. package/dist/bin/exe-dispatch.js +692 -70
  13. package/dist/bin/exe-doctor.js +648 -26
  14. package/dist/bin/exe-export-behaviors.js +650 -20
  15. package/dist/bin/exe-forget.js +635 -13
  16. package/dist/bin/exe-gateway.js +1053 -271
  17. package/dist/bin/exe-heartbeat.js +665 -43
  18. package/dist/bin/exe-kill.js +646 -16
  19. package/dist/bin/exe-launch-agent.js +887 -97
  20. package/dist/bin/exe-link.js +658 -43
  21. package/dist/bin/exe-new-employee.js +378 -177
  22. package/dist/bin/exe-pending-messages.js +656 -34
  23. package/dist/bin/exe-pending-notifications.js +635 -13
  24. package/dist/bin/exe-pending-reviews.js +659 -37
  25. package/dist/bin/exe-rename.js +645 -30
  26. package/dist/bin/exe-review.js +635 -13
  27. package/dist/bin/exe-search.js +771 -88
  28. package/dist/bin/exe-session-cleanup.js +834 -150
  29. package/dist/bin/exe-settings.js +127 -91
  30. package/dist/bin/exe-start-codex.js +729 -94
  31. package/dist/bin/exe-start-opencode.js +717 -82
  32. package/dist/bin/exe-status.js +657 -35
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +720 -89
  35. package/dist/bin/graph-backfill.js +643 -13
  36. package/dist/bin/graph-export.js +646 -16
  37. package/dist/bin/install.js +596 -193
  38. package/dist/bin/scan-tasks.js +724 -93
  39. package/dist/bin/setup.js +1038 -210
  40. package/dist/bin/shard-migrate.js +645 -15
  41. package/dist/bin/wiki-sync.js +646 -16
  42. package/dist/gateway/index.js +1027 -245
  43. package/dist/hooks/bug-report-worker.js +891 -170
  44. package/dist/hooks/commit-complete.js +718 -87
  45. package/dist/hooks/error-recall.js +776 -93
  46. package/dist/hooks/exe-heartbeat-hook.js +85 -71
  47. package/dist/hooks/ingest-worker.js +840 -156
  48. package/dist/hooks/ingest.js +90 -73
  49. package/dist/hooks/instructions-loaded.js +669 -38
  50. package/dist/hooks/notification.js +661 -30
  51. package/dist/hooks/post-compact.js +674 -43
  52. package/dist/hooks/pre-compact.js +718 -87
  53. package/dist/hooks/pre-tool-use.js +872 -125
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1060 -319
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +721 -90
  58. package/dist/hooks/session-start.js +1031 -207
  59. package/dist/hooks/stop.js +680 -49
  60. package/dist/hooks/subagent-stop.js +674 -43
  61. package/dist/hooks/summary-worker.js +816 -132
  62. package/dist/index.js +1015 -232
  63. package/dist/lib/cloud-sync.js +663 -48
  64. package/dist/lib/consolidation.js +26 -3
  65. package/dist/lib/database.js +626 -18
  66. package/dist/lib/db.js +2261 -0
  67. package/dist/lib/device-registry.js +640 -25
  68. package/dist/lib/embedder.js +96 -43
  69. package/dist/lib/employee-templates.js +16 -0
  70. package/dist/lib/employees.js +259 -83
  71. package/dist/lib/exe-daemon-client.js +101 -63
  72. package/dist/lib/exe-daemon.js +894 -162
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +55 -28
  76. package/dist/lib/reminders.js +21 -1
  77. package/dist/lib/schedules.js +636 -14
  78. package/dist/lib/skill-learning.js +21 -1
  79. package/dist/lib/store.js +643 -13
  80. package/dist/lib/task-router.js +82 -71
  81. package/dist/lib/tasks.js +98 -71
  82. package/dist/lib/tmux-routing.js +87 -60
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1784 -458
  85. package/dist/mcp/tools/complete-reminder.js +21 -1
  86. package/dist/mcp/tools/create-reminder.js +21 -1
  87. package/dist/mcp/tools/create-task.js +290 -164
  88. package/dist/mcp/tools/deactivate-behavior.js +24 -4
  89. package/dist/mcp/tools/list-reminders.js +21 -1
  90. package/dist/mcp/tools/list-tasks.js +195 -38
  91. package/dist/mcp/tools/send-message.js +58 -31
  92. package/dist/mcp/tools/update-task.js +75 -48
  93. package/dist/runtime/index.js +720 -89
  94. package/dist/tui/App.js +853 -123
  95. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -265,6 +265,118 @@ var init_config = __esm({
265
265
  }
266
266
  });
267
267
 
268
+ // src/lib/runtime-table.ts
269
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
270
+ var init_runtime_table = __esm({
271
+ "src/lib/runtime-table.ts"() {
272
+ "use strict";
273
+ RUNTIME_TABLE = {
274
+ codex: {
275
+ binary: "codex",
276
+ launchMode: "interactive",
277
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
278
+ inlineFlag: "--no-alt-screen",
279
+ apiKeyEnv: "OPENAI_API_KEY",
280
+ defaultModel: "gpt-5.4"
281
+ },
282
+ opencode: {
283
+ binary: "opencode",
284
+ launchMode: "exec",
285
+ autoApproveFlag: "--dangerously-skip-permissions",
286
+ inlineFlag: "",
287
+ apiKeyEnv: "ANTHROPIC_API_KEY",
288
+ defaultModel: "anthropic/claude-sonnet-4-6"
289
+ }
290
+ };
291
+ DEFAULT_RUNTIME = "claude";
292
+ }
293
+ });
294
+
295
+ // src/lib/agent-config.ts
296
+ var agent_config_exports = {};
297
+ __export(agent_config_exports, {
298
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
299
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
300
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
301
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
302
+ clearAgentRuntime: () => clearAgentRuntime,
303
+ getAgentRuntime: () => getAgentRuntime,
304
+ loadAgentConfig: () => loadAgentConfig,
305
+ saveAgentConfig: () => saveAgentConfig,
306
+ setAgentRuntime: () => setAgentRuntime
307
+ });
308
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
309
+ import path3 from "path";
310
+ function loadAgentConfig() {
311
+ if (!existsSync2(AGENT_CONFIG_PATH)) return {};
312
+ try {
313
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
314
+ } catch {
315
+ return {};
316
+ }
317
+ }
318
+ function saveAgentConfig(config2) {
319
+ const dir = path3.dirname(AGENT_CONFIG_PATH);
320
+ if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
321
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
322
+ }
323
+ function getAgentRuntime(agentId) {
324
+ const config2 = loadAgentConfig();
325
+ const entry = config2[agentId];
326
+ if (entry) return entry;
327
+ const orgDefault = config2["default"];
328
+ if (orgDefault) return orgDefault;
329
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
330
+ }
331
+ function setAgentRuntime(agentId, runtime, model) {
332
+ const knownModels = KNOWN_RUNTIMES[runtime];
333
+ if (!knownModels) {
334
+ return {
335
+ ok: false,
336
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
337
+ };
338
+ }
339
+ if (!knownModels.includes(model)) {
340
+ return {
341
+ ok: false,
342
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
343
+ };
344
+ }
345
+ const config2 = loadAgentConfig();
346
+ config2[agentId] = { runtime, model };
347
+ saveAgentConfig(config2);
348
+ return { ok: true };
349
+ }
350
+ function clearAgentRuntime(agentId) {
351
+ const config2 = loadAgentConfig();
352
+ delete config2[agentId];
353
+ saveAgentConfig(config2);
354
+ }
355
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
356
+ var init_agent_config = __esm({
357
+ "src/lib/agent-config.ts"() {
358
+ "use strict";
359
+ init_config();
360
+ init_runtime_table();
361
+ AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
362
+ KNOWN_RUNTIMES = {
363
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
364
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
365
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
366
+ };
367
+ RUNTIME_LABELS = {
368
+ claude: "Claude Code (Anthropic)",
369
+ codex: "Codex (OpenAI)",
370
+ opencode: "OpenCode (open source)"
371
+ };
372
+ DEFAULT_MODELS = {
373
+ claude: "claude-opus-4",
374
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
375
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
376
+ };
377
+ }
378
+ });
379
+
268
380
  // src/lib/employees.ts
269
381
  var employees_exports = {};
270
382
  __export(employees_exports, {
@@ -280,6 +392,7 @@ __export(employees_exports, {
280
392
  getEmployeeByRole: () => getEmployeeByRole,
281
393
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
282
394
  hasRole: () => hasRole,
395
+ hireEmployee: () => hireEmployee,
283
396
  isCoordinatorName: () => isCoordinatorName,
284
397
  isCoordinatorRole: () => isCoordinatorRole,
285
398
  isMultiInstance: () => isMultiInstance,
@@ -292,9 +405,9 @@ __export(employees_exports, {
292
405
  validateEmployeeName: () => validateEmployeeName
293
406
  });
294
407
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
295
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
408
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
296
409
  import { execSync } from "child_process";
297
- import path3 from "path";
410
+ import path4 from "path";
298
411
  import os3 from "os";
299
412
  function normalizeRole(role) {
300
413
  return (role ?? "").trim().toLowerCase();
@@ -331,7 +444,7 @@ function validateEmployeeName(name) {
331
444
  return { valid: true };
332
445
  }
333
446
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
334
- if (!existsSync2(employeesPath)) {
447
+ if (!existsSync3(employeesPath)) {
335
448
  return [];
336
449
  }
337
450
  const raw = await readFile2(employeesPath, "utf-8");
@@ -342,13 +455,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
342
455
  }
343
456
  }
344
457
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
345
- await mkdir2(path3.dirname(employeesPath), { recursive: true });
458
+ await mkdir2(path4.dirname(employeesPath), { recursive: true });
346
459
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
347
460
  }
348
461
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
349
- if (!existsSync2(employeesPath)) return [];
462
+ if (!existsSync3(employeesPath)) return [];
350
463
  try {
351
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
464
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
352
465
  } catch {
353
466
  return [];
354
467
  }
@@ -390,6 +503,52 @@ function addEmployee(employees, employee) {
390
503
  }
391
504
  return [...employees, normalized];
392
505
  }
506
+ function appendToCoordinatorTeam(employee) {
507
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
508
+ if (!coordinator) return;
509
+ const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
510
+ if (!existsSync3(idPath)) return;
511
+ const content = readFileSync3(idPath, "utf-8");
512
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
513
+ const teamMatch = content.match(TEAM_SECTION_RE);
514
+ if (!teamMatch || teamMatch.index === void 0) return;
515
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
516
+ const nextHeading = afterTeam.match(/\n## /);
517
+ const entry = `
518
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
519
+ `;
520
+ let updated;
521
+ if (nextHeading && nextHeading.index !== void 0) {
522
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
523
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
524
+ } else {
525
+ updated = content.trimEnd() + "\n" + entry;
526
+ }
527
+ writeFileSync2(idPath, updated, "utf-8");
528
+ }
529
+ function capitalize(s) {
530
+ return s.charAt(0).toUpperCase() + s.slice(1);
531
+ }
532
+ async function hireEmployee(employee) {
533
+ const employees = await loadEmployees();
534
+ const updated = addEmployee(employees, employee);
535
+ await saveEmployees(updated);
536
+ try {
537
+ appendToCoordinatorTeam(employee);
538
+ } catch {
539
+ }
540
+ try {
541
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
542
+ const config2 = loadAgentConfig2();
543
+ const name = employee.name.toLowerCase();
544
+ if (!config2[name] && config2["default"]) {
545
+ config2[name] = { ...config2["default"] };
546
+ saveAgentConfig2(config2);
547
+ }
548
+ } catch {
549
+ }
550
+ return updated;
551
+ }
393
552
  async function normalizeRosterCase(rosterPath) {
394
553
  const employees = await loadEmployees(rosterPath);
395
554
  let changed = false;
@@ -399,14 +558,14 @@ async function normalizeRosterCase(rosterPath) {
399
558
  emp.name = emp.name.toLowerCase();
400
559
  changed = true;
401
560
  try {
402
- const identityDir = path3.join(os3.homedir(), ".exe-os", "identity");
403
- const oldPath = path3.join(identityDir, `${oldName}.md`);
404
- const newPath = path3.join(identityDir, `${emp.name}.md`);
405
- if (existsSync2(oldPath) && !existsSync2(newPath)) {
561
+ const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
562
+ const oldPath = path4.join(identityDir, `${oldName}.md`);
563
+ const newPath = path4.join(identityDir, `${emp.name}.md`);
564
+ if (existsSync3(oldPath) && !existsSync3(newPath)) {
406
565
  renameSync2(oldPath, newPath);
407
- } else if (existsSync2(oldPath) && oldPath !== newPath) {
408
- const content = readFileSync2(oldPath, "utf-8");
409
- writeFileSync(newPath, content, "utf-8");
566
+ } else if (existsSync3(oldPath) && oldPath !== newPath) {
567
+ const content = readFileSync3(oldPath, "utf-8");
568
+ writeFileSync2(newPath, content, "utf-8");
410
569
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
411
570
  unlinkSync(oldPath);
412
571
  }
@@ -436,7 +595,7 @@ function registerBinSymlinks(name) {
436
595
  errors.push("Could not find 'exe-os' in PATH");
437
596
  return { created, skipped, errors };
438
597
  }
439
- const binDir = path3.dirname(exeBinPath);
598
+ const binDir = path4.dirname(exeBinPath);
440
599
  let target;
441
600
  try {
442
601
  target = readlinkSync(exeBinPath);
@@ -446,8 +605,8 @@ function registerBinSymlinks(name) {
446
605
  }
447
606
  for (const suffix of ["", "-opencode"]) {
448
607
  const linkName = `${name}${suffix}`;
449
- const linkPath = path3.join(binDir, linkName);
450
- if (existsSync2(linkPath)) {
608
+ const linkPath = path4.join(binDir, linkName);
609
+ if (existsSync3(linkPath)) {
451
610
  skipped.push(linkName);
452
611
  continue;
453
612
  }
@@ -460,15 +619,17 @@ function registerBinSymlinks(name) {
460
619
  }
461
620
  return { created, skipped, errors };
462
621
  }
463
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
622
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
464
623
  var init_employees = __esm({
465
624
  "src/lib/employees.ts"() {
466
625
  "use strict";
467
626
  init_config();
468
- EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
627
+ EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
469
628
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
470
629
  COORDINATOR_ROLE = "COO";
471
630
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
631
+ IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
632
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
472
633
  }
473
634
  });
474
635
 
@@ -479,14 +640,14 @@ __export(session_registry_exports, {
479
640
  pruneStaleSessions: () => pruneStaleSessions,
480
641
  registerSession: () => registerSession
481
642
  });
482
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync3 } from "fs";
643
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
483
644
  import { execSync as execSync2 } from "child_process";
484
- import path4 from "path";
645
+ import path5 from "path";
485
646
  import os4 from "os";
486
647
  function registerSession(entry) {
487
- const dir = path4.dirname(REGISTRY_PATH);
488
- if (!existsSync3(dir)) {
489
- mkdirSync(dir, { recursive: true });
648
+ const dir = path5.dirname(REGISTRY_PATH);
649
+ if (!existsSync4(dir)) {
650
+ mkdirSync2(dir, { recursive: true });
490
651
  }
491
652
  const sessions = listSessions();
492
653
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -495,11 +656,11 @@ function registerSession(entry) {
495
656
  } else {
496
657
  sessions.push(entry);
497
658
  }
498
- writeFileSync2(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
659
+ writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
499
660
  }
500
661
  function listSessions() {
501
662
  try {
502
- const raw = readFileSync3(REGISTRY_PATH, "utf8");
663
+ const raw = readFileSync4(REGISTRY_PATH, "utf8");
503
664
  return JSON.parse(raw);
504
665
  } catch {
505
666
  return [];
@@ -520,7 +681,7 @@ function pruneStaleSessions() {
520
681
  const alive = sessions.filter((s) => liveSet.has(s.windowName));
521
682
  const pruned = sessions.length - alive.length;
522
683
  if (pruned > 0) {
523
- writeFileSync2(REGISTRY_PATH, JSON.stringify(alive, null, 2));
684
+ writeFileSync3(REGISTRY_PATH, JSON.stringify(alive, null, 2));
524
685
  }
525
686
  return pruned;
526
687
  }
@@ -528,7 +689,7 @@ var REGISTRY_PATH;
528
689
  var init_session_registry = __esm({
529
690
  "src/lib/session-registry.ts"() {
530
691
  "use strict";
531
- REGISTRY_PATH = path4.join(os4.homedir(), ".exe-os", "session-registry.json");
692
+ REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
532
693
  }
533
694
  });
534
695
 
@@ -790,67 +951,6 @@ var init_provider_table = __esm({
790
951
  }
791
952
  });
792
953
 
793
- // src/lib/runtime-table.ts
794
- var RUNTIME_TABLE, DEFAULT_RUNTIME;
795
- var init_runtime_table = __esm({
796
- "src/lib/runtime-table.ts"() {
797
- "use strict";
798
- RUNTIME_TABLE = {
799
- codex: {
800
- binary: "codex",
801
- launchMode: "interactive",
802
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
803
- inlineFlag: "--no-alt-screen",
804
- apiKeyEnv: "OPENAI_API_KEY",
805
- defaultModel: "gpt-5.4"
806
- },
807
- opencode: {
808
- binary: "opencode",
809
- launchMode: "exec",
810
- autoApproveFlag: "--dangerously-skip-permissions",
811
- inlineFlag: "",
812
- apiKeyEnv: "ANTHROPIC_API_KEY",
813
- defaultModel: "anthropic/claude-sonnet-4-6"
814
- }
815
- };
816
- DEFAULT_RUNTIME = "claude";
817
- }
818
- });
819
-
820
- // src/lib/agent-config.ts
821
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
822
- import path5 from "path";
823
- function loadAgentConfig() {
824
- if (!existsSync4(AGENT_CONFIG_PATH)) return {};
825
- try {
826
- return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
827
- } catch {
828
- return {};
829
- }
830
- }
831
- function getAgentRuntime(agentId) {
832
- const config2 = loadAgentConfig();
833
- const entry = config2[agentId];
834
- if (entry) return entry;
835
- const orgDefault = config2["default"];
836
- if (orgDefault) return orgDefault;
837
- return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
838
- }
839
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
840
- var init_agent_config = __esm({
841
- "src/lib/agent-config.ts"() {
842
- "use strict";
843
- init_config();
844
- init_runtime_table();
845
- AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
846
- DEFAULT_MODELS = {
847
- claude: "claude-opus-4",
848
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
849
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
850
- };
851
- }
852
- });
853
-
854
954
  // src/lib/intercom-queue.ts
855
955
  var intercom_queue_exports = {};
856
956
  __export(intercom_queue_exports, {
@@ -1032,13 +1132,597 @@ var init_db_retry = __esm({
1032
1132
  }
1033
1133
  });
1034
1134
 
1135
+ // src/lib/database-adapter.ts
1136
+ import os6 from "os";
1137
+ import path7 from "path";
1138
+ import { createRequire } from "module";
1139
+ import { pathToFileURL } from "url";
1140
+ function quotedIdentifier(identifier) {
1141
+ return `"${identifier.replace(/"/g, '""')}"`;
1142
+ }
1143
+ function unqualifiedTableName(name) {
1144
+ const raw = name.trim().replace(/^"|"$/g, "");
1145
+ const parts = raw.split(".");
1146
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
1147
+ }
1148
+ function stripTrailingSemicolon(sql) {
1149
+ return sql.trim().replace(/;+\s*$/u, "");
1150
+ }
1151
+ function appendClause(sql, clause) {
1152
+ const trimmed = stripTrailingSemicolon(sql);
1153
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
1154
+ if (!returningMatch) {
1155
+ return `${trimmed}${clause}`;
1156
+ }
1157
+ const idx = returningMatch.index;
1158
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
1159
+ }
1160
+ function normalizeStatement(stmt) {
1161
+ if (typeof stmt === "string") {
1162
+ return { kind: "positional", sql: stmt, args: [] };
1163
+ }
1164
+ const sql = stmt.sql;
1165
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
1166
+ return { kind: "positional", sql, args: stmt.args ?? [] };
1167
+ }
1168
+ return { kind: "named", sql, args: stmt.args };
1169
+ }
1170
+ function rewriteBooleanLiterals(sql) {
1171
+ let out = sql;
1172
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1173
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
1174
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
1175
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
1176
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
1177
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
1178
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
1179
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
1180
+ }
1181
+ return out;
1182
+ }
1183
+ function rewriteInsertOrIgnore(sql) {
1184
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
1185
+ return sql;
1186
+ }
1187
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
1188
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
1189
+ }
1190
+ function rewriteInsertOrReplace(sql) {
1191
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
1192
+ if (!match) {
1193
+ return sql;
1194
+ }
1195
+ const rawTable = match[1];
1196
+ const rawColumns = match[2];
1197
+ const remainder = match[3];
1198
+ const tableName = unqualifiedTableName(rawTable);
1199
+ const conflictKeys = UPSERT_KEYS[tableName];
1200
+ if (!conflictKeys?.length) {
1201
+ return sql;
1202
+ }
1203
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1204
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
1205
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
1206
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
1207
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
1208
+ }
1209
+ function rewriteSql(sql) {
1210
+ let out = sql;
1211
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
1212
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
1213
+ out = rewriteBooleanLiterals(out);
1214
+ out = rewriteInsertOrReplace(out);
1215
+ out = rewriteInsertOrIgnore(out);
1216
+ return stripTrailingSemicolon(out);
1217
+ }
1218
+ function toBoolean(value) {
1219
+ if (value === null || value === void 0) return value;
1220
+ if (typeof value === "boolean") return value;
1221
+ if (typeof value === "number") return value !== 0;
1222
+ if (typeof value === "bigint") return value !== 0n;
1223
+ if (typeof value === "string") {
1224
+ const normalized = value.trim().toLowerCase();
1225
+ if (normalized === "0" || normalized === "false") return false;
1226
+ if (normalized === "1" || normalized === "true") return true;
1227
+ }
1228
+ return Boolean(value);
1229
+ }
1230
+ function countQuestionMarks(sql, end) {
1231
+ let count = 0;
1232
+ let inSingle = false;
1233
+ let inDouble = false;
1234
+ let inLineComment = false;
1235
+ let inBlockComment = false;
1236
+ for (let i = 0; i < end; i++) {
1237
+ const ch = sql[i];
1238
+ const next = sql[i + 1];
1239
+ if (inLineComment) {
1240
+ if (ch === "\n") inLineComment = false;
1241
+ continue;
1242
+ }
1243
+ if (inBlockComment) {
1244
+ if (ch === "*" && next === "/") {
1245
+ inBlockComment = false;
1246
+ i += 1;
1247
+ }
1248
+ continue;
1249
+ }
1250
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1251
+ inLineComment = true;
1252
+ i += 1;
1253
+ continue;
1254
+ }
1255
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1256
+ inBlockComment = true;
1257
+ i += 1;
1258
+ continue;
1259
+ }
1260
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1261
+ inSingle = !inSingle;
1262
+ continue;
1263
+ }
1264
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1265
+ inDouble = !inDouble;
1266
+ continue;
1267
+ }
1268
+ if (!inSingle && !inDouble && ch === "?") {
1269
+ count += 1;
1270
+ }
1271
+ }
1272
+ return count;
1273
+ }
1274
+ function findBooleanPlaceholderIndexes(sql) {
1275
+ const indexes = /* @__PURE__ */ new Set();
1276
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1277
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
1278
+ for (const match of sql.matchAll(pattern)) {
1279
+ const matchText = match[0];
1280
+ const qIndex = match.index + matchText.lastIndexOf("?");
1281
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
1282
+ }
1283
+ }
1284
+ return indexes;
1285
+ }
1286
+ function coerceInsertBooleanArgs(sql, args) {
1287
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
1288
+ if (!match) return;
1289
+ const rawTable = match[1];
1290
+ const rawColumns = match[2];
1291
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1292
+ if (!boolColumns?.size) return;
1293
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1294
+ for (const [index, column] of columns.entries()) {
1295
+ if (boolColumns.has(column) && index < args.length) {
1296
+ args[index] = toBoolean(args[index]);
1297
+ }
1298
+ }
1299
+ }
1300
+ function coerceUpdateBooleanArgs(sql, args) {
1301
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
1302
+ if (!match) return;
1303
+ const rawTable = match[1];
1304
+ const setClause = match[2];
1305
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1306
+ if (!boolColumns?.size) return;
1307
+ const assignments = setClause.split(",");
1308
+ let placeholderIndex = 0;
1309
+ for (const assignment of assignments) {
1310
+ if (!assignment.includes("?")) continue;
1311
+ placeholderIndex += 1;
1312
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
1313
+ if (colMatch && boolColumns.has(colMatch[1])) {
1314
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
1315
+ }
1316
+ }
1317
+ }
1318
+ function coerceBooleanArgs(sql, args) {
1319
+ const nextArgs = [...args];
1320
+ coerceInsertBooleanArgs(sql, nextArgs);
1321
+ coerceUpdateBooleanArgs(sql, nextArgs);
1322
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
1323
+ for (const index of placeholderIndexes) {
1324
+ if (index > 0 && index <= nextArgs.length) {
1325
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
1326
+ }
1327
+ }
1328
+ return nextArgs;
1329
+ }
1330
+ function convertQuestionMarksToDollarParams(sql) {
1331
+ let out = "";
1332
+ let placeholder = 0;
1333
+ let inSingle = false;
1334
+ let inDouble = false;
1335
+ let inLineComment = false;
1336
+ let inBlockComment = false;
1337
+ for (let i = 0; i < sql.length; i++) {
1338
+ const ch = sql[i];
1339
+ const next = sql[i + 1];
1340
+ if (inLineComment) {
1341
+ out += ch;
1342
+ if (ch === "\n") inLineComment = false;
1343
+ continue;
1344
+ }
1345
+ if (inBlockComment) {
1346
+ out += ch;
1347
+ if (ch === "*" && next === "/") {
1348
+ out += next;
1349
+ inBlockComment = false;
1350
+ i += 1;
1351
+ }
1352
+ continue;
1353
+ }
1354
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1355
+ out += ch + next;
1356
+ inLineComment = true;
1357
+ i += 1;
1358
+ continue;
1359
+ }
1360
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1361
+ out += ch + next;
1362
+ inBlockComment = true;
1363
+ i += 1;
1364
+ continue;
1365
+ }
1366
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1367
+ inSingle = !inSingle;
1368
+ out += ch;
1369
+ continue;
1370
+ }
1371
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1372
+ inDouble = !inDouble;
1373
+ out += ch;
1374
+ continue;
1375
+ }
1376
+ if (!inSingle && !inDouble && ch === "?") {
1377
+ placeholder += 1;
1378
+ out += `$${placeholder}`;
1379
+ continue;
1380
+ }
1381
+ out += ch;
1382
+ }
1383
+ return out;
1384
+ }
1385
+ function translateStatementForPostgres(stmt) {
1386
+ const normalized = normalizeStatement(stmt);
1387
+ if (normalized.kind === "named") {
1388
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1389
+ }
1390
+ const rewrittenSql = rewriteSql(normalized.sql);
1391
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1392
+ return {
1393
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1394
+ args: coercedArgs
1395
+ };
1396
+ }
1397
+ function shouldBypassPostgres(stmt) {
1398
+ const normalized = normalizeStatement(stmt);
1399
+ if (normalized.kind === "named") {
1400
+ return true;
1401
+ }
1402
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1403
+ }
1404
+ function shouldFallbackOnError(error) {
1405
+ const message = error instanceof Error ? error.message : String(error);
1406
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1407
+ }
1408
+ function isReadQuery(sql) {
1409
+ const trimmed = sql.trimStart();
1410
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1411
+ }
1412
+ function buildRow(row, columns) {
1413
+ const values = columns.map((column) => row[column]);
1414
+ return Object.assign(values, row);
1415
+ }
1416
+ function buildResultSet(rows, rowsAffected = 0) {
1417
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1418
+ const resultRows = rows.map((row) => buildRow(row, columns));
1419
+ return {
1420
+ columns,
1421
+ columnTypes: columns.map(() => ""),
1422
+ rows: resultRows,
1423
+ rowsAffected,
1424
+ lastInsertRowid: void 0,
1425
+ toJSON() {
1426
+ return {
1427
+ columns,
1428
+ columnTypes: columns.map(() => ""),
1429
+ rows,
1430
+ rowsAffected,
1431
+ lastInsertRowid: void 0
1432
+ };
1433
+ }
1434
+ };
1435
+ }
1436
+ async function loadPrismaClient() {
1437
+ if (!prismaClientPromise) {
1438
+ prismaClientPromise = (async () => {
1439
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1440
+ if (explicitPath) {
1441
+ const module2 = await import(pathToFileURL(explicitPath).href);
1442
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1443
+ if (!PrismaClient2) {
1444
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1445
+ }
1446
+ return new PrismaClient2();
1447
+ }
1448
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os6.homedir(), "exe-db");
1449
+ const requireFromExeDb = createRequire(path7.join(exeDbRoot, "package.json"));
1450
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1451
+ const module = await import(pathToFileURL(prismaEntry).href);
1452
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1453
+ if (!PrismaClient) {
1454
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1455
+ }
1456
+ return new PrismaClient();
1457
+ })();
1458
+ }
1459
+ return prismaClientPromise;
1460
+ }
1461
+ async function ensureCompatibilityViews(prisma) {
1462
+ if (!compatibilityBootstrapPromise) {
1463
+ compatibilityBootstrapPromise = (async () => {
1464
+ for (const mapping of VIEW_MAPPINGS) {
1465
+ const relation = mapping.source.replace(/"/g, "");
1466
+ const rows = await prisma.$queryRawUnsafe(
1467
+ "SELECT to_regclass($1) AS regclass",
1468
+ relation
1469
+ );
1470
+ if (!rows[0]?.regclass) {
1471
+ continue;
1472
+ }
1473
+ await prisma.$executeRawUnsafe(
1474
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1475
+ );
1476
+ }
1477
+ })();
1478
+ }
1479
+ return compatibilityBootstrapPromise;
1480
+ }
1481
+ async function executeOnPrisma(executor, stmt) {
1482
+ const translated = translateStatementForPostgres(stmt);
1483
+ if (isReadQuery(translated.sql)) {
1484
+ const rows = await executor.$queryRawUnsafe(
1485
+ translated.sql,
1486
+ ...translated.args
1487
+ );
1488
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1489
+ }
1490
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1491
+ return buildResultSet([], rowsAffected);
1492
+ }
1493
+ function splitSqlStatements(sql) {
1494
+ const parts = [];
1495
+ let current = "";
1496
+ let inSingle = false;
1497
+ let inDouble = false;
1498
+ let inLineComment = false;
1499
+ let inBlockComment = false;
1500
+ for (let i = 0; i < sql.length; i++) {
1501
+ const ch = sql[i];
1502
+ const next = sql[i + 1];
1503
+ if (inLineComment) {
1504
+ current += ch;
1505
+ if (ch === "\n") inLineComment = false;
1506
+ continue;
1507
+ }
1508
+ if (inBlockComment) {
1509
+ current += ch;
1510
+ if (ch === "*" && next === "/") {
1511
+ current += next;
1512
+ inBlockComment = false;
1513
+ i += 1;
1514
+ }
1515
+ continue;
1516
+ }
1517
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1518
+ current += ch + next;
1519
+ inLineComment = true;
1520
+ i += 1;
1521
+ continue;
1522
+ }
1523
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1524
+ current += ch + next;
1525
+ inBlockComment = true;
1526
+ i += 1;
1527
+ continue;
1528
+ }
1529
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1530
+ inSingle = !inSingle;
1531
+ current += ch;
1532
+ continue;
1533
+ }
1534
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1535
+ inDouble = !inDouble;
1536
+ current += ch;
1537
+ continue;
1538
+ }
1539
+ if (!inSingle && !inDouble && ch === ";") {
1540
+ if (current.trim()) {
1541
+ parts.push(current.trim());
1542
+ }
1543
+ current = "";
1544
+ continue;
1545
+ }
1546
+ current += ch;
1547
+ }
1548
+ if (current.trim()) {
1549
+ parts.push(current.trim());
1550
+ }
1551
+ return parts;
1552
+ }
1553
+ async function createPrismaDbAdapter(fallbackClient) {
1554
+ const prisma = await loadPrismaClient();
1555
+ await ensureCompatibilityViews(prisma);
1556
+ let closed = false;
1557
+ let adapter;
1558
+ const fallbackExecute = async (stmt, error) => {
1559
+ if (!fallbackClient) {
1560
+ if (error) throw error;
1561
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1562
+ }
1563
+ if (error) {
1564
+ process.stderr.write(
1565
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1566
+ `
1567
+ );
1568
+ }
1569
+ return fallbackClient.execute(stmt);
1570
+ };
1571
+ adapter = {
1572
+ async execute(stmt) {
1573
+ if (shouldBypassPostgres(stmt)) {
1574
+ return fallbackExecute(stmt);
1575
+ }
1576
+ try {
1577
+ return await executeOnPrisma(prisma, stmt);
1578
+ } catch (error) {
1579
+ if (shouldFallbackOnError(error)) {
1580
+ return fallbackExecute(stmt, error);
1581
+ }
1582
+ throw error;
1583
+ }
1584
+ },
1585
+ async batch(stmts, mode) {
1586
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1587
+ if (!fallbackClient) {
1588
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1589
+ }
1590
+ return fallbackClient.batch(stmts, mode);
1591
+ }
1592
+ try {
1593
+ if (prisma.$transaction) {
1594
+ return await prisma.$transaction(async (tx) => {
1595
+ const results2 = [];
1596
+ for (const stmt of stmts) {
1597
+ results2.push(await executeOnPrisma(tx, stmt));
1598
+ }
1599
+ return results2;
1600
+ });
1601
+ }
1602
+ const results = [];
1603
+ for (const stmt of stmts) {
1604
+ results.push(await executeOnPrisma(prisma, stmt));
1605
+ }
1606
+ return results;
1607
+ } catch (error) {
1608
+ if (fallbackClient && shouldFallbackOnError(error)) {
1609
+ process.stderr.write(
1610
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1611
+ `
1612
+ );
1613
+ return fallbackClient.batch(stmts, mode);
1614
+ }
1615
+ throw error;
1616
+ }
1617
+ },
1618
+ async migrate(stmts) {
1619
+ if (fallbackClient) {
1620
+ return fallbackClient.migrate(stmts);
1621
+ }
1622
+ return adapter.batch(stmts, "deferred");
1623
+ },
1624
+ async transaction(mode) {
1625
+ if (!fallbackClient) {
1626
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1627
+ }
1628
+ return fallbackClient.transaction(mode);
1629
+ },
1630
+ async executeMultiple(sql) {
1631
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1632
+ return fallbackClient.executeMultiple(sql);
1633
+ }
1634
+ for (const statement of splitSqlStatements(sql)) {
1635
+ await adapter.execute(statement);
1636
+ }
1637
+ },
1638
+ async sync() {
1639
+ if (fallbackClient) {
1640
+ return fallbackClient.sync();
1641
+ }
1642
+ return { frame_no: 0, frames_synced: 0 };
1643
+ },
1644
+ close() {
1645
+ closed = true;
1646
+ prismaClientPromise = null;
1647
+ compatibilityBootstrapPromise = null;
1648
+ void prisma.$disconnect?.();
1649
+ },
1650
+ get closed() {
1651
+ return closed;
1652
+ },
1653
+ get protocol() {
1654
+ return "prisma-postgres";
1655
+ }
1656
+ };
1657
+ return adapter;
1658
+ }
1659
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1660
+ var init_database_adapter = __esm({
1661
+ "src/lib/database-adapter.ts"() {
1662
+ "use strict";
1663
+ VIEW_MAPPINGS = [
1664
+ { view: "memories", source: "memory.memory_records" },
1665
+ { view: "tasks", source: "memory.tasks" },
1666
+ { view: "behaviors", source: "memory.behaviors" },
1667
+ { view: "entities", source: "memory.entities" },
1668
+ { view: "relationships", source: "memory.relationships" },
1669
+ { view: "entity_memories", source: "memory.entity_memories" },
1670
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1671
+ { view: "notifications", source: "memory.notifications" },
1672
+ { view: "messages", source: "memory.messages" },
1673
+ { view: "users", source: "wiki.users" },
1674
+ { view: "workspaces", source: "wiki.workspaces" },
1675
+ { view: "workspace_users", source: "wiki.workspace_users" },
1676
+ { view: "documents", source: "wiki.workspace_documents" },
1677
+ { view: "chats", source: "wiki.workspace_chats" }
1678
+ ];
1679
+ UPSERT_KEYS = {
1680
+ memories: ["id"],
1681
+ tasks: ["id"],
1682
+ behaviors: ["id"],
1683
+ entities: ["id"],
1684
+ relationships: ["id"],
1685
+ entity_aliases: ["alias"],
1686
+ notifications: ["id"],
1687
+ messages: ["id"],
1688
+ users: ["id"],
1689
+ workspaces: ["id"],
1690
+ workspace_users: ["id"],
1691
+ documents: ["id"],
1692
+ chats: ["id"]
1693
+ };
1694
+ BOOLEAN_COLUMNS_BY_TABLE = {
1695
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1696
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1697
+ notifications: /* @__PURE__ */ new Set(["read"]),
1698
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1699
+ };
1700
+ BOOLEAN_COLUMN_NAMES = new Set(
1701
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1702
+ );
1703
+ IMMEDIATE_FALLBACK_PATTERNS = [
1704
+ /\bPRAGMA\b/i,
1705
+ /\bsqlite_master\b/i,
1706
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1707
+ /\bMATCH\b/i,
1708
+ /\bvector_distance_cos\s*\(/i,
1709
+ /\bjson_extract\s*\(/i,
1710
+ /\bjulianday\s*\(/i,
1711
+ /\bstrftime\s*\(/i,
1712
+ /\blast_insert_rowid\s*\(/i
1713
+ ];
1714
+ prismaClientPromise = null;
1715
+ compatibilityBootstrapPromise = null;
1716
+ }
1717
+ });
1718
+
1035
1719
  // src/lib/exe-daemon-client.ts
1036
1720
  import net from "net";
1037
- import os6 from "os";
1721
+ import os7 from "os";
1038
1722
  import { spawn } from "child_process";
1039
1723
  import { randomUUID as randomUUID2 } from "crypto";
1040
1724
  import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
1041
- import path7 from "path";
1725
+ import path8 from "path";
1042
1726
  import { fileURLToPath } from "url";
1043
1727
  function handleData(chunk) {
1044
1728
  _buffer += chunk.toString();
@@ -1089,17 +1773,17 @@ function cleanupStaleFiles() {
1089
1773
  }
1090
1774
  }
1091
1775
  function findPackageRoot() {
1092
- let dir = path7.dirname(fileURLToPath(import.meta.url));
1093
- const { root } = path7.parse(dir);
1776
+ let dir = path8.dirname(fileURLToPath(import.meta.url));
1777
+ const { root } = path8.parse(dir);
1094
1778
  while (dir !== root) {
1095
- if (existsSync6(path7.join(dir, "package.json"))) return dir;
1096
- dir = path7.dirname(dir);
1779
+ if (existsSync6(path8.join(dir, "package.json"))) return dir;
1780
+ dir = path8.dirname(dir);
1097
1781
  }
1098
1782
  return null;
1099
1783
  }
1100
1784
  function spawnDaemon() {
1101
- const freeGB = os6.freemem() / (1024 * 1024 * 1024);
1102
- const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1785
+ const freeGB = os7.freemem() / (1024 * 1024 * 1024);
1786
+ const totalGB = os7.totalmem() / (1024 * 1024 * 1024);
1103
1787
  if (totalGB <= 8) {
1104
1788
  process.stderr.write(
1105
1789
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -1119,7 +1803,7 @@ function spawnDaemon() {
1119
1803
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1120
1804
  return;
1121
1805
  }
1122
- const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1806
+ const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1123
1807
  if (!existsSync6(daemonPath)) {
1124
1808
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1125
1809
  `);
@@ -1128,7 +1812,7 @@ function spawnDaemon() {
1128
1812
  const resolvedPath = daemonPath;
1129
1813
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1130
1814
  `);
1131
- const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
1815
+ const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
1132
1816
  let stderrFd = "ignore";
1133
1817
  try {
1134
1818
  stderrFd = openSync(logPath, "a");
@@ -1279,74 +1963,123 @@ async function pingDaemon() {
1279
1963
  return null;
1280
1964
  }
1281
1965
  function killAndRespawnDaemon() {
1282
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
1283
- if (existsSync6(PID_PATH)) {
1284
- try {
1285
- const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
1286
- if (pid > 0) {
1287
- try {
1288
- process.kill(pid, "SIGKILL");
1289
- } catch {
1966
+ if (!acquireSpawnLock()) {
1967
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1968
+ if (_socket) {
1969
+ _socket.destroy();
1970
+ _socket = null;
1971
+ }
1972
+ _connected = false;
1973
+ _buffer = "";
1974
+ return;
1975
+ }
1976
+ try {
1977
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1978
+ if (existsSync6(PID_PATH)) {
1979
+ try {
1980
+ const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
1981
+ if (pid > 0) {
1982
+ try {
1983
+ process.kill(pid, "SIGKILL");
1984
+ } catch {
1985
+ }
1290
1986
  }
1987
+ } catch {
1291
1988
  }
1989
+ }
1990
+ if (_socket) {
1991
+ _socket.destroy();
1992
+ _socket = null;
1993
+ }
1994
+ _connected = false;
1995
+ _buffer = "";
1996
+ try {
1997
+ unlinkSync2(PID_PATH);
1292
1998
  } catch {
1293
1999
  }
2000
+ try {
2001
+ unlinkSync2(SOCKET_PATH);
2002
+ } catch {
2003
+ }
2004
+ spawnDaemon();
2005
+ } finally {
2006
+ releaseSpawnLock();
1294
2007
  }
1295
- if (_socket) {
1296
- _socket.destroy();
1297
- _socket = null;
1298
- }
1299
- _connected = false;
1300
- _buffer = "";
2008
+ }
2009
+ function isDaemonTooYoung() {
1301
2010
  try {
1302
- unlinkSync2(PID_PATH);
2011
+ const stat = statSync(PID_PATH);
2012
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1303
2013
  } catch {
2014
+ return false;
1304
2015
  }
1305
- try {
1306
- unlinkSync2(SOCKET_PATH);
1307
- } catch {
2016
+ }
2017
+ async function retryThenRestart(doRequest, label) {
2018
+ const result = await doRequest();
2019
+ if (!result.error) {
2020
+ _consecutiveFailures = 0;
2021
+ return result;
2022
+ }
2023
+ _consecutiveFailures++;
2024
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
2025
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
2026
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
2027
+ `);
2028
+ await new Promise((r) => setTimeout(r, delayMs));
2029
+ if (!_connected) {
2030
+ if (!await connectToSocket()) continue;
2031
+ }
2032
+ const retry = await doRequest();
2033
+ if (!retry.error) {
2034
+ _consecutiveFailures = 0;
2035
+ return retry;
2036
+ }
2037
+ _consecutiveFailures++;
2038
+ }
2039
+ if (isDaemonTooYoung()) {
2040
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
2041
+ `);
2042
+ return { error: result.error };
1308
2043
  }
1309
- spawnDaemon();
2044
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
2045
+ `);
2046
+ killAndRespawnDaemon();
2047
+ const start = Date.now();
2048
+ let delay2 = 200;
2049
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2050
+ await new Promise((r) => setTimeout(r, delay2));
2051
+ if (await connectToSocket()) break;
2052
+ delay2 = Math.min(delay2 * 2, 3e3);
2053
+ }
2054
+ if (!_connected) return { error: "Daemon restart failed" };
2055
+ const final = await doRequest();
2056
+ if (!final.error) _consecutiveFailures = 0;
2057
+ return final;
1310
2058
  }
1311
2059
  async function embedViaClient(text, priority = "high") {
1312
2060
  if (!_connected && !await connectEmbedDaemon()) return null;
1313
2061
  _requestCount++;
1314
2062
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1315
2063
  const health = await pingDaemon();
1316
- if (!health) {
2064
+ if (!health && !isDaemonTooYoung()) {
1317
2065
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1318
2066
  `);
1319
2067
  killAndRespawnDaemon();
1320
2068
  const start = Date.now();
1321
- let delay2 = 200;
2069
+ let d = 200;
1322
2070
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1323
- await new Promise((r) => setTimeout(r, delay2));
2071
+ await new Promise((r) => setTimeout(r, d));
1324
2072
  if (await connectToSocket()) break;
1325
- delay2 = Math.min(delay2 * 2, 3e3);
2073
+ d = Math.min(d * 2, 3e3);
1326
2074
  }
1327
2075
  if (!_connected) return null;
1328
2076
  }
1329
2077
  }
1330
- const result = await sendRequest([text], priority);
1331
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
1332
- if (result.error) {
1333
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
1334
- `);
1335
- killAndRespawnDaemon();
1336
- const start = Date.now();
1337
- let delay2 = 200;
1338
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1339
- await new Promise((r) => setTimeout(r, delay2));
1340
- if (await connectToSocket()) break;
1341
- delay2 = Math.min(delay2 * 2, 3e3);
1342
- }
1343
- if (!_connected) return null;
1344
- const retry = await sendRequest([text], priority);
1345
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
1346
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
1347
- `);
1348
- }
1349
- return null;
2078
+ const result = await retryThenRestart(
2079
+ () => sendRequest([text], priority),
2080
+ "Embed"
2081
+ );
2082
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
1350
2083
  }
1351
2084
  function disconnectClient() {
1352
2085
  if (_socket) {
@@ -1364,14 +2097,14 @@ function disconnectClient() {
1364
2097
  function isClientConnected() {
1365
2098
  return _connected;
1366
2099
  }
1367
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
2100
+ 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;
1368
2101
  var init_exe_daemon_client = __esm({
1369
2102
  "src/lib/exe-daemon-client.ts"() {
1370
2103
  "use strict";
1371
2104
  init_config();
1372
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
1373
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
1374
- SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
2105
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
2106
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
2107
+ SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
1375
2108
  SPAWN_LOCK_STALE_MS = 3e4;
1376
2109
  CONNECT_TIMEOUT_MS = 15e3;
1377
2110
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1379,7 +2112,11 @@ var init_exe_daemon_client = __esm({
1379
2112
  _connected = false;
1380
2113
  _buffer = "";
1381
2114
  _requestCount = 0;
2115
+ _consecutiveFailures = 0;
1382
2116
  HEALTH_CHECK_INTERVAL = 100;
2117
+ MAX_RETRIES_BEFORE_RESTART = 3;
2118
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
2119
+ MIN_DAEMON_AGE_MS = 3e4;
1383
2120
  _pending = /* @__PURE__ */ new Map();
1384
2121
  MAX_BUFFER = 1e7;
1385
2122
  }
@@ -1455,7 +2192,7 @@ __export(db_daemon_client_exports, {
1455
2192
  createDaemonDbClient: () => createDaemonDbClient,
1456
2193
  initDaemonDbClient: () => initDaemonDbClient
1457
2194
  });
1458
- function normalizeStatement(stmt) {
2195
+ function normalizeStatement2(stmt) {
1459
2196
  if (typeof stmt === "string") {
1460
2197
  return { sql: stmt, args: [] };
1461
2198
  }
@@ -1479,7 +2216,7 @@ function createDaemonDbClient(fallbackClient) {
1479
2216
  if (!_useDaemon || !isClientConnected()) {
1480
2217
  return fallbackClient.execute(stmt);
1481
2218
  }
1482
- const { sql, args } = normalizeStatement(stmt);
2219
+ const { sql, args } = normalizeStatement2(stmt);
1483
2220
  const response = await sendDaemonRequest({
1484
2221
  type: "db-execute",
1485
2222
  sql,
@@ -1504,7 +2241,7 @@ function createDaemonDbClient(fallbackClient) {
1504
2241
  if (!_useDaemon || !isClientConnected()) {
1505
2242
  return fallbackClient.batch(stmts, mode);
1506
2243
  }
1507
- const statements = stmts.map(normalizeStatement);
2244
+ const statements = stmts.map(normalizeStatement2);
1508
2245
  const response = await sendDaemonRequest({
1509
2246
  type: "db-batch",
1510
2247
  statements,
@@ -1599,6 +2336,18 @@ __export(database_exports, {
1599
2336
  });
1600
2337
  import { createClient } from "@libsql/client";
1601
2338
  async function initDatabase(config2) {
2339
+ if (_walCheckpointTimer) {
2340
+ clearInterval(_walCheckpointTimer);
2341
+ _walCheckpointTimer = null;
2342
+ }
2343
+ if (_daemonClient) {
2344
+ _daemonClient.close();
2345
+ _daemonClient = null;
2346
+ }
2347
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2348
+ _adapterClient.close();
2349
+ }
2350
+ _adapterClient = null;
1602
2351
  if (_client) {
1603
2352
  _client.close();
1604
2353
  _client = null;
@@ -1612,6 +2361,7 @@ async function initDatabase(config2) {
1612
2361
  }
1613
2362
  _client = createClient(opts);
1614
2363
  _resilientClient = wrapWithRetry(_client);
2364
+ _adapterClient = _resilientClient;
1615
2365
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1616
2366
  });
1617
2367
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1622,14 +2372,20 @@ async function initDatabase(config2) {
1622
2372
  });
1623
2373
  }, 3e4);
1624
2374
  _walCheckpointTimer.unref();
2375
+ if (process.env.DATABASE_URL) {
2376
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
2377
+ }
1625
2378
  }
1626
2379
  function isInitialized() {
1627
- return _client !== null;
2380
+ return _adapterClient !== null || _client !== null;
1628
2381
  }
1629
2382
  function getClient() {
1630
- if (!_resilientClient) {
2383
+ if (!_adapterClient) {
1631
2384
  throw new Error("Database client not initialized. Call initDatabase() first.");
1632
2385
  }
2386
+ if (process.env.DATABASE_URL) {
2387
+ return _adapterClient;
2388
+ }
1633
2389
  if (process.env.EXE_IS_DAEMON === "1") {
1634
2390
  return _resilientClient;
1635
2391
  }
@@ -1639,6 +2395,7 @@ function getClient() {
1639
2395
  return _resilientClient;
1640
2396
  }
1641
2397
  async function initDaemonClient() {
2398
+ if (process.env.DATABASE_URL) return;
1642
2399
  if (process.env.EXE_IS_DAEMON === "1") return;
1643
2400
  if (!_resilientClient) return;
1644
2401
  try {
@@ -2583,26 +3340,36 @@ async function ensureSchema() {
2583
3340
  }
2584
3341
  }
2585
3342
  async function disposeDatabase() {
3343
+ if (_walCheckpointTimer) {
3344
+ clearInterval(_walCheckpointTimer);
3345
+ _walCheckpointTimer = null;
3346
+ }
2586
3347
  if (_daemonClient) {
2587
3348
  _daemonClient.close();
2588
3349
  _daemonClient = null;
2589
3350
  }
3351
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3352
+ _adapterClient.close();
3353
+ }
3354
+ _adapterClient = null;
2590
3355
  if (_client) {
2591
3356
  _client.close();
2592
3357
  _client = null;
2593
3358
  _resilientClient = null;
2594
3359
  }
2595
3360
  }
2596
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
3361
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2597
3362
  var init_database = __esm({
2598
3363
  "src/lib/database.ts"() {
2599
3364
  "use strict";
2600
3365
  init_db_retry();
2601
3366
  init_employees();
3367
+ init_database_adapter();
2602
3368
  _client = null;
2603
3369
  _resilientClient = null;
2604
3370
  _walCheckpointTimer = null;
2605
3371
  _daemonClient = null;
3372
+ _adapterClient = null;
2606
3373
  initTurso = initDatabase;
2607
3374
  disposeTurso = disposeDatabase;
2608
3375
  }
@@ -2611,16 +3378,16 @@ var init_database = __esm({
2611
3378
  // src/lib/license.ts
2612
3379
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2613
3380
  import { randomUUID as randomUUID3 } from "crypto";
2614
- import path8 from "path";
3381
+ import path9 from "path";
2615
3382
  import { jwtVerify, importSPKI } from "jose";
2616
3383
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2617
3384
  var init_license = __esm({
2618
3385
  "src/lib/license.ts"() {
2619
3386
  "use strict";
2620
3387
  init_config();
2621
- LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2622
- CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2623
- DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
3388
+ LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3389
+ CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3390
+ DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2624
3391
  PLAN_LIMITS = {
2625
3392
  free: { devices: 1, employees: 1, memories: 5e3 },
2626
3393
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2633,7 +3400,7 @@ var init_license = __esm({
2633
3400
 
2634
3401
  // src/lib/plan-limits.ts
2635
3402
  import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
2636
- import path9 from "path";
3403
+ import path10 from "path";
2637
3404
  function getLicenseSync() {
2638
3405
  try {
2639
3406
  if (!existsSync8(CACHE_PATH2)) return freeLicense();
@@ -2705,14 +3472,14 @@ var init_plan_limits = __esm({
2705
3472
  this.name = "PlanLimitError";
2706
3473
  }
2707
3474
  };
2708
- CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
3475
+ CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2709
3476
  }
2710
3477
  });
2711
3478
 
2712
3479
  // src/lib/notifications.ts
2713
3480
  import crypto from "crypto";
2714
- import path10 from "path";
2715
- import os7 from "os";
3481
+ import path11 from "path";
3482
+ import os8 from "os";
2716
3483
  import {
2717
3484
  readFileSync as readFileSync9,
2718
3485
  readdirSync,
@@ -2881,8 +3648,8 @@ var init_state_bus = __esm({
2881
3648
 
2882
3649
  // src/lib/tasks-crud.ts
2883
3650
  import crypto3 from "crypto";
2884
- import path11 from "path";
2885
- import os8 from "os";
3651
+ import path12 from "path";
3652
+ import os9 from "os";
2886
3653
  import { execSync as execSync5 } from "child_process";
2887
3654
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2888
3655
  import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
@@ -3060,8 +3827,8 @@ ${laneWarning}` : laneWarning;
3060
3827
  }
3061
3828
  if (input.baseDir) {
3062
3829
  try {
3063
- await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
3064
- await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
3830
+ await mkdir3(path12.join(input.baseDir, "exe", "output"), { recursive: true });
3831
+ await mkdir3(path12.join(input.baseDir, "exe", "research"), { recursive: true });
3065
3832
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3066
3833
  await ensureGitignoreExe(input.baseDir);
3067
3834
  } catch {
@@ -3097,9 +3864,9 @@ ${laneWarning}` : laneWarning;
3097
3864
  });
3098
3865
  if (input.baseDir) {
3099
3866
  try {
3100
- const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
3101
- const mdPath = path11.join(EXE_OS_DIR, taskFile);
3102
- const mdDir = path11.dirname(mdPath);
3867
+ const EXE_OS_DIR = path12.join(os9.homedir(), ".exe-os");
3868
+ const mdPath = path12.join(EXE_OS_DIR, taskFile);
3869
+ const mdDir = path12.dirname(mdPath);
3103
3870
  if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3104
3871
  const reviewer = input.reviewer ?? input.assignedBy;
3105
3872
  const mdContent = `# ${input.title}
@@ -3400,7 +4167,7 @@ async function deleteTaskCore(taskId, _baseDir) {
3400
4167
  return { taskFile, assignedTo, assignedBy, taskSlug };
3401
4168
  }
3402
4169
  async function ensureArchitectureDoc(baseDir, projectName) {
3403
- const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
4170
+ const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
3404
4171
  try {
3405
4172
  if (existsSync10(archPath)) return;
3406
4173
  const template = [
@@ -3435,7 +4202,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3435
4202
  }
3436
4203
  }
3437
4204
  async function ensureGitignoreExe(baseDir) {
3438
- const gitignorePath = path11.join(baseDir, ".gitignore");
4205
+ const gitignorePath = path12.join(baseDir, ".gitignore");
3439
4206
  try {
3440
4207
  if (existsSync10(gitignorePath)) {
3441
4208
  const content = readFileSync10(gitignorePath, "utf-8");
@@ -3469,13 +4236,13 @@ var init_tasks_crud = __esm({
3469
4236
  });
3470
4237
 
3471
4238
  // src/lib/tasks-review.ts
3472
- import path12 from "path";
4239
+ import path13 from "path";
3473
4240
  import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3474
4241
  async function countPendingReviews(sessionScope) {
3475
4242
  const client = getClient();
3476
4243
  if (sessionScope) {
3477
4244
  const result2 = await client.execute({
3478
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
4245
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
3479
4246
  args: [sessionScope]
3480
4247
  });
3481
4248
  return Number(result2.rows[0]?.cnt) || 0;
@@ -3651,11 +4418,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3651
4418
  );
3652
4419
  }
3653
4420
  try {
3654
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
4421
+ const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
3655
4422
  if (existsSync11(cacheDir)) {
3656
4423
  for (const f of readdirSync2(cacheDir)) {
3657
4424
  if (f.startsWith("review-notified-")) {
3658
- unlinkSync4(path12.join(cacheDir, f));
4425
+ unlinkSync4(path13.join(cacheDir, f));
3659
4426
  }
3660
4427
  }
3661
4428
  }
@@ -3676,7 +4443,7 @@ var init_tasks_review = __esm({
3676
4443
  });
3677
4444
 
3678
4445
  // src/lib/tasks-chain.ts
3679
- import path13 from "path";
4446
+ import path14 from "path";
3680
4447
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3681
4448
  async function cascadeUnblock(taskId, baseDir, now) {
3682
4449
  const client = getClient();
@@ -3693,7 +4460,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3693
4460
  });
3694
4461
  for (const ur of unblockedRows.rows) {
3695
4462
  try {
3696
- const ubFile = path13.join(baseDir, String(ur.task_file));
4463
+ const ubFile = path14.join(baseDir, String(ur.task_file));
3697
4464
  let ubContent = await readFile3(ubFile, "utf-8");
3698
4465
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3699
4466
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3762,7 +4529,7 @@ var init_tasks_chain = __esm({
3762
4529
 
3763
4530
  // src/lib/project-name.ts
3764
4531
  import { execSync as execSync6 } from "child_process";
3765
- import path14 from "path";
4532
+ import path15 from "path";
3766
4533
  function getProjectName(cwd) {
3767
4534
  const dir = cwd ?? process.cwd();
3768
4535
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3775,7 +4542,7 @@ function getProjectName(cwd) {
3775
4542
  timeout: 2e3,
3776
4543
  stdio: ["pipe", "pipe", "pipe"]
3777
4544
  }).trim();
3778
- repoRoot = path14.dirname(gitCommonDir);
4545
+ repoRoot = path15.dirname(gitCommonDir);
3779
4546
  } catch {
3780
4547
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3781
4548
  cwd: dir,
@@ -3784,11 +4551,11 @@ function getProjectName(cwd) {
3784
4551
  stdio: ["pipe", "pipe", "pipe"]
3785
4552
  }).trim();
3786
4553
  }
3787
- _cached2 = path14.basename(repoRoot);
4554
+ _cached2 = path15.basename(repoRoot);
3788
4555
  _cachedCwd = dir;
3789
4556
  return _cached2;
3790
4557
  } catch {
3791
- _cached2 = path14.basename(dir);
4558
+ _cached2 = path15.basename(dir);
3792
4559
  _cachedCwd = dir;
3793
4560
  return _cached2;
3794
4561
  }
@@ -4323,7 +5090,7 @@ __export(tasks_exports, {
4323
5090
  updateTaskStatus: () => updateTaskStatus,
4324
5091
  writeCheckpoint: () => writeCheckpoint
4325
5092
  });
4326
- import path15 from "path";
5093
+ import path16 from "path";
4327
5094
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
4328
5095
  async function createTask(input) {
4329
5096
  const result = await createTaskCore(input);
@@ -4343,8 +5110,8 @@ async function updateTask(input) {
4343
5110
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4344
5111
  try {
4345
5112
  const agent = String(row.assigned_to);
4346
- const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
4347
- const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
5113
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5114
+ const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
4348
5115
  if (input.status === "in_progress") {
4349
5116
  mkdirSync5(cacheDir, { recursive: true });
4350
5117
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -4815,12 +5582,12 @@ __export(tmux_routing_exports, {
4815
5582
  });
4816
5583
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4817
5584
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
4818
- import path16 from "path";
4819
- import os9 from "os";
5585
+ import path17 from "path";
5586
+ import os10 from "os";
4820
5587
  import { fileURLToPath as fileURLToPath2 } from "url";
4821
5588
  import { unlinkSync as unlinkSync6 } from "fs";
4822
5589
  function spawnLockPath(sessionName) {
4823
- return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5590
+ return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4824
5591
  }
4825
5592
  function isProcessAlive(pid) {
4826
5593
  try {
@@ -4857,8 +5624,8 @@ function releaseSpawnLock2(sessionName) {
4857
5624
  function resolveBehaviorsExporterScript() {
4858
5625
  try {
4859
5626
  const thisFile = fileURLToPath2(import.meta.url);
4860
- const scriptPath = path16.join(
4861
- path16.dirname(thisFile),
5627
+ const scriptPath = path17.join(
5628
+ path17.dirname(thisFile),
4862
5629
  "..",
4863
5630
  "bin",
4864
5631
  "exe-export-behaviors.js"
@@ -4933,7 +5700,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4933
5700
  mkdirSync6(SESSION_CACHE, { recursive: true });
4934
5701
  }
4935
5702
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4936
- const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5703
+ const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4937
5704
  writeFileSync7(filePath, JSON.stringify({
4938
5705
  parentExe: rootExe,
4939
5706
  dispatchedBy: dispatchedBy || rootExe,
@@ -4942,7 +5709,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4942
5709
  }
4943
5710
  function getParentExe(sessionKey) {
4944
5711
  try {
4945
- const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5712
+ const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4946
5713
  return data.parentExe || null;
4947
5714
  } catch {
4948
5715
  return null;
@@ -4951,7 +5718,7 @@ function getParentExe(sessionKey) {
4951
5718
  function getDispatchedBy(sessionKey) {
4952
5719
  try {
4953
5720
  const data = JSON.parse(readFileSync11(
4954
- path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5721
+ path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4955
5722
  "utf8"
4956
5723
  ));
4957
5724
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5137,7 +5904,7 @@ function sendIntercom(targetSession) {
5137
5904
  try {
5138
5905
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5139
5906
  const agent = baseAgentName(rawAgent);
5140
- const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
5907
+ const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
5141
5908
  if (existsSync12(markerPath)) {
5142
5909
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
5143
5910
  return "debounced";
@@ -5147,7 +5914,7 @@ function sendIntercom(targetSession) {
5147
5914
  try {
5148
5915
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5149
5916
  const agent = baseAgentName(rawAgent);
5150
- const taskDir = path16.join(process.cwd(), "exe", agent);
5917
+ const taskDir = path17.join(process.cwd(), "exe", agent);
5151
5918
  if (existsSync12(taskDir)) {
5152
5919
  const files = readdirSync3(taskDir).filter(
5153
5920
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -5281,8 +6048,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5281
6048
  const transport = getTransport();
5282
6049
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5283
6050
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5284
- const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
5285
- const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6051
+ const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
6052
+ const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5286
6053
  if (!existsSync12(logDir)) {
5287
6054
  mkdirSync6(logDir, { recursive: true });
5288
6055
  }
@@ -5290,14 +6057,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5290
6057
  let cleanupSuffix = "";
5291
6058
  try {
5292
6059
  const thisFile = fileURLToPath2(import.meta.url);
5293
- const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6060
+ const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5294
6061
  if (existsSync12(cleanupScript)) {
5295
6062
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5296
6063
  }
5297
6064
  } catch {
5298
6065
  }
5299
6066
  try {
5300
- const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
6067
+ const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
5301
6068
  let claudeJson = {};
5302
6069
  try {
5303
6070
  claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
@@ -5312,10 +6079,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5312
6079
  } catch {
5313
6080
  }
5314
6081
  try {
5315
- const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
6082
+ const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
5316
6083
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5317
- const projSettingsDir = path16.join(settingsDir, normalizedKey);
5318
- const settingsPath = path16.join(projSettingsDir, "settings.json");
6084
+ const projSettingsDir = path17.join(settingsDir, normalizedKey);
6085
+ const settingsPath = path17.join(projSettingsDir, "settings.json");
5319
6086
  let settings = {};
5320
6087
  try {
5321
6088
  settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
@@ -5362,8 +6129,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5362
6129
  let behaviorsFlag = "";
5363
6130
  let legacyFallbackWarned = false;
5364
6131
  if (!useExeAgent && !useBinSymlink) {
5365
- const identityPath = path16.join(
5366
- os9.homedir(),
6132
+ const identityPath = path17.join(
6133
+ os10.homedir(),
5367
6134
  ".exe-os",
5368
6135
  "identity",
5369
6136
  `${employeeName}.md`
@@ -5378,7 +6145,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5378
6145
  }
5379
6146
  const behaviorsFile = exportBehaviorsSync(
5380
6147
  employeeName,
5381
- path16.basename(spawnCwd),
6148
+ path17.basename(spawnCwd),
5382
6149
  sessionName
5383
6150
  );
5384
6151
  if (behaviorsFile) {
@@ -5393,9 +6160,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5393
6160
  }
5394
6161
  let sessionContextFlag = "";
5395
6162
  try {
5396
- const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
6163
+ const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
5397
6164
  mkdirSync6(ctxDir, { recursive: true });
5398
- const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
6165
+ const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
5399
6166
  const ctxContent = [
5400
6167
  `## Session Context`,
5401
6168
  `You are running in tmux session: ${sessionName}.`,
@@ -5479,7 +6246,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5479
6246
  transport.pipeLog(sessionName, logFile);
5480
6247
  try {
5481
6248
  const mySession = getMySession();
5482
- const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6249
+ const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5483
6250
  writeFileSync7(dispatchInfo, JSON.stringify({
5484
6251
  dispatchedBy: mySession,
5485
6252
  rootExe: exeSession,
@@ -5554,15 +6321,15 @@ var init_tmux_routing = __esm({
5554
6321
  init_intercom_queue();
5555
6322
  init_plan_limits();
5556
6323
  init_employees();
5557
- SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
5558
- SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
6324
+ SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
6325
+ SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
5559
6326
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5560
6327
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5561
6328
  VERIFY_PANE_LINES = 200;
5562
6329
  INTERCOM_DEBOUNCE_MS = 3e4;
5563
6330
  CODEX_DEBOUNCE_MS = 12e4;
5564
- INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
5565
- DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
6331
+ INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
6332
+ DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
5566
6333
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5567
6334
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5568
6335
  }
@@ -5580,13 +6347,13 @@ var init_memory = __esm({
5580
6347
  // src/lib/keychain.ts
5581
6348
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
5582
6349
  import { existsSync as existsSync13 } from "fs";
5583
- import path17 from "path";
5584
- import os10 from "os";
6350
+ import path18 from "path";
6351
+ import os11 from "os";
5585
6352
  function getKeyDir() {
5586
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os10.homedir(), ".exe-os");
6353
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os11.homedir(), ".exe-os");
5587
6354
  }
5588
6355
  function getKeyPath() {
5589
- return path17.join(getKeyDir(), "master.key");
6356
+ return path18.join(getKeyDir(), "master.key");
5590
6357
  }
5591
6358
  async function tryKeytar() {
5592
6359
  try {
@@ -5609,7 +6376,7 @@ async function getMasterKey() {
5609
6376
  const keyPath = getKeyPath();
5610
6377
  if (!existsSync13(keyPath)) {
5611
6378
  process.stderr.write(
5612
- `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6379
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5613
6380
  `
5614
6381
  );
5615
6382
  return null;
@@ -5647,7 +6414,7 @@ __export(shard_manager_exports, {
5647
6414
  listShards: () => listShards,
5648
6415
  shardExists: () => shardExists
5649
6416
  });
5650
- import path18 from "path";
6417
+ import path19 from "path";
5651
6418
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
5652
6419
  import { createClient as createClient2 } from "@libsql/client";
5653
6420
  function initShardManager(encryptionKey) {
@@ -5673,7 +6440,7 @@ function getShardClient(projectName) {
5673
6440
  }
5674
6441
  const cached = _shards.get(safeName);
5675
6442
  if (cached) return cached;
5676
- const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
6443
+ const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
5677
6444
  const client = createClient2({
5678
6445
  url: `file:${dbPath}`,
5679
6446
  encryptionKey: _encryptionKey
@@ -5683,7 +6450,7 @@ function getShardClient(projectName) {
5683
6450
  }
5684
6451
  function shardExists(projectName) {
5685
6452
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
5686
- return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
6453
+ return existsSync14(path19.join(SHARDS_DIR, `${safeName}.db`));
5687
6454
  }
5688
6455
  function listShards() {
5689
6456
  if (!existsSync14(SHARDS_DIR)) return [];
@@ -5760,7 +6527,23 @@ async function ensureShardSchema(client) {
5760
6527
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
5761
6528
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
5762
6529
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
5763
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
6530
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
6531
+ // Metadata enrichment columns (must match database.ts)
6532
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
6533
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
6534
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
6535
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
6536
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
6537
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
6538
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
6539
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
6540
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
6541
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
6542
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
6543
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
6544
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
6545
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
6546
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
5764
6547
  ]) {
5765
6548
  try {
5766
6549
  await client.execute(col);
@@ -5872,7 +6655,7 @@ var init_shard_manager = __esm({
5872
6655
  "src/lib/shard-manager.ts"() {
5873
6656
  "use strict";
5874
6657
  init_config();
5875
- SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
6658
+ SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
5876
6659
  _shards = /* @__PURE__ */ new Map();
5877
6660
  _encryptionKey = null;
5878
6661
  _shardingEnabled = false;
@@ -6736,9 +7519,9 @@ function extractBash(input, response) {
6736
7519
  }
6737
7520
  function extractGrep(input, response) {
6738
7521
  const pattern = String(input.pattern ?? "");
6739
- const path21 = input.path ? String(input.path) : "";
7522
+ const path22 = input.path ? String(input.path) : "";
6740
7523
  const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
6741
- return `Searched for "${pattern}"${path21 ? ` in ${path21}` : ""}
7524
+ return `Searched for "${pattern}"${path22 ? ` in ${path22}` : ""}
6742
7525
  ${output.slice(0, MAX_OUTPUT)}`;
6743
7526
  }
6744
7527
  function extractGlob(input, response) {
@@ -7277,8 +8060,8 @@ async function embedDirect(text) {
7277
8060
  const llamaCpp = await import("node-llama-cpp");
7278
8061
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
7279
8062
  const { existsSync: existsSync16 } = await import("fs");
7280
- const path21 = await import("path");
7281
- const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
8063
+ const path22 = await import("path");
8064
+ const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
7282
8065
  if (!existsSync16(modelPath)) {
7283
8066
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
7284
8067
  }
@@ -7316,8 +8099,8 @@ __export(wiki_client_exports, {
7316
8099
  listDocuments: () => listDocuments,
7317
8100
  listWorkspaces: () => listWorkspaces
7318
8101
  });
7319
- async function wikiFetch(config2, path21, method = "GET", body) {
7320
- const url = `${config2.baseUrl}/api/v1${path21}`;
8102
+ async function wikiFetch(config2, path22, method = "GET", body) {
8103
+ const url = `${config2.baseUrl}/api/v1${path22}`;
7321
8104
  const headers = {
7322
8105
  Authorization: `Bearer ${config2.apiKey}`,
7323
8106
  "Content-Type": "application/json"
@@ -7350,7 +8133,7 @@ async function wikiFetch(config2, path21, method = "GET", body) {
7350
8133
  }
7351
8134
  }
7352
8135
  if (!response.ok) {
7353
- throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
8136
+ throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
7354
8137
  }
7355
8138
  return response.json();
7356
8139
  } finally {
@@ -13877,12 +14660,12 @@ var SlackAdapter = class {
13877
14660
  // src/gateway/adapters/imessage.ts
13878
14661
  import { execFile } from "child_process";
13879
14662
  import { promisify } from "util";
13880
- import os11 from "os";
13881
- import path19 from "path";
14663
+ import os12 from "os";
14664
+ import path20 from "path";
13882
14665
  var execFileAsync = promisify(execFile);
13883
14666
  var POLL_INTERVAL_MS = 5e3;
13884
- var MESSAGES_DB_PATH = path19.join(
13885
- process.env.HOME ?? os11.homedir(),
14667
+ var MESSAGES_DB_PATH = path20.join(
14668
+ process.env.HOME ?? os12.homedir(),
13886
14669
  "Library/Messages/chat.db"
13887
14670
  );
13888
14671
  var IMessageAdapter = class {
@@ -14727,9 +15510,9 @@ async function ensureCRMContact(info) {
14727
15510
  // src/automation/trigger-engine.ts
14728
15511
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
14729
15512
  import { randomUUID as randomUUID15 } from "crypto";
14730
- import path20 from "path";
14731
- import os12 from "os";
14732
- var TRIGGERS_PATH = path20.join(os12.homedir(), ".exe-os", "triggers.json");
15513
+ import path21 from "path";
15514
+ import os13 from "os";
15515
+ var TRIGGERS_PATH = path21.join(os13.homedir(), ".exe-os", "triggers.json");
14733
15516
  var GRAPH_API_VERSION = "v21.0";
14734
15517
  function substituteTemplate(template, record) {
14735
15518
  return template.replace(