@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
@@ -290,7 +290,7 @@ function baseAgentName(name, employees) {
290
290
  if (getEmployee(roster, base)) return base;
291
291
  return name;
292
292
  }
293
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
293
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
294
294
  var init_employees = __esm({
295
295
  "src/lib/employees.ts"() {
296
296
  "use strict";
@@ -298,12 +298,609 @@ var init_employees = __esm({
298
298
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
299
299
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
300
300
  COORDINATOR_ROLE = "COO";
301
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
302
+ }
303
+ });
304
+
305
+ // src/lib/database-adapter.ts
306
+ import os3 from "os";
307
+ import path3 from "path";
308
+ import { createRequire } from "module";
309
+ import { pathToFileURL } from "url";
310
+ function quotedIdentifier(identifier) {
311
+ return `"${identifier.replace(/"/g, '""')}"`;
312
+ }
313
+ function unqualifiedTableName(name) {
314
+ const raw = name.trim().replace(/^"|"$/g, "");
315
+ const parts = raw.split(".");
316
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
317
+ }
318
+ function stripTrailingSemicolon(sql) {
319
+ return sql.trim().replace(/;+\s*$/u, "");
320
+ }
321
+ function appendClause(sql, clause) {
322
+ const trimmed = stripTrailingSemicolon(sql);
323
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
324
+ if (!returningMatch) {
325
+ return `${trimmed}${clause}`;
326
+ }
327
+ const idx = returningMatch.index;
328
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
329
+ }
330
+ function normalizeStatement(stmt) {
331
+ if (typeof stmt === "string") {
332
+ return { kind: "positional", sql: stmt, args: [] };
333
+ }
334
+ const sql = stmt.sql;
335
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
336
+ return { kind: "positional", sql, args: stmt.args ?? [] };
337
+ }
338
+ return { kind: "named", sql, args: stmt.args };
339
+ }
340
+ function rewriteBooleanLiterals(sql) {
341
+ let out = sql;
342
+ for (const column of BOOLEAN_COLUMN_NAMES) {
343
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
344
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
345
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
346
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
347
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
348
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
349
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
350
+ }
351
+ return out;
352
+ }
353
+ function rewriteInsertOrIgnore(sql) {
354
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
355
+ return sql;
356
+ }
357
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
358
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
359
+ }
360
+ function rewriteInsertOrReplace(sql) {
361
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
362
+ if (!match) {
363
+ return sql;
364
+ }
365
+ const rawTable = match[1];
366
+ const rawColumns = match[2];
367
+ const remainder = match[3];
368
+ const tableName = unqualifiedTableName(rawTable);
369
+ const conflictKeys = UPSERT_KEYS[tableName];
370
+ if (!conflictKeys?.length) {
371
+ return sql;
372
+ }
373
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
374
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
375
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
376
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
377
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
378
+ }
379
+ function rewriteSql(sql) {
380
+ let out = sql;
381
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
382
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
383
+ out = rewriteBooleanLiterals(out);
384
+ out = rewriteInsertOrReplace(out);
385
+ out = rewriteInsertOrIgnore(out);
386
+ return stripTrailingSemicolon(out);
387
+ }
388
+ function toBoolean(value) {
389
+ if (value === null || value === void 0) return value;
390
+ if (typeof value === "boolean") return value;
391
+ if (typeof value === "number") return value !== 0;
392
+ if (typeof value === "bigint") return value !== 0n;
393
+ if (typeof value === "string") {
394
+ const normalized = value.trim().toLowerCase();
395
+ if (normalized === "0" || normalized === "false") return false;
396
+ if (normalized === "1" || normalized === "true") return true;
397
+ }
398
+ return Boolean(value);
399
+ }
400
+ function countQuestionMarks(sql, end) {
401
+ let count = 0;
402
+ let inSingle = false;
403
+ let inDouble = false;
404
+ let inLineComment = false;
405
+ let inBlockComment = false;
406
+ for (let i = 0; i < end; i++) {
407
+ const ch = sql[i];
408
+ const next = sql[i + 1];
409
+ if (inLineComment) {
410
+ if (ch === "\n") inLineComment = false;
411
+ continue;
412
+ }
413
+ if (inBlockComment) {
414
+ if (ch === "*" && next === "/") {
415
+ inBlockComment = false;
416
+ i += 1;
417
+ }
418
+ continue;
419
+ }
420
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
421
+ inLineComment = true;
422
+ i += 1;
423
+ continue;
424
+ }
425
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
426
+ inBlockComment = true;
427
+ i += 1;
428
+ continue;
429
+ }
430
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
431
+ inSingle = !inSingle;
432
+ continue;
433
+ }
434
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
435
+ inDouble = !inDouble;
436
+ continue;
437
+ }
438
+ if (!inSingle && !inDouble && ch === "?") {
439
+ count += 1;
440
+ }
441
+ }
442
+ return count;
443
+ }
444
+ function findBooleanPlaceholderIndexes(sql) {
445
+ const indexes = /* @__PURE__ */ new Set();
446
+ for (const column of BOOLEAN_COLUMN_NAMES) {
447
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
448
+ for (const match of sql.matchAll(pattern)) {
449
+ const matchText = match[0];
450
+ const qIndex = match.index + matchText.lastIndexOf("?");
451
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
452
+ }
453
+ }
454
+ return indexes;
455
+ }
456
+ function coerceInsertBooleanArgs(sql, args) {
457
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
458
+ if (!match) return;
459
+ const rawTable = match[1];
460
+ const rawColumns = match[2];
461
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
462
+ if (!boolColumns?.size) return;
463
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
464
+ for (const [index, column] of columns.entries()) {
465
+ if (boolColumns.has(column) && index < args.length) {
466
+ args[index] = toBoolean(args[index]);
467
+ }
468
+ }
469
+ }
470
+ function coerceUpdateBooleanArgs(sql, args) {
471
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
472
+ if (!match) return;
473
+ const rawTable = match[1];
474
+ const setClause = match[2];
475
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
476
+ if (!boolColumns?.size) return;
477
+ const assignments = setClause.split(",");
478
+ let placeholderIndex = 0;
479
+ for (const assignment of assignments) {
480
+ if (!assignment.includes("?")) continue;
481
+ placeholderIndex += 1;
482
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
483
+ if (colMatch && boolColumns.has(colMatch[1])) {
484
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
485
+ }
486
+ }
487
+ }
488
+ function coerceBooleanArgs(sql, args) {
489
+ const nextArgs = [...args];
490
+ coerceInsertBooleanArgs(sql, nextArgs);
491
+ coerceUpdateBooleanArgs(sql, nextArgs);
492
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
493
+ for (const index of placeholderIndexes) {
494
+ if (index > 0 && index <= nextArgs.length) {
495
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
496
+ }
497
+ }
498
+ return nextArgs;
499
+ }
500
+ function convertQuestionMarksToDollarParams(sql) {
501
+ let out = "";
502
+ let placeholder = 0;
503
+ let inSingle = false;
504
+ let inDouble = false;
505
+ let inLineComment = false;
506
+ let inBlockComment = false;
507
+ for (let i = 0; i < sql.length; i++) {
508
+ const ch = sql[i];
509
+ const next = sql[i + 1];
510
+ if (inLineComment) {
511
+ out += ch;
512
+ if (ch === "\n") inLineComment = false;
513
+ continue;
514
+ }
515
+ if (inBlockComment) {
516
+ out += ch;
517
+ if (ch === "*" && next === "/") {
518
+ out += next;
519
+ inBlockComment = false;
520
+ i += 1;
521
+ }
522
+ continue;
523
+ }
524
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
525
+ out += ch + next;
526
+ inLineComment = true;
527
+ i += 1;
528
+ continue;
529
+ }
530
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
531
+ out += ch + next;
532
+ inBlockComment = true;
533
+ i += 1;
534
+ continue;
535
+ }
536
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
537
+ inSingle = !inSingle;
538
+ out += ch;
539
+ continue;
540
+ }
541
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
542
+ inDouble = !inDouble;
543
+ out += ch;
544
+ continue;
545
+ }
546
+ if (!inSingle && !inDouble && ch === "?") {
547
+ placeholder += 1;
548
+ out += `$${placeholder}`;
549
+ continue;
550
+ }
551
+ out += ch;
552
+ }
553
+ return out;
554
+ }
555
+ function translateStatementForPostgres(stmt) {
556
+ const normalized = normalizeStatement(stmt);
557
+ if (normalized.kind === "named") {
558
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
559
+ }
560
+ const rewrittenSql = rewriteSql(normalized.sql);
561
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
562
+ return {
563
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
564
+ args: coercedArgs
565
+ };
566
+ }
567
+ function shouldBypassPostgres(stmt) {
568
+ const normalized = normalizeStatement(stmt);
569
+ if (normalized.kind === "named") {
570
+ return true;
571
+ }
572
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
573
+ }
574
+ function shouldFallbackOnError(error) {
575
+ const message = error instanceof Error ? error.message : String(error);
576
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
577
+ }
578
+ function isReadQuery(sql) {
579
+ const trimmed = sql.trimStart();
580
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
581
+ }
582
+ function buildRow(row, columns) {
583
+ const values = columns.map((column) => row[column]);
584
+ return Object.assign(values, row);
585
+ }
586
+ function buildResultSet(rows, rowsAffected = 0) {
587
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
588
+ const resultRows = rows.map((row) => buildRow(row, columns));
589
+ return {
590
+ columns,
591
+ columnTypes: columns.map(() => ""),
592
+ rows: resultRows,
593
+ rowsAffected,
594
+ lastInsertRowid: void 0,
595
+ toJSON() {
596
+ return {
597
+ columns,
598
+ columnTypes: columns.map(() => ""),
599
+ rows,
600
+ rowsAffected,
601
+ lastInsertRowid: void 0
602
+ };
603
+ }
604
+ };
605
+ }
606
+ async function loadPrismaClient() {
607
+ if (!prismaClientPromise) {
608
+ prismaClientPromise = (async () => {
609
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
610
+ if (explicitPath) {
611
+ const module2 = await import(pathToFileURL(explicitPath).href);
612
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
613
+ if (!PrismaClient2) {
614
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
615
+ }
616
+ return new PrismaClient2();
617
+ }
618
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
619
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
620
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
621
+ const module = await import(pathToFileURL(prismaEntry).href);
622
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
623
+ if (!PrismaClient) {
624
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
625
+ }
626
+ return new PrismaClient();
627
+ })();
628
+ }
629
+ return prismaClientPromise;
630
+ }
631
+ async function ensureCompatibilityViews(prisma) {
632
+ if (!compatibilityBootstrapPromise) {
633
+ compatibilityBootstrapPromise = (async () => {
634
+ for (const mapping of VIEW_MAPPINGS) {
635
+ const relation = mapping.source.replace(/"/g, "");
636
+ const rows = await prisma.$queryRawUnsafe(
637
+ "SELECT to_regclass($1) AS regclass",
638
+ relation
639
+ );
640
+ if (!rows[0]?.regclass) {
641
+ continue;
642
+ }
643
+ await prisma.$executeRawUnsafe(
644
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
645
+ );
646
+ }
647
+ })();
648
+ }
649
+ return compatibilityBootstrapPromise;
650
+ }
651
+ async function executeOnPrisma(executor, stmt) {
652
+ const translated = translateStatementForPostgres(stmt);
653
+ if (isReadQuery(translated.sql)) {
654
+ const rows = await executor.$queryRawUnsafe(
655
+ translated.sql,
656
+ ...translated.args
657
+ );
658
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
659
+ }
660
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
661
+ return buildResultSet([], rowsAffected);
662
+ }
663
+ function splitSqlStatements(sql) {
664
+ const parts = [];
665
+ let current = "";
666
+ let inSingle = false;
667
+ let inDouble = false;
668
+ let inLineComment = false;
669
+ let inBlockComment = false;
670
+ for (let i = 0; i < sql.length; i++) {
671
+ const ch = sql[i];
672
+ const next = sql[i + 1];
673
+ if (inLineComment) {
674
+ current += ch;
675
+ if (ch === "\n") inLineComment = false;
676
+ continue;
677
+ }
678
+ if (inBlockComment) {
679
+ current += ch;
680
+ if (ch === "*" && next === "/") {
681
+ current += next;
682
+ inBlockComment = false;
683
+ i += 1;
684
+ }
685
+ continue;
686
+ }
687
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
688
+ current += ch + next;
689
+ inLineComment = true;
690
+ i += 1;
691
+ continue;
692
+ }
693
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
694
+ current += ch + next;
695
+ inBlockComment = true;
696
+ i += 1;
697
+ continue;
698
+ }
699
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
700
+ inSingle = !inSingle;
701
+ current += ch;
702
+ continue;
703
+ }
704
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
705
+ inDouble = !inDouble;
706
+ current += ch;
707
+ continue;
708
+ }
709
+ if (!inSingle && !inDouble && ch === ";") {
710
+ if (current.trim()) {
711
+ parts.push(current.trim());
712
+ }
713
+ current = "";
714
+ continue;
715
+ }
716
+ current += ch;
717
+ }
718
+ if (current.trim()) {
719
+ parts.push(current.trim());
720
+ }
721
+ return parts;
722
+ }
723
+ async function createPrismaDbAdapter(fallbackClient) {
724
+ const prisma = await loadPrismaClient();
725
+ await ensureCompatibilityViews(prisma);
726
+ let closed = false;
727
+ let adapter;
728
+ const fallbackExecute = async (stmt, error) => {
729
+ if (!fallbackClient) {
730
+ if (error) throw error;
731
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
732
+ }
733
+ if (error) {
734
+ process.stderr.write(
735
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
736
+ `
737
+ );
738
+ }
739
+ return fallbackClient.execute(stmt);
740
+ };
741
+ adapter = {
742
+ async execute(stmt) {
743
+ if (shouldBypassPostgres(stmt)) {
744
+ return fallbackExecute(stmt);
745
+ }
746
+ try {
747
+ return await executeOnPrisma(prisma, stmt);
748
+ } catch (error) {
749
+ if (shouldFallbackOnError(error)) {
750
+ return fallbackExecute(stmt, error);
751
+ }
752
+ throw error;
753
+ }
754
+ },
755
+ async batch(stmts, mode) {
756
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
757
+ if (!fallbackClient) {
758
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
759
+ }
760
+ return fallbackClient.batch(stmts, mode);
761
+ }
762
+ try {
763
+ if (prisma.$transaction) {
764
+ return await prisma.$transaction(async (tx) => {
765
+ const results2 = [];
766
+ for (const stmt of stmts) {
767
+ results2.push(await executeOnPrisma(tx, stmt));
768
+ }
769
+ return results2;
770
+ });
771
+ }
772
+ const results = [];
773
+ for (const stmt of stmts) {
774
+ results.push(await executeOnPrisma(prisma, stmt));
775
+ }
776
+ return results;
777
+ } catch (error) {
778
+ if (fallbackClient && shouldFallbackOnError(error)) {
779
+ process.stderr.write(
780
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
781
+ `
782
+ );
783
+ return fallbackClient.batch(stmts, mode);
784
+ }
785
+ throw error;
786
+ }
787
+ },
788
+ async migrate(stmts) {
789
+ if (fallbackClient) {
790
+ return fallbackClient.migrate(stmts);
791
+ }
792
+ return adapter.batch(stmts, "deferred");
793
+ },
794
+ async transaction(mode) {
795
+ if (!fallbackClient) {
796
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
797
+ }
798
+ return fallbackClient.transaction(mode);
799
+ },
800
+ async executeMultiple(sql) {
801
+ if (fallbackClient && shouldBypassPostgres(sql)) {
802
+ return fallbackClient.executeMultiple(sql);
803
+ }
804
+ for (const statement of splitSqlStatements(sql)) {
805
+ await adapter.execute(statement);
806
+ }
807
+ },
808
+ async sync() {
809
+ if (fallbackClient) {
810
+ return fallbackClient.sync();
811
+ }
812
+ return { frame_no: 0, frames_synced: 0 };
813
+ },
814
+ close() {
815
+ closed = true;
816
+ prismaClientPromise = null;
817
+ compatibilityBootstrapPromise = null;
818
+ void prisma.$disconnect?.();
819
+ },
820
+ get closed() {
821
+ return closed;
822
+ },
823
+ get protocol() {
824
+ return "prisma-postgres";
825
+ }
826
+ };
827
+ return adapter;
828
+ }
829
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
830
+ var init_database_adapter = __esm({
831
+ "src/lib/database-adapter.ts"() {
832
+ "use strict";
833
+ VIEW_MAPPINGS = [
834
+ { view: "memories", source: "memory.memory_records" },
835
+ { view: "tasks", source: "memory.tasks" },
836
+ { view: "behaviors", source: "memory.behaviors" },
837
+ { view: "entities", source: "memory.entities" },
838
+ { view: "relationships", source: "memory.relationships" },
839
+ { view: "entity_memories", source: "memory.entity_memories" },
840
+ { view: "entity_aliases", source: "memory.entity_aliases" },
841
+ { view: "notifications", source: "memory.notifications" },
842
+ { view: "messages", source: "memory.messages" },
843
+ { view: "users", source: "wiki.users" },
844
+ { view: "workspaces", source: "wiki.workspaces" },
845
+ { view: "workspace_users", source: "wiki.workspace_users" },
846
+ { view: "documents", source: "wiki.workspace_documents" },
847
+ { view: "chats", source: "wiki.workspace_chats" }
848
+ ];
849
+ UPSERT_KEYS = {
850
+ memories: ["id"],
851
+ tasks: ["id"],
852
+ behaviors: ["id"],
853
+ entities: ["id"],
854
+ relationships: ["id"],
855
+ entity_aliases: ["alias"],
856
+ notifications: ["id"],
857
+ messages: ["id"],
858
+ users: ["id"],
859
+ workspaces: ["id"],
860
+ workspace_users: ["id"],
861
+ documents: ["id"],
862
+ chats: ["id"]
863
+ };
864
+ BOOLEAN_COLUMNS_BY_TABLE = {
865
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
866
+ behaviors: /* @__PURE__ */ new Set(["active"]),
867
+ notifications: /* @__PURE__ */ new Set(["read"]),
868
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
869
+ };
870
+ BOOLEAN_COLUMN_NAMES = new Set(
871
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
872
+ );
873
+ IMMEDIATE_FALLBACK_PATTERNS = [
874
+ /\bPRAGMA\b/i,
875
+ /\bsqlite_master\b/i,
876
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
877
+ /\bMATCH\b/i,
878
+ /\bvector_distance_cos\s*\(/i,
879
+ /\bjson_extract\s*\(/i,
880
+ /\bjulianday\s*\(/i,
881
+ /\bstrftime\s*\(/i,
882
+ /\blast_insert_rowid\s*\(/i
883
+ ];
884
+ prismaClientPromise = null;
885
+ compatibilityBootstrapPromise = null;
301
886
  }
302
887
  });
303
888
 
304
889
  // src/lib/database.ts
305
890
  import { createClient } from "@libsql/client";
306
891
  async function initDatabase(config) {
892
+ if (_walCheckpointTimer) {
893
+ clearInterval(_walCheckpointTimer);
894
+ _walCheckpointTimer = null;
895
+ }
896
+ if (_daemonClient) {
897
+ _daemonClient.close();
898
+ _daemonClient = null;
899
+ }
900
+ if (_adapterClient && _adapterClient !== _resilientClient) {
901
+ _adapterClient.close();
902
+ }
903
+ _adapterClient = null;
307
904
  if (_client) {
308
905
  _client.close();
309
906
  _client = null;
@@ -317,6 +914,7 @@ async function initDatabase(config) {
317
914
  }
318
915
  _client = createClient(opts);
319
916
  _resilientClient = wrapWithRetry(_client);
917
+ _adapterClient = _resilientClient;
320
918
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
321
919
  });
322
920
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -327,11 +925,17 @@ async function initDatabase(config) {
327
925
  });
328
926
  }, 3e4);
329
927
  _walCheckpointTimer.unref();
928
+ if (process.env.DATABASE_URL) {
929
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
930
+ }
330
931
  }
331
932
  function getClient() {
332
- if (!_resilientClient) {
933
+ if (!_adapterClient) {
333
934
  throw new Error("Database client not initialized. Call initDatabase() first.");
334
935
  }
936
+ if (process.env.DATABASE_URL) {
937
+ return _adapterClient;
938
+ }
335
939
  if (process.env.EXE_IS_DAEMON === "1") {
336
940
  return _resilientClient;
337
941
  }
@@ -1272,26 +1876,36 @@ async function ensureSchema() {
1272
1876
  }
1273
1877
  }
1274
1878
  async function disposeDatabase() {
1879
+ if (_walCheckpointTimer) {
1880
+ clearInterval(_walCheckpointTimer);
1881
+ _walCheckpointTimer = null;
1882
+ }
1275
1883
  if (_daemonClient) {
1276
1884
  _daemonClient.close();
1277
1885
  _daemonClient = null;
1278
1886
  }
1887
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1888
+ _adapterClient.close();
1889
+ }
1890
+ _adapterClient = null;
1279
1891
  if (_client) {
1280
1892
  _client.close();
1281
1893
  _client = null;
1282
1894
  _resilientClient = null;
1283
1895
  }
1284
1896
  }
1285
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
1897
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1286
1898
  var init_database = __esm({
1287
1899
  "src/lib/database.ts"() {
1288
1900
  "use strict";
1289
1901
  init_db_retry();
1290
1902
  init_employees();
1903
+ init_database_adapter();
1291
1904
  _client = null;
1292
1905
  _resilientClient = null;
1293
1906
  _walCheckpointTimer = null;
1294
1907
  _daemonClient = null;
1908
+ _adapterClient = null;
1295
1909
  initTurso = initDatabase;
1296
1910
  disposeTurso = disposeDatabase;
1297
1911
  }
@@ -1310,7 +1924,7 @@ __export(shard_manager_exports, {
1310
1924
  listShards: () => listShards,
1311
1925
  shardExists: () => shardExists
1312
1926
  });
1313
- import path4 from "path";
1927
+ import path5 from "path";
1314
1928
  import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1315
1929
  import { createClient as createClient2 } from "@libsql/client";
1316
1930
  function initShardManager(encryptionKey) {
@@ -1336,7 +1950,7 @@ function getShardClient(projectName) {
1336
1950
  }
1337
1951
  const cached = _shards.get(safeName);
1338
1952
  if (cached) return cached;
1339
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
1953
+ const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1340
1954
  const client = createClient2({
1341
1955
  url: `file:${dbPath}`,
1342
1956
  encryptionKey: _encryptionKey
@@ -1346,7 +1960,7 @@ function getShardClient(projectName) {
1346
1960
  }
1347
1961
  function shardExists(projectName) {
1348
1962
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1349
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
1963
+ return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
1350
1964
  }
1351
1965
  function listShards() {
1352
1966
  if (!existsSync4(SHARDS_DIR)) return [];
@@ -1423,7 +2037,23 @@ async function ensureShardSchema(client) {
1423
2037
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1424
2038
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1425
2039
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1426
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2040
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2041
+ // Metadata enrichment columns (must match database.ts)
2042
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2043
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2044
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2045
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2046
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2047
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2048
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2049
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2050
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2051
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2052
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2053
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2054
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2055
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2056
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1427
2057
  ]) {
1428
2058
  try {
1429
2059
  await client.execute(col);
@@ -1535,7 +2165,7 @@ var init_shard_manager = __esm({
1535
2165
  "src/lib/shard-manager.ts"() {
1536
2166
  "use strict";
1537
2167
  init_config();
1538
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2168
+ SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
1539
2169
  _shards = /* @__PURE__ */ new Map();
1540
2170
  _encryptionKey = null;
1541
2171
  _shardingEnabled = false;
@@ -1729,6 +2359,32 @@ ${p.content}`).join("\n\n");
1729
2359
  }
1730
2360
  });
1731
2361
 
2362
+ // src/lib/runtime-table.ts
2363
+ var RUNTIME_TABLE;
2364
+ var init_runtime_table = __esm({
2365
+ "src/lib/runtime-table.ts"() {
2366
+ "use strict";
2367
+ RUNTIME_TABLE = {
2368
+ codex: {
2369
+ binary: "codex",
2370
+ launchMode: "interactive",
2371
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2372
+ inlineFlag: "--no-alt-screen",
2373
+ apiKeyEnv: "OPENAI_API_KEY",
2374
+ defaultModel: "gpt-5.4"
2375
+ },
2376
+ opencode: {
2377
+ binary: "opencode",
2378
+ launchMode: "exec",
2379
+ autoApproveFlag: "--dangerously-skip-permissions",
2380
+ inlineFlag: "",
2381
+ apiKeyEnv: "ANTHROPIC_API_KEY",
2382
+ defaultModel: "anthropic/claude-sonnet-4-6"
2383
+ }
2384
+ };
2385
+ }
2386
+ });
2387
+
1732
2388
  // src/lib/session-key.ts
1733
2389
  import { execSync as execSync2 } from "child_process";
1734
2390
  function normalizeCommand(command) {
@@ -1809,7 +2465,7 @@ __export(active_agent_exports, {
1809
2465
  });
1810
2466
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
1811
2467
  import { execSync as execSync3 } from "child_process";
1812
- import path6 from "path";
2468
+ import path7 from "path";
1813
2469
  function isNameWithOptionalInstance(candidate, baseName) {
1814
2470
  if (candidate === baseName) return true;
1815
2471
  if (!candidate.startsWith(baseName)) return false;
@@ -1853,7 +2509,7 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
1853
2509
  return null;
1854
2510
  }
1855
2511
  function getMarkerPath() {
1856
- return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
2512
+ return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1857
2513
  }
1858
2514
  function writeActiveAgent(agentId, agentRole) {
1859
2515
  try {
@@ -1922,14 +2578,14 @@ function getAllActiveAgents() {
1922
2578
  const key = file.slice("active-agent-".length, -".json".length);
1923
2579
  if (key === "undefined") continue;
1924
2580
  try {
1925
- const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
2581
+ const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
1926
2582
  const data = JSON.parse(raw);
1927
2583
  if (!data.agentId) continue;
1928
2584
  if (data.startedAt) {
1929
2585
  const age = Date.now() - new Date(data.startedAt).getTime();
1930
2586
  if (age > STALE_MS) {
1931
2587
  try {
1932
- unlinkSync3(path6.join(CACHE_DIR, file));
2588
+ unlinkSync3(path7.join(CACHE_DIR, file));
1933
2589
  } catch {
1934
2590
  }
1935
2591
  continue;
@@ -1952,11 +2608,11 @@ function getAllActiveAgents() {
1952
2608
  function cleanupSessionMarkers() {
1953
2609
  const key = getSessionKey();
1954
2610
  try {
1955
- unlinkSync3(path6.join(CACHE_DIR, `active-agent-${key}.json`));
2611
+ unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
1956
2612
  } catch {
1957
2613
  }
1958
2614
  try {
1959
- unlinkSync3(path6.join(CACHE_DIR, "active-agent-undefined.json"));
2615
+ unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
1960
2616
  } catch {
1961
2617
  }
1962
2618
  }
@@ -1967,14 +2623,14 @@ var init_active_agent = __esm({
1967
2623
  init_config();
1968
2624
  init_session_key();
1969
2625
  init_employees();
1970
- CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
2626
+ CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
1971
2627
  STALE_MS = 24 * 60 * 60 * 1e3;
1972
2628
  }
1973
2629
  });
1974
2630
 
1975
2631
  // src/lib/agent-symlinks.ts
1976
- import os5 from "os";
1977
- import path7 from "path";
2632
+ import os6 from "os";
2633
+ import path8 from "path";
1978
2634
  import {
1979
2635
  existsSync as existsSync6,
1980
2636
  lstatSync,
@@ -2005,10 +2661,10 @@ var init_mcp_prefix = __esm({
2005
2661
 
2006
2662
  // src/lib/preferences.ts
2007
2663
  import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
2008
- import path8 from "path";
2009
- import os6 from "os";
2010
- function loadPreferences(homeDir = os6.homedir()) {
2011
- const configPath = path8.join(homeDir, ".exe-os", "config.json");
2664
+ import path9 from "path";
2665
+ import os7 from "os";
2666
+ function loadPreferences(homeDir = os7.homedir()) {
2667
+ const configPath = path9.join(homeDir, ".exe-os", "config.json");
2012
2668
  if (!existsSync7(configPath)) return {};
2013
2669
  try {
2014
2670
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
@@ -2026,16 +2682,16 @@ var init_preferences = __esm({
2026
2682
  // src/adapters/claude/installer.ts
2027
2683
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
2028
2684
  import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2029
- import path9 from "path";
2030
- import os7 from "os";
2685
+ import path10 from "path";
2686
+ import os8 from "os";
2031
2687
  import { execSync as execSync4 } from "child_process";
2032
2688
  import { fileURLToPath } from "url";
2033
2689
  function resolvePackageRoot() {
2034
2690
  const thisFile = fileURLToPath(import.meta.url);
2035
- let dir = path9.dirname(thisFile);
2036
- const root = path9.parse(dir).root;
2691
+ let dir = path10.dirname(thisFile);
2692
+ const root = path10.parse(dir).root;
2037
2693
  while (dir !== root) {
2038
- const pkgPath = path9.join(dir, "package.json");
2694
+ const pkgPath = path10.join(dir, "package.json");
2039
2695
  if (existsSync8(pkgPath)) {
2040
2696
  try {
2041
2697
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
@@ -2043,9 +2699,9 @@ function resolvePackageRoot() {
2043
2699
  } catch {
2044
2700
  }
2045
2701
  }
2046
- dir = path9.dirname(dir);
2702
+ dir = path10.dirname(dir);
2047
2703
  }
2048
- return path9.resolve(path9.dirname(thisFile), "..", "..", "..");
2704
+ return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
2049
2705
  }
2050
2706
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
2051
2707
  var init_installer = __esm({
@@ -2080,13 +2736,13 @@ __export(installer_exports, {
2080
2736
  });
2081
2737
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2082
2738
  import { existsSync as existsSync9 } from "fs";
2083
- import path10 from "path";
2084
- import os8 from "os";
2085
- async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2086
- const codexDir = path10.join(homeDir, ".codex");
2087
- const hooksPath = path10.join(codexDir, "hooks.json");
2088
- const logsDir = path10.join(homeDir, ".exe-os", "logs");
2089
- const hookLogPath = path10.join(logsDir, "hooks.log");
2739
+ import path11 from "path";
2740
+ import os9 from "os";
2741
+ async function mergeCodexHooks(packageRoot, homeDir = os9.homedir()) {
2742
+ const codexDir = path11.join(homeDir, ".codex");
2743
+ const hooksPath = path11.join(codexDir, "hooks.json");
2744
+ const logsDir = path11.join(homeDir, ".exe-os", "logs");
2745
+ const hookLogPath = path11.join(logsDir, "hooks.log");
2090
2746
  const logSuffix = ` 2>> "${hookLogPath}"`;
2091
2747
  await mkdir5(codexDir, { recursive: true });
2092
2748
  await mkdir5(logsDir, { recursive: true });
@@ -2108,7 +2764,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2108
2764
  hooks: [
2109
2765
  {
2110
2766
  type: "command",
2111
- command: `node "${path10.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
2767
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
2112
2768
  timeout: 30,
2113
2769
  statusMessage: "exe-os: loading memory brief"
2114
2770
  }
@@ -2123,11 +2779,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2123
2779
  hooks: [
2124
2780
  {
2125
2781
  type: "command",
2126
- command: `node "${path10.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
2782
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
2127
2783
  },
2128
2784
  {
2129
2785
  type: "command",
2130
- command: `node "${path10.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
2786
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
2131
2787
  }
2132
2788
  ]
2133
2789
  },
@@ -2139,11 +2795,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2139
2795
  hooks: [
2140
2796
  {
2141
2797
  type: "command",
2142
- command: `node "${path10.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
2798
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
2143
2799
  },
2144
2800
  {
2145
2801
  type: "command",
2146
- command: `node "${path10.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
2802
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
2147
2803
  timeout: 5
2148
2804
  }
2149
2805
  ]
@@ -2156,7 +2812,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2156
2812
  hooks: [
2157
2813
  {
2158
2814
  type: "command",
2159
- command: `node "${path10.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
2815
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
2160
2816
  }
2161
2817
  ]
2162
2818
  },
@@ -2169,7 +2825,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2169
2825
  hooks: [
2170
2826
  {
2171
2827
  type: "command",
2172
- command: `node "${path10.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
2828
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
2173
2829
  }
2174
2830
  ]
2175
2831
  },
@@ -2200,15 +2856,15 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2200
2856
  await writeFile5(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
2201
2857
  return { added, skipped };
2202
2858
  }
2203
- function verifyCodexHooks(homeDir = os8.homedir()) {
2204
- const hooksPath = path10.join(homeDir, ".codex", "hooks.json");
2859
+ function verifyCodexHooks(homeDir = os9.homedir()) {
2860
+ const hooksPath = path11.join(homeDir, ".codex", "hooks.json");
2205
2861
  if (!existsSync9(hooksPath)) return false;
2206
2862
  try {
2207
2863
  const hooksJson = JSON.parse(
2208
2864
  __require("fs").readFileSync(hooksPath, "utf-8")
2209
2865
  );
2210
2866
  if (!hooksJson.hooks) return false;
2211
- const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop"];
2867
+ const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
2212
2868
  for (const event of required) {
2213
2869
  const groups = hooksJson.hooks[event];
2214
2870
  if (!groups || !groups.some(
@@ -2222,11 +2878,11 @@ function verifyCodexHooks(homeDir = os8.homedir()) {
2222
2878
  return false;
2223
2879
  }
2224
2880
  }
2225
- async function installCodexStatusLine(homeDir = os8.homedir()) {
2881
+ async function installCodexStatusLine(homeDir = os9.homedir()) {
2226
2882
  const prefs = loadPreferences(homeDir);
2227
2883
  if (prefs.codexStatusLine === false) return "opted-out";
2228
- const codexDir = path10.join(homeDir, ".codex");
2229
- const configPath = path10.join(codexDir, "config.toml");
2884
+ const codexDir = path11.join(homeDir, ".codex");
2885
+ const configPath = path11.join(codexDir, "config.toml");
2230
2886
  await mkdir5(codexDir, { recursive: true });
2231
2887
  let content = "";
2232
2888
  if (existsSync9(configPath)) {
@@ -2277,8 +2933,8 @@ var init_installer2 = __esm({
2277
2933
  });
2278
2934
 
2279
2935
  // src/bin/exe-start-codex.ts
2280
- import os9 from "os";
2281
- import path11 from "path";
2936
+ import os10 from "os";
2937
+ import path12 from "path";
2282
2938
  import {
2283
2939
  existsSync as existsSync10,
2284
2940
  readFileSync as readFileSync6,
@@ -2295,15 +2951,15 @@ init_database();
2295
2951
  // src/lib/keychain.ts
2296
2952
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2297
2953
  import { existsSync as existsSync3 } from "fs";
2298
- import path3 from "path";
2299
- import os3 from "os";
2954
+ import path4 from "path";
2955
+ import os4 from "os";
2300
2956
  var SERVICE = "exe-mem";
2301
2957
  var ACCOUNT = "master-key";
2302
2958
  function getKeyDir() {
2303
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
2959
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
2304
2960
  }
2305
2961
  function getKeyPath() {
2306
- return path3.join(getKeyDir(), "master.key");
2962
+ return path4.join(getKeyDir(), "master.key");
2307
2963
  }
2308
2964
  async function tryKeytar() {
2309
2965
  try {
@@ -2326,7 +2982,7 @@ async function getMasterKey() {
2326
2982
  const keyPath = getKeyPath();
2327
2983
  if (!existsSync3(keyPath)) {
2328
2984
  process.stderr.write(
2329
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2985
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2330
2986
  `
2331
2987
  );
2332
2988
  return null;
@@ -2637,8 +3293,8 @@ function vectorToBlob(vector) {
2637
3293
  }
2638
3294
 
2639
3295
  // src/lib/behaviors-export.ts
2640
- import os4 from "os";
2641
- import path5 from "path";
3296
+ import os5 from "os";
3297
+ import path6 from "path";
2642
3298
  import {
2643
3299
  existsSync as existsSync5,
2644
3300
  mkdirSync as mkdirSync2,
@@ -2680,8 +3336,8 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2680
3336
  }
2681
3337
 
2682
3338
  // src/lib/behaviors-export.ts
2683
- var BEHAVIORS_EXPORT_DIR = path5.join(
2684
- os4.homedir(),
3339
+ var BEHAVIORS_EXPORT_DIR = path6.join(
3340
+ os5.homedir(),
2685
3341
  ".exe-os",
2686
3342
  "behaviors-export"
2687
3343
  );
@@ -2696,7 +3352,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
2696
3352
  return;
2697
3353
  }
2698
3354
  for (const entry of entries) {
2699
- const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
3355
+ const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
2700
3356
  try {
2701
3357
  const stat = statSync(filePath);
2702
3358
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -2729,10 +3385,10 @@ function renderBehaviorExport(behaviors) {
2729
3385
  }
2730
3386
  function exportFilePath(agentId, projectName, sessionKey) {
2731
3387
  if (!sessionKey) {
2732
- return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
3388
+ return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
2733
3389
  }
2734
3390
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2735
- return path5.join(
3391
+ return path6.join(
2736
3392
  BEHAVIORS_EXPORT_DIR,
2737
3393
  `${agentId}-${safeProject}-${sessionKey}.md`
2738
3394
  );
@@ -2750,28 +3406,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2750
3406
 
2751
3407
  // src/bin/exe-start-codex.ts
2752
3408
  init_employees();
2753
-
2754
- // src/lib/runtime-table.ts
2755
- var RUNTIME_TABLE = {
2756
- codex: {
2757
- binary: "codex",
2758
- launchMode: "interactive",
2759
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2760
- inlineFlag: "--no-alt-screen",
2761
- apiKeyEnv: "OPENAI_API_KEY",
2762
- defaultModel: "gpt-5.4"
2763
- },
2764
- opencode: {
2765
- binary: "opencode",
2766
- launchMode: "exec",
2767
- autoApproveFlag: "--dangerously-skip-permissions",
2768
- inlineFlag: "",
2769
- apiKeyEnv: "ANTHROPIC_API_KEY",
2770
- defaultModel: "anthropic/claude-sonnet-4-6"
2771
- }
2772
- };
2773
-
2774
- // src/bin/exe-start-codex.ts
3409
+ init_runtime_table();
2775
3410
  var CODEX = RUNTIME_TABLE.codex;
2776
3411
  var BOOT_INSTRUCTIONS = `
2777
3412
  ---
@@ -2782,7 +3417,7 @@ When done with a task: call update_task with status "done".
2782
3417
  Always call store_memory to persist important findings.
2783
3418
  `;
2784
3419
  function resolveAgent(argv) {
2785
- const invokedAs = path11.basename(argv[1] ?? "");
3420
+ const invokedAs = path12.basename(argv[1] ?? "");
2786
3421
  if (invokedAs && invokedAs !== "exe-start-codex" && !invokedAs.endsWith(".js")) {
2787
3422
  const agent2 = invokedAs.replace(/-codex$/, "").toLowerCase();
2788
3423
  return { agent: agent2, passthrough: argv.slice(2) };
@@ -2808,8 +3443,8 @@ function resolveAgent(argv) {
2808
3443
  return { agent, passthrough, sessionName };
2809
3444
  }
2810
3445
  function loadIdentity(agent) {
2811
- const dir = path11.join(os9.homedir(), ".exe-os", "identity");
2812
- const exact = path11.join(dir, `${agent}.md`);
3446
+ const dir = path12.join(os10.homedir(), ".exe-os", "identity");
3447
+ const exact = path12.join(dir, `${agent}.md`);
2813
3448
  if (existsSync10(exact)) {
2814
3449
  const content = readFileSync6(exact, "utf-8").trim();
2815
3450
  if (content) return content;
@@ -2818,13 +3453,13 @@ function loadIdentity(agent) {
2818
3453
  const files = readdirSync4(dir);
2819
3454
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
2820
3455
  if (match) {
2821
- const content = readFileSync6(path11.join(dir, match), "utf-8").trim();
3456
+ const content = readFileSync6(path12.join(dir, match), "utf-8").trim();
2822
3457
  if (content) return content;
2823
3458
  }
2824
3459
  } catch {
2825
3460
  }
2826
3461
  try {
2827
- const rosterPath = path11.join(os9.homedir(), ".exe-os", "exe-employees.json");
3462
+ const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
2828
3463
  const roster = JSON.parse(readFileSync6(rosterPath, "utf8"));
2829
3464
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
2830
3465
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
@@ -2835,7 +3470,7 @@ function loadIdentity(agent) {
2835
3470
  return null;
2836
3471
  }
2837
3472
  function writePromptFile(agent, identity, behaviorsPath) {
2838
- const promptDir = path11.join(os9.homedir(), ".exe-os", "codex-prompt");
3473
+ const promptDir = path12.join(os10.homedir(), ".exe-os", "codex-prompt");
2839
3474
  mkdirSync7(promptDir, { recursive: true });
2840
3475
  let prompt = identity;
2841
3476
  if (behaviorsPath && existsSync10(behaviorsPath)) {
@@ -2845,7 +3480,7 @@ function writePromptFile(agent, identity, behaviorsPath) {
2845
3480
  }
2846
3481
  }
2847
3482
  prompt += "\n" + BOOT_INSTRUCTIONS;
2848
- const outPath = path11.join(promptDir, `${agent}.md`);
3483
+ const outPath = path12.join(promptDir, `${agent}.md`);
2849
3484
  writeFileSync6(outPath, prompt, "utf-8");
2850
3485
  return outPath;
2851
3486
  }
@@ -2926,7 +3561,7 @@ async function main() {
2926
3561
  const empRole = (() => {
2927
3562
  try {
2928
3563
  const emps = readFileSync6(
2929
- path11.join(os9.homedir(), ".exe-os", "exe-employees.json"),
3564
+ path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
2930
3565
  "utf-8"
2931
3566
  );
2932
3567
  const found = JSON.parse(emps).find(
@@ -2963,14 +3598,14 @@ async function main() {
2963
3598
  if (WORKTREE_ROLES.has(empRole)) {
2964
3599
  try {
2965
3600
  const { execSync: es } = await import("child_process");
2966
- const worktreeDir = path11.join(process.cwd(), ".worktrees", worktreeName);
3601
+ const worktreeDir = path12.join(process.cwd(), ".worktrees", worktreeName);
2967
3602
  const branchName = `${worktreeName}/codex-${Date.now()}`;
2968
3603
  if (existsSync10(worktreeDir)) {
2969
3604
  worktreePath = worktreeDir;
2970
3605
  process.stderr.write(`[exe-start-codex] Reusing worktree at ${worktreeDir}
2971
3606
  `);
2972
3607
  } else {
2973
- mkdirSync7(path11.dirname(worktreeDir), { recursive: true });
3608
+ mkdirSync7(path12.dirname(worktreeDir), { recursive: true });
2974
3609
  es(`git worktree add "${worktreeDir}" -b "${branchName}" HEAD`, {
2975
3610
  encoding: "utf-8",
2976
3611
  timeout: 3e4