@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
@@ -263,6 +263,118 @@ var init_config = __esm({
263
263
  }
264
264
  });
265
265
 
266
+ // src/lib/runtime-table.ts
267
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
268
+ var init_runtime_table = __esm({
269
+ "src/lib/runtime-table.ts"() {
270
+ "use strict";
271
+ RUNTIME_TABLE = {
272
+ codex: {
273
+ binary: "codex",
274
+ launchMode: "interactive",
275
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
276
+ inlineFlag: "--no-alt-screen",
277
+ apiKeyEnv: "OPENAI_API_KEY",
278
+ defaultModel: "gpt-5.4"
279
+ },
280
+ opencode: {
281
+ binary: "opencode",
282
+ launchMode: "exec",
283
+ autoApproveFlag: "--dangerously-skip-permissions",
284
+ inlineFlag: "",
285
+ apiKeyEnv: "ANTHROPIC_API_KEY",
286
+ defaultModel: "anthropic/claude-sonnet-4-6"
287
+ }
288
+ };
289
+ DEFAULT_RUNTIME = "claude";
290
+ }
291
+ });
292
+
293
+ // src/lib/agent-config.ts
294
+ var agent_config_exports = {};
295
+ __export(agent_config_exports, {
296
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
297
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
298
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
299
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
300
+ clearAgentRuntime: () => clearAgentRuntime,
301
+ getAgentRuntime: () => getAgentRuntime,
302
+ loadAgentConfig: () => loadAgentConfig,
303
+ saveAgentConfig: () => saveAgentConfig,
304
+ setAgentRuntime: () => setAgentRuntime
305
+ });
306
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
307
+ import path2 from "path";
308
+ function loadAgentConfig() {
309
+ if (!existsSync2(AGENT_CONFIG_PATH)) return {};
310
+ try {
311
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
312
+ } catch {
313
+ return {};
314
+ }
315
+ }
316
+ function saveAgentConfig(config) {
317
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
318
+ if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
319
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
320
+ }
321
+ function getAgentRuntime(agentId) {
322
+ const config = loadAgentConfig();
323
+ const entry = config[agentId];
324
+ if (entry) return entry;
325
+ const orgDefault = config["default"];
326
+ if (orgDefault) return orgDefault;
327
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
328
+ }
329
+ function setAgentRuntime(agentId, runtime, model) {
330
+ const knownModels = KNOWN_RUNTIMES[runtime];
331
+ if (!knownModels) {
332
+ return {
333
+ ok: false,
334
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
335
+ };
336
+ }
337
+ if (!knownModels.includes(model)) {
338
+ return {
339
+ ok: false,
340
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
341
+ };
342
+ }
343
+ const config = loadAgentConfig();
344
+ config[agentId] = { runtime, model };
345
+ saveAgentConfig(config);
346
+ return { ok: true };
347
+ }
348
+ function clearAgentRuntime(agentId) {
349
+ const config = loadAgentConfig();
350
+ delete config[agentId];
351
+ saveAgentConfig(config);
352
+ }
353
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
354
+ var init_agent_config = __esm({
355
+ "src/lib/agent-config.ts"() {
356
+ "use strict";
357
+ init_config();
358
+ init_runtime_table();
359
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
360
+ KNOWN_RUNTIMES = {
361
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
362
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
363
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
364
+ };
365
+ RUNTIME_LABELS = {
366
+ claude: "Claude Code (Anthropic)",
367
+ codex: "Codex (OpenAI)",
368
+ opencode: "OpenCode (open source)"
369
+ };
370
+ DEFAULT_MODELS = {
371
+ claude: "claude-opus-4",
372
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
373
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
374
+ };
375
+ }
376
+ });
377
+
266
378
  // src/lib/employees.ts
267
379
  var employees_exports = {};
268
380
  __export(employees_exports, {
@@ -278,6 +390,7 @@ __export(employees_exports, {
278
390
  getEmployeeByRole: () => getEmployeeByRole,
279
391
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
280
392
  hasRole: () => hasRole,
393
+ hireEmployee: () => hireEmployee,
281
394
  isCoordinatorName: () => isCoordinatorName,
282
395
  isCoordinatorRole: () => isCoordinatorRole,
283
396
  isMultiInstance: () => isMultiInstance,
@@ -290,9 +403,9 @@ __export(employees_exports, {
290
403
  validateEmployeeName: () => validateEmployeeName
291
404
  });
292
405
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
293
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
406
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
294
407
  import { execSync } from "child_process";
295
- import path2 from "path";
408
+ import path3 from "path";
296
409
  import os2 from "os";
297
410
  function normalizeRole(role) {
298
411
  return (role ?? "").trim().toLowerCase();
@@ -329,7 +442,7 @@ function validateEmployeeName(name) {
329
442
  return { valid: true };
330
443
  }
331
444
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
332
- if (!existsSync2(employeesPath)) {
445
+ if (!existsSync3(employeesPath)) {
333
446
  return [];
334
447
  }
335
448
  const raw = await readFile2(employeesPath, "utf-8");
@@ -340,13 +453,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
340
453
  }
341
454
  }
342
455
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
343
- await mkdir2(path2.dirname(employeesPath), { recursive: true });
456
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
344
457
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
345
458
  }
346
459
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
347
- if (!existsSync2(employeesPath)) return [];
460
+ if (!existsSync3(employeesPath)) return [];
348
461
  try {
349
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
462
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
350
463
  } catch {
351
464
  return [];
352
465
  }
@@ -388,6 +501,52 @@ function addEmployee(employees, employee) {
388
501
  }
389
502
  return [...employees, normalized];
390
503
  }
504
+ function appendToCoordinatorTeam(employee) {
505
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
506
+ if (!coordinator) return;
507
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
508
+ if (!existsSync3(idPath)) return;
509
+ const content = readFileSync3(idPath, "utf-8");
510
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
511
+ const teamMatch = content.match(TEAM_SECTION_RE);
512
+ if (!teamMatch || teamMatch.index === void 0) return;
513
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
514
+ const nextHeading = afterTeam.match(/\n## /);
515
+ const entry = `
516
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
517
+ `;
518
+ let updated;
519
+ if (nextHeading && nextHeading.index !== void 0) {
520
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
521
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
522
+ } else {
523
+ updated = content.trimEnd() + "\n" + entry;
524
+ }
525
+ writeFileSync2(idPath, updated, "utf-8");
526
+ }
527
+ function capitalize(s) {
528
+ return s.charAt(0).toUpperCase() + s.slice(1);
529
+ }
530
+ async function hireEmployee(employee) {
531
+ const employees = await loadEmployees();
532
+ const updated = addEmployee(employees, employee);
533
+ await saveEmployees(updated);
534
+ try {
535
+ appendToCoordinatorTeam(employee);
536
+ } catch {
537
+ }
538
+ try {
539
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
540
+ const config = loadAgentConfig2();
541
+ const name = employee.name.toLowerCase();
542
+ if (!config[name] && config["default"]) {
543
+ config[name] = { ...config["default"] };
544
+ saveAgentConfig2(config);
545
+ }
546
+ } catch {
547
+ }
548
+ return updated;
549
+ }
391
550
  async function normalizeRosterCase(rosterPath) {
392
551
  const employees = await loadEmployees(rosterPath);
393
552
  let changed = false;
@@ -397,14 +556,14 @@ async function normalizeRosterCase(rosterPath) {
397
556
  emp.name = emp.name.toLowerCase();
398
557
  changed = true;
399
558
  try {
400
- const identityDir = path2.join(os2.homedir(), ".exe-os", "identity");
401
- const oldPath = path2.join(identityDir, `${oldName}.md`);
402
- const newPath = path2.join(identityDir, `${emp.name}.md`);
403
- if (existsSync2(oldPath) && !existsSync2(newPath)) {
559
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
560
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
561
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
562
+ if (existsSync3(oldPath) && !existsSync3(newPath)) {
404
563
  renameSync2(oldPath, newPath);
405
- } else if (existsSync2(oldPath) && oldPath !== newPath) {
406
- const content = readFileSync2(oldPath, "utf-8");
407
- writeFileSync(newPath, content, "utf-8");
564
+ } else if (existsSync3(oldPath) && oldPath !== newPath) {
565
+ const content = readFileSync3(oldPath, "utf-8");
566
+ writeFileSync2(newPath, content, "utf-8");
408
567
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
409
568
  unlinkSync(oldPath);
410
569
  }
@@ -434,7 +593,7 @@ function registerBinSymlinks(name) {
434
593
  errors.push("Could not find 'exe-os' in PATH");
435
594
  return { created, skipped, errors };
436
595
  }
437
- const binDir = path2.dirname(exeBinPath);
596
+ const binDir = path3.dirname(exeBinPath);
438
597
  let target;
439
598
  try {
440
599
  target = readlinkSync(exeBinPath);
@@ -444,8 +603,8 @@ function registerBinSymlinks(name) {
444
603
  }
445
604
  for (const suffix of ["", "-opencode"]) {
446
605
  const linkName = `${name}${suffix}`;
447
- const linkPath = path2.join(binDir, linkName);
448
- if (existsSync2(linkPath)) {
606
+ const linkPath = path3.join(binDir, linkName);
607
+ if (existsSync3(linkPath)) {
449
608
  skipped.push(linkName);
450
609
  continue;
451
610
  }
@@ -458,21 +617,619 @@ function registerBinSymlinks(name) {
458
617
  }
459
618
  return { created, skipped, errors };
460
619
  }
461
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
620
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
462
621
  var init_employees = __esm({
463
622
  "src/lib/employees.ts"() {
464
623
  "use strict";
465
624
  init_config();
466
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
625
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
467
626
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
468
627
  COORDINATOR_ROLE = "COO";
469
628
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
629
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
630
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
631
+ }
632
+ });
633
+
634
+ // src/lib/database-adapter.ts
635
+ import os3 from "os";
636
+ import path4 from "path";
637
+ import { createRequire } from "module";
638
+ import { pathToFileURL } from "url";
639
+ function quotedIdentifier(identifier) {
640
+ return `"${identifier.replace(/"/g, '""')}"`;
641
+ }
642
+ function unqualifiedTableName(name) {
643
+ const raw = name.trim().replace(/^"|"$/g, "");
644
+ const parts = raw.split(".");
645
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
646
+ }
647
+ function stripTrailingSemicolon(sql) {
648
+ return sql.trim().replace(/;+\s*$/u, "");
649
+ }
650
+ function appendClause(sql, clause) {
651
+ const trimmed = stripTrailingSemicolon(sql);
652
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
653
+ if (!returningMatch) {
654
+ return `${trimmed}${clause}`;
655
+ }
656
+ const idx = returningMatch.index;
657
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
658
+ }
659
+ function normalizeStatement(stmt) {
660
+ if (typeof stmt === "string") {
661
+ return { kind: "positional", sql: stmt, args: [] };
662
+ }
663
+ const sql = stmt.sql;
664
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
665
+ return { kind: "positional", sql, args: stmt.args ?? [] };
666
+ }
667
+ return { kind: "named", sql, args: stmt.args };
668
+ }
669
+ function rewriteBooleanLiterals(sql) {
670
+ let out = sql;
671
+ for (const column of BOOLEAN_COLUMN_NAMES) {
672
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
673
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
674
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
675
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
676
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
677
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
678
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
679
+ }
680
+ return out;
681
+ }
682
+ function rewriteInsertOrIgnore(sql) {
683
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
684
+ return sql;
685
+ }
686
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
687
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
688
+ }
689
+ function rewriteInsertOrReplace(sql) {
690
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
691
+ if (!match) {
692
+ return sql;
693
+ }
694
+ const rawTable = match[1];
695
+ const rawColumns = match[2];
696
+ const remainder = match[3];
697
+ const tableName = unqualifiedTableName(rawTable);
698
+ const conflictKeys = UPSERT_KEYS[tableName];
699
+ if (!conflictKeys?.length) {
700
+ return sql;
701
+ }
702
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
703
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
704
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
705
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
706
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
707
+ }
708
+ function rewriteSql(sql) {
709
+ let out = sql;
710
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
711
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
712
+ out = rewriteBooleanLiterals(out);
713
+ out = rewriteInsertOrReplace(out);
714
+ out = rewriteInsertOrIgnore(out);
715
+ return stripTrailingSemicolon(out);
716
+ }
717
+ function toBoolean(value) {
718
+ if (value === null || value === void 0) return value;
719
+ if (typeof value === "boolean") return value;
720
+ if (typeof value === "number") return value !== 0;
721
+ if (typeof value === "bigint") return value !== 0n;
722
+ if (typeof value === "string") {
723
+ const normalized = value.trim().toLowerCase();
724
+ if (normalized === "0" || normalized === "false") return false;
725
+ if (normalized === "1" || normalized === "true") return true;
726
+ }
727
+ return Boolean(value);
728
+ }
729
+ function countQuestionMarks(sql, end) {
730
+ let count = 0;
731
+ let inSingle = false;
732
+ let inDouble = false;
733
+ let inLineComment = false;
734
+ let inBlockComment = false;
735
+ for (let i = 0; i < end; i++) {
736
+ const ch = sql[i];
737
+ const next = sql[i + 1];
738
+ if (inLineComment) {
739
+ if (ch === "\n") inLineComment = false;
740
+ continue;
741
+ }
742
+ if (inBlockComment) {
743
+ if (ch === "*" && next === "/") {
744
+ inBlockComment = false;
745
+ i += 1;
746
+ }
747
+ continue;
748
+ }
749
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
750
+ inLineComment = true;
751
+ i += 1;
752
+ continue;
753
+ }
754
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
755
+ inBlockComment = true;
756
+ i += 1;
757
+ continue;
758
+ }
759
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
760
+ inSingle = !inSingle;
761
+ continue;
762
+ }
763
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
764
+ inDouble = !inDouble;
765
+ continue;
766
+ }
767
+ if (!inSingle && !inDouble && ch === "?") {
768
+ count += 1;
769
+ }
770
+ }
771
+ return count;
772
+ }
773
+ function findBooleanPlaceholderIndexes(sql) {
774
+ const indexes = /* @__PURE__ */ new Set();
775
+ for (const column of BOOLEAN_COLUMN_NAMES) {
776
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
777
+ for (const match of sql.matchAll(pattern)) {
778
+ const matchText = match[0];
779
+ const qIndex = match.index + matchText.lastIndexOf("?");
780
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
781
+ }
782
+ }
783
+ return indexes;
784
+ }
785
+ function coerceInsertBooleanArgs(sql, args) {
786
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
787
+ if (!match) return;
788
+ const rawTable = match[1];
789
+ const rawColumns = match[2];
790
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
791
+ if (!boolColumns?.size) return;
792
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
793
+ for (const [index, column] of columns.entries()) {
794
+ if (boolColumns.has(column) && index < args.length) {
795
+ args[index] = toBoolean(args[index]);
796
+ }
797
+ }
798
+ }
799
+ function coerceUpdateBooleanArgs(sql, args) {
800
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
801
+ if (!match) return;
802
+ const rawTable = match[1];
803
+ const setClause = match[2];
804
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
805
+ if (!boolColumns?.size) return;
806
+ const assignments = setClause.split(",");
807
+ let placeholderIndex = 0;
808
+ for (const assignment of assignments) {
809
+ if (!assignment.includes("?")) continue;
810
+ placeholderIndex += 1;
811
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
812
+ if (colMatch && boolColumns.has(colMatch[1])) {
813
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
814
+ }
815
+ }
816
+ }
817
+ function coerceBooleanArgs(sql, args) {
818
+ const nextArgs = [...args];
819
+ coerceInsertBooleanArgs(sql, nextArgs);
820
+ coerceUpdateBooleanArgs(sql, nextArgs);
821
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
822
+ for (const index of placeholderIndexes) {
823
+ if (index > 0 && index <= nextArgs.length) {
824
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
825
+ }
826
+ }
827
+ return nextArgs;
828
+ }
829
+ function convertQuestionMarksToDollarParams(sql) {
830
+ let out = "";
831
+ let placeholder = 0;
832
+ let inSingle = false;
833
+ let inDouble = false;
834
+ let inLineComment = false;
835
+ let inBlockComment = false;
836
+ for (let i = 0; i < sql.length; i++) {
837
+ const ch = sql[i];
838
+ const next = sql[i + 1];
839
+ if (inLineComment) {
840
+ out += ch;
841
+ if (ch === "\n") inLineComment = false;
842
+ continue;
843
+ }
844
+ if (inBlockComment) {
845
+ out += ch;
846
+ if (ch === "*" && next === "/") {
847
+ out += next;
848
+ inBlockComment = false;
849
+ i += 1;
850
+ }
851
+ continue;
852
+ }
853
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
854
+ out += ch + next;
855
+ inLineComment = true;
856
+ i += 1;
857
+ continue;
858
+ }
859
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
860
+ out += ch + next;
861
+ inBlockComment = true;
862
+ i += 1;
863
+ continue;
864
+ }
865
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
866
+ inSingle = !inSingle;
867
+ out += ch;
868
+ continue;
869
+ }
870
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
871
+ inDouble = !inDouble;
872
+ out += ch;
873
+ continue;
874
+ }
875
+ if (!inSingle && !inDouble && ch === "?") {
876
+ placeholder += 1;
877
+ out += `$${placeholder}`;
878
+ continue;
879
+ }
880
+ out += ch;
881
+ }
882
+ return out;
883
+ }
884
+ function translateStatementForPostgres(stmt) {
885
+ const normalized = normalizeStatement(stmt);
886
+ if (normalized.kind === "named") {
887
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
888
+ }
889
+ const rewrittenSql = rewriteSql(normalized.sql);
890
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
891
+ return {
892
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
893
+ args: coercedArgs
894
+ };
895
+ }
896
+ function shouldBypassPostgres(stmt) {
897
+ const normalized = normalizeStatement(stmt);
898
+ if (normalized.kind === "named") {
899
+ return true;
900
+ }
901
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
902
+ }
903
+ function shouldFallbackOnError(error) {
904
+ const message = error instanceof Error ? error.message : String(error);
905
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
906
+ }
907
+ function isReadQuery(sql) {
908
+ const trimmed = sql.trimStart();
909
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
910
+ }
911
+ function buildRow(row, columns) {
912
+ const values = columns.map((column) => row[column]);
913
+ return Object.assign(values, row);
914
+ }
915
+ function buildResultSet(rows, rowsAffected = 0) {
916
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
917
+ const resultRows = rows.map((row) => buildRow(row, columns));
918
+ return {
919
+ columns,
920
+ columnTypes: columns.map(() => ""),
921
+ rows: resultRows,
922
+ rowsAffected,
923
+ lastInsertRowid: void 0,
924
+ toJSON() {
925
+ return {
926
+ columns,
927
+ columnTypes: columns.map(() => ""),
928
+ rows,
929
+ rowsAffected,
930
+ lastInsertRowid: void 0
931
+ };
932
+ }
933
+ };
934
+ }
935
+ async function loadPrismaClient() {
936
+ if (!prismaClientPromise) {
937
+ prismaClientPromise = (async () => {
938
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
939
+ if (explicitPath) {
940
+ const module2 = await import(pathToFileURL(explicitPath).href);
941
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
942
+ if (!PrismaClient2) {
943
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
944
+ }
945
+ return new PrismaClient2();
946
+ }
947
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
948
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
949
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
950
+ const module = await import(pathToFileURL(prismaEntry).href);
951
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
952
+ if (!PrismaClient) {
953
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
954
+ }
955
+ return new PrismaClient();
956
+ })();
957
+ }
958
+ return prismaClientPromise;
959
+ }
960
+ async function ensureCompatibilityViews(prisma) {
961
+ if (!compatibilityBootstrapPromise) {
962
+ compatibilityBootstrapPromise = (async () => {
963
+ for (const mapping of VIEW_MAPPINGS) {
964
+ const relation = mapping.source.replace(/"/g, "");
965
+ const rows = await prisma.$queryRawUnsafe(
966
+ "SELECT to_regclass($1) AS regclass",
967
+ relation
968
+ );
969
+ if (!rows[0]?.regclass) {
970
+ continue;
971
+ }
972
+ await prisma.$executeRawUnsafe(
973
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
974
+ );
975
+ }
976
+ })();
977
+ }
978
+ return compatibilityBootstrapPromise;
979
+ }
980
+ async function executeOnPrisma(executor, stmt) {
981
+ const translated = translateStatementForPostgres(stmt);
982
+ if (isReadQuery(translated.sql)) {
983
+ const rows = await executor.$queryRawUnsafe(
984
+ translated.sql,
985
+ ...translated.args
986
+ );
987
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
988
+ }
989
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
990
+ return buildResultSet([], rowsAffected);
991
+ }
992
+ function splitSqlStatements(sql) {
993
+ const parts = [];
994
+ let current = "";
995
+ let inSingle = false;
996
+ let inDouble = false;
997
+ let inLineComment = false;
998
+ let inBlockComment = false;
999
+ for (let i = 0; i < sql.length; i++) {
1000
+ const ch = sql[i];
1001
+ const next = sql[i + 1];
1002
+ if (inLineComment) {
1003
+ current += ch;
1004
+ if (ch === "\n") inLineComment = false;
1005
+ continue;
1006
+ }
1007
+ if (inBlockComment) {
1008
+ current += ch;
1009
+ if (ch === "*" && next === "/") {
1010
+ current += next;
1011
+ inBlockComment = false;
1012
+ i += 1;
1013
+ }
1014
+ continue;
1015
+ }
1016
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1017
+ current += ch + next;
1018
+ inLineComment = true;
1019
+ i += 1;
1020
+ continue;
1021
+ }
1022
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1023
+ current += ch + next;
1024
+ inBlockComment = true;
1025
+ i += 1;
1026
+ continue;
1027
+ }
1028
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1029
+ inSingle = !inSingle;
1030
+ current += ch;
1031
+ continue;
1032
+ }
1033
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1034
+ inDouble = !inDouble;
1035
+ current += ch;
1036
+ continue;
1037
+ }
1038
+ if (!inSingle && !inDouble && ch === ";") {
1039
+ if (current.trim()) {
1040
+ parts.push(current.trim());
1041
+ }
1042
+ current = "";
1043
+ continue;
1044
+ }
1045
+ current += ch;
1046
+ }
1047
+ if (current.trim()) {
1048
+ parts.push(current.trim());
1049
+ }
1050
+ return parts;
1051
+ }
1052
+ async function createPrismaDbAdapter(fallbackClient) {
1053
+ const prisma = await loadPrismaClient();
1054
+ await ensureCompatibilityViews(prisma);
1055
+ let closed = false;
1056
+ let adapter;
1057
+ const fallbackExecute = async (stmt, error) => {
1058
+ if (!fallbackClient) {
1059
+ if (error) throw error;
1060
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1061
+ }
1062
+ if (error) {
1063
+ process.stderr.write(
1064
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1065
+ `
1066
+ );
1067
+ }
1068
+ return fallbackClient.execute(stmt);
1069
+ };
1070
+ adapter = {
1071
+ async execute(stmt) {
1072
+ if (shouldBypassPostgres(stmt)) {
1073
+ return fallbackExecute(stmt);
1074
+ }
1075
+ try {
1076
+ return await executeOnPrisma(prisma, stmt);
1077
+ } catch (error) {
1078
+ if (shouldFallbackOnError(error)) {
1079
+ return fallbackExecute(stmt, error);
1080
+ }
1081
+ throw error;
1082
+ }
1083
+ },
1084
+ async batch(stmts, mode) {
1085
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1086
+ if (!fallbackClient) {
1087
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1088
+ }
1089
+ return fallbackClient.batch(stmts, mode);
1090
+ }
1091
+ try {
1092
+ if (prisma.$transaction) {
1093
+ return await prisma.$transaction(async (tx) => {
1094
+ const results2 = [];
1095
+ for (const stmt of stmts) {
1096
+ results2.push(await executeOnPrisma(tx, stmt));
1097
+ }
1098
+ return results2;
1099
+ });
1100
+ }
1101
+ const results = [];
1102
+ for (const stmt of stmts) {
1103
+ results.push(await executeOnPrisma(prisma, stmt));
1104
+ }
1105
+ return results;
1106
+ } catch (error) {
1107
+ if (fallbackClient && shouldFallbackOnError(error)) {
1108
+ process.stderr.write(
1109
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1110
+ `
1111
+ );
1112
+ return fallbackClient.batch(stmts, mode);
1113
+ }
1114
+ throw error;
1115
+ }
1116
+ },
1117
+ async migrate(stmts) {
1118
+ if (fallbackClient) {
1119
+ return fallbackClient.migrate(stmts);
1120
+ }
1121
+ return adapter.batch(stmts, "deferred");
1122
+ },
1123
+ async transaction(mode) {
1124
+ if (!fallbackClient) {
1125
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1126
+ }
1127
+ return fallbackClient.transaction(mode);
1128
+ },
1129
+ async executeMultiple(sql) {
1130
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1131
+ return fallbackClient.executeMultiple(sql);
1132
+ }
1133
+ for (const statement of splitSqlStatements(sql)) {
1134
+ await adapter.execute(statement);
1135
+ }
1136
+ },
1137
+ async sync() {
1138
+ if (fallbackClient) {
1139
+ return fallbackClient.sync();
1140
+ }
1141
+ return { frame_no: 0, frames_synced: 0 };
1142
+ },
1143
+ close() {
1144
+ closed = true;
1145
+ prismaClientPromise = null;
1146
+ compatibilityBootstrapPromise = null;
1147
+ void prisma.$disconnect?.();
1148
+ },
1149
+ get closed() {
1150
+ return closed;
1151
+ },
1152
+ get protocol() {
1153
+ return "prisma-postgres";
1154
+ }
1155
+ };
1156
+ return adapter;
1157
+ }
1158
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1159
+ var init_database_adapter = __esm({
1160
+ "src/lib/database-adapter.ts"() {
1161
+ "use strict";
1162
+ VIEW_MAPPINGS = [
1163
+ { view: "memories", source: "memory.memory_records" },
1164
+ { view: "tasks", source: "memory.tasks" },
1165
+ { view: "behaviors", source: "memory.behaviors" },
1166
+ { view: "entities", source: "memory.entities" },
1167
+ { view: "relationships", source: "memory.relationships" },
1168
+ { view: "entity_memories", source: "memory.entity_memories" },
1169
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1170
+ { view: "notifications", source: "memory.notifications" },
1171
+ { view: "messages", source: "memory.messages" },
1172
+ { view: "users", source: "wiki.users" },
1173
+ { view: "workspaces", source: "wiki.workspaces" },
1174
+ { view: "workspace_users", source: "wiki.workspace_users" },
1175
+ { view: "documents", source: "wiki.workspace_documents" },
1176
+ { view: "chats", source: "wiki.workspace_chats" }
1177
+ ];
1178
+ UPSERT_KEYS = {
1179
+ memories: ["id"],
1180
+ tasks: ["id"],
1181
+ behaviors: ["id"],
1182
+ entities: ["id"],
1183
+ relationships: ["id"],
1184
+ entity_aliases: ["alias"],
1185
+ notifications: ["id"],
1186
+ messages: ["id"],
1187
+ users: ["id"],
1188
+ workspaces: ["id"],
1189
+ workspace_users: ["id"],
1190
+ documents: ["id"],
1191
+ chats: ["id"]
1192
+ };
1193
+ BOOLEAN_COLUMNS_BY_TABLE = {
1194
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1195
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1196
+ notifications: /* @__PURE__ */ new Set(["read"]),
1197
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1198
+ };
1199
+ BOOLEAN_COLUMN_NAMES = new Set(
1200
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1201
+ );
1202
+ IMMEDIATE_FALLBACK_PATTERNS = [
1203
+ /\bPRAGMA\b/i,
1204
+ /\bsqlite_master\b/i,
1205
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1206
+ /\bMATCH\b/i,
1207
+ /\bvector_distance_cos\s*\(/i,
1208
+ /\bjson_extract\s*\(/i,
1209
+ /\bjulianday\s*\(/i,
1210
+ /\bstrftime\s*\(/i,
1211
+ /\blast_insert_rowid\s*\(/i
1212
+ ];
1213
+ prismaClientPromise = null;
1214
+ compatibilityBootstrapPromise = null;
470
1215
  }
471
1216
  });
472
1217
 
473
1218
  // src/lib/database.ts
474
1219
  import { createClient } from "@libsql/client";
475
1220
  async function initDatabase(config) {
1221
+ if (_walCheckpointTimer) {
1222
+ clearInterval(_walCheckpointTimer);
1223
+ _walCheckpointTimer = null;
1224
+ }
1225
+ if (_daemonClient) {
1226
+ _daemonClient.close();
1227
+ _daemonClient = null;
1228
+ }
1229
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1230
+ _adapterClient.close();
1231
+ }
1232
+ _adapterClient = null;
476
1233
  if (_client) {
477
1234
  _client.close();
478
1235
  _client = null;
@@ -486,6 +1243,7 @@ async function initDatabase(config) {
486
1243
  }
487
1244
  _client = createClient(opts);
488
1245
  _resilientClient = wrapWithRetry(_client);
1246
+ _adapterClient = _resilientClient;
489
1247
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
490
1248
  });
491
1249
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -496,11 +1254,17 @@ async function initDatabase(config) {
496
1254
  });
497
1255
  }, 3e4);
498
1256
  _walCheckpointTimer.unref();
1257
+ if (process.env.DATABASE_URL) {
1258
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1259
+ }
499
1260
  }
500
1261
  function getClient() {
501
- if (!_resilientClient) {
1262
+ if (!_adapterClient) {
502
1263
  throw new Error("Database client not initialized. Call initDatabase() first.");
503
1264
  }
1265
+ if (process.env.DATABASE_URL) {
1266
+ return _adapterClient;
1267
+ }
504
1268
  if (process.env.EXE_IS_DAEMON === "1") {
505
1269
  return _resilientClient;
506
1270
  }
@@ -1440,16 +2204,18 @@ async function ensureSchema() {
1440
2204
  }
1441
2205
  }
1442
2206
  }
1443
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso;
2207
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1444
2208
  var init_database = __esm({
1445
2209
  "src/lib/database.ts"() {
1446
2210
  "use strict";
1447
2211
  init_db_retry();
1448
2212
  init_employees();
2213
+ init_database_adapter();
1449
2214
  _client = null;
1450
2215
  _resilientClient = null;
1451
2216
  _walCheckpointTimer = null;
1452
2217
  _daemonClient = null;
2218
+ _adapterClient = null;
1453
2219
  initTurso = initDatabase;
1454
2220
  }
1455
2221
  });
@@ -1522,13 +2288,13 @@ __export(shard_manager_exports, {
1522
2288
  listShards: () => listShards,
1523
2289
  shardExists: () => shardExists
1524
2290
  });
1525
- import path4 from "path";
1526
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
2291
+ import path6 from "path";
2292
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1527
2293
  import { createClient as createClient2 } from "@libsql/client";
1528
2294
  function initShardManager(encryptionKey) {
1529
2295
  _encryptionKey = encryptionKey;
1530
- if (!existsSync4(SHARDS_DIR)) {
1531
- mkdirSync(SHARDS_DIR, { recursive: true });
2296
+ if (!existsSync5(SHARDS_DIR)) {
2297
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1532
2298
  }
1533
2299
  _shardingEnabled = true;
1534
2300
  }
@@ -1548,7 +2314,7 @@ function getShardClient(projectName) {
1548
2314
  }
1549
2315
  const cached = _shards.get(safeName);
1550
2316
  if (cached) return cached;
1551
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
2317
+ const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
1552
2318
  const client = createClient2({
1553
2319
  url: `file:${dbPath}`,
1554
2320
  encryptionKey: _encryptionKey
@@ -1558,10 +2324,10 @@ function getShardClient(projectName) {
1558
2324
  }
1559
2325
  function shardExists(projectName) {
1560
2326
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1561
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
2327
+ return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
1562
2328
  }
1563
2329
  function listShards() {
1564
- if (!existsSync4(SHARDS_DIR)) return [];
2330
+ if (!existsSync5(SHARDS_DIR)) return [];
1565
2331
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1566
2332
  }
1567
2333
  async function ensureShardSchema(client) {
@@ -1635,7 +2401,23 @@ async function ensureShardSchema(client) {
1635
2401
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1636
2402
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1637
2403
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1638
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2404
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2405
+ // Metadata enrichment columns (must match database.ts)
2406
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2407
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2408
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2409
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2410
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2411
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2412
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2413
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2414
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2415
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2416
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2417
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2418
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2419
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2420
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1639
2421
  ]) {
1640
2422
  try {
1641
2423
  await client.execute(col);
@@ -1747,7 +2529,7 @@ var init_shard_manager = __esm({
1747
2529
  "src/lib/shard-manager.ts"() {
1748
2530
  "use strict";
1749
2531
  init_config();
1750
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2532
+ SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
1751
2533
  _shards = /* @__PURE__ */ new Map();
1752
2534
  _encryptionKey = null;
1753
2535
  _shardingEnabled = false;
@@ -1943,13 +2725,13 @@ ${p.content}`).join("\n\n");
1943
2725
 
1944
2726
  // src/lib/notifications.ts
1945
2727
  import crypto from "crypto";
1946
- import path5 from "path";
1947
- import os4 from "os";
2728
+ import path7 from "path";
2729
+ import os5 from "os";
1948
2730
  import {
1949
- readFileSync as readFileSync3,
2731
+ readFileSync as readFileSync4,
1950
2732
  readdirSync as readdirSync2,
1951
2733
  unlinkSync as unlinkSync2,
1952
- existsSync as existsSync5,
2734
+ existsSync as existsSync6,
1953
2735
  rmdirSync
1954
2736
  } from "fs";
1955
2737
  async function writeNotification(notification) {
@@ -1994,13 +2776,13 @@ var init_notifications = __esm({
1994
2776
  });
1995
2777
 
1996
2778
  // src/lib/session-registry.ts
1997
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
1998
- import path6 from "path";
1999
- import os5 from "os";
2779
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "fs";
2780
+ import path8 from "path";
2781
+ import os6 from "os";
2000
2782
  function registerSession(entry) {
2001
- const dir = path6.dirname(REGISTRY_PATH);
2002
- if (!existsSync6(dir)) {
2003
- mkdirSync2(dir, { recursive: true });
2783
+ const dir = path8.dirname(REGISTRY_PATH);
2784
+ if (!existsSync7(dir)) {
2785
+ mkdirSync3(dir, { recursive: true });
2004
2786
  }
2005
2787
  const sessions = listSessions();
2006
2788
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -2009,11 +2791,11 @@ function registerSession(entry) {
2009
2791
  } else {
2010
2792
  sessions.push(entry);
2011
2793
  }
2012
- writeFileSync2(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
2794
+ writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
2013
2795
  }
2014
2796
  function listSessions() {
2015
2797
  try {
2016
- const raw = readFileSync4(REGISTRY_PATH, "utf8");
2798
+ const raw = readFileSync5(REGISTRY_PATH, "utf8");
2017
2799
  return JSON.parse(raw);
2018
2800
  } catch {
2019
2801
  return [];
@@ -2023,7 +2805,7 @@ var REGISTRY_PATH;
2023
2805
  var init_session_registry = __esm({
2024
2806
  "src/lib/session-registry.ts"() {
2025
2807
  "use strict";
2026
- REGISTRY_PATH = path6.join(os5.homedir(), ".exe-os", "session-registry.json");
2808
+ REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
2027
2809
  }
2028
2810
  });
2029
2811
 
@@ -2275,67 +3057,6 @@ var init_provider_table = __esm({
2275
3057
  }
2276
3058
  });
2277
3059
 
2278
- // src/lib/runtime-table.ts
2279
- var RUNTIME_TABLE, DEFAULT_RUNTIME;
2280
- var init_runtime_table = __esm({
2281
- "src/lib/runtime-table.ts"() {
2282
- "use strict";
2283
- RUNTIME_TABLE = {
2284
- codex: {
2285
- binary: "codex",
2286
- launchMode: "interactive",
2287
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2288
- inlineFlag: "--no-alt-screen",
2289
- apiKeyEnv: "OPENAI_API_KEY",
2290
- defaultModel: "gpt-5.4"
2291
- },
2292
- opencode: {
2293
- binary: "opencode",
2294
- launchMode: "exec",
2295
- autoApproveFlag: "--dangerously-skip-permissions",
2296
- inlineFlag: "",
2297
- apiKeyEnv: "ANTHROPIC_API_KEY",
2298
- defaultModel: "anthropic/claude-sonnet-4-6"
2299
- }
2300
- };
2301
- DEFAULT_RUNTIME = "claude";
2302
- }
2303
- });
2304
-
2305
- // src/lib/agent-config.ts
2306
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
2307
- import path7 from "path";
2308
- function loadAgentConfig() {
2309
- if (!existsSync7(AGENT_CONFIG_PATH)) return {};
2310
- try {
2311
- return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
2312
- } catch {
2313
- return {};
2314
- }
2315
- }
2316
- function getAgentRuntime(agentId) {
2317
- const config = loadAgentConfig();
2318
- const entry = config[agentId];
2319
- if (entry) return entry;
2320
- const orgDefault = config["default"];
2321
- if (orgDefault) return orgDefault;
2322
- return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
2323
- }
2324
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
2325
- var init_agent_config = __esm({
2326
- "src/lib/agent-config.ts"() {
2327
- "use strict";
2328
- init_config();
2329
- init_runtime_table();
2330
- AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
2331
- DEFAULT_MODELS = {
2332
- claude: "claude-opus-4",
2333
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
2334
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
2335
- };
2336
- }
2337
- });
2338
-
2339
3060
  // src/lib/intercom-queue.ts
2340
3061
  var intercom_queue_exports = {};
2341
3062
  __export(intercom_queue_exports, {
@@ -2346,10 +3067,10 @@ __export(intercom_queue_exports, {
2346
3067
  readQueue: () => readQueue
2347
3068
  });
2348
3069
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2349
- import path8 from "path";
2350
- import os6 from "os";
3070
+ import path9 from "path";
3071
+ import os7 from "os";
2351
3072
  function ensureDir() {
2352
- const dir = path8.dirname(QUEUE_PATH);
3073
+ const dir = path9.dirname(QUEUE_PATH);
2353
3074
  if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
2354
3075
  }
2355
3076
  function readQueue() {
@@ -2455,26 +3176,26 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
2455
3176
  var init_intercom_queue = __esm({
2456
3177
  "src/lib/intercom-queue.ts"() {
2457
3178
  "use strict";
2458
- QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
3179
+ QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
2459
3180
  MAX_RETRIES2 = 5;
2460
3181
  TTL_MS = 60 * 60 * 1e3;
2461
- INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
3182
+ INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
2462
3183
  }
2463
3184
  });
2464
3185
 
2465
3186
  // src/lib/license.ts
2466
3187
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
2467
3188
  import { randomUUID as randomUUID2 } from "crypto";
2468
- import path9 from "path";
3189
+ import path10 from "path";
2469
3190
  import { jwtVerify, importSPKI } from "jose";
2470
3191
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2471
3192
  var init_license = __esm({
2472
3193
  "src/lib/license.ts"() {
2473
3194
  "use strict";
2474
3195
  init_config();
2475
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
2476
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
2477
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3196
+ LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
3197
+ CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3198
+ DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
2478
3199
  PLAN_LIMITS = {
2479
3200
  free: { devices: 1, employees: 1, memories: 5e3 },
2480
3201
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2487,7 +3208,7 @@ var init_license = __esm({
2487
3208
 
2488
3209
  // src/lib/plan-limits.ts
2489
3210
  import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
2490
- import path10 from "path";
3211
+ import path11 from "path";
2491
3212
  function getLicenseSync() {
2492
3213
  try {
2493
3214
  if (!existsSync10(CACHE_PATH2)) return freeLicense();
@@ -2559,7 +3280,7 @@ var init_plan_limits = __esm({
2559
3280
  this.name = "PlanLimitError";
2560
3281
  }
2561
3282
  };
2562
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
3283
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
2563
3284
  }
2564
3285
  });
2565
3286
 
@@ -2908,12 +3629,12 @@ __export(tmux_routing_exports, {
2908
3629
  });
2909
3630
  import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
2910
3631
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync3 } from "fs";
2911
- import path11 from "path";
2912
- import os7 from "os";
3632
+ import path12 from "path";
3633
+ import os8 from "os";
2913
3634
  import { fileURLToPath } from "url";
2914
3635
  import { unlinkSync as unlinkSync3 } from "fs";
2915
3636
  function spawnLockPath(sessionName) {
2916
- return path11.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3637
+ return path12.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
2917
3638
  }
2918
3639
  function isProcessAlive(pid) {
2919
3640
  try {
@@ -2950,8 +3671,8 @@ function releaseSpawnLock(sessionName) {
2950
3671
  function resolveBehaviorsExporterScript() {
2951
3672
  try {
2952
3673
  const thisFile = fileURLToPath(import.meta.url);
2953
- const scriptPath = path11.join(
2954
- path11.dirname(thisFile),
3674
+ const scriptPath = path12.join(
3675
+ path12.dirname(thisFile),
2955
3676
  "..",
2956
3677
  "bin",
2957
3678
  "exe-export-behaviors.js"
@@ -3026,7 +3747,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3026
3747
  mkdirSync6(SESSION_CACHE, { recursive: true });
3027
3748
  }
3028
3749
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3029
- const filePath = path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3750
+ const filePath = path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3030
3751
  writeFileSync6(filePath, JSON.stringify({
3031
3752
  parentExe: rootExe,
3032
3753
  dispatchedBy: dispatchedBy || rootExe,
@@ -3035,7 +3756,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3035
3756
  }
3036
3757
  function getParentExe(sessionKey) {
3037
3758
  try {
3038
- const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3759
+ const data = JSON.parse(readFileSync9(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3039
3760
  return data.parentExe || null;
3040
3761
  } catch {
3041
3762
  return null;
@@ -3044,7 +3765,7 @@ function getParentExe(sessionKey) {
3044
3765
  function getDispatchedBy(sessionKey) {
3045
3766
  try {
3046
3767
  const data = JSON.parse(readFileSync9(
3047
- path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3768
+ path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3048
3769
  "utf8"
3049
3770
  ));
3050
3771
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -3230,7 +3951,7 @@ function sendIntercom(targetSession) {
3230
3951
  try {
3231
3952
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3232
3953
  const agent = baseAgentName(rawAgent);
3233
- const markerPath = path11.join(SESSION_CACHE, `current-task-${agent}.json`);
3954
+ const markerPath = path12.join(SESSION_CACHE, `current-task-${agent}.json`);
3234
3955
  if (existsSync11(markerPath)) {
3235
3956
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
3236
3957
  return "debounced";
@@ -3240,7 +3961,7 @@ function sendIntercom(targetSession) {
3240
3961
  try {
3241
3962
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3242
3963
  const agent = baseAgentName(rawAgent);
3243
- const taskDir = path11.join(process.cwd(), "exe", agent);
3964
+ const taskDir = path12.join(process.cwd(), "exe", agent);
3244
3965
  if (existsSync11(taskDir)) {
3245
3966
  const files = readdirSync3(taskDir).filter(
3246
3967
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -3374,8 +4095,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3374
4095
  const transport = getTransport();
3375
4096
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3376
4097
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3377
- const logDir = path11.join(os7.homedir(), ".exe-os", "session-logs");
3378
- const logFile = path11.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4098
+ const logDir = path12.join(os8.homedir(), ".exe-os", "session-logs");
4099
+ const logFile = path12.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3379
4100
  if (!existsSync11(logDir)) {
3380
4101
  mkdirSync6(logDir, { recursive: true });
3381
4102
  }
@@ -3383,14 +4104,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3383
4104
  let cleanupSuffix = "";
3384
4105
  try {
3385
4106
  const thisFile = fileURLToPath(import.meta.url);
3386
- const cleanupScript = path11.join(path11.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4107
+ const cleanupScript = path12.join(path12.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
3387
4108
  if (existsSync11(cleanupScript)) {
3388
4109
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
3389
4110
  }
3390
4111
  } catch {
3391
4112
  }
3392
4113
  try {
3393
- const claudeJsonPath = path11.join(os7.homedir(), ".claude.json");
4114
+ const claudeJsonPath = path12.join(os8.homedir(), ".claude.json");
3394
4115
  let claudeJson = {};
3395
4116
  try {
3396
4117
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -3405,10 +4126,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3405
4126
  } catch {
3406
4127
  }
3407
4128
  try {
3408
- const settingsDir = path11.join(os7.homedir(), ".claude", "projects");
4129
+ const settingsDir = path12.join(os8.homedir(), ".claude", "projects");
3409
4130
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3410
- const projSettingsDir = path11.join(settingsDir, normalizedKey);
3411
- const settingsPath = path11.join(projSettingsDir, "settings.json");
4131
+ const projSettingsDir = path12.join(settingsDir, normalizedKey);
4132
+ const settingsPath = path12.join(projSettingsDir, "settings.json");
3412
4133
  let settings = {};
3413
4134
  try {
3414
4135
  settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
@@ -3455,8 +4176,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3455
4176
  let behaviorsFlag = "";
3456
4177
  let legacyFallbackWarned = false;
3457
4178
  if (!useExeAgent && !useBinSymlink) {
3458
- const identityPath = path11.join(
3459
- os7.homedir(),
4179
+ const identityPath = path12.join(
4180
+ os8.homedir(),
3460
4181
  ".exe-os",
3461
4182
  "identity",
3462
4183
  `${employeeName}.md`
@@ -3471,7 +4192,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3471
4192
  }
3472
4193
  const behaviorsFile = exportBehaviorsSync(
3473
4194
  employeeName,
3474
- path11.basename(spawnCwd),
4195
+ path12.basename(spawnCwd),
3475
4196
  sessionName
3476
4197
  );
3477
4198
  if (behaviorsFile) {
@@ -3486,9 +4207,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3486
4207
  }
3487
4208
  let sessionContextFlag = "";
3488
4209
  try {
3489
- const ctxDir = path11.join(os7.homedir(), ".exe-os", "session-cache");
4210
+ const ctxDir = path12.join(os8.homedir(), ".exe-os", "session-cache");
3490
4211
  mkdirSync6(ctxDir, { recursive: true });
3491
- const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
4212
+ const ctxFile = path12.join(ctxDir, `session-context-${sessionName}.md`);
3492
4213
  const ctxContent = [
3493
4214
  `## Session Context`,
3494
4215
  `You are running in tmux session: ${sessionName}.`,
@@ -3572,7 +4293,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3572
4293
  transport.pipeLog(sessionName, logFile);
3573
4294
  try {
3574
4295
  const mySession = getMySession();
3575
- const dispatchInfo = path11.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4296
+ const dispatchInfo = path12.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
3576
4297
  writeFileSync6(dispatchInfo, JSON.stringify({
3577
4298
  dispatchedBy: mySession,
3578
4299
  rootExe: exeSession,
@@ -3647,15 +4368,15 @@ var init_tmux_routing = __esm({
3647
4368
  init_intercom_queue();
3648
4369
  init_plan_limits();
3649
4370
  init_employees();
3650
- SPAWN_LOCK_DIR = path11.join(os7.homedir(), ".exe-os", "spawn-locks");
3651
- SESSION_CACHE = path11.join(os7.homedir(), ".exe-os", "session-cache");
4371
+ SPAWN_LOCK_DIR = path12.join(os8.homedir(), ".exe-os", "spawn-locks");
4372
+ SESSION_CACHE = path12.join(os8.homedir(), ".exe-os", "session-cache");
3652
4373
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
3653
4374
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
3654
4375
  VERIFY_PANE_LINES = 200;
3655
4376
  INTERCOM_DEBOUNCE_MS = 3e4;
3656
4377
  CODEX_DEBOUNCE_MS = 12e4;
3657
- INTERCOM_LOG2 = path11.join(os7.homedir(), ".exe-os", "intercom.log");
3658
- DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
4378
+ INTERCOM_LOG2 = path12.join(os8.homedir(), ".exe-os", "intercom.log");
4379
+ DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
3659
4380
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3660
4381
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
3661
4382
  }
@@ -3687,8 +4408,8 @@ var init_task_scope = __esm({
3687
4408
 
3688
4409
  // src/lib/tasks-crud.ts
3689
4410
  import crypto3 from "crypto";
3690
- import path12 from "path";
3691
- import os8 from "os";
4411
+ import path13 from "path";
4412
+ import os9 from "os";
3692
4413
  import { execSync as execSync5 } from "child_process";
3693
4414
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3694
4415
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
@@ -3866,8 +4587,8 @@ ${laneWarning}` : laneWarning;
3866
4587
  }
3867
4588
  if (input.baseDir) {
3868
4589
  try {
3869
- await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
3870
- await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
4590
+ await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
4591
+ await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
3871
4592
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3872
4593
  await ensureGitignoreExe(input.baseDir);
3873
4594
  } catch {
@@ -3903,9 +4624,9 @@ ${laneWarning}` : laneWarning;
3903
4624
  });
3904
4625
  if (input.baseDir) {
3905
4626
  try {
3906
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
3907
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
3908
- const mdDir = path12.dirname(mdPath);
4627
+ const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
4628
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
4629
+ const mdDir = path13.dirname(mdPath);
3909
4630
  if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
3910
4631
  const reviewer = input.reviewer ?? input.assignedBy;
3911
4632
  const mdContent = `# ${input.title}
@@ -4206,7 +4927,7 @@ async function deleteTaskCore(taskId, _baseDir) {
4206
4927
  return { taskFile, assignedTo, assignedBy, taskSlug };
4207
4928
  }
4208
4929
  async function ensureArchitectureDoc(baseDir, projectName) {
4209
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
4930
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
4210
4931
  try {
4211
4932
  if (existsSync12(archPath)) return;
4212
4933
  const template = [
@@ -4241,7 +4962,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
4241
4962
  }
4242
4963
  }
4243
4964
  async function ensureGitignoreExe(baseDir) {
4244
- const gitignorePath = path12.join(baseDir, ".gitignore");
4965
+ const gitignorePath = path13.join(baseDir, ".gitignore");
4245
4966
  try {
4246
4967
  if (existsSync12(gitignorePath)) {
4247
4968
  const content = readFileSync10(gitignorePath, "utf-8");
@@ -4275,13 +4996,13 @@ var init_tasks_crud = __esm({
4275
4996
  });
4276
4997
 
4277
4998
  // src/lib/tasks-review.ts
4278
- import path13 from "path";
4999
+ import path14 from "path";
4279
5000
  import { existsSync as existsSync13, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "fs";
4280
5001
  async function countPendingReviews(sessionScope) {
4281
5002
  const client = getClient();
4282
5003
  if (sessionScope) {
4283
5004
  const result2 = await client.execute({
4284
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
5005
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
4285
5006
  args: [sessionScope]
4286
5007
  });
4287
5008
  return Number(result2.rows[0]?.cnt) || 0;
@@ -4457,11 +5178,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4457
5178
  );
4458
5179
  }
4459
5180
  try {
4460
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
5181
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4461
5182
  if (existsSync13(cacheDir)) {
4462
5183
  for (const f of readdirSync4(cacheDir)) {
4463
5184
  if (f.startsWith("review-notified-")) {
4464
- unlinkSync4(path13.join(cacheDir, f));
5185
+ unlinkSync4(path14.join(cacheDir, f));
4465
5186
  }
4466
5187
  }
4467
5188
  }
@@ -4482,7 +5203,7 @@ var init_tasks_review = __esm({
4482
5203
  });
4483
5204
 
4484
5205
  // src/lib/tasks-chain.ts
4485
- import path14 from "path";
5206
+ import path15 from "path";
4486
5207
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
4487
5208
  async function cascadeUnblock(taskId, baseDir, now) {
4488
5209
  const client = getClient();
@@ -4499,7 +5220,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
4499
5220
  });
4500
5221
  for (const ur of unblockedRows.rows) {
4501
5222
  try {
4502
- const ubFile = path14.join(baseDir, String(ur.task_file));
5223
+ const ubFile = path15.join(baseDir, String(ur.task_file));
4503
5224
  let ubContent = await readFile4(ubFile, "utf-8");
4504
5225
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
4505
5226
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -4568,7 +5289,7 @@ var init_tasks_chain = __esm({
4568
5289
 
4569
5290
  // src/lib/project-name.ts
4570
5291
  import { execSync as execSync6 } from "child_process";
4571
- import path15 from "path";
5292
+ import path16 from "path";
4572
5293
  function getProjectName(cwd) {
4573
5294
  const dir = cwd ?? process.cwd();
4574
5295
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -4581,7 +5302,7 @@ function getProjectName(cwd) {
4581
5302
  timeout: 2e3,
4582
5303
  stdio: ["pipe", "pipe", "pipe"]
4583
5304
  }).trim();
4584
- repoRoot = path15.dirname(gitCommonDir);
5305
+ repoRoot = path16.dirname(gitCommonDir);
4585
5306
  } catch {
4586
5307
  repoRoot = execSync6("git rev-parse --show-toplevel", {
4587
5308
  cwd: dir,
@@ -4590,11 +5311,11 @@ function getProjectName(cwd) {
4590
5311
  stdio: ["pipe", "pipe", "pipe"]
4591
5312
  }).trim();
4592
5313
  }
4593
- _cached2 = path15.basename(repoRoot);
5314
+ _cached2 = path16.basename(repoRoot);
4594
5315
  _cachedCwd = dir;
4595
5316
  return _cached2;
4596
5317
  } catch {
4597
- _cached2 = path15.basename(dir);
5318
+ _cached2 = path16.basename(dir);
4598
5319
  _cachedCwd = dir;
4599
5320
  return _cached2;
4600
5321
  }
@@ -5067,7 +5788,7 @@ __export(tasks_exports, {
5067
5788
  updateTaskStatus: () => updateTaskStatus,
5068
5789
  writeCheckpoint: () => writeCheckpoint
5069
5790
  });
5070
- import path16 from "path";
5791
+ import path17 from "path";
5071
5792
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
5072
5793
  async function createTask(input) {
5073
5794
  const result = await createTaskCore(input);
@@ -5087,8 +5808,8 @@ async function updateTask(input) {
5087
5808
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
5088
5809
  try {
5089
5810
  const agent = String(row.assigned_to);
5090
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5091
- const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
5811
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5812
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
5092
5813
  if (input.status === "in_progress") {
5093
5814
  mkdirSync7(cacheDir, { recursive: true });
5094
5815
  writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -5254,16 +5975,16 @@ init_database();
5254
5975
 
5255
5976
  // src/lib/keychain.ts
5256
5977
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5257
- import { existsSync as existsSync3 } from "fs";
5258
- import path3 from "path";
5259
- import os3 from "os";
5978
+ import { existsSync as existsSync4 } from "fs";
5979
+ import path5 from "path";
5980
+ import os4 from "os";
5260
5981
  var SERVICE = "exe-mem";
5261
5982
  var ACCOUNT = "master-key";
5262
5983
  function getKeyDir() {
5263
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
5984
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
5264
5985
  }
5265
5986
  function getKeyPath() {
5266
- return path3.join(getKeyDir(), "master.key");
5987
+ return path5.join(getKeyDir(), "master.key");
5267
5988
  }
5268
5989
  async function tryKeytar() {
5269
5990
  try {
@@ -5284,9 +6005,9 @@ async function getMasterKey() {
5284
6005
  }
5285
6006
  }
5286
6007
  const keyPath = getKeyPath();
5287
- if (!existsSync3(keyPath)) {
6008
+ if (!existsSync4(keyPath)) {
5288
6009
  process.stderr.write(
5289
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6010
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5290
6011
  `
5291
6012
  );
5292
6013
  return null;