@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
@@ -180,7 +180,7 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
180
180
  return [];
181
181
  }
182
182
  }
183
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
183
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
184
184
  var init_employees = __esm({
185
185
  "src/lib/employees.ts"() {
186
186
  "use strict";
@@ -188,16 +188,601 @@ var init_employees = __esm({
188
188
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
189
189
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
190
190
  COORDINATOR_ROLE = "COO";
191
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
192
+ }
193
+ });
194
+
195
+ // src/lib/database-adapter.ts
196
+ import os3 from "os";
197
+ import path3 from "path";
198
+ import { createRequire } from "module";
199
+ import { pathToFileURL } from "url";
200
+ function quotedIdentifier(identifier) {
201
+ return `"${identifier.replace(/"/g, '""')}"`;
202
+ }
203
+ function unqualifiedTableName(name) {
204
+ const raw = name.trim().replace(/^"|"$/g, "");
205
+ const parts = raw.split(".");
206
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
207
+ }
208
+ function stripTrailingSemicolon(sql) {
209
+ return sql.trim().replace(/;+\s*$/u, "");
210
+ }
211
+ function appendClause(sql, clause) {
212
+ const trimmed = stripTrailingSemicolon(sql);
213
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
214
+ if (!returningMatch) {
215
+ return `${trimmed}${clause}`;
216
+ }
217
+ const idx = returningMatch.index;
218
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
219
+ }
220
+ function normalizeStatement(stmt) {
221
+ if (typeof stmt === "string") {
222
+ return { kind: "positional", sql: stmt, args: [] };
223
+ }
224
+ const sql = stmt.sql;
225
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
226
+ return { kind: "positional", sql, args: stmt.args ?? [] };
227
+ }
228
+ return { kind: "named", sql, args: stmt.args };
229
+ }
230
+ function rewriteBooleanLiterals(sql) {
231
+ let out = sql;
232
+ for (const column of BOOLEAN_COLUMN_NAMES) {
233
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
234
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
235
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
236
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
237
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
238
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
239
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
240
+ }
241
+ return out;
242
+ }
243
+ function rewriteInsertOrIgnore(sql) {
244
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
245
+ return sql;
246
+ }
247
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
248
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
249
+ }
250
+ function rewriteInsertOrReplace(sql) {
251
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
252
+ if (!match) {
253
+ return sql;
254
+ }
255
+ const rawTable = match[1];
256
+ const rawColumns = match[2];
257
+ const remainder = match[3];
258
+ const tableName = unqualifiedTableName(rawTable);
259
+ const conflictKeys = UPSERT_KEYS[tableName];
260
+ if (!conflictKeys?.length) {
261
+ return sql;
262
+ }
263
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
264
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
265
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
266
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
267
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
268
+ }
269
+ function rewriteSql(sql) {
270
+ let out = sql;
271
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
272
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
273
+ out = rewriteBooleanLiterals(out);
274
+ out = rewriteInsertOrReplace(out);
275
+ out = rewriteInsertOrIgnore(out);
276
+ return stripTrailingSemicolon(out);
277
+ }
278
+ function toBoolean(value) {
279
+ if (value === null || value === void 0) return value;
280
+ if (typeof value === "boolean") return value;
281
+ if (typeof value === "number") return value !== 0;
282
+ if (typeof value === "bigint") return value !== 0n;
283
+ if (typeof value === "string") {
284
+ const normalized = value.trim().toLowerCase();
285
+ if (normalized === "0" || normalized === "false") return false;
286
+ if (normalized === "1" || normalized === "true") return true;
287
+ }
288
+ return Boolean(value);
289
+ }
290
+ function countQuestionMarks(sql, end) {
291
+ let count = 0;
292
+ let inSingle = false;
293
+ let inDouble = false;
294
+ let inLineComment = false;
295
+ let inBlockComment = false;
296
+ for (let i = 0; i < end; i++) {
297
+ const ch = sql[i];
298
+ const next = sql[i + 1];
299
+ if (inLineComment) {
300
+ if (ch === "\n") inLineComment = false;
301
+ continue;
302
+ }
303
+ if (inBlockComment) {
304
+ if (ch === "*" && next === "/") {
305
+ inBlockComment = false;
306
+ i += 1;
307
+ }
308
+ continue;
309
+ }
310
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
311
+ inLineComment = true;
312
+ i += 1;
313
+ continue;
314
+ }
315
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
316
+ inBlockComment = true;
317
+ i += 1;
318
+ continue;
319
+ }
320
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
321
+ inSingle = !inSingle;
322
+ continue;
323
+ }
324
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
325
+ inDouble = !inDouble;
326
+ continue;
327
+ }
328
+ if (!inSingle && !inDouble && ch === "?") {
329
+ count += 1;
330
+ }
331
+ }
332
+ return count;
333
+ }
334
+ function findBooleanPlaceholderIndexes(sql) {
335
+ const indexes = /* @__PURE__ */ new Set();
336
+ for (const column of BOOLEAN_COLUMN_NAMES) {
337
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
338
+ for (const match of sql.matchAll(pattern)) {
339
+ const matchText = match[0];
340
+ const qIndex = match.index + matchText.lastIndexOf("?");
341
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
342
+ }
343
+ }
344
+ return indexes;
345
+ }
346
+ function coerceInsertBooleanArgs(sql, args) {
347
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
348
+ if (!match) return;
349
+ const rawTable = match[1];
350
+ const rawColumns = match[2];
351
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
352
+ if (!boolColumns?.size) return;
353
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
354
+ for (const [index, column] of columns.entries()) {
355
+ if (boolColumns.has(column) && index < args.length) {
356
+ args[index] = toBoolean(args[index]);
357
+ }
358
+ }
359
+ }
360
+ function coerceUpdateBooleanArgs(sql, args) {
361
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
362
+ if (!match) return;
363
+ const rawTable = match[1];
364
+ const setClause = match[2];
365
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
366
+ if (!boolColumns?.size) return;
367
+ const assignments = setClause.split(",");
368
+ let placeholderIndex = 0;
369
+ for (const assignment of assignments) {
370
+ if (!assignment.includes("?")) continue;
371
+ placeholderIndex += 1;
372
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
373
+ if (colMatch && boolColumns.has(colMatch[1])) {
374
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
375
+ }
376
+ }
377
+ }
378
+ function coerceBooleanArgs(sql, args) {
379
+ const nextArgs = [...args];
380
+ coerceInsertBooleanArgs(sql, nextArgs);
381
+ coerceUpdateBooleanArgs(sql, nextArgs);
382
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
383
+ for (const index of placeholderIndexes) {
384
+ if (index > 0 && index <= nextArgs.length) {
385
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
386
+ }
387
+ }
388
+ return nextArgs;
389
+ }
390
+ function convertQuestionMarksToDollarParams(sql) {
391
+ let out = "";
392
+ let placeholder = 0;
393
+ let inSingle = false;
394
+ let inDouble = false;
395
+ let inLineComment = false;
396
+ let inBlockComment = false;
397
+ for (let i = 0; i < sql.length; i++) {
398
+ const ch = sql[i];
399
+ const next = sql[i + 1];
400
+ if (inLineComment) {
401
+ out += ch;
402
+ if (ch === "\n") inLineComment = false;
403
+ continue;
404
+ }
405
+ if (inBlockComment) {
406
+ out += ch;
407
+ if (ch === "*" && next === "/") {
408
+ out += next;
409
+ inBlockComment = false;
410
+ i += 1;
411
+ }
412
+ continue;
413
+ }
414
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
415
+ out += ch + next;
416
+ inLineComment = true;
417
+ i += 1;
418
+ continue;
419
+ }
420
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
421
+ out += ch + next;
422
+ inBlockComment = true;
423
+ i += 1;
424
+ continue;
425
+ }
426
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
427
+ inSingle = !inSingle;
428
+ out += ch;
429
+ continue;
430
+ }
431
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
432
+ inDouble = !inDouble;
433
+ out += ch;
434
+ continue;
435
+ }
436
+ if (!inSingle && !inDouble && ch === "?") {
437
+ placeholder += 1;
438
+ out += `$${placeholder}`;
439
+ continue;
440
+ }
441
+ out += ch;
442
+ }
443
+ return out;
444
+ }
445
+ function translateStatementForPostgres(stmt) {
446
+ const normalized = normalizeStatement(stmt);
447
+ if (normalized.kind === "named") {
448
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
449
+ }
450
+ const rewrittenSql = rewriteSql(normalized.sql);
451
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
452
+ return {
453
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
454
+ args: coercedArgs
455
+ };
456
+ }
457
+ function shouldBypassPostgres(stmt) {
458
+ const normalized = normalizeStatement(stmt);
459
+ if (normalized.kind === "named") {
460
+ return true;
461
+ }
462
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
463
+ }
464
+ function shouldFallbackOnError(error) {
465
+ const message = error instanceof Error ? error.message : String(error);
466
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
467
+ }
468
+ function isReadQuery(sql) {
469
+ const trimmed = sql.trimStart();
470
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
471
+ }
472
+ function buildRow(row, columns) {
473
+ const values = columns.map((column) => row[column]);
474
+ return Object.assign(values, row);
475
+ }
476
+ function buildResultSet(rows, rowsAffected = 0) {
477
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
478
+ const resultRows = rows.map((row) => buildRow(row, columns));
479
+ return {
480
+ columns,
481
+ columnTypes: columns.map(() => ""),
482
+ rows: resultRows,
483
+ rowsAffected,
484
+ lastInsertRowid: void 0,
485
+ toJSON() {
486
+ return {
487
+ columns,
488
+ columnTypes: columns.map(() => ""),
489
+ rows,
490
+ rowsAffected,
491
+ lastInsertRowid: void 0
492
+ };
493
+ }
494
+ };
495
+ }
496
+ async function loadPrismaClient() {
497
+ if (!prismaClientPromise) {
498
+ prismaClientPromise = (async () => {
499
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
500
+ if (explicitPath) {
501
+ const module2 = await import(pathToFileURL(explicitPath).href);
502
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
503
+ if (!PrismaClient2) {
504
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
505
+ }
506
+ return new PrismaClient2();
507
+ }
508
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
509
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
510
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
511
+ const module = await import(pathToFileURL(prismaEntry).href);
512
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
513
+ if (!PrismaClient) {
514
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
515
+ }
516
+ return new PrismaClient();
517
+ })();
518
+ }
519
+ return prismaClientPromise;
520
+ }
521
+ async function ensureCompatibilityViews(prisma) {
522
+ if (!compatibilityBootstrapPromise) {
523
+ compatibilityBootstrapPromise = (async () => {
524
+ for (const mapping of VIEW_MAPPINGS) {
525
+ const relation = mapping.source.replace(/"/g, "");
526
+ const rows = await prisma.$queryRawUnsafe(
527
+ "SELECT to_regclass($1) AS regclass",
528
+ relation
529
+ );
530
+ if (!rows[0]?.regclass) {
531
+ continue;
532
+ }
533
+ await prisma.$executeRawUnsafe(
534
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
535
+ );
536
+ }
537
+ })();
538
+ }
539
+ return compatibilityBootstrapPromise;
540
+ }
541
+ async function executeOnPrisma(executor, stmt) {
542
+ const translated = translateStatementForPostgres(stmt);
543
+ if (isReadQuery(translated.sql)) {
544
+ const rows = await executor.$queryRawUnsafe(
545
+ translated.sql,
546
+ ...translated.args
547
+ );
548
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
549
+ }
550
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
551
+ return buildResultSet([], rowsAffected);
552
+ }
553
+ function splitSqlStatements(sql) {
554
+ const parts = [];
555
+ let current = "";
556
+ let inSingle = false;
557
+ let inDouble = false;
558
+ let inLineComment = false;
559
+ let inBlockComment = false;
560
+ for (let i = 0; i < sql.length; i++) {
561
+ const ch = sql[i];
562
+ const next = sql[i + 1];
563
+ if (inLineComment) {
564
+ current += ch;
565
+ if (ch === "\n") inLineComment = false;
566
+ continue;
567
+ }
568
+ if (inBlockComment) {
569
+ current += ch;
570
+ if (ch === "*" && next === "/") {
571
+ current += next;
572
+ inBlockComment = false;
573
+ i += 1;
574
+ }
575
+ continue;
576
+ }
577
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
578
+ current += ch + next;
579
+ inLineComment = true;
580
+ i += 1;
581
+ continue;
582
+ }
583
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
584
+ current += ch + next;
585
+ inBlockComment = true;
586
+ i += 1;
587
+ continue;
588
+ }
589
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
590
+ inSingle = !inSingle;
591
+ current += ch;
592
+ continue;
593
+ }
594
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
595
+ inDouble = !inDouble;
596
+ current += ch;
597
+ continue;
598
+ }
599
+ if (!inSingle && !inDouble && ch === ";") {
600
+ if (current.trim()) {
601
+ parts.push(current.trim());
602
+ }
603
+ current = "";
604
+ continue;
605
+ }
606
+ current += ch;
607
+ }
608
+ if (current.trim()) {
609
+ parts.push(current.trim());
610
+ }
611
+ return parts;
612
+ }
613
+ async function createPrismaDbAdapter(fallbackClient) {
614
+ const prisma = await loadPrismaClient();
615
+ await ensureCompatibilityViews(prisma);
616
+ let closed = false;
617
+ let adapter;
618
+ const fallbackExecute = async (stmt, error) => {
619
+ if (!fallbackClient) {
620
+ if (error) throw error;
621
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
622
+ }
623
+ if (error) {
624
+ process.stderr.write(
625
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
626
+ `
627
+ );
628
+ }
629
+ return fallbackClient.execute(stmt);
630
+ };
631
+ adapter = {
632
+ async execute(stmt) {
633
+ if (shouldBypassPostgres(stmt)) {
634
+ return fallbackExecute(stmt);
635
+ }
636
+ try {
637
+ return await executeOnPrisma(prisma, stmt);
638
+ } catch (error) {
639
+ if (shouldFallbackOnError(error)) {
640
+ return fallbackExecute(stmt, error);
641
+ }
642
+ throw error;
643
+ }
644
+ },
645
+ async batch(stmts, mode) {
646
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
647
+ if (!fallbackClient) {
648
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
649
+ }
650
+ return fallbackClient.batch(stmts, mode);
651
+ }
652
+ try {
653
+ if (prisma.$transaction) {
654
+ return await prisma.$transaction(async (tx) => {
655
+ const results2 = [];
656
+ for (const stmt of stmts) {
657
+ results2.push(await executeOnPrisma(tx, stmt));
658
+ }
659
+ return results2;
660
+ });
661
+ }
662
+ const results = [];
663
+ for (const stmt of stmts) {
664
+ results.push(await executeOnPrisma(prisma, stmt));
665
+ }
666
+ return results;
667
+ } catch (error) {
668
+ if (fallbackClient && shouldFallbackOnError(error)) {
669
+ process.stderr.write(
670
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
671
+ `
672
+ );
673
+ return fallbackClient.batch(stmts, mode);
674
+ }
675
+ throw error;
676
+ }
677
+ },
678
+ async migrate(stmts) {
679
+ if (fallbackClient) {
680
+ return fallbackClient.migrate(stmts);
681
+ }
682
+ return adapter.batch(stmts, "deferred");
683
+ },
684
+ async transaction(mode) {
685
+ if (!fallbackClient) {
686
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
687
+ }
688
+ return fallbackClient.transaction(mode);
689
+ },
690
+ async executeMultiple(sql) {
691
+ if (fallbackClient && shouldBypassPostgres(sql)) {
692
+ return fallbackClient.executeMultiple(sql);
693
+ }
694
+ for (const statement of splitSqlStatements(sql)) {
695
+ await adapter.execute(statement);
696
+ }
697
+ },
698
+ async sync() {
699
+ if (fallbackClient) {
700
+ return fallbackClient.sync();
701
+ }
702
+ return { frame_no: 0, frames_synced: 0 };
703
+ },
704
+ close() {
705
+ closed = true;
706
+ prismaClientPromise = null;
707
+ compatibilityBootstrapPromise = null;
708
+ void prisma.$disconnect?.();
709
+ },
710
+ get closed() {
711
+ return closed;
712
+ },
713
+ get protocol() {
714
+ return "prisma-postgres";
715
+ }
716
+ };
717
+ return adapter;
718
+ }
719
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
720
+ var init_database_adapter = __esm({
721
+ "src/lib/database-adapter.ts"() {
722
+ "use strict";
723
+ VIEW_MAPPINGS = [
724
+ { view: "memories", source: "memory.memory_records" },
725
+ { view: "tasks", source: "memory.tasks" },
726
+ { view: "behaviors", source: "memory.behaviors" },
727
+ { view: "entities", source: "memory.entities" },
728
+ { view: "relationships", source: "memory.relationships" },
729
+ { view: "entity_memories", source: "memory.entity_memories" },
730
+ { view: "entity_aliases", source: "memory.entity_aliases" },
731
+ { view: "notifications", source: "memory.notifications" },
732
+ { view: "messages", source: "memory.messages" },
733
+ { view: "users", source: "wiki.users" },
734
+ { view: "workspaces", source: "wiki.workspaces" },
735
+ { view: "workspace_users", source: "wiki.workspace_users" },
736
+ { view: "documents", source: "wiki.workspace_documents" },
737
+ { view: "chats", source: "wiki.workspace_chats" }
738
+ ];
739
+ UPSERT_KEYS = {
740
+ memories: ["id"],
741
+ tasks: ["id"],
742
+ behaviors: ["id"],
743
+ entities: ["id"],
744
+ relationships: ["id"],
745
+ entity_aliases: ["alias"],
746
+ notifications: ["id"],
747
+ messages: ["id"],
748
+ users: ["id"],
749
+ workspaces: ["id"],
750
+ workspace_users: ["id"],
751
+ documents: ["id"],
752
+ chats: ["id"]
753
+ };
754
+ BOOLEAN_COLUMNS_BY_TABLE = {
755
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
756
+ behaviors: /* @__PURE__ */ new Set(["active"]),
757
+ notifications: /* @__PURE__ */ new Set(["read"]),
758
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
759
+ };
760
+ BOOLEAN_COLUMN_NAMES = new Set(
761
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
762
+ );
763
+ IMMEDIATE_FALLBACK_PATTERNS = [
764
+ /\bPRAGMA\b/i,
765
+ /\bsqlite_master\b/i,
766
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
767
+ /\bMATCH\b/i,
768
+ /\bvector_distance_cos\s*\(/i,
769
+ /\bjson_extract\s*\(/i,
770
+ /\bjulianday\s*\(/i,
771
+ /\bstrftime\s*\(/i,
772
+ /\blast_insert_rowid\s*\(/i
773
+ ];
774
+ prismaClientPromise = null;
775
+ compatibilityBootstrapPromise = null;
191
776
  }
192
777
  });
193
778
 
194
779
  // src/lib/exe-daemon-client.ts
195
780
  import net from "net";
196
- import os3 from "os";
781
+ import os4 from "os";
197
782
  import { spawn } from "child_process";
198
783
  import { randomUUID } from "crypto";
199
784
  import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
200
- import path3 from "path";
785
+ import path4 from "path";
201
786
  import { fileURLToPath } from "url";
202
787
  function handleData(chunk) {
203
788
  _buffer += chunk.toString();
@@ -248,17 +833,17 @@ function cleanupStaleFiles() {
248
833
  }
249
834
  }
250
835
  function findPackageRoot() {
251
- let dir = path3.dirname(fileURLToPath(import.meta.url));
252
- const { root } = path3.parse(dir);
836
+ let dir = path4.dirname(fileURLToPath(import.meta.url));
837
+ const { root } = path4.parse(dir);
253
838
  while (dir !== root) {
254
- if (existsSync3(path3.join(dir, "package.json"))) return dir;
255
- dir = path3.dirname(dir);
839
+ if (existsSync3(path4.join(dir, "package.json"))) return dir;
840
+ dir = path4.dirname(dir);
256
841
  }
257
842
  return null;
258
843
  }
259
844
  function spawnDaemon() {
260
- const freeGB = os3.freemem() / (1024 * 1024 * 1024);
261
- const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
845
+ const freeGB = os4.freemem() / (1024 * 1024 * 1024);
846
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
262
847
  if (totalGB <= 8) {
263
848
  process.stderr.write(
264
849
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -278,7 +863,7 @@ function spawnDaemon() {
278
863
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
279
864
  return;
280
865
  }
281
- const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
866
+ const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
282
867
  if (!existsSync3(daemonPath)) {
283
868
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
284
869
  `);
@@ -287,7 +872,7 @@ function spawnDaemon() {
287
872
  const resolvedPath = daemonPath;
288
873
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
289
874
  `);
290
- const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
875
+ const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
291
876
  let stderrFd = "ignore";
292
877
  try {
293
878
  stderrFd = openSync(logPath, "a");
@@ -434,9 +1019,9 @@ var init_exe_daemon_client = __esm({
434
1019
  "src/lib/exe-daemon-client.ts"() {
435
1020
  "use strict";
436
1021
  init_config();
437
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
438
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
439
- SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
1022
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
1023
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
1024
+ SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
440
1025
  SPAWN_LOCK_STALE_MS = 3e4;
441
1026
  CONNECT_TIMEOUT_MS = 15e3;
442
1027
  REQUEST_TIMEOUT_MS = 3e4;
@@ -518,7 +1103,7 @@ __export(db_daemon_client_exports, {
518
1103
  createDaemonDbClient: () => createDaemonDbClient,
519
1104
  initDaemonDbClient: () => initDaemonDbClient
520
1105
  });
521
- function normalizeStatement(stmt) {
1106
+ function normalizeStatement2(stmt) {
522
1107
  if (typeof stmt === "string") {
523
1108
  return { sql: stmt, args: [] };
524
1109
  }
@@ -542,7 +1127,7 @@ function createDaemonDbClient(fallbackClient) {
542
1127
  if (!_useDaemon || !isClientConnected()) {
543
1128
  return fallbackClient.execute(stmt);
544
1129
  }
545
- const { sql, args } = normalizeStatement(stmt);
1130
+ const { sql, args } = normalizeStatement2(stmt);
546
1131
  const response = await sendDaemonRequest({
547
1132
  type: "db-execute",
548
1133
  sql,
@@ -567,7 +1152,7 @@ function createDaemonDbClient(fallbackClient) {
567
1152
  if (!_useDaemon || !isClientConnected()) {
568
1153
  return fallbackClient.batch(stmts, mode);
569
1154
  }
570
- const statements = stmts.map(normalizeStatement);
1155
+ const statements = stmts.map(normalizeStatement2);
571
1156
  const response = await sendDaemonRequest({
572
1157
  type: "db-batch",
573
1158
  statements,
@@ -662,6 +1247,18 @@ __export(database_exports, {
662
1247
  });
663
1248
  import { createClient } from "@libsql/client";
664
1249
  async function initDatabase(config) {
1250
+ if (_walCheckpointTimer) {
1251
+ clearInterval(_walCheckpointTimer);
1252
+ _walCheckpointTimer = null;
1253
+ }
1254
+ if (_daemonClient) {
1255
+ _daemonClient.close();
1256
+ _daemonClient = null;
1257
+ }
1258
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1259
+ _adapterClient.close();
1260
+ }
1261
+ _adapterClient = null;
665
1262
  if (_client) {
666
1263
  _client.close();
667
1264
  _client = null;
@@ -675,6 +1272,7 @@ async function initDatabase(config) {
675
1272
  }
676
1273
  _client = createClient(opts);
677
1274
  _resilientClient = wrapWithRetry(_client);
1275
+ _adapterClient = _resilientClient;
678
1276
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
679
1277
  });
680
1278
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -685,14 +1283,20 @@ async function initDatabase(config) {
685
1283
  });
686
1284
  }, 3e4);
687
1285
  _walCheckpointTimer.unref();
1286
+ if (process.env.DATABASE_URL) {
1287
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1288
+ }
688
1289
  }
689
1290
  function isInitialized() {
690
- return _client !== null;
1291
+ return _adapterClient !== null || _client !== null;
691
1292
  }
692
1293
  function getClient() {
693
- if (!_resilientClient) {
1294
+ if (!_adapterClient) {
694
1295
  throw new Error("Database client not initialized. Call initDatabase() first.");
695
1296
  }
1297
+ if (process.env.DATABASE_URL) {
1298
+ return _adapterClient;
1299
+ }
696
1300
  if (process.env.EXE_IS_DAEMON === "1") {
697
1301
  return _resilientClient;
698
1302
  }
@@ -702,6 +1306,7 @@ function getClient() {
702
1306
  return _resilientClient;
703
1307
  }
704
1308
  async function initDaemonClient() {
1309
+ if (process.env.DATABASE_URL) return;
705
1310
  if (process.env.EXE_IS_DAEMON === "1") return;
706
1311
  if (!_resilientClient) return;
707
1312
  try {
@@ -1646,26 +2251,36 @@ async function ensureSchema() {
1646
2251
  }
1647
2252
  }
1648
2253
  async function disposeDatabase() {
2254
+ if (_walCheckpointTimer) {
2255
+ clearInterval(_walCheckpointTimer);
2256
+ _walCheckpointTimer = null;
2257
+ }
1649
2258
  if (_daemonClient) {
1650
2259
  _daemonClient.close();
1651
2260
  _daemonClient = null;
1652
2261
  }
2262
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2263
+ _adapterClient.close();
2264
+ }
2265
+ _adapterClient = null;
1653
2266
  if (_client) {
1654
2267
  _client.close();
1655
2268
  _client = null;
1656
2269
  _resilientClient = null;
1657
2270
  }
1658
2271
  }
1659
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2272
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1660
2273
  var init_database = __esm({
1661
2274
  "src/lib/database.ts"() {
1662
2275
  "use strict";
1663
2276
  init_db_retry();
1664
2277
  init_employees();
2278
+ init_database_adapter();
1665
2279
  _client = null;
1666
2280
  _resilientClient = null;
1667
2281
  _walCheckpointTimer = null;
1668
2282
  _daemonClient = null;
2283
+ _adapterClient = null;
1669
2284
  initTurso = initDatabase;
1670
2285
  disposeTurso = disposeDatabase;
1671
2286
  }
@@ -1674,10 +2289,10 @@ var init_database = __esm({
1674
2289
  // src/lib/device-registry.ts
1675
2290
  init_config();
1676
2291
  import crypto from "crypto";
1677
- import os4 from "os";
2292
+ import os5 from "os";
1678
2293
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync4 } from "fs";
1679
- import path4 from "path";
1680
- var DEVICE_JSON_PATH = path4.join(EXE_AI_DIR, "device.json");
2294
+ import path5 from "path";
2295
+ var DEVICE_JSON_PATH = path5.join(EXE_AI_DIR, "device.json");
1681
2296
  function getDeviceInfo() {
1682
2297
  if (existsSync4(DEVICE_JSON_PATH)) {
1683
2298
  try {
@@ -1689,13 +2304,13 @@ function getDeviceInfo() {
1689
2304
  } catch {
1690
2305
  }
1691
2306
  }
1692
- const hostname = os4.hostname();
2307
+ const hostname = os5.hostname();
1693
2308
  const info = {
1694
2309
  deviceId: crypto.randomUUID(),
1695
2310
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
1696
2311
  hostname
1697
2312
  };
1698
- mkdirSync(path4.dirname(DEVICE_JSON_PATH), { recursive: true });
2313
+ mkdirSync(path5.dirname(DEVICE_JSON_PATH), { recursive: true });
1699
2314
  writeFileSync2(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
1700
2315
  return info;
1701
2316
  }