@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
@@ -284,7 +284,7 @@ function baseAgentName(name, employees) {
284
284
  if (getEmployee(roster, base)) return base;
285
285
  return name;
286
286
  }
287
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
287
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
288
288
  var init_employees = __esm({
289
289
  "src/lib/employees.ts"() {
290
290
  "use strict";
@@ -292,12 +292,609 @@ var init_employees = __esm({
292
292
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
293
293
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
294
294
  COORDINATOR_ROLE = "COO";
295
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
296
+ }
297
+ });
298
+
299
+ // src/lib/database-adapter.ts
300
+ import os3 from "os";
301
+ import path3 from "path";
302
+ import { createRequire } from "module";
303
+ import { pathToFileURL } from "url";
304
+ function quotedIdentifier(identifier) {
305
+ return `"${identifier.replace(/"/g, '""')}"`;
306
+ }
307
+ function unqualifiedTableName(name) {
308
+ const raw = name.trim().replace(/^"|"$/g, "");
309
+ const parts = raw.split(".");
310
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
311
+ }
312
+ function stripTrailingSemicolon(sql) {
313
+ return sql.trim().replace(/;+\s*$/u, "");
314
+ }
315
+ function appendClause(sql, clause) {
316
+ const trimmed = stripTrailingSemicolon(sql);
317
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
318
+ if (!returningMatch) {
319
+ return `${trimmed}${clause}`;
320
+ }
321
+ const idx = returningMatch.index;
322
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
323
+ }
324
+ function normalizeStatement(stmt) {
325
+ if (typeof stmt === "string") {
326
+ return { kind: "positional", sql: stmt, args: [] };
327
+ }
328
+ const sql = stmt.sql;
329
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
330
+ return { kind: "positional", sql, args: stmt.args ?? [] };
331
+ }
332
+ return { kind: "named", sql, args: stmt.args };
333
+ }
334
+ function rewriteBooleanLiterals(sql) {
335
+ let out = sql;
336
+ for (const column of BOOLEAN_COLUMN_NAMES) {
337
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
338
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
339
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
340
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
341
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
342
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
343
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
344
+ }
345
+ return out;
346
+ }
347
+ function rewriteInsertOrIgnore(sql) {
348
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
349
+ return sql;
350
+ }
351
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
352
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
353
+ }
354
+ function rewriteInsertOrReplace(sql) {
355
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
356
+ if (!match) {
357
+ return sql;
358
+ }
359
+ const rawTable = match[1];
360
+ const rawColumns = match[2];
361
+ const remainder = match[3];
362
+ const tableName = unqualifiedTableName(rawTable);
363
+ const conflictKeys = UPSERT_KEYS[tableName];
364
+ if (!conflictKeys?.length) {
365
+ return sql;
366
+ }
367
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
368
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
369
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
370
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
371
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
372
+ }
373
+ function rewriteSql(sql) {
374
+ let out = sql;
375
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
376
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
377
+ out = rewriteBooleanLiterals(out);
378
+ out = rewriteInsertOrReplace(out);
379
+ out = rewriteInsertOrIgnore(out);
380
+ return stripTrailingSemicolon(out);
381
+ }
382
+ function toBoolean(value) {
383
+ if (value === null || value === void 0) return value;
384
+ if (typeof value === "boolean") return value;
385
+ if (typeof value === "number") return value !== 0;
386
+ if (typeof value === "bigint") return value !== 0n;
387
+ if (typeof value === "string") {
388
+ const normalized = value.trim().toLowerCase();
389
+ if (normalized === "0" || normalized === "false") return false;
390
+ if (normalized === "1" || normalized === "true") return true;
391
+ }
392
+ return Boolean(value);
393
+ }
394
+ function countQuestionMarks(sql, end) {
395
+ let count = 0;
396
+ let inSingle = false;
397
+ let inDouble = false;
398
+ let inLineComment = false;
399
+ let inBlockComment = false;
400
+ for (let i = 0; i < end; i++) {
401
+ const ch = sql[i];
402
+ const next = sql[i + 1];
403
+ if (inLineComment) {
404
+ if (ch === "\n") inLineComment = false;
405
+ continue;
406
+ }
407
+ if (inBlockComment) {
408
+ if (ch === "*" && next === "/") {
409
+ inBlockComment = false;
410
+ i += 1;
411
+ }
412
+ continue;
413
+ }
414
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
415
+ inLineComment = true;
416
+ i += 1;
417
+ continue;
418
+ }
419
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
420
+ inBlockComment = true;
421
+ i += 1;
422
+ continue;
423
+ }
424
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
425
+ inSingle = !inSingle;
426
+ continue;
427
+ }
428
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
429
+ inDouble = !inDouble;
430
+ continue;
431
+ }
432
+ if (!inSingle && !inDouble && ch === "?") {
433
+ count += 1;
434
+ }
435
+ }
436
+ return count;
437
+ }
438
+ function findBooleanPlaceholderIndexes(sql) {
439
+ const indexes = /* @__PURE__ */ new Set();
440
+ for (const column of BOOLEAN_COLUMN_NAMES) {
441
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
442
+ for (const match of sql.matchAll(pattern)) {
443
+ const matchText = match[0];
444
+ const qIndex = match.index + matchText.lastIndexOf("?");
445
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
446
+ }
447
+ }
448
+ return indexes;
449
+ }
450
+ function coerceInsertBooleanArgs(sql, args) {
451
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
452
+ if (!match) return;
453
+ const rawTable = match[1];
454
+ const rawColumns = match[2];
455
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
456
+ if (!boolColumns?.size) return;
457
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
458
+ for (const [index, column] of columns.entries()) {
459
+ if (boolColumns.has(column) && index < args.length) {
460
+ args[index] = toBoolean(args[index]);
461
+ }
462
+ }
463
+ }
464
+ function coerceUpdateBooleanArgs(sql, args) {
465
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
466
+ if (!match) return;
467
+ const rawTable = match[1];
468
+ const setClause = match[2];
469
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
470
+ if (!boolColumns?.size) return;
471
+ const assignments = setClause.split(",");
472
+ let placeholderIndex = 0;
473
+ for (const assignment of assignments) {
474
+ if (!assignment.includes("?")) continue;
475
+ placeholderIndex += 1;
476
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
477
+ if (colMatch && boolColumns.has(colMatch[1])) {
478
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
479
+ }
480
+ }
481
+ }
482
+ function coerceBooleanArgs(sql, args) {
483
+ const nextArgs = [...args];
484
+ coerceInsertBooleanArgs(sql, nextArgs);
485
+ coerceUpdateBooleanArgs(sql, nextArgs);
486
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
487
+ for (const index of placeholderIndexes) {
488
+ if (index > 0 && index <= nextArgs.length) {
489
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
490
+ }
491
+ }
492
+ return nextArgs;
493
+ }
494
+ function convertQuestionMarksToDollarParams(sql) {
495
+ let out = "";
496
+ let placeholder = 0;
497
+ let inSingle = false;
498
+ let inDouble = false;
499
+ let inLineComment = false;
500
+ let inBlockComment = false;
501
+ for (let i = 0; i < sql.length; i++) {
502
+ const ch = sql[i];
503
+ const next = sql[i + 1];
504
+ if (inLineComment) {
505
+ out += ch;
506
+ if (ch === "\n") inLineComment = false;
507
+ continue;
508
+ }
509
+ if (inBlockComment) {
510
+ out += ch;
511
+ if (ch === "*" && next === "/") {
512
+ out += next;
513
+ inBlockComment = false;
514
+ i += 1;
515
+ }
516
+ continue;
517
+ }
518
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
519
+ out += ch + next;
520
+ inLineComment = true;
521
+ i += 1;
522
+ continue;
523
+ }
524
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
525
+ out += ch + next;
526
+ inBlockComment = true;
527
+ i += 1;
528
+ continue;
529
+ }
530
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
531
+ inSingle = !inSingle;
532
+ out += ch;
533
+ continue;
534
+ }
535
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
536
+ inDouble = !inDouble;
537
+ out += ch;
538
+ continue;
539
+ }
540
+ if (!inSingle && !inDouble && ch === "?") {
541
+ placeholder += 1;
542
+ out += `$${placeholder}`;
543
+ continue;
544
+ }
545
+ out += ch;
546
+ }
547
+ return out;
548
+ }
549
+ function translateStatementForPostgres(stmt) {
550
+ const normalized = normalizeStatement(stmt);
551
+ if (normalized.kind === "named") {
552
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
553
+ }
554
+ const rewrittenSql = rewriteSql(normalized.sql);
555
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
556
+ return {
557
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
558
+ args: coercedArgs
559
+ };
560
+ }
561
+ function shouldBypassPostgres(stmt) {
562
+ const normalized = normalizeStatement(stmt);
563
+ if (normalized.kind === "named") {
564
+ return true;
565
+ }
566
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
567
+ }
568
+ function shouldFallbackOnError(error) {
569
+ const message = error instanceof Error ? error.message : String(error);
570
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
571
+ }
572
+ function isReadQuery(sql) {
573
+ const trimmed = sql.trimStart();
574
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
575
+ }
576
+ function buildRow(row, columns) {
577
+ const values = columns.map((column) => row[column]);
578
+ return Object.assign(values, row);
579
+ }
580
+ function buildResultSet(rows, rowsAffected = 0) {
581
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
582
+ const resultRows = rows.map((row) => buildRow(row, columns));
583
+ return {
584
+ columns,
585
+ columnTypes: columns.map(() => ""),
586
+ rows: resultRows,
587
+ rowsAffected,
588
+ lastInsertRowid: void 0,
589
+ toJSON() {
590
+ return {
591
+ columns,
592
+ columnTypes: columns.map(() => ""),
593
+ rows,
594
+ rowsAffected,
595
+ lastInsertRowid: void 0
596
+ };
597
+ }
598
+ };
599
+ }
600
+ async function loadPrismaClient() {
601
+ if (!prismaClientPromise) {
602
+ prismaClientPromise = (async () => {
603
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
604
+ if (explicitPath) {
605
+ const module2 = await import(pathToFileURL(explicitPath).href);
606
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
607
+ if (!PrismaClient2) {
608
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
609
+ }
610
+ return new PrismaClient2();
611
+ }
612
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
613
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
614
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
615
+ const module = await import(pathToFileURL(prismaEntry).href);
616
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
617
+ if (!PrismaClient) {
618
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
619
+ }
620
+ return new PrismaClient();
621
+ })();
622
+ }
623
+ return prismaClientPromise;
624
+ }
625
+ async function ensureCompatibilityViews(prisma) {
626
+ if (!compatibilityBootstrapPromise) {
627
+ compatibilityBootstrapPromise = (async () => {
628
+ for (const mapping of VIEW_MAPPINGS) {
629
+ const relation = mapping.source.replace(/"/g, "");
630
+ const rows = await prisma.$queryRawUnsafe(
631
+ "SELECT to_regclass($1) AS regclass",
632
+ relation
633
+ );
634
+ if (!rows[0]?.regclass) {
635
+ continue;
636
+ }
637
+ await prisma.$executeRawUnsafe(
638
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
639
+ );
640
+ }
641
+ })();
642
+ }
643
+ return compatibilityBootstrapPromise;
644
+ }
645
+ async function executeOnPrisma(executor, stmt) {
646
+ const translated = translateStatementForPostgres(stmt);
647
+ if (isReadQuery(translated.sql)) {
648
+ const rows = await executor.$queryRawUnsafe(
649
+ translated.sql,
650
+ ...translated.args
651
+ );
652
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
653
+ }
654
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
655
+ return buildResultSet([], rowsAffected);
656
+ }
657
+ function splitSqlStatements(sql) {
658
+ const parts = [];
659
+ let current = "";
660
+ let inSingle = false;
661
+ let inDouble = false;
662
+ let inLineComment = false;
663
+ let inBlockComment = false;
664
+ for (let i = 0; i < sql.length; i++) {
665
+ const ch = sql[i];
666
+ const next = sql[i + 1];
667
+ if (inLineComment) {
668
+ current += ch;
669
+ if (ch === "\n") inLineComment = false;
670
+ continue;
671
+ }
672
+ if (inBlockComment) {
673
+ current += ch;
674
+ if (ch === "*" && next === "/") {
675
+ current += next;
676
+ inBlockComment = false;
677
+ i += 1;
678
+ }
679
+ continue;
680
+ }
681
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
682
+ current += ch + next;
683
+ inLineComment = true;
684
+ i += 1;
685
+ continue;
686
+ }
687
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
688
+ current += ch + next;
689
+ inBlockComment = true;
690
+ i += 1;
691
+ continue;
692
+ }
693
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
694
+ inSingle = !inSingle;
695
+ current += ch;
696
+ continue;
697
+ }
698
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
699
+ inDouble = !inDouble;
700
+ current += ch;
701
+ continue;
702
+ }
703
+ if (!inSingle && !inDouble && ch === ";") {
704
+ if (current.trim()) {
705
+ parts.push(current.trim());
706
+ }
707
+ current = "";
708
+ continue;
709
+ }
710
+ current += ch;
711
+ }
712
+ if (current.trim()) {
713
+ parts.push(current.trim());
714
+ }
715
+ return parts;
716
+ }
717
+ async function createPrismaDbAdapter(fallbackClient) {
718
+ const prisma = await loadPrismaClient();
719
+ await ensureCompatibilityViews(prisma);
720
+ let closed = false;
721
+ let adapter;
722
+ const fallbackExecute = async (stmt, error) => {
723
+ if (!fallbackClient) {
724
+ if (error) throw error;
725
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
726
+ }
727
+ if (error) {
728
+ process.stderr.write(
729
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
730
+ `
731
+ );
732
+ }
733
+ return fallbackClient.execute(stmt);
734
+ };
735
+ adapter = {
736
+ async execute(stmt) {
737
+ if (shouldBypassPostgres(stmt)) {
738
+ return fallbackExecute(stmt);
739
+ }
740
+ try {
741
+ return await executeOnPrisma(prisma, stmt);
742
+ } catch (error) {
743
+ if (shouldFallbackOnError(error)) {
744
+ return fallbackExecute(stmt, error);
745
+ }
746
+ throw error;
747
+ }
748
+ },
749
+ async batch(stmts, mode) {
750
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
751
+ if (!fallbackClient) {
752
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
753
+ }
754
+ return fallbackClient.batch(stmts, mode);
755
+ }
756
+ try {
757
+ if (prisma.$transaction) {
758
+ return await prisma.$transaction(async (tx) => {
759
+ const results2 = [];
760
+ for (const stmt of stmts) {
761
+ results2.push(await executeOnPrisma(tx, stmt));
762
+ }
763
+ return results2;
764
+ });
765
+ }
766
+ const results = [];
767
+ for (const stmt of stmts) {
768
+ results.push(await executeOnPrisma(prisma, stmt));
769
+ }
770
+ return results;
771
+ } catch (error) {
772
+ if (fallbackClient && shouldFallbackOnError(error)) {
773
+ process.stderr.write(
774
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
775
+ `
776
+ );
777
+ return fallbackClient.batch(stmts, mode);
778
+ }
779
+ throw error;
780
+ }
781
+ },
782
+ async migrate(stmts) {
783
+ if (fallbackClient) {
784
+ return fallbackClient.migrate(stmts);
785
+ }
786
+ return adapter.batch(stmts, "deferred");
787
+ },
788
+ async transaction(mode) {
789
+ if (!fallbackClient) {
790
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
791
+ }
792
+ return fallbackClient.transaction(mode);
793
+ },
794
+ async executeMultiple(sql) {
795
+ if (fallbackClient && shouldBypassPostgres(sql)) {
796
+ return fallbackClient.executeMultiple(sql);
797
+ }
798
+ for (const statement of splitSqlStatements(sql)) {
799
+ await adapter.execute(statement);
800
+ }
801
+ },
802
+ async sync() {
803
+ if (fallbackClient) {
804
+ return fallbackClient.sync();
805
+ }
806
+ return { frame_no: 0, frames_synced: 0 };
807
+ },
808
+ close() {
809
+ closed = true;
810
+ prismaClientPromise = null;
811
+ compatibilityBootstrapPromise = null;
812
+ void prisma.$disconnect?.();
813
+ },
814
+ get closed() {
815
+ return closed;
816
+ },
817
+ get protocol() {
818
+ return "prisma-postgres";
819
+ }
820
+ };
821
+ return adapter;
822
+ }
823
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
824
+ var init_database_adapter = __esm({
825
+ "src/lib/database-adapter.ts"() {
826
+ "use strict";
827
+ VIEW_MAPPINGS = [
828
+ { view: "memories", source: "memory.memory_records" },
829
+ { view: "tasks", source: "memory.tasks" },
830
+ { view: "behaviors", source: "memory.behaviors" },
831
+ { view: "entities", source: "memory.entities" },
832
+ { view: "relationships", source: "memory.relationships" },
833
+ { view: "entity_memories", source: "memory.entity_memories" },
834
+ { view: "entity_aliases", source: "memory.entity_aliases" },
835
+ { view: "notifications", source: "memory.notifications" },
836
+ { view: "messages", source: "memory.messages" },
837
+ { view: "users", source: "wiki.users" },
838
+ { view: "workspaces", source: "wiki.workspaces" },
839
+ { view: "workspace_users", source: "wiki.workspace_users" },
840
+ { view: "documents", source: "wiki.workspace_documents" },
841
+ { view: "chats", source: "wiki.workspace_chats" }
842
+ ];
843
+ UPSERT_KEYS = {
844
+ memories: ["id"],
845
+ tasks: ["id"],
846
+ behaviors: ["id"],
847
+ entities: ["id"],
848
+ relationships: ["id"],
849
+ entity_aliases: ["alias"],
850
+ notifications: ["id"],
851
+ messages: ["id"],
852
+ users: ["id"],
853
+ workspaces: ["id"],
854
+ workspace_users: ["id"],
855
+ documents: ["id"],
856
+ chats: ["id"]
857
+ };
858
+ BOOLEAN_COLUMNS_BY_TABLE = {
859
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
860
+ behaviors: /* @__PURE__ */ new Set(["active"]),
861
+ notifications: /* @__PURE__ */ new Set(["read"]),
862
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
863
+ };
864
+ BOOLEAN_COLUMN_NAMES = new Set(
865
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
866
+ );
867
+ IMMEDIATE_FALLBACK_PATTERNS = [
868
+ /\bPRAGMA\b/i,
869
+ /\bsqlite_master\b/i,
870
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
871
+ /\bMATCH\b/i,
872
+ /\bvector_distance_cos\s*\(/i,
873
+ /\bjson_extract\s*\(/i,
874
+ /\bjulianday\s*\(/i,
875
+ /\bstrftime\s*\(/i,
876
+ /\blast_insert_rowid\s*\(/i
877
+ ];
878
+ prismaClientPromise = null;
879
+ compatibilityBootstrapPromise = null;
295
880
  }
296
881
  });
297
882
 
298
883
  // src/lib/database.ts
299
884
  import { createClient } from "@libsql/client";
300
885
  async function initDatabase(config) {
886
+ if (_walCheckpointTimer) {
887
+ clearInterval(_walCheckpointTimer);
888
+ _walCheckpointTimer = null;
889
+ }
890
+ if (_daemonClient) {
891
+ _daemonClient.close();
892
+ _daemonClient = null;
893
+ }
894
+ if (_adapterClient && _adapterClient !== _resilientClient) {
895
+ _adapterClient.close();
896
+ }
897
+ _adapterClient = null;
301
898
  if (_client) {
302
899
  _client.close();
303
900
  _client = null;
@@ -311,6 +908,7 @@ async function initDatabase(config) {
311
908
  }
312
909
  _client = createClient(opts);
313
910
  _resilientClient = wrapWithRetry(_client);
911
+ _adapterClient = _resilientClient;
314
912
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
315
913
  });
316
914
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -321,11 +919,17 @@ async function initDatabase(config) {
321
919
  });
322
920
  }, 3e4);
323
921
  _walCheckpointTimer.unref();
922
+ if (process.env.DATABASE_URL) {
923
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
924
+ }
324
925
  }
325
926
  function getClient() {
326
- if (!_resilientClient) {
927
+ if (!_adapterClient) {
327
928
  throw new Error("Database client not initialized. Call initDatabase() first.");
328
929
  }
930
+ if (process.env.DATABASE_URL) {
931
+ return _adapterClient;
932
+ }
329
933
  if (process.env.EXE_IS_DAEMON === "1") {
330
934
  return _resilientClient;
331
935
  }
@@ -1266,26 +1870,36 @@ async function ensureSchema() {
1266
1870
  }
1267
1871
  }
1268
1872
  async function disposeDatabase() {
1873
+ if (_walCheckpointTimer) {
1874
+ clearInterval(_walCheckpointTimer);
1875
+ _walCheckpointTimer = null;
1876
+ }
1269
1877
  if (_daemonClient) {
1270
1878
  _daemonClient.close();
1271
1879
  _daemonClient = null;
1272
1880
  }
1881
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1882
+ _adapterClient.close();
1883
+ }
1884
+ _adapterClient = null;
1273
1885
  if (_client) {
1274
1886
  _client.close();
1275
1887
  _client = null;
1276
1888
  _resilientClient = null;
1277
1889
  }
1278
1890
  }
1279
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
1891
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1280
1892
  var init_database = __esm({
1281
1893
  "src/lib/database.ts"() {
1282
1894
  "use strict";
1283
1895
  init_db_retry();
1284
1896
  init_employees();
1897
+ init_database_adapter();
1285
1898
  _client = null;
1286
1899
  _resilientClient = null;
1287
1900
  _walCheckpointTimer = null;
1288
1901
  _daemonClient = null;
1902
+ _adapterClient = null;
1289
1903
  initTurso = initDatabase;
1290
1904
  disposeTurso = disposeDatabase;
1291
1905
  }
@@ -1304,7 +1918,7 @@ __export(shard_manager_exports, {
1304
1918
  listShards: () => listShards,
1305
1919
  shardExists: () => shardExists
1306
1920
  });
1307
- import path4 from "path";
1921
+ import path5 from "path";
1308
1922
  import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1309
1923
  import { createClient as createClient2 } from "@libsql/client";
1310
1924
  function initShardManager(encryptionKey) {
@@ -1330,7 +1944,7 @@ function getShardClient(projectName) {
1330
1944
  }
1331
1945
  const cached = _shards.get(safeName);
1332
1946
  if (cached) return cached;
1333
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
1947
+ const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1334
1948
  const client = createClient2({
1335
1949
  url: `file:${dbPath}`,
1336
1950
  encryptionKey: _encryptionKey
@@ -1340,7 +1954,7 @@ function getShardClient(projectName) {
1340
1954
  }
1341
1955
  function shardExists(projectName) {
1342
1956
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1343
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
1957
+ return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
1344
1958
  }
1345
1959
  function listShards() {
1346
1960
  if (!existsSync4(SHARDS_DIR)) return [];
@@ -1417,7 +2031,23 @@ async function ensureShardSchema(client) {
1417
2031
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1418
2032
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1419
2033
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1420
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2034
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2035
+ // Metadata enrichment columns (must match database.ts)
2036
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2037
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2038
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2039
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2040
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2041
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2042
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2043
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2044
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2045
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2046
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2047
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2048
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2049
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2050
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1421
2051
  ]) {
1422
2052
  try {
1423
2053
  await client.execute(col);
@@ -1529,7 +2159,7 @@ var init_shard_manager = __esm({
1529
2159
  "src/lib/shard-manager.ts"() {
1530
2160
  "use strict";
1531
2161
  init_config();
1532
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2162
+ SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
1533
2163
  _shards = /* @__PURE__ */ new Map();
1534
2164
  _encryptionKey = null;
1535
2165
  _shardingEnabled = false;
@@ -1723,6 +2353,32 @@ ${p.content}`).join("\n\n");
1723
2353
  }
1724
2354
  });
1725
2355
 
2356
+ // src/lib/runtime-table.ts
2357
+ var RUNTIME_TABLE;
2358
+ var init_runtime_table = __esm({
2359
+ "src/lib/runtime-table.ts"() {
2360
+ "use strict";
2361
+ RUNTIME_TABLE = {
2362
+ codex: {
2363
+ binary: "codex",
2364
+ launchMode: "interactive",
2365
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2366
+ inlineFlag: "--no-alt-screen",
2367
+ apiKeyEnv: "OPENAI_API_KEY",
2368
+ defaultModel: "gpt-5.4"
2369
+ },
2370
+ opencode: {
2371
+ binary: "opencode",
2372
+ launchMode: "exec",
2373
+ autoApproveFlag: "--dangerously-skip-permissions",
2374
+ inlineFlag: "",
2375
+ apiKeyEnv: "ANTHROPIC_API_KEY",
2376
+ defaultModel: "anthropic/claude-sonnet-4-6"
2377
+ }
2378
+ };
2379
+ }
2380
+ });
2381
+
1726
2382
  // src/lib/session-key.ts
1727
2383
  import { execSync as execSync2 } from "child_process";
1728
2384
  function normalizeCommand(command) {
@@ -1803,7 +2459,7 @@ __export(active_agent_exports, {
1803
2459
  });
1804
2460
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
1805
2461
  import { execSync as execSync3 } from "child_process";
1806
- import path6 from "path";
2462
+ import path7 from "path";
1807
2463
  function isNameWithOptionalInstance(candidate, baseName) {
1808
2464
  if (candidate === baseName) return true;
1809
2465
  if (!candidate.startsWith(baseName)) return false;
@@ -1847,7 +2503,7 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
1847
2503
  return null;
1848
2504
  }
1849
2505
  function getMarkerPath() {
1850
- return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
2506
+ return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1851
2507
  }
1852
2508
  function writeActiveAgent(agentId, agentRole) {
1853
2509
  try {
@@ -1916,14 +2572,14 @@ function getAllActiveAgents() {
1916
2572
  const key = file.slice("active-agent-".length, -".json".length);
1917
2573
  if (key === "undefined") continue;
1918
2574
  try {
1919
- const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
2575
+ const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
1920
2576
  const data = JSON.parse(raw);
1921
2577
  if (!data.agentId) continue;
1922
2578
  if (data.startedAt) {
1923
2579
  const age = Date.now() - new Date(data.startedAt).getTime();
1924
2580
  if (age > STALE_MS) {
1925
2581
  try {
1926
- unlinkSync3(path6.join(CACHE_DIR, file));
2582
+ unlinkSync3(path7.join(CACHE_DIR, file));
1927
2583
  } catch {
1928
2584
  }
1929
2585
  continue;
@@ -1946,11 +2602,11 @@ function getAllActiveAgents() {
1946
2602
  function cleanupSessionMarkers() {
1947
2603
  const key = getSessionKey();
1948
2604
  try {
1949
- unlinkSync3(path6.join(CACHE_DIR, `active-agent-${key}.json`));
2605
+ unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
1950
2606
  } catch {
1951
2607
  }
1952
2608
  try {
1953
- unlinkSync3(path6.join(CACHE_DIR, "active-agent-undefined.json"));
2609
+ unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
1954
2610
  } catch {
1955
2611
  }
1956
2612
  }
@@ -1961,14 +2617,14 @@ var init_active_agent = __esm({
1961
2617
  init_config();
1962
2618
  init_session_key();
1963
2619
  init_employees();
1964
- CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
2620
+ CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
1965
2621
  STALE_MS = 24 * 60 * 60 * 1e3;
1966
2622
  }
1967
2623
  });
1968
2624
 
1969
2625
  // src/lib/agent-symlinks.ts
1970
- import os5 from "os";
1971
- import path7 from "path";
2626
+ import os6 from "os";
2627
+ import path8 from "path";
1972
2628
  import {
1973
2629
  existsSync as existsSync6,
1974
2630
  lstatSync,
@@ -1999,8 +2655,8 @@ var init_mcp_prefix = __esm({
1999
2655
 
2000
2656
  // src/lib/preferences.ts
2001
2657
  import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
2002
- import path8 from "path";
2003
- import os6 from "os";
2658
+ import path9 from "path";
2659
+ import os7 from "os";
2004
2660
  var init_preferences = __esm({
2005
2661
  "src/lib/preferences.ts"() {
2006
2662
  "use strict";
@@ -2010,16 +2666,16 @@ var init_preferences = __esm({
2010
2666
  // src/adapters/claude/installer.ts
2011
2667
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
2012
2668
  import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2013
- import path9 from "path";
2014
- import os7 from "os";
2669
+ import path10 from "path";
2670
+ import os8 from "os";
2015
2671
  import { execSync as execSync4 } from "child_process";
2016
2672
  import { fileURLToPath } from "url";
2017
2673
  function resolvePackageRoot() {
2018
2674
  const thisFile = fileURLToPath(import.meta.url);
2019
- let dir = path9.dirname(thisFile);
2020
- const root = path9.parse(dir).root;
2675
+ let dir = path10.dirname(thisFile);
2676
+ const root = path10.parse(dir).root;
2021
2677
  while (dir !== root) {
2022
- const pkgPath = path9.join(dir, "package.json");
2678
+ const pkgPath = path10.join(dir, "package.json");
2023
2679
  if (existsSync8(pkgPath)) {
2024
2680
  try {
2025
2681
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
@@ -2027,9 +2683,9 @@ function resolvePackageRoot() {
2027
2683
  } catch {
2028
2684
  }
2029
2685
  }
2030
- dir = path9.dirname(dir);
2686
+ dir = path10.dirname(dir);
2031
2687
  }
2032
- return path9.resolve(path9.dirname(thisFile), "..", "..", "..");
2688
+ return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
2033
2689
  }
2034
2690
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
2035
2691
  var init_installer = __esm({
@@ -2248,11 +2904,11 @@ __export(installer_exports, {
2248
2904
  });
2249
2905
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2250
2906
  import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
2251
- import path10 from "path";
2252
- import os8 from "os";
2253
- async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2254
- const configDir = path10.join(homeDir, ".config", "opencode");
2255
- const configPath = path10.join(configDir, "opencode.json");
2907
+ import path11 from "path";
2908
+ import os9 from "os";
2909
+ async function registerOpenCodeMcp(packageRoot, homeDir = os9.homedir()) {
2910
+ const configDir = path11.join(homeDir, ".config", "opencode");
2911
+ const configPath = path11.join(configDir, "opencode.json");
2256
2912
  await mkdir5(configDir, { recursive: true });
2257
2913
  let config = {};
2258
2914
  if (existsSync9(configPath)) {
@@ -2267,7 +2923,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2267
2923
  }
2268
2924
  const newEntry = {
2269
2925
  type: "local",
2270
- command: ["node", path10.join(packageRoot, "dist", "mcp", "server.js")],
2926
+ command: ["node", path11.join(packageRoot, "dist", "mcp", "server.js")],
2271
2927
  enabled: true
2272
2928
  };
2273
2929
  const current = config.mcp["exe-os"];
@@ -2281,9 +2937,9 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2281
2937
  await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n");
2282
2938
  return true;
2283
2939
  }
2284
- async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
2285
- const pluginDir = path10.join(homeDir, ".config", "opencode", "plugins");
2286
- const pluginPath = path10.join(pluginDir, "exe-os.mjs");
2940
+ async function installOpenCodePlugin(packageRoot, homeDir = os9.homedir()) {
2941
+ const pluginDir = path11.join(homeDir, ".config", "opencode", "plugins");
2942
+ const pluginPath = path11.join(pluginDir, "exe-os.mjs");
2287
2943
  await mkdir5(pluginDir, { recursive: true });
2288
2944
  const pluginContent = PLUGIN_TEMPLATE.replace(
2289
2945
  /__PACKAGE_ROOT__/g,
@@ -2298,9 +2954,9 @@ async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
2298
2954
  await writeFile5(pluginPath, pluginContent);
2299
2955
  return true;
2300
2956
  }
2301
- function verifyOpenCodeHooks(homeDir = os8.homedir()) {
2302
- const configPath = path10.join(homeDir, ".config", "opencode", "opencode.json");
2303
- const pluginPath = path10.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
2957
+ function verifyOpenCodeHooks(homeDir = os9.homedir()) {
2958
+ const configPath = path11.join(homeDir, ".config", "opencode", "opencode.json");
2959
+ const pluginPath = path11.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
2304
2960
  if (!existsSync9(configPath)) return false;
2305
2961
  try {
2306
2962
  const config = JSON.parse(readFileSync6(configPath, "utf-8"));
@@ -2333,8 +2989,8 @@ var init_installer2 = __esm({
2333
2989
  });
2334
2990
 
2335
2991
  // src/bin/exe-start-opencode.ts
2336
- import os9 from "os";
2337
- import path11 from "path";
2992
+ import os10 from "os";
2993
+ import path12 from "path";
2338
2994
  import {
2339
2995
  existsSync as existsSync10,
2340
2996
  readFileSync as readFileSync7,
@@ -2351,15 +3007,15 @@ init_database();
2351
3007
  // src/lib/keychain.ts
2352
3008
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2353
3009
  import { existsSync as existsSync3 } from "fs";
2354
- import path3 from "path";
2355
- import os3 from "os";
3010
+ import path4 from "path";
3011
+ import os4 from "os";
2356
3012
  var SERVICE = "exe-mem";
2357
3013
  var ACCOUNT = "master-key";
2358
3014
  function getKeyDir() {
2359
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
3015
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
2360
3016
  }
2361
3017
  function getKeyPath() {
2362
- return path3.join(getKeyDir(), "master.key");
3018
+ return path4.join(getKeyDir(), "master.key");
2363
3019
  }
2364
3020
  async function tryKeytar() {
2365
3021
  try {
@@ -2382,7 +3038,7 @@ async function getMasterKey() {
2382
3038
  const keyPath = getKeyPath();
2383
3039
  if (!existsSync3(keyPath)) {
2384
3040
  process.stderr.write(
2385
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3041
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2386
3042
  `
2387
3043
  );
2388
3044
  return null;
@@ -2693,8 +3349,8 @@ function vectorToBlob(vector) {
2693
3349
  }
2694
3350
 
2695
3351
  // src/lib/behaviors-export.ts
2696
- import os4 from "os";
2697
- import path5 from "path";
3352
+ import os5 from "os";
3353
+ import path6 from "path";
2698
3354
  import {
2699
3355
  existsSync as existsSync5,
2700
3356
  mkdirSync as mkdirSync2,
@@ -2736,8 +3392,8 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2736
3392
  }
2737
3393
 
2738
3394
  // src/lib/behaviors-export.ts
2739
- var BEHAVIORS_EXPORT_DIR = path5.join(
2740
- os4.homedir(),
3395
+ var BEHAVIORS_EXPORT_DIR = path6.join(
3396
+ os5.homedir(),
2741
3397
  ".exe-os",
2742
3398
  "behaviors-export"
2743
3399
  );
@@ -2752,7 +3408,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
2752
3408
  return;
2753
3409
  }
2754
3410
  for (const entry of entries) {
2755
- const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
3411
+ const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
2756
3412
  try {
2757
3413
  const stat = statSync(filePath);
2758
3414
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -2785,10 +3441,10 @@ function renderBehaviorExport(behaviors) {
2785
3441
  }
2786
3442
  function exportFilePath(agentId, projectName, sessionKey) {
2787
3443
  if (!sessionKey) {
2788
- return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
3444
+ return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
2789
3445
  }
2790
3446
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2791
- return path5.join(
3447
+ return path6.join(
2792
3448
  BEHAVIORS_EXPORT_DIR,
2793
3449
  `${agentId}-${safeProject}-${sessionKey}.md`
2794
3450
  );
@@ -2806,28 +3462,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2806
3462
 
2807
3463
  // src/bin/exe-start-opencode.ts
2808
3464
  init_employees();
2809
-
2810
- // src/lib/runtime-table.ts
2811
- var RUNTIME_TABLE = {
2812
- codex: {
2813
- binary: "codex",
2814
- launchMode: "interactive",
2815
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2816
- inlineFlag: "--no-alt-screen",
2817
- apiKeyEnv: "OPENAI_API_KEY",
2818
- defaultModel: "gpt-5.4"
2819
- },
2820
- opencode: {
2821
- binary: "opencode",
2822
- launchMode: "exec",
2823
- autoApproveFlag: "--dangerously-skip-permissions",
2824
- inlineFlag: "",
2825
- apiKeyEnv: "ANTHROPIC_API_KEY",
2826
- defaultModel: "anthropic/claude-sonnet-4-6"
2827
- }
2828
- };
2829
-
2830
- // src/bin/exe-start-opencode.ts
3465
+ init_runtime_table();
2831
3466
  var OC = RUNTIME_TABLE.opencode;
2832
3467
  var BOOT_INSTRUCTIONS = `
2833
3468
  ---
@@ -2838,7 +3473,7 @@ When done with a task: call update_task with status "done".
2838
3473
  Always call store_memory to persist important findings.
2839
3474
  `;
2840
3475
  function resolveAgent(argv) {
2841
- const invokedAs = path11.basename(argv[1] ?? "");
3476
+ const invokedAs = path12.basename(argv[1] ?? "");
2842
3477
  if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
2843
3478
  const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
2844
3479
  return { agent: agent2, passthrough: argv.slice(2) };
@@ -2855,8 +3490,8 @@ function resolveAgent(argv) {
2855
3490
  return { agent, passthrough };
2856
3491
  }
2857
3492
  function loadIdentity(agent) {
2858
- const dir = path11.join(os9.homedir(), ".exe-os", "identity");
2859
- const exact = path11.join(dir, `${agent}.md`);
3493
+ const dir = path12.join(os10.homedir(), ".exe-os", "identity");
3494
+ const exact = path12.join(dir, `${agent}.md`);
2860
3495
  if (existsSync10(exact)) {
2861
3496
  const content = readFileSync7(exact, "utf-8").trim();
2862
3497
  if (content) return content;
@@ -2865,13 +3500,13 @@ function loadIdentity(agent) {
2865
3500
  const files = readdirSync4(dir);
2866
3501
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
2867
3502
  if (match) {
2868
- const content = readFileSync7(path11.join(dir, match), "utf-8").trim();
3503
+ const content = readFileSync7(path12.join(dir, match), "utf-8").trim();
2869
3504
  if (content) return content;
2870
3505
  }
2871
3506
  } catch {
2872
3507
  }
2873
3508
  try {
2874
- const rosterPath = path11.join(os9.homedir(), ".exe-os", "exe-employees.json");
3509
+ const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
2875
3510
  const roster = JSON.parse(readFileSync7(rosterPath, "utf8"));
2876
3511
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
2877
3512
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
@@ -2882,7 +3517,7 @@ function loadIdentity(agent) {
2882
3517
  return null;
2883
3518
  }
2884
3519
  function writeAgentFile(agent, identity, behaviorsPath) {
2885
- const agentDir = path11.join(os9.homedir(), ".config", "opencode", "agents");
3520
+ const agentDir = path12.join(os10.homedir(), ".config", "opencode", "agents");
2886
3521
  mkdirSync7(agentDir, { recursive: true });
2887
3522
  let content = identity;
2888
3523
  if (behaviorsPath && existsSync10(behaviorsPath)) {
@@ -2892,7 +3527,7 @@ function writeAgentFile(agent, identity, behaviorsPath) {
2892
3527
  }
2893
3528
  }
2894
3529
  content += "\n" + BOOT_INSTRUCTIONS;
2895
- const outPath = path11.join(agentDir, `${agent}.md`);
3530
+ const outPath = path12.join(agentDir, `${agent}.md`);
2896
3531
  writeFileSync6(outPath, content, "utf-8");
2897
3532
  return outPath;
2898
3533
  }
@@ -2955,7 +3590,7 @@ async function main() {
2955
3590
  const empRole = (() => {
2956
3591
  try {
2957
3592
  const emps = readFileSync7(
2958
- path11.join(os9.homedir(), ".exe-os", "exe-employees.json"),
3593
+ path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
2959
3594
  "utf-8"
2960
3595
  );
2961
3596
  const found = JSON.parse(emps).find(