@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
@@ -355,7 +355,7 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
355
355
  return [];
356
356
  }
357
357
  }
358
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
358
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
359
359
  var init_employees = __esm({
360
360
  "src/lib/employees.ts"() {
361
361
  "use strict";
@@ -363,12 +363,609 @@ var init_employees = __esm({
363
363
  EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
364
364
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
365
365
  COORDINATOR_ROLE = "COO";
366
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
367
+ }
368
+ });
369
+
370
+ // src/lib/database-adapter.ts
371
+ import os3 from "os";
372
+ import path4 from "path";
373
+ import { createRequire } from "module";
374
+ import { pathToFileURL } from "url";
375
+ function quotedIdentifier(identifier) {
376
+ return `"${identifier.replace(/"/g, '""')}"`;
377
+ }
378
+ function unqualifiedTableName(name) {
379
+ const raw = name.trim().replace(/^"|"$/g, "");
380
+ const parts = raw.split(".");
381
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
382
+ }
383
+ function stripTrailingSemicolon(sql) {
384
+ return sql.trim().replace(/;+\s*$/u, "");
385
+ }
386
+ function appendClause(sql, clause) {
387
+ const trimmed = stripTrailingSemicolon(sql);
388
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
389
+ if (!returningMatch) {
390
+ return `${trimmed}${clause}`;
391
+ }
392
+ const idx = returningMatch.index;
393
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
394
+ }
395
+ function normalizeStatement(stmt) {
396
+ if (typeof stmt === "string") {
397
+ return { kind: "positional", sql: stmt, args: [] };
398
+ }
399
+ const sql = stmt.sql;
400
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
401
+ return { kind: "positional", sql, args: stmt.args ?? [] };
402
+ }
403
+ return { kind: "named", sql, args: stmt.args };
404
+ }
405
+ function rewriteBooleanLiterals(sql) {
406
+ let out = sql;
407
+ for (const column of BOOLEAN_COLUMN_NAMES) {
408
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
409
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
410
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
411
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
412
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
413
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
414
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
415
+ }
416
+ return out;
417
+ }
418
+ function rewriteInsertOrIgnore(sql) {
419
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
420
+ return sql;
421
+ }
422
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
423
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
424
+ }
425
+ function rewriteInsertOrReplace(sql) {
426
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
427
+ if (!match) {
428
+ return sql;
429
+ }
430
+ const rawTable = match[1];
431
+ const rawColumns = match[2];
432
+ const remainder = match[3];
433
+ const tableName = unqualifiedTableName(rawTable);
434
+ const conflictKeys = UPSERT_KEYS[tableName];
435
+ if (!conflictKeys?.length) {
436
+ return sql;
437
+ }
438
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
439
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
440
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
441
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
442
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
443
+ }
444
+ function rewriteSql(sql) {
445
+ let out = sql;
446
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
447
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
448
+ out = rewriteBooleanLiterals(out);
449
+ out = rewriteInsertOrReplace(out);
450
+ out = rewriteInsertOrIgnore(out);
451
+ return stripTrailingSemicolon(out);
452
+ }
453
+ function toBoolean(value) {
454
+ if (value === null || value === void 0) return value;
455
+ if (typeof value === "boolean") return value;
456
+ if (typeof value === "number") return value !== 0;
457
+ if (typeof value === "bigint") return value !== 0n;
458
+ if (typeof value === "string") {
459
+ const normalized = value.trim().toLowerCase();
460
+ if (normalized === "0" || normalized === "false") return false;
461
+ if (normalized === "1" || normalized === "true") return true;
462
+ }
463
+ return Boolean(value);
464
+ }
465
+ function countQuestionMarks(sql, end) {
466
+ let count = 0;
467
+ let inSingle = false;
468
+ let inDouble = false;
469
+ let inLineComment = false;
470
+ let inBlockComment = false;
471
+ for (let i = 0; i < end; i++) {
472
+ const ch = sql[i];
473
+ const next = sql[i + 1];
474
+ if (inLineComment) {
475
+ if (ch === "\n") inLineComment = false;
476
+ continue;
477
+ }
478
+ if (inBlockComment) {
479
+ if (ch === "*" && next === "/") {
480
+ inBlockComment = false;
481
+ i += 1;
482
+ }
483
+ continue;
484
+ }
485
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
486
+ inLineComment = true;
487
+ i += 1;
488
+ continue;
489
+ }
490
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
491
+ inBlockComment = true;
492
+ i += 1;
493
+ continue;
494
+ }
495
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
496
+ inSingle = !inSingle;
497
+ continue;
498
+ }
499
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
500
+ inDouble = !inDouble;
501
+ continue;
502
+ }
503
+ if (!inSingle && !inDouble && ch === "?") {
504
+ count += 1;
505
+ }
506
+ }
507
+ return count;
508
+ }
509
+ function findBooleanPlaceholderIndexes(sql) {
510
+ const indexes = /* @__PURE__ */ new Set();
511
+ for (const column of BOOLEAN_COLUMN_NAMES) {
512
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
513
+ for (const match of sql.matchAll(pattern)) {
514
+ const matchText = match[0];
515
+ const qIndex = match.index + matchText.lastIndexOf("?");
516
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
517
+ }
518
+ }
519
+ return indexes;
520
+ }
521
+ function coerceInsertBooleanArgs(sql, args) {
522
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
523
+ if (!match) return;
524
+ const rawTable = match[1];
525
+ const rawColumns = match[2];
526
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
527
+ if (!boolColumns?.size) return;
528
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
529
+ for (const [index, column] of columns.entries()) {
530
+ if (boolColumns.has(column) && index < args.length) {
531
+ args[index] = toBoolean(args[index]);
532
+ }
533
+ }
534
+ }
535
+ function coerceUpdateBooleanArgs(sql, args) {
536
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
537
+ if (!match) return;
538
+ const rawTable = match[1];
539
+ const setClause = match[2];
540
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
541
+ if (!boolColumns?.size) return;
542
+ const assignments = setClause.split(",");
543
+ let placeholderIndex = 0;
544
+ for (const assignment of assignments) {
545
+ if (!assignment.includes("?")) continue;
546
+ placeholderIndex += 1;
547
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
548
+ if (colMatch && boolColumns.has(colMatch[1])) {
549
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
550
+ }
551
+ }
552
+ }
553
+ function coerceBooleanArgs(sql, args) {
554
+ const nextArgs = [...args];
555
+ coerceInsertBooleanArgs(sql, nextArgs);
556
+ coerceUpdateBooleanArgs(sql, nextArgs);
557
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
558
+ for (const index of placeholderIndexes) {
559
+ if (index > 0 && index <= nextArgs.length) {
560
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
561
+ }
562
+ }
563
+ return nextArgs;
564
+ }
565
+ function convertQuestionMarksToDollarParams(sql) {
566
+ let out = "";
567
+ let placeholder = 0;
568
+ let inSingle = false;
569
+ let inDouble = false;
570
+ let inLineComment = false;
571
+ let inBlockComment = false;
572
+ for (let i = 0; i < sql.length; i++) {
573
+ const ch = sql[i];
574
+ const next = sql[i + 1];
575
+ if (inLineComment) {
576
+ out += ch;
577
+ if (ch === "\n") inLineComment = false;
578
+ continue;
579
+ }
580
+ if (inBlockComment) {
581
+ out += ch;
582
+ if (ch === "*" && next === "/") {
583
+ out += next;
584
+ inBlockComment = false;
585
+ i += 1;
586
+ }
587
+ continue;
588
+ }
589
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
590
+ out += ch + next;
591
+ inLineComment = true;
592
+ i += 1;
593
+ continue;
594
+ }
595
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
596
+ out += ch + next;
597
+ inBlockComment = true;
598
+ i += 1;
599
+ continue;
600
+ }
601
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
602
+ inSingle = !inSingle;
603
+ out += ch;
604
+ continue;
605
+ }
606
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
607
+ inDouble = !inDouble;
608
+ out += ch;
609
+ continue;
610
+ }
611
+ if (!inSingle && !inDouble && ch === "?") {
612
+ placeholder += 1;
613
+ out += `$${placeholder}`;
614
+ continue;
615
+ }
616
+ out += ch;
617
+ }
618
+ return out;
619
+ }
620
+ function translateStatementForPostgres(stmt) {
621
+ const normalized = normalizeStatement(stmt);
622
+ if (normalized.kind === "named") {
623
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
624
+ }
625
+ const rewrittenSql = rewriteSql(normalized.sql);
626
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
627
+ return {
628
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
629
+ args: coercedArgs
630
+ };
631
+ }
632
+ function shouldBypassPostgres(stmt) {
633
+ const normalized = normalizeStatement(stmt);
634
+ if (normalized.kind === "named") {
635
+ return true;
636
+ }
637
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
638
+ }
639
+ function shouldFallbackOnError(error) {
640
+ const message = error instanceof Error ? error.message : String(error);
641
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
642
+ }
643
+ function isReadQuery(sql) {
644
+ const trimmed = sql.trimStart();
645
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
646
+ }
647
+ function buildRow(row, columns) {
648
+ const values = columns.map((column) => row[column]);
649
+ return Object.assign(values, row);
650
+ }
651
+ function buildResultSet(rows, rowsAffected = 0) {
652
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
653
+ const resultRows = rows.map((row) => buildRow(row, columns));
654
+ return {
655
+ columns,
656
+ columnTypes: columns.map(() => ""),
657
+ rows: resultRows,
658
+ rowsAffected,
659
+ lastInsertRowid: void 0,
660
+ toJSON() {
661
+ return {
662
+ columns,
663
+ columnTypes: columns.map(() => ""),
664
+ rows,
665
+ rowsAffected,
666
+ lastInsertRowid: void 0
667
+ };
668
+ }
669
+ };
670
+ }
671
+ async function loadPrismaClient() {
672
+ if (!prismaClientPromise) {
673
+ prismaClientPromise = (async () => {
674
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
675
+ if (explicitPath) {
676
+ const module2 = await import(pathToFileURL(explicitPath).href);
677
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
678
+ if (!PrismaClient2) {
679
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
680
+ }
681
+ return new PrismaClient2();
682
+ }
683
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
684
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
685
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
686
+ const module = await import(pathToFileURL(prismaEntry).href);
687
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
688
+ if (!PrismaClient) {
689
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
690
+ }
691
+ return new PrismaClient();
692
+ })();
693
+ }
694
+ return prismaClientPromise;
695
+ }
696
+ async function ensureCompatibilityViews(prisma) {
697
+ if (!compatibilityBootstrapPromise) {
698
+ compatibilityBootstrapPromise = (async () => {
699
+ for (const mapping of VIEW_MAPPINGS) {
700
+ const relation = mapping.source.replace(/"/g, "");
701
+ const rows = await prisma.$queryRawUnsafe(
702
+ "SELECT to_regclass($1) AS regclass",
703
+ relation
704
+ );
705
+ if (!rows[0]?.regclass) {
706
+ continue;
707
+ }
708
+ await prisma.$executeRawUnsafe(
709
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
710
+ );
711
+ }
712
+ })();
713
+ }
714
+ return compatibilityBootstrapPromise;
715
+ }
716
+ async function executeOnPrisma(executor, stmt) {
717
+ const translated = translateStatementForPostgres(stmt);
718
+ if (isReadQuery(translated.sql)) {
719
+ const rows = await executor.$queryRawUnsafe(
720
+ translated.sql,
721
+ ...translated.args
722
+ );
723
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
724
+ }
725
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
726
+ return buildResultSet([], rowsAffected);
727
+ }
728
+ function splitSqlStatements(sql) {
729
+ const parts = [];
730
+ let current = "";
731
+ let inSingle = false;
732
+ let inDouble = false;
733
+ let inLineComment = false;
734
+ let inBlockComment = false;
735
+ for (let i = 0; i < sql.length; i++) {
736
+ const ch = sql[i];
737
+ const next = sql[i + 1];
738
+ if (inLineComment) {
739
+ current += ch;
740
+ if (ch === "\n") inLineComment = false;
741
+ continue;
742
+ }
743
+ if (inBlockComment) {
744
+ current += ch;
745
+ if (ch === "*" && next === "/") {
746
+ current += next;
747
+ inBlockComment = false;
748
+ i += 1;
749
+ }
750
+ continue;
751
+ }
752
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
753
+ current += ch + next;
754
+ inLineComment = true;
755
+ i += 1;
756
+ continue;
757
+ }
758
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
759
+ current += ch + next;
760
+ inBlockComment = true;
761
+ i += 1;
762
+ continue;
763
+ }
764
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
765
+ inSingle = !inSingle;
766
+ current += ch;
767
+ continue;
768
+ }
769
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
770
+ inDouble = !inDouble;
771
+ current += ch;
772
+ continue;
773
+ }
774
+ if (!inSingle && !inDouble && ch === ";") {
775
+ if (current.trim()) {
776
+ parts.push(current.trim());
777
+ }
778
+ current = "";
779
+ continue;
780
+ }
781
+ current += ch;
782
+ }
783
+ if (current.trim()) {
784
+ parts.push(current.trim());
785
+ }
786
+ return parts;
787
+ }
788
+ async function createPrismaDbAdapter(fallbackClient) {
789
+ const prisma = await loadPrismaClient();
790
+ await ensureCompatibilityViews(prisma);
791
+ let closed = false;
792
+ let adapter;
793
+ const fallbackExecute = async (stmt, error) => {
794
+ if (!fallbackClient) {
795
+ if (error) throw error;
796
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
797
+ }
798
+ if (error) {
799
+ process.stderr.write(
800
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
801
+ `
802
+ );
803
+ }
804
+ return fallbackClient.execute(stmt);
805
+ };
806
+ adapter = {
807
+ async execute(stmt) {
808
+ if (shouldBypassPostgres(stmt)) {
809
+ return fallbackExecute(stmt);
810
+ }
811
+ try {
812
+ return await executeOnPrisma(prisma, stmt);
813
+ } catch (error) {
814
+ if (shouldFallbackOnError(error)) {
815
+ return fallbackExecute(stmt, error);
816
+ }
817
+ throw error;
818
+ }
819
+ },
820
+ async batch(stmts, mode) {
821
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
822
+ if (!fallbackClient) {
823
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
824
+ }
825
+ return fallbackClient.batch(stmts, mode);
826
+ }
827
+ try {
828
+ if (prisma.$transaction) {
829
+ return await prisma.$transaction(async (tx) => {
830
+ const results2 = [];
831
+ for (const stmt of stmts) {
832
+ results2.push(await executeOnPrisma(tx, stmt));
833
+ }
834
+ return results2;
835
+ });
836
+ }
837
+ const results = [];
838
+ for (const stmt of stmts) {
839
+ results.push(await executeOnPrisma(prisma, stmt));
840
+ }
841
+ return results;
842
+ } catch (error) {
843
+ if (fallbackClient && shouldFallbackOnError(error)) {
844
+ process.stderr.write(
845
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
846
+ `
847
+ );
848
+ return fallbackClient.batch(stmts, mode);
849
+ }
850
+ throw error;
851
+ }
852
+ },
853
+ async migrate(stmts) {
854
+ if (fallbackClient) {
855
+ return fallbackClient.migrate(stmts);
856
+ }
857
+ return adapter.batch(stmts, "deferred");
858
+ },
859
+ async transaction(mode) {
860
+ if (!fallbackClient) {
861
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
862
+ }
863
+ return fallbackClient.transaction(mode);
864
+ },
865
+ async executeMultiple(sql) {
866
+ if (fallbackClient && shouldBypassPostgres(sql)) {
867
+ return fallbackClient.executeMultiple(sql);
868
+ }
869
+ for (const statement of splitSqlStatements(sql)) {
870
+ await adapter.execute(statement);
871
+ }
872
+ },
873
+ async sync() {
874
+ if (fallbackClient) {
875
+ return fallbackClient.sync();
876
+ }
877
+ return { frame_no: 0, frames_synced: 0 };
878
+ },
879
+ close() {
880
+ closed = true;
881
+ prismaClientPromise = null;
882
+ compatibilityBootstrapPromise = null;
883
+ void prisma.$disconnect?.();
884
+ },
885
+ get closed() {
886
+ return closed;
887
+ },
888
+ get protocol() {
889
+ return "prisma-postgres";
890
+ }
891
+ };
892
+ return adapter;
893
+ }
894
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
895
+ var init_database_adapter = __esm({
896
+ "src/lib/database-adapter.ts"() {
897
+ "use strict";
898
+ VIEW_MAPPINGS = [
899
+ { view: "memories", source: "memory.memory_records" },
900
+ { view: "tasks", source: "memory.tasks" },
901
+ { view: "behaviors", source: "memory.behaviors" },
902
+ { view: "entities", source: "memory.entities" },
903
+ { view: "relationships", source: "memory.relationships" },
904
+ { view: "entity_memories", source: "memory.entity_memories" },
905
+ { view: "entity_aliases", source: "memory.entity_aliases" },
906
+ { view: "notifications", source: "memory.notifications" },
907
+ { view: "messages", source: "memory.messages" },
908
+ { view: "users", source: "wiki.users" },
909
+ { view: "workspaces", source: "wiki.workspaces" },
910
+ { view: "workspace_users", source: "wiki.workspace_users" },
911
+ { view: "documents", source: "wiki.workspace_documents" },
912
+ { view: "chats", source: "wiki.workspace_chats" }
913
+ ];
914
+ UPSERT_KEYS = {
915
+ memories: ["id"],
916
+ tasks: ["id"],
917
+ behaviors: ["id"],
918
+ entities: ["id"],
919
+ relationships: ["id"],
920
+ entity_aliases: ["alias"],
921
+ notifications: ["id"],
922
+ messages: ["id"],
923
+ users: ["id"],
924
+ workspaces: ["id"],
925
+ workspace_users: ["id"],
926
+ documents: ["id"],
927
+ chats: ["id"]
928
+ };
929
+ BOOLEAN_COLUMNS_BY_TABLE = {
930
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
931
+ behaviors: /* @__PURE__ */ new Set(["active"]),
932
+ notifications: /* @__PURE__ */ new Set(["read"]),
933
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
934
+ };
935
+ BOOLEAN_COLUMN_NAMES = new Set(
936
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
937
+ );
938
+ IMMEDIATE_FALLBACK_PATTERNS = [
939
+ /\bPRAGMA\b/i,
940
+ /\bsqlite_master\b/i,
941
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
942
+ /\bMATCH\b/i,
943
+ /\bvector_distance_cos\s*\(/i,
944
+ /\bjson_extract\s*\(/i,
945
+ /\bjulianday\s*\(/i,
946
+ /\bstrftime\s*\(/i,
947
+ /\blast_insert_rowid\s*\(/i
948
+ ];
949
+ prismaClientPromise = null;
950
+ compatibilityBootstrapPromise = null;
366
951
  }
367
952
  });
368
953
 
369
954
  // src/lib/database.ts
370
955
  import { createClient } from "@libsql/client";
371
956
  async function initDatabase(config) {
957
+ if (_walCheckpointTimer) {
958
+ clearInterval(_walCheckpointTimer);
959
+ _walCheckpointTimer = null;
960
+ }
961
+ if (_daemonClient) {
962
+ _daemonClient.close();
963
+ _daemonClient = null;
964
+ }
965
+ if (_adapterClient && _adapterClient !== _resilientClient) {
966
+ _adapterClient.close();
967
+ }
968
+ _adapterClient = null;
372
969
  if (_client) {
373
970
  _client.close();
374
971
  _client = null;
@@ -382,6 +979,7 @@ async function initDatabase(config) {
382
979
  }
383
980
  _client = createClient(opts);
384
981
  _resilientClient = wrapWithRetry(_client);
982
+ _adapterClient = _resilientClient;
385
983
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
386
984
  });
387
985
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -392,14 +990,20 @@ async function initDatabase(config) {
392
990
  });
393
991
  }, 3e4);
394
992
  _walCheckpointTimer.unref();
993
+ if (process.env.DATABASE_URL) {
994
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
995
+ }
395
996
  }
396
997
  function isInitialized() {
397
- return _client !== null;
998
+ return _adapterClient !== null || _client !== null;
398
999
  }
399
1000
  function getClient() {
400
- if (!_resilientClient) {
1001
+ if (!_adapterClient) {
401
1002
  throw new Error("Database client not initialized. Call initDatabase() first.");
402
1003
  }
1004
+ if (process.env.DATABASE_URL) {
1005
+ return _adapterClient;
1006
+ }
403
1007
  if (process.env.EXE_IS_DAEMON === "1") {
404
1008
  return _resilientClient;
405
1009
  }
@@ -1339,16 +1943,18 @@ async function ensureSchema() {
1339
1943
  }
1340
1944
  }
1341
1945
  }
1342
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso;
1946
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1343
1947
  var init_database = __esm({
1344
1948
  "src/lib/database.ts"() {
1345
1949
  "use strict";
1346
1950
  init_db_retry();
1347
1951
  init_employees();
1952
+ init_database_adapter();
1348
1953
  _client = null;
1349
1954
  _resilientClient = null;
1350
1955
  _walCheckpointTimer = null;
1351
1956
  _daemonClient = null;
1957
+ _adapterClient = null;
1352
1958
  initTurso = initDatabase;
1353
1959
  }
1354
1960
  });
@@ -1366,7 +1972,7 @@ __export(shard_manager_exports, {
1366
1972
  listShards: () => listShards,
1367
1973
  shardExists: () => shardExists
1368
1974
  });
1369
- import path5 from "path";
1975
+ import path6 from "path";
1370
1976
  import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1371
1977
  import { createClient as createClient2 } from "@libsql/client";
1372
1978
  function initShardManager(encryptionKey) {
@@ -1392,7 +1998,7 @@ function getShardClient(projectName) {
1392
1998
  }
1393
1999
  const cached = _shards.get(safeName);
1394
2000
  if (cached) return cached;
1395
- const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
2001
+ const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
1396
2002
  const client = createClient2({
1397
2003
  url: `file:${dbPath}`,
1398
2004
  encryptionKey: _encryptionKey
@@ -1402,7 +2008,7 @@ function getShardClient(projectName) {
1402
2008
  }
1403
2009
  function shardExists(projectName) {
1404
2010
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1405
- return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
2011
+ return existsSync4(path6.join(SHARDS_DIR, `${safeName}.db`));
1406
2012
  }
1407
2013
  function listShards() {
1408
2014
  if (!existsSync4(SHARDS_DIR)) return [];
@@ -1479,7 +2085,23 @@ async function ensureShardSchema(client) {
1479
2085
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1480
2086
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1481
2087
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1482
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2088
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2089
+ // Metadata enrichment columns (must match database.ts)
2090
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2091
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2092
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2093
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2094
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2095
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2096
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2097
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2098
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2099
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2100
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2101
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2102
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2103
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2104
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1483
2105
  ]) {
1484
2106
  try {
1485
2107
  await client.execute(col);
@@ -1591,7 +2213,7 @@ var init_shard_manager = __esm({
1591
2213
  "src/lib/shard-manager.ts"() {
1592
2214
  "use strict";
1593
2215
  init_config();
1594
- SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2216
+ SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
1595
2217
  _shards = /* @__PURE__ */ new Map();
1596
2218
  _encryptionKey = null;
1597
2219
  _shardingEnabled = false;
@@ -1787,11 +2409,11 @@ ${p.content}`).join("\n\n");
1787
2409
 
1788
2410
  // src/lib/exe-daemon-client.ts
1789
2411
  import net from "net";
1790
- import os4 from "os";
2412
+ import os5 from "os";
1791
2413
  import { spawn } from "child_process";
1792
2414
  import { randomUUID as randomUUID2 } from "crypto";
1793
2415
  import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
1794
- import path6 from "path";
2416
+ import path7 from "path";
1795
2417
  import { fileURLToPath } from "url";
1796
2418
  function handleData(chunk) {
1797
2419
  _buffer += chunk.toString();
@@ -1842,17 +2464,17 @@ function cleanupStaleFiles() {
1842
2464
  }
1843
2465
  }
1844
2466
  function findPackageRoot() {
1845
- let dir = path6.dirname(fileURLToPath(import.meta.url));
1846
- const { root } = path6.parse(dir);
2467
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
2468
+ const { root } = path7.parse(dir);
1847
2469
  while (dir !== root) {
1848
- if (existsSync5(path6.join(dir, "package.json"))) return dir;
1849
- dir = path6.dirname(dir);
2470
+ if (existsSync5(path7.join(dir, "package.json"))) return dir;
2471
+ dir = path7.dirname(dir);
1850
2472
  }
1851
2473
  return null;
1852
2474
  }
1853
2475
  function spawnDaemon() {
1854
- const freeGB = os4.freemem() / (1024 * 1024 * 1024);
1855
- const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
2476
+ const freeGB = os5.freemem() / (1024 * 1024 * 1024);
2477
+ const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1856
2478
  if (totalGB <= 8) {
1857
2479
  process.stderr.write(
1858
2480
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -1872,7 +2494,7 @@ function spawnDaemon() {
1872
2494
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1873
2495
  return;
1874
2496
  }
1875
- const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2497
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1876
2498
  if (!existsSync5(daemonPath)) {
1877
2499
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1878
2500
  `);
@@ -1881,7 +2503,7 @@ function spawnDaemon() {
1881
2503
  const resolvedPath = daemonPath;
1882
2504
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1883
2505
  `);
1884
- const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
2506
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
1885
2507
  let stderrFd = "ignore";
1886
2508
  try {
1887
2509
  stderrFd = openSync(logPath, "a");
@@ -2032,74 +2654,123 @@ async function pingDaemon() {
2032
2654
  return null;
2033
2655
  }
2034
2656
  function killAndRespawnDaemon() {
2035
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
2036
- if (existsSync5(PID_PATH)) {
2037
- try {
2038
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2039
- if (pid > 0) {
2040
- try {
2041
- process.kill(pid, "SIGKILL");
2042
- } catch {
2657
+ if (!acquireSpawnLock()) {
2658
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
2659
+ if (_socket) {
2660
+ _socket.destroy();
2661
+ _socket = null;
2662
+ }
2663
+ _connected = false;
2664
+ _buffer = "";
2665
+ return;
2666
+ }
2667
+ try {
2668
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
2669
+ if (existsSync5(PID_PATH)) {
2670
+ try {
2671
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2672
+ if (pid > 0) {
2673
+ try {
2674
+ process.kill(pid, "SIGKILL");
2675
+ } catch {
2676
+ }
2043
2677
  }
2678
+ } catch {
2044
2679
  }
2680
+ }
2681
+ if (_socket) {
2682
+ _socket.destroy();
2683
+ _socket = null;
2684
+ }
2685
+ _connected = false;
2686
+ _buffer = "";
2687
+ try {
2688
+ unlinkSync2(PID_PATH);
2045
2689
  } catch {
2046
2690
  }
2691
+ try {
2692
+ unlinkSync2(SOCKET_PATH);
2693
+ } catch {
2694
+ }
2695
+ spawnDaemon();
2696
+ } finally {
2697
+ releaseSpawnLock();
2047
2698
  }
2048
- if (_socket) {
2049
- _socket.destroy();
2050
- _socket = null;
2051
- }
2052
- _connected = false;
2053
- _buffer = "";
2699
+ }
2700
+ function isDaemonTooYoung() {
2054
2701
  try {
2055
- unlinkSync2(PID_PATH);
2702
+ const stat = statSync(PID_PATH);
2703
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
2056
2704
  } catch {
2705
+ return false;
2057
2706
  }
2058
- try {
2059
- unlinkSync2(SOCKET_PATH);
2060
- } catch {
2707
+ }
2708
+ async function retryThenRestart(doRequest, label) {
2709
+ const result = await doRequest();
2710
+ if (!result.error) {
2711
+ _consecutiveFailures = 0;
2712
+ return result;
2713
+ }
2714
+ _consecutiveFailures++;
2715
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
2716
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
2717
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
2718
+ `);
2719
+ await new Promise((r) => setTimeout(r, delayMs));
2720
+ if (!_connected) {
2721
+ if (!await connectToSocket()) continue;
2722
+ }
2723
+ const retry = await doRequest();
2724
+ if (!retry.error) {
2725
+ _consecutiveFailures = 0;
2726
+ return retry;
2727
+ }
2728
+ _consecutiveFailures++;
2729
+ }
2730
+ if (isDaemonTooYoung()) {
2731
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
2732
+ `);
2733
+ return { error: result.error };
2734
+ }
2735
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
2736
+ `);
2737
+ killAndRespawnDaemon();
2738
+ const start = Date.now();
2739
+ let delay2 = 200;
2740
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2741
+ await new Promise((r) => setTimeout(r, delay2));
2742
+ if (await connectToSocket()) break;
2743
+ delay2 = Math.min(delay2 * 2, 3e3);
2061
2744
  }
2062
- spawnDaemon();
2745
+ if (!_connected) return { error: "Daemon restart failed" };
2746
+ const final = await doRequest();
2747
+ if (!final.error) _consecutiveFailures = 0;
2748
+ return final;
2063
2749
  }
2064
2750
  async function embedViaClient(text, priority = "high") {
2065
2751
  if (!_connected && !await connectEmbedDaemon()) return null;
2066
2752
  _requestCount++;
2067
2753
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
2068
2754
  const health = await pingDaemon();
2069
- if (!health) {
2755
+ if (!health && !isDaemonTooYoung()) {
2070
2756
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
2071
2757
  `);
2072
2758
  killAndRespawnDaemon();
2073
2759
  const start = Date.now();
2074
- let delay2 = 200;
2760
+ let d = 200;
2075
2761
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2076
- await new Promise((r) => setTimeout(r, delay2));
2762
+ await new Promise((r) => setTimeout(r, d));
2077
2763
  if (await connectToSocket()) break;
2078
- delay2 = Math.min(delay2 * 2, 3e3);
2764
+ d = Math.min(d * 2, 3e3);
2079
2765
  }
2080
2766
  if (!_connected) return null;
2081
2767
  }
2082
2768
  }
2083
- const result = await sendRequest([text], priority);
2084
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
2085
- if (result.error) {
2086
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
2087
- `);
2088
- killAndRespawnDaemon();
2089
- const start = Date.now();
2090
- let delay2 = 200;
2091
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2092
- await new Promise((r) => setTimeout(r, delay2));
2093
- if (await connectToSocket()) break;
2094
- delay2 = Math.min(delay2 * 2, 3e3);
2095
- }
2096
- if (!_connected) return null;
2097
- const retry = await sendRequest([text], priority);
2098
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
2099
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
2100
- `);
2101
- }
2102
- return null;
2769
+ const result = await retryThenRestart(
2770
+ () => sendRequest([text], priority),
2771
+ "Embed"
2772
+ );
2773
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
2103
2774
  }
2104
2775
  function disconnectClient() {
2105
2776
  if (_socket) {
@@ -2114,14 +2785,14 @@ function disconnectClient() {
2114
2785
  entry.resolve({ error: "Client disconnected" });
2115
2786
  }
2116
2787
  }
2117
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
2788
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2118
2789
  var init_exe_daemon_client = __esm({
2119
2790
  "src/lib/exe-daemon-client.ts"() {
2120
2791
  "use strict";
2121
2792
  init_config();
2122
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
2123
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
2124
- SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
2793
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
2794
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
2795
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
2125
2796
  SPAWN_LOCK_STALE_MS = 3e4;
2126
2797
  CONNECT_TIMEOUT_MS = 15e3;
2127
2798
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2129,7 +2800,11 @@ var init_exe_daemon_client = __esm({
2129
2800
  _connected = false;
2130
2801
  _buffer = "";
2131
2802
  _requestCount = 0;
2803
+ _consecutiveFailures = 0;
2132
2804
  HEALTH_CHECK_INTERVAL = 100;
2805
+ MAX_RETRIES_BEFORE_RESTART = 3;
2806
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
2807
+ MIN_DAEMON_AGE_MS = 3e4;
2133
2808
  _pending = /* @__PURE__ */ new Map();
2134
2809
  MAX_BUFFER = 1e7;
2135
2810
  }
@@ -2173,8 +2848,8 @@ async function embedDirect(text) {
2173
2848
  const llamaCpp = await import("node-llama-cpp");
2174
2849
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2175
2850
  const { existsSync: existsSync8 } = await import("fs");
2176
- const path10 = await import("path");
2177
- const modelPath = path10.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2851
+ const path11 = await import("path");
2852
+ const modelPath = path11.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2178
2853
  if (!existsSync8(modelPath)) {
2179
2854
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2180
2855
  }
@@ -2206,7 +2881,7 @@ var init_embedder = __esm({
2206
2881
  // src/lib/license.ts
2207
2882
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
2208
2883
  import { randomUUID as randomUUID3 } from "crypto";
2209
- import path7 from "path";
2884
+ import path8 from "path";
2210
2885
  import { jwtVerify, importSPKI } from "jose";
2211
2886
  async function fetchRetry(url, init) {
2212
2887
  try {
@@ -2217,7 +2892,7 @@ async function fetchRetry(url, init) {
2217
2892
  }
2218
2893
  }
2219
2894
  function loadDeviceId() {
2220
- const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
2895
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
2221
2896
  try {
2222
2897
  if (existsSync6(deviceJsonPath)) {
2223
2898
  const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
@@ -2382,7 +3057,7 @@ async function checkLicense() {
2382
3057
  let key = loadLicense();
2383
3058
  if (!key) {
2384
3059
  try {
2385
- const configPath = path7.join(EXE_AI_DIR, "config.json");
3060
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
2386
3061
  if (existsSync6(configPath)) {
2387
3062
  const raw = JSON.parse(readFileSync4(configPath, "utf8"));
2388
3063
  const cloud = raw.cloud;
@@ -2415,9 +3090,9 @@ var init_license = __esm({
2415
3090
  "src/lib/license.ts"() {
2416
3091
  "use strict";
2417
3092
  init_config();
2418
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2419
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2420
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
3093
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
3094
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
3095
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2421
3096
  API_BASE = "https://askexe.com/cloud";
2422
3097
  RETRY_DELAY_MS = 500;
2423
3098
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2457,7 +3132,7 @@ __export(plan_limits_exports, {
2457
3132
  getLicenseSync: () => getLicenseSync
2458
3133
  });
2459
3134
  import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
2460
- import path8 from "path";
3135
+ import path9 from "path";
2461
3136
  function getLicenseSync() {
2462
3137
  try {
2463
3138
  if (!existsSync7(CACHE_PATH2)) return freeLicense();
@@ -2566,14 +3241,14 @@ var init_plan_limits = __esm({
2566
3241
  this.name = "PlanLimitError";
2567
3242
  }
2568
3243
  };
2569
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3244
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2570
3245
  }
2571
3246
  });
2572
3247
 
2573
3248
  // src/adapters/claude/hooks/response-ingest-worker.ts
2574
3249
  import crypto from "crypto";
2575
3250
  import { writeFileSync as writeFileSync3 } from "fs";
2576
- import path9 from "path";
3251
+ import path10 from "path";
2577
3252
 
2578
3253
  // src/lib/project-name.ts
2579
3254
  import { execSync } from "child_process";
@@ -2619,15 +3294,15 @@ import { createHash } from "crypto";
2619
3294
  // src/lib/keychain.ts
2620
3295
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2621
3296
  import { existsSync as existsSync3 } from "fs";
2622
- import path4 from "path";
2623
- import os3 from "os";
3297
+ import path5 from "path";
3298
+ import os4 from "os";
2624
3299
  var SERVICE = "exe-mem";
2625
3300
  var ACCOUNT = "master-key";
2626
3301
  function getKeyDir() {
2627
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os3.homedir(), ".exe-os");
3302
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
2628
3303
  }
2629
3304
  function getKeyPath() {
2630
- return path4.join(getKeyDir(), "master.key");
3305
+ return path5.join(getKeyDir(), "master.key");
2631
3306
  }
2632
3307
  async function tryKeytar() {
2633
3308
  try {
@@ -2650,7 +3325,7 @@ async function getMasterKey() {
2650
3325
  const keyPath = getKeyPath();
2651
3326
  if (!existsSync3(keyPath)) {
2652
3327
  process.stderr.write(
2653
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3328
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2654
3329
  `
2655
3330
  );
2656
3331
  return null;
@@ -3123,7 +3798,7 @@ async function main() {
3123
3798
  if (needsBackfill) {
3124
3799
  try {
3125
3800
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
3126
- const flagPath = path9.join(exeDir, "session-cache", "needs-backfill");
3801
+ const flagPath = path10.join(exeDir, "session-cache", "needs-backfill");
3127
3802
  writeFileSync3(flagPath, "1");
3128
3803
  } catch (err) {
3129
3804
  process.stderr.write(`[response-ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}