@askexenow/exe-os 0.9.6 → 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 +668 -37
  5. package/dist/bin/cli.js +1399 -607
  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 +795 -155
  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 +703 -72
  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 +1064 -273
  17. package/dist/bin/exe-heartbeat.js +676 -45
  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 +845 -152
  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 +668 -37
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +731 -91
  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 +735 -95
  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 +1038 -247
  43. package/dist/hooks/bug-report-worker.js +902 -172
  44. package/dist/hooks/commit-complete.js +729 -89
  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 +851 -158
  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 +685 -45
  52. package/dist/hooks/pre-compact.js +729 -89
  53. package/dist/hooks/pre-tool-use.js +883 -127
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1071 -321
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +732 -92
  58. package/dist/hooks/session-start.js +1042 -209
  59. package/dist/hooks/stop.js +691 -51
  60. package/dist/hooks/subagent-stop.js +685 -45
  61. package/dist/hooks/summary-worker.js +827 -134
  62. package/dist/index.js +1026 -234
  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 +905 -164
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +66 -30
  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 +109 -73
  82. package/dist/lib/tmux-routing.js +98 -62
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1807 -472
  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 +301 -166
  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 +206 -40
  91. package/dist/mcp/tools/send-message.js +69 -33
  92. package/dist/mcp/tools/update-task.js +86 -50
  93. package/dist/runtime/index.js +731 -91
  94. package/dist/tui/App.js +864 -125
  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;
@@ -3055,15 +3776,24 @@ function getDispatchedBy(sessionKey) {
3055
3776
  function resolveExeSession() {
3056
3777
  const mySession = getMySession();
3057
3778
  if (!mySession) return null;
3779
+ const fromSessionName = extractRootExe(mySession);
3058
3780
  try {
3059
3781
  const key = getSessionKey();
3060
3782
  const parentExe = getParentExe(key);
3061
3783
  if (parentExe) {
3062
- return extractRootExe(parentExe) ?? parentExe;
3784
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
3785
+ if (fromSessionName && fromCache !== fromSessionName) {
3786
+ process.stderr.write(
3787
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3788
+ `
3789
+ );
3790
+ return fromSessionName;
3791
+ }
3792
+ return fromCache;
3063
3793
  }
3064
3794
  } catch {
3065
3795
  }
3066
- return extractRootExe(mySession) ?? mySession;
3796
+ return fromSessionName ?? mySession;
3067
3797
  }
3068
3798
  function isEmployeeAlive(sessionName) {
3069
3799
  return getTransport().isAlive(sessionName);
@@ -3221,7 +3951,7 @@ function sendIntercom(targetSession) {
3221
3951
  try {
3222
3952
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3223
3953
  const agent = baseAgentName(rawAgent);
3224
- const markerPath = path11.join(SESSION_CACHE, `current-task-${agent}.json`);
3954
+ const markerPath = path12.join(SESSION_CACHE, `current-task-${agent}.json`);
3225
3955
  if (existsSync11(markerPath)) {
3226
3956
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
3227
3957
  return "debounced";
@@ -3231,7 +3961,7 @@ function sendIntercom(targetSession) {
3231
3961
  try {
3232
3962
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3233
3963
  const agent = baseAgentName(rawAgent);
3234
- const taskDir = path11.join(process.cwd(), "exe", agent);
3964
+ const taskDir = path12.join(process.cwd(), "exe", agent);
3235
3965
  if (existsSync11(taskDir)) {
3236
3966
  const files = readdirSync3(taskDir).filter(
3237
3967
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -3365,8 +4095,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3365
4095
  const transport = getTransport();
3366
4096
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3367
4097
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3368
- const logDir = path11.join(os7.homedir(), ".exe-os", "session-logs");
3369
- 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`);
3370
4100
  if (!existsSync11(logDir)) {
3371
4101
  mkdirSync6(logDir, { recursive: true });
3372
4102
  }
@@ -3374,14 +4104,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3374
4104
  let cleanupSuffix = "";
3375
4105
  try {
3376
4106
  const thisFile = fileURLToPath(import.meta.url);
3377
- 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");
3378
4108
  if (existsSync11(cleanupScript)) {
3379
4109
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
3380
4110
  }
3381
4111
  } catch {
3382
4112
  }
3383
4113
  try {
3384
- const claudeJsonPath = path11.join(os7.homedir(), ".claude.json");
4114
+ const claudeJsonPath = path12.join(os8.homedir(), ".claude.json");
3385
4115
  let claudeJson = {};
3386
4116
  try {
3387
4117
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -3396,10 +4126,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3396
4126
  } catch {
3397
4127
  }
3398
4128
  try {
3399
- const settingsDir = path11.join(os7.homedir(), ".claude", "projects");
4129
+ const settingsDir = path12.join(os8.homedir(), ".claude", "projects");
3400
4130
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3401
- const projSettingsDir = path11.join(settingsDir, normalizedKey);
3402
- const settingsPath = path11.join(projSettingsDir, "settings.json");
4131
+ const projSettingsDir = path12.join(settingsDir, normalizedKey);
4132
+ const settingsPath = path12.join(projSettingsDir, "settings.json");
3403
4133
  let settings = {};
3404
4134
  try {
3405
4135
  settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
@@ -3446,8 +4176,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3446
4176
  let behaviorsFlag = "";
3447
4177
  let legacyFallbackWarned = false;
3448
4178
  if (!useExeAgent && !useBinSymlink) {
3449
- const identityPath = path11.join(
3450
- os7.homedir(),
4179
+ const identityPath = path12.join(
4180
+ os8.homedir(),
3451
4181
  ".exe-os",
3452
4182
  "identity",
3453
4183
  `${employeeName}.md`
@@ -3462,7 +4192,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3462
4192
  }
3463
4193
  const behaviorsFile = exportBehaviorsSync(
3464
4194
  employeeName,
3465
- path11.basename(spawnCwd),
4195
+ path12.basename(spawnCwd),
3466
4196
  sessionName
3467
4197
  );
3468
4198
  if (behaviorsFile) {
@@ -3477,9 +4207,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3477
4207
  }
3478
4208
  let sessionContextFlag = "";
3479
4209
  try {
3480
- const ctxDir = path11.join(os7.homedir(), ".exe-os", "session-cache");
4210
+ const ctxDir = path12.join(os8.homedir(), ".exe-os", "session-cache");
3481
4211
  mkdirSync6(ctxDir, { recursive: true });
3482
- const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
4212
+ const ctxFile = path12.join(ctxDir, `session-context-${sessionName}.md`);
3483
4213
  const ctxContent = [
3484
4214
  `## Session Context`,
3485
4215
  `You are running in tmux session: ${sessionName}.`,
@@ -3563,7 +4293,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3563
4293
  transport.pipeLog(sessionName, logFile);
3564
4294
  try {
3565
4295
  const mySession = getMySession();
3566
- const dispatchInfo = path11.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4296
+ const dispatchInfo = path12.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
3567
4297
  writeFileSync6(dispatchInfo, JSON.stringify({
3568
4298
  dispatchedBy: mySession,
3569
4299
  rootExe: exeSession,
@@ -3638,15 +4368,15 @@ var init_tmux_routing = __esm({
3638
4368
  init_intercom_queue();
3639
4369
  init_plan_limits();
3640
4370
  init_employees();
3641
- SPAWN_LOCK_DIR = path11.join(os7.homedir(), ".exe-os", "spawn-locks");
3642
- 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");
3643
4373
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
3644
4374
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
3645
4375
  VERIFY_PANE_LINES = 200;
3646
4376
  INTERCOM_DEBOUNCE_MS = 3e4;
3647
4377
  CODEX_DEBOUNCE_MS = 12e4;
3648
- INTERCOM_LOG2 = path11.join(os7.homedir(), ".exe-os", "intercom.log");
3649
- 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");
3650
4380
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3651
4381
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
3652
4382
  }
@@ -3678,8 +4408,8 @@ var init_task_scope = __esm({
3678
4408
 
3679
4409
  // src/lib/tasks-crud.ts
3680
4410
  import crypto3 from "crypto";
3681
- import path12 from "path";
3682
- import os8 from "os";
4411
+ import path13 from "path";
4412
+ import os9 from "os";
3683
4413
  import { execSync as execSync5 } from "child_process";
3684
4414
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3685
4415
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
@@ -3857,8 +4587,8 @@ ${laneWarning}` : laneWarning;
3857
4587
  }
3858
4588
  if (input.baseDir) {
3859
4589
  try {
3860
- await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
3861
- 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 });
3862
4592
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3863
4593
  await ensureGitignoreExe(input.baseDir);
3864
4594
  } catch {
@@ -3894,9 +4624,9 @@ ${laneWarning}` : laneWarning;
3894
4624
  });
3895
4625
  if (input.baseDir) {
3896
4626
  try {
3897
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
3898
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
3899
- 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);
3900
4630
  if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
3901
4631
  const reviewer = input.reviewer ?? input.assignedBy;
3902
4632
  const mdContent = `# ${input.title}
@@ -4197,7 +4927,7 @@ async function deleteTaskCore(taskId, _baseDir) {
4197
4927
  return { taskFile, assignedTo, assignedBy, taskSlug };
4198
4928
  }
4199
4929
  async function ensureArchitectureDoc(baseDir, projectName) {
4200
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
4930
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
4201
4931
  try {
4202
4932
  if (existsSync12(archPath)) return;
4203
4933
  const template = [
@@ -4232,7 +4962,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
4232
4962
  }
4233
4963
  }
4234
4964
  async function ensureGitignoreExe(baseDir) {
4235
- const gitignorePath = path12.join(baseDir, ".gitignore");
4965
+ const gitignorePath = path13.join(baseDir, ".gitignore");
4236
4966
  try {
4237
4967
  if (existsSync12(gitignorePath)) {
4238
4968
  const content = readFileSync10(gitignorePath, "utf-8");
@@ -4266,13 +4996,13 @@ var init_tasks_crud = __esm({
4266
4996
  });
4267
4997
 
4268
4998
  // src/lib/tasks-review.ts
4269
- import path13 from "path";
4999
+ import path14 from "path";
4270
5000
  import { existsSync as existsSync13, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "fs";
4271
5001
  async function countPendingReviews(sessionScope) {
4272
5002
  const client = getClient();
4273
5003
  if (sessionScope) {
4274
5004
  const result2 = await client.execute({
4275
- 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 = ?",
4276
5006
  args: [sessionScope]
4277
5007
  });
4278
5008
  return Number(result2.rows[0]?.cnt) || 0;
@@ -4448,11 +5178,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4448
5178
  );
4449
5179
  }
4450
5180
  try {
4451
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
5181
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4452
5182
  if (existsSync13(cacheDir)) {
4453
5183
  for (const f of readdirSync4(cacheDir)) {
4454
5184
  if (f.startsWith("review-notified-")) {
4455
- unlinkSync4(path13.join(cacheDir, f));
5185
+ unlinkSync4(path14.join(cacheDir, f));
4456
5186
  }
4457
5187
  }
4458
5188
  }
@@ -4473,7 +5203,7 @@ var init_tasks_review = __esm({
4473
5203
  });
4474
5204
 
4475
5205
  // src/lib/tasks-chain.ts
4476
- import path14 from "path";
5206
+ import path15 from "path";
4477
5207
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
4478
5208
  async function cascadeUnblock(taskId, baseDir, now) {
4479
5209
  const client = getClient();
@@ -4490,7 +5220,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
4490
5220
  });
4491
5221
  for (const ur of unblockedRows.rows) {
4492
5222
  try {
4493
- const ubFile = path14.join(baseDir, String(ur.task_file));
5223
+ const ubFile = path15.join(baseDir, String(ur.task_file));
4494
5224
  let ubContent = await readFile4(ubFile, "utf-8");
4495
5225
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
4496
5226
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -4559,7 +5289,7 @@ var init_tasks_chain = __esm({
4559
5289
 
4560
5290
  // src/lib/project-name.ts
4561
5291
  import { execSync as execSync6 } from "child_process";
4562
- import path15 from "path";
5292
+ import path16 from "path";
4563
5293
  function getProjectName(cwd) {
4564
5294
  const dir = cwd ?? process.cwd();
4565
5295
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -4572,7 +5302,7 @@ function getProjectName(cwd) {
4572
5302
  timeout: 2e3,
4573
5303
  stdio: ["pipe", "pipe", "pipe"]
4574
5304
  }).trim();
4575
- repoRoot = path15.dirname(gitCommonDir);
5305
+ repoRoot = path16.dirname(gitCommonDir);
4576
5306
  } catch {
4577
5307
  repoRoot = execSync6("git rev-parse --show-toplevel", {
4578
5308
  cwd: dir,
@@ -4581,11 +5311,11 @@ function getProjectName(cwd) {
4581
5311
  stdio: ["pipe", "pipe", "pipe"]
4582
5312
  }).trim();
4583
5313
  }
4584
- _cached2 = path15.basename(repoRoot);
5314
+ _cached2 = path16.basename(repoRoot);
4585
5315
  _cachedCwd = dir;
4586
5316
  return _cached2;
4587
5317
  } catch {
4588
- _cached2 = path15.basename(dir);
5318
+ _cached2 = path16.basename(dir);
4589
5319
  _cachedCwd = dir;
4590
5320
  return _cached2;
4591
5321
  }
@@ -5058,7 +5788,7 @@ __export(tasks_exports, {
5058
5788
  updateTaskStatus: () => updateTaskStatus,
5059
5789
  writeCheckpoint: () => writeCheckpoint
5060
5790
  });
5061
- import path16 from "path";
5791
+ import path17 from "path";
5062
5792
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
5063
5793
  async function createTask(input) {
5064
5794
  const result = await createTaskCore(input);
@@ -5078,8 +5808,8 @@ async function updateTask(input) {
5078
5808
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
5079
5809
  try {
5080
5810
  const agent = String(row.assigned_to);
5081
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5082
- 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`);
5083
5813
  if (input.status === "in_progress") {
5084
5814
  mkdirSync7(cacheDir, { recursive: true });
5085
5815
  writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -5245,16 +5975,16 @@ init_database();
5245
5975
 
5246
5976
  // src/lib/keychain.ts
5247
5977
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5248
- import { existsSync as existsSync3 } from "fs";
5249
- import path3 from "path";
5250
- import os3 from "os";
5978
+ import { existsSync as existsSync4 } from "fs";
5979
+ import path5 from "path";
5980
+ import os4 from "os";
5251
5981
  var SERVICE = "exe-mem";
5252
5982
  var ACCOUNT = "master-key";
5253
5983
  function getKeyDir() {
5254
- 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");
5255
5985
  }
5256
5986
  function getKeyPath() {
5257
- return path3.join(getKeyDir(), "master.key");
5987
+ return path5.join(getKeyDir(), "master.key");
5258
5988
  }
5259
5989
  async function tryKeytar() {
5260
5990
  try {
@@ -5275,9 +6005,9 @@ async function getMasterKey() {
5275
6005
  }
5276
6006
  }
5277
6007
  const keyPath = getKeyPath();
5278
- if (!existsSync3(keyPath)) {
6008
+ if (!existsSync4(keyPath)) {
5279
6009
  process.stderr.write(
5280
- `[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"})
5281
6011
  `
5282
6012
  );
5283
6013
  return null;