@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
@@ -235,7 +235,7 @@ function registerBinSymlinks(name) {
235
235
  }
236
236
  return { created, skipped, errors };
237
237
  }
238
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
238
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
239
239
  var init_employees = __esm({
240
240
  "src/lib/employees.ts"() {
241
241
  "use strict";
@@ -243,16 +243,601 @@ var init_employees = __esm({
243
243
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
244
244
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
245
245
  COORDINATOR_ROLE = "COO";
246
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
247
+ }
248
+ });
249
+
250
+ // src/lib/database-adapter.ts
251
+ import os3 from "os";
252
+ import path3 from "path";
253
+ import { createRequire } from "module";
254
+ import { pathToFileURL } from "url";
255
+ function quotedIdentifier(identifier) {
256
+ return `"${identifier.replace(/"/g, '""')}"`;
257
+ }
258
+ function unqualifiedTableName(name) {
259
+ const raw = name.trim().replace(/^"|"$/g, "");
260
+ const parts = raw.split(".");
261
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
262
+ }
263
+ function stripTrailingSemicolon(sql) {
264
+ return sql.trim().replace(/;+\s*$/u, "");
265
+ }
266
+ function appendClause(sql, clause) {
267
+ const trimmed = stripTrailingSemicolon(sql);
268
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
269
+ if (!returningMatch) {
270
+ return `${trimmed}${clause}`;
271
+ }
272
+ const idx = returningMatch.index;
273
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
274
+ }
275
+ function normalizeStatement(stmt) {
276
+ if (typeof stmt === "string") {
277
+ return { kind: "positional", sql: stmt, args: [] };
278
+ }
279
+ const sql = stmt.sql;
280
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
281
+ return { kind: "positional", sql, args: stmt.args ?? [] };
282
+ }
283
+ return { kind: "named", sql, args: stmt.args };
284
+ }
285
+ function rewriteBooleanLiterals(sql) {
286
+ let out = sql;
287
+ for (const column of BOOLEAN_COLUMN_NAMES) {
288
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
289
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
290
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
291
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
292
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
293
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
294
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
295
+ }
296
+ return out;
297
+ }
298
+ function rewriteInsertOrIgnore(sql) {
299
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
300
+ return sql;
301
+ }
302
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
303
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
304
+ }
305
+ function rewriteInsertOrReplace(sql) {
306
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
307
+ if (!match) {
308
+ return sql;
309
+ }
310
+ const rawTable = match[1];
311
+ const rawColumns = match[2];
312
+ const remainder = match[3];
313
+ const tableName = unqualifiedTableName(rawTable);
314
+ const conflictKeys = UPSERT_KEYS[tableName];
315
+ if (!conflictKeys?.length) {
316
+ return sql;
317
+ }
318
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
319
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
320
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
321
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
322
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
323
+ }
324
+ function rewriteSql(sql) {
325
+ let out = sql;
326
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
327
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
328
+ out = rewriteBooleanLiterals(out);
329
+ out = rewriteInsertOrReplace(out);
330
+ out = rewriteInsertOrIgnore(out);
331
+ return stripTrailingSemicolon(out);
332
+ }
333
+ function toBoolean(value) {
334
+ if (value === null || value === void 0) return value;
335
+ if (typeof value === "boolean") return value;
336
+ if (typeof value === "number") return value !== 0;
337
+ if (typeof value === "bigint") return value !== 0n;
338
+ if (typeof value === "string") {
339
+ const normalized = value.trim().toLowerCase();
340
+ if (normalized === "0" || normalized === "false") return false;
341
+ if (normalized === "1" || normalized === "true") return true;
342
+ }
343
+ return Boolean(value);
344
+ }
345
+ function countQuestionMarks(sql, end) {
346
+ let count = 0;
347
+ let inSingle = false;
348
+ let inDouble = false;
349
+ let inLineComment = false;
350
+ let inBlockComment = false;
351
+ for (let i = 0; i < end; i++) {
352
+ const ch = sql[i];
353
+ const next = sql[i + 1];
354
+ if (inLineComment) {
355
+ if (ch === "\n") inLineComment = false;
356
+ continue;
357
+ }
358
+ if (inBlockComment) {
359
+ if (ch === "*" && next === "/") {
360
+ inBlockComment = false;
361
+ i += 1;
362
+ }
363
+ continue;
364
+ }
365
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
366
+ inLineComment = true;
367
+ i += 1;
368
+ continue;
369
+ }
370
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
371
+ inBlockComment = true;
372
+ i += 1;
373
+ continue;
374
+ }
375
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
376
+ inSingle = !inSingle;
377
+ continue;
378
+ }
379
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
380
+ inDouble = !inDouble;
381
+ continue;
382
+ }
383
+ if (!inSingle && !inDouble && ch === "?") {
384
+ count += 1;
385
+ }
386
+ }
387
+ return count;
388
+ }
389
+ function findBooleanPlaceholderIndexes(sql) {
390
+ const indexes = /* @__PURE__ */ new Set();
391
+ for (const column of BOOLEAN_COLUMN_NAMES) {
392
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
393
+ for (const match of sql.matchAll(pattern)) {
394
+ const matchText = match[0];
395
+ const qIndex = match.index + matchText.lastIndexOf("?");
396
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
397
+ }
398
+ }
399
+ return indexes;
400
+ }
401
+ function coerceInsertBooleanArgs(sql, args) {
402
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
403
+ if (!match) return;
404
+ const rawTable = match[1];
405
+ const rawColumns = match[2];
406
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
407
+ if (!boolColumns?.size) return;
408
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
409
+ for (const [index, column] of columns.entries()) {
410
+ if (boolColumns.has(column) && index < args.length) {
411
+ args[index] = toBoolean(args[index]);
412
+ }
413
+ }
414
+ }
415
+ function coerceUpdateBooleanArgs(sql, args) {
416
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
417
+ if (!match) return;
418
+ const rawTable = match[1];
419
+ const setClause = match[2];
420
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
421
+ if (!boolColumns?.size) return;
422
+ const assignments = setClause.split(",");
423
+ let placeholderIndex = 0;
424
+ for (const assignment of assignments) {
425
+ if (!assignment.includes("?")) continue;
426
+ placeholderIndex += 1;
427
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
428
+ if (colMatch && boolColumns.has(colMatch[1])) {
429
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
430
+ }
431
+ }
432
+ }
433
+ function coerceBooleanArgs(sql, args) {
434
+ const nextArgs = [...args];
435
+ coerceInsertBooleanArgs(sql, nextArgs);
436
+ coerceUpdateBooleanArgs(sql, nextArgs);
437
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
438
+ for (const index of placeholderIndexes) {
439
+ if (index > 0 && index <= nextArgs.length) {
440
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
441
+ }
442
+ }
443
+ return nextArgs;
444
+ }
445
+ function convertQuestionMarksToDollarParams(sql) {
446
+ let out = "";
447
+ let placeholder = 0;
448
+ let inSingle = false;
449
+ let inDouble = false;
450
+ let inLineComment = false;
451
+ let inBlockComment = false;
452
+ for (let i = 0; i < sql.length; i++) {
453
+ const ch = sql[i];
454
+ const next = sql[i + 1];
455
+ if (inLineComment) {
456
+ out += ch;
457
+ if (ch === "\n") inLineComment = false;
458
+ continue;
459
+ }
460
+ if (inBlockComment) {
461
+ out += ch;
462
+ if (ch === "*" && next === "/") {
463
+ out += next;
464
+ inBlockComment = false;
465
+ i += 1;
466
+ }
467
+ continue;
468
+ }
469
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
470
+ out += ch + next;
471
+ inLineComment = true;
472
+ i += 1;
473
+ continue;
474
+ }
475
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
476
+ out += ch + next;
477
+ inBlockComment = true;
478
+ i += 1;
479
+ continue;
480
+ }
481
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
482
+ inSingle = !inSingle;
483
+ out += ch;
484
+ continue;
485
+ }
486
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
487
+ inDouble = !inDouble;
488
+ out += ch;
489
+ continue;
490
+ }
491
+ if (!inSingle && !inDouble && ch === "?") {
492
+ placeholder += 1;
493
+ out += `$${placeholder}`;
494
+ continue;
495
+ }
496
+ out += ch;
497
+ }
498
+ return out;
499
+ }
500
+ function translateStatementForPostgres(stmt) {
501
+ const normalized = normalizeStatement(stmt);
502
+ if (normalized.kind === "named") {
503
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
504
+ }
505
+ const rewrittenSql = rewriteSql(normalized.sql);
506
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
507
+ return {
508
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
509
+ args: coercedArgs
510
+ };
511
+ }
512
+ function shouldBypassPostgres(stmt) {
513
+ const normalized = normalizeStatement(stmt);
514
+ if (normalized.kind === "named") {
515
+ return true;
516
+ }
517
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
518
+ }
519
+ function shouldFallbackOnError(error) {
520
+ const message = error instanceof Error ? error.message : String(error);
521
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
522
+ }
523
+ function isReadQuery(sql) {
524
+ const trimmed = sql.trimStart();
525
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
526
+ }
527
+ function buildRow(row, columns) {
528
+ const values = columns.map((column) => row[column]);
529
+ return Object.assign(values, row);
530
+ }
531
+ function buildResultSet(rows, rowsAffected = 0) {
532
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
533
+ const resultRows = rows.map((row) => buildRow(row, columns));
534
+ return {
535
+ columns,
536
+ columnTypes: columns.map(() => ""),
537
+ rows: resultRows,
538
+ rowsAffected,
539
+ lastInsertRowid: void 0,
540
+ toJSON() {
541
+ return {
542
+ columns,
543
+ columnTypes: columns.map(() => ""),
544
+ rows,
545
+ rowsAffected,
546
+ lastInsertRowid: void 0
547
+ };
548
+ }
549
+ };
550
+ }
551
+ async function loadPrismaClient() {
552
+ if (!prismaClientPromise) {
553
+ prismaClientPromise = (async () => {
554
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
555
+ if (explicitPath) {
556
+ const module2 = await import(pathToFileURL(explicitPath).href);
557
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
558
+ if (!PrismaClient2) {
559
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
560
+ }
561
+ return new PrismaClient2();
562
+ }
563
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
564
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
565
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
566
+ const module = await import(pathToFileURL(prismaEntry).href);
567
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
568
+ if (!PrismaClient) {
569
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
570
+ }
571
+ return new PrismaClient();
572
+ })();
573
+ }
574
+ return prismaClientPromise;
575
+ }
576
+ async function ensureCompatibilityViews(prisma) {
577
+ if (!compatibilityBootstrapPromise) {
578
+ compatibilityBootstrapPromise = (async () => {
579
+ for (const mapping of VIEW_MAPPINGS) {
580
+ const relation = mapping.source.replace(/"/g, "");
581
+ const rows = await prisma.$queryRawUnsafe(
582
+ "SELECT to_regclass($1) AS regclass",
583
+ relation
584
+ );
585
+ if (!rows[0]?.regclass) {
586
+ continue;
587
+ }
588
+ await prisma.$executeRawUnsafe(
589
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
590
+ );
591
+ }
592
+ })();
593
+ }
594
+ return compatibilityBootstrapPromise;
595
+ }
596
+ async function executeOnPrisma(executor, stmt) {
597
+ const translated = translateStatementForPostgres(stmt);
598
+ if (isReadQuery(translated.sql)) {
599
+ const rows = await executor.$queryRawUnsafe(
600
+ translated.sql,
601
+ ...translated.args
602
+ );
603
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
604
+ }
605
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
606
+ return buildResultSet([], rowsAffected);
607
+ }
608
+ function splitSqlStatements(sql) {
609
+ const parts = [];
610
+ let current = "";
611
+ let inSingle = false;
612
+ let inDouble = false;
613
+ let inLineComment = false;
614
+ let inBlockComment = false;
615
+ for (let i = 0; i < sql.length; i++) {
616
+ const ch = sql[i];
617
+ const next = sql[i + 1];
618
+ if (inLineComment) {
619
+ current += ch;
620
+ if (ch === "\n") inLineComment = false;
621
+ continue;
622
+ }
623
+ if (inBlockComment) {
624
+ current += ch;
625
+ if (ch === "*" && next === "/") {
626
+ current += next;
627
+ inBlockComment = false;
628
+ i += 1;
629
+ }
630
+ continue;
631
+ }
632
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
633
+ current += ch + next;
634
+ inLineComment = true;
635
+ i += 1;
636
+ continue;
637
+ }
638
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
639
+ current += ch + next;
640
+ inBlockComment = true;
641
+ i += 1;
642
+ continue;
643
+ }
644
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
645
+ inSingle = !inSingle;
646
+ current += ch;
647
+ continue;
648
+ }
649
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
650
+ inDouble = !inDouble;
651
+ current += ch;
652
+ continue;
653
+ }
654
+ if (!inSingle && !inDouble && ch === ";") {
655
+ if (current.trim()) {
656
+ parts.push(current.trim());
657
+ }
658
+ current = "";
659
+ continue;
660
+ }
661
+ current += ch;
662
+ }
663
+ if (current.trim()) {
664
+ parts.push(current.trim());
665
+ }
666
+ return parts;
667
+ }
668
+ async function createPrismaDbAdapter(fallbackClient) {
669
+ const prisma = await loadPrismaClient();
670
+ await ensureCompatibilityViews(prisma);
671
+ let closed = false;
672
+ let adapter;
673
+ const fallbackExecute = async (stmt, error) => {
674
+ if (!fallbackClient) {
675
+ if (error) throw error;
676
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
677
+ }
678
+ if (error) {
679
+ process.stderr.write(
680
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
681
+ `
682
+ );
683
+ }
684
+ return fallbackClient.execute(stmt);
685
+ };
686
+ adapter = {
687
+ async execute(stmt) {
688
+ if (shouldBypassPostgres(stmt)) {
689
+ return fallbackExecute(stmt);
690
+ }
691
+ try {
692
+ return await executeOnPrisma(prisma, stmt);
693
+ } catch (error) {
694
+ if (shouldFallbackOnError(error)) {
695
+ return fallbackExecute(stmt, error);
696
+ }
697
+ throw error;
698
+ }
699
+ },
700
+ async batch(stmts, mode) {
701
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
702
+ if (!fallbackClient) {
703
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
704
+ }
705
+ return fallbackClient.batch(stmts, mode);
706
+ }
707
+ try {
708
+ if (prisma.$transaction) {
709
+ return await prisma.$transaction(async (tx) => {
710
+ const results2 = [];
711
+ for (const stmt of stmts) {
712
+ results2.push(await executeOnPrisma(tx, stmt));
713
+ }
714
+ return results2;
715
+ });
716
+ }
717
+ const results = [];
718
+ for (const stmt of stmts) {
719
+ results.push(await executeOnPrisma(prisma, stmt));
720
+ }
721
+ return results;
722
+ } catch (error) {
723
+ if (fallbackClient && shouldFallbackOnError(error)) {
724
+ process.stderr.write(
725
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
726
+ `
727
+ );
728
+ return fallbackClient.batch(stmts, mode);
729
+ }
730
+ throw error;
731
+ }
732
+ },
733
+ async migrate(stmts) {
734
+ if (fallbackClient) {
735
+ return fallbackClient.migrate(stmts);
736
+ }
737
+ return adapter.batch(stmts, "deferred");
738
+ },
739
+ async transaction(mode) {
740
+ if (!fallbackClient) {
741
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
742
+ }
743
+ return fallbackClient.transaction(mode);
744
+ },
745
+ async executeMultiple(sql) {
746
+ if (fallbackClient && shouldBypassPostgres(sql)) {
747
+ return fallbackClient.executeMultiple(sql);
748
+ }
749
+ for (const statement of splitSqlStatements(sql)) {
750
+ await adapter.execute(statement);
751
+ }
752
+ },
753
+ async sync() {
754
+ if (fallbackClient) {
755
+ return fallbackClient.sync();
756
+ }
757
+ return { frame_no: 0, frames_synced: 0 };
758
+ },
759
+ close() {
760
+ closed = true;
761
+ prismaClientPromise = null;
762
+ compatibilityBootstrapPromise = null;
763
+ void prisma.$disconnect?.();
764
+ },
765
+ get closed() {
766
+ return closed;
767
+ },
768
+ get protocol() {
769
+ return "prisma-postgres";
770
+ }
771
+ };
772
+ return adapter;
773
+ }
774
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
775
+ var init_database_adapter = __esm({
776
+ "src/lib/database-adapter.ts"() {
777
+ "use strict";
778
+ VIEW_MAPPINGS = [
779
+ { view: "memories", source: "memory.memory_records" },
780
+ { view: "tasks", source: "memory.tasks" },
781
+ { view: "behaviors", source: "memory.behaviors" },
782
+ { view: "entities", source: "memory.entities" },
783
+ { view: "relationships", source: "memory.relationships" },
784
+ { view: "entity_memories", source: "memory.entity_memories" },
785
+ { view: "entity_aliases", source: "memory.entity_aliases" },
786
+ { view: "notifications", source: "memory.notifications" },
787
+ { view: "messages", source: "memory.messages" },
788
+ { view: "users", source: "wiki.users" },
789
+ { view: "workspaces", source: "wiki.workspaces" },
790
+ { view: "workspace_users", source: "wiki.workspace_users" },
791
+ { view: "documents", source: "wiki.workspace_documents" },
792
+ { view: "chats", source: "wiki.workspace_chats" }
793
+ ];
794
+ UPSERT_KEYS = {
795
+ memories: ["id"],
796
+ tasks: ["id"],
797
+ behaviors: ["id"],
798
+ entities: ["id"],
799
+ relationships: ["id"],
800
+ entity_aliases: ["alias"],
801
+ notifications: ["id"],
802
+ messages: ["id"],
803
+ users: ["id"],
804
+ workspaces: ["id"],
805
+ workspace_users: ["id"],
806
+ documents: ["id"],
807
+ chats: ["id"]
808
+ };
809
+ BOOLEAN_COLUMNS_BY_TABLE = {
810
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
811
+ behaviors: /* @__PURE__ */ new Set(["active"]),
812
+ notifications: /* @__PURE__ */ new Set(["read"]),
813
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
814
+ };
815
+ BOOLEAN_COLUMN_NAMES = new Set(
816
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
817
+ );
818
+ IMMEDIATE_FALLBACK_PATTERNS = [
819
+ /\bPRAGMA\b/i,
820
+ /\bsqlite_master\b/i,
821
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
822
+ /\bMATCH\b/i,
823
+ /\bvector_distance_cos\s*\(/i,
824
+ /\bjson_extract\s*\(/i,
825
+ /\bjulianday\s*\(/i,
826
+ /\bstrftime\s*\(/i,
827
+ /\blast_insert_rowid\s*\(/i
828
+ ];
829
+ prismaClientPromise = null;
830
+ compatibilityBootstrapPromise = null;
246
831
  }
247
832
  });
248
833
 
249
834
  // src/lib/exe-daemon-client.ts
250
835
  import net from "net";
251
- import os3 from "os";
836
+ import os4 from "os";
252
837
  import { spawn } from "child_process";
253
838
  import { randomUUID } from "crypto";
254
839
  import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
255
- import path3 from "path";
840
+ import path4 from "path";
256
841
  import { fileURLToPath } from "url";
257
842
  function handleData(chunk) {
258
843
  _buffer += chunk.toString();
@@ -303,17 +888,17 @@ function cleanupStaleFiles() {
303
888
  }
304
889
  }
305
890
  function findPackageRoot() {
306
- let dir = path3.dirname(fileURLToPath(import.meta.url));
307
- const { root } = path3.parse(dir);
891
+ let dir = path4.dirname(fileURLToPath(import.meta.url));
892
+ const { root } = path4.parse(dir);
308
893
  while (dir !== root) {
309
- if (existsSync3(path3.join(dir, "package.json"))) return dir;
310
- dir = path3.dirname(dir);
894
+ if (existsSync3(path4.join(dir, "package.json"))) return dir;
895
+ dir = path4.dirname(dir);
311
896
  }
312
897
  return null;
313
898
  }
314
899
  function spawnDaemon() {
315
- const freeGB = os3.freemem() / (1024 * 1024 * 1024);
316
- const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
900
+ const freeGB = os4.freemem() / (1024 * 1024 * 1024);
901
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
317
902
  if (totalGB <= 8) {
318
903
  process.stderr.write(
319
904
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -333,7 +918,7 @@ function spawnDaemon() {
333
918
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
334
919
  return;
335
920
  }
336
- const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
921
+ const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
337
922
  if (!existsSync3(daemonPath)) {
338
923
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
339
924
  `);
@@ -342,7 +927,7 @@ function spawnDaemon() {
342
927
  const resolvedPath = daemonPath;
343
928
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
344
929
  `);
345
- const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
930
+ const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
346
931
  let stderrFd = "ignore";
347
932
  try {
348
933
  stderrFd = openSync(logPath, "a");
@@ -489,9 +1074,9 @@ var init_exe_daemon_client = __esm({
489
1074
  "src/lib/exe-daemon-client.ts"() {
490
1075
  "use strict";
491
1076
  init_config();
492
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
493
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
494
- SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
1077
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
1078
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
1079
+ SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
495
1080
  SPAWN_LOCK_STALE_MS = 3e4;
496
1081
  CONNECT_TIMEOUT_MS = 15e3;
497
1082
  REQUEST_TIMEOUT_MS = 3e4;
@@ -573,7 +1158,7 @@ __export(db_daemon_client_exports, {
573
1158
  createDaemonDbClient: () => createDaemonDbClient,
574
1159
  initDaemonDbClient: () => initDaemonDbClient
575
1160
  });
576
- function normalizeStatement(stmt) {
1161
+ function normalizeStatement2(stmt) {
577
1162
  if (typeof stmt === "string") {
578
1163
  return { sql: stmt, args: [] };
579
1164
  }
@@ -597,7 +1182,7 @@ function createDaemonDbClient(fallbackClient) {
597
1182
  if (!_useDaemon || !isClientConnected()) {
598
1183
  return fallbackClient.execute(stmt);
599
1184
  }
600
- const { sql, args } = normalizeStatement(stmt);
1185
+ const { sql, args } = normalizeStatement2(stmt);
601
1186
  const response = await sendDaemonRequest({
602
1187
  type: "db-execute",
603
1188
  sql,
@@ -622,7 +1207,7 @@ function createDaemonDbClient(fallbackClient) {
622
1207
  if (!_useDaemon || !isClientConnected()) {
623
1208
  return fallbackClient.batch(stmts, mode);
624
1209
  }
625
- const statements = stmts.map(normalizeStatement);
1210
+ const statements = stmts.map(normalizeStatement2);
626
1211
  const response = await sendDaemonRequest({
627
1212
  type: "db-batch",
628
1213
  statements,
@@ -717,6 +1302,18 @@ __export(database_exports, {
717
1302
  });
718
1303
  import { createClient } from "@libsql/client";
719
1304
  async function initDatabase(config) {
1305
+ if (_walCheckpointTimer) {
1306
+ clearInterval(_walCheckpointTimer);
1307
+ _walCheckpointTimer = null;
1308
+ }
1309
+ if (_daemonClient) {
1310
+ _daemonClient.close();
1311
+ _daemonClient = null;
1312
+ }
1313
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1314
+ _adapterClient.close();
1315
+ }
1316
+ _adapterClient = null;
720
1317
  if (_client) {
721
1318
  _client.close();
722
1319
  _client = null;
@@ -730,6 +1327,7 @@ async function initDatabase(config) {
730
1327
  }
731
1328
  _client = createClient(opts);
732
1329
  _resilientClient = wrapWithRetry(_client);
1330
+ _adapterClient = _resilientClient;
733
1331
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
734
1332
  });
735
1333
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -740,14 +1338,20 @@ async function initDatabase(config) {
740
1338
  });
741
1339
  }, 3e4);
742
1340
  _walCheckpointTimer.unref();
1341
+ if (process.env.DATABASE_URL) {
1342
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1343
+ }
743
1344
  }
744
1345
  function isInitialized() {
745
- return _client !== null;
1346
+ return _adapterClient !== null || _client !== null;
746
1347
  }
747
1348
  function getClient() {
748
- if (!_resilientClient) {
1349
+ if (!_adapterClient) {
749
1350
  throw new Error("Database client not initialized. Call initDatabase() first.");
750
1351
  }
1352
+ if (process.env.DATABASE_URL) {
1353
+ return _adapterClient;
1354
+ }
751
1355
  if (process.env.EXE_IS_DAEMON === "1") {
752
1356
  return _resilientClient;
753
1357
  }
@@ -757,6 +1361,7 @@ function getClient() {
757
1361
  return _resilientClient;
758
1362
  }
759
1363
  async function initDaemonClient() {
1364
+ if (process.env.DATABASE_URL) return;
760
1365
  if (process.env.EXE_IS_DAEMON === "1") return;
761
1366
  if (!_resilientClient) return;
762
1367
  try {
@@ -1701,26 +2306,36 @@ async function ensureSchema() {
1701
2306
  }
1702
2307
  }
1703
2308
  async function disposeDatabase() {
2309
+ if (_walCheckpointTimer) {
2310
+ clearInterval(_walCheckpointTimer);
2311
+ _walCheckpointTimer = null;
2312
+ }
1704
2313
  if (_daemonClient) {
1705
2314
  _daemonClient.close();
1706
2315
  _daemonClient = null;
1707
2316
  }
2317
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2318
+ _adapterClient.close();
2319
+ }
2320
+ _adapterClient = null;
1708
2321
  if (_client) {
1709
2322
  _client.close();
1710
2323
  _client = null;
1711
2324
  _resilientClient = null;
1712
2325
  }
1713
2326
  }
1714
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2327
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1715
2328
  var init_database = __esm({
1716
2329
  "src/lib/database.ts"() {
1717
2330
  "use strict";
1718
2331
  init_db_retry();
1719
2332
  init_employees();
2333
+ init_database_adapter();
1720
2334
  _client = null;
1721
2335
  _resilientClient = null;
1722
2336
  _walCheckpointTimer = null;
1723
2337
  _daemonClient = null;
2338
+ _adapterClient = null;
1724
2339
  initTurso = initDatabase;
1725
2340
  disposeTurso = disposeDatabase;
1726
2341
  }
@@ -1746,7 +2361,7 @@ __export(crdt_sync_exports, {
1746
2361
  });
1747
2362
  import * as Y from "yjs";
1748
2363
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
1749
- import path5 from "path";
2364
+ import path6 from "path";
1750
2365
  import { homedir } from "os";
1751
2366
  function getStatePath() {
1752
2367
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -1902,7 +2517,7 @@ function persistState() {
1902
2517
  if (!doc) return;
1903
2518
  try {
1904
2519
  const sp = getStatePath();
1905
- const dir = path5.dirname(sp);
2520
+ const dir = path6.dirname(sp);
1906
2521
  if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
1907
2522
  const state = Y.encodeStateAsUpdate(doc);
1908
2523
  writeFileSync3(sp, Buffer.from(state));
@@ -1946,7 +2561,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
1946
2561
  var init_crdt_sync = __esm({
1947
2562
  "src/lib/crdt-sync.ts"() {
1948
2563
  "use strict";
1949
- DEFAULT_STATE_PATH = path5.join(homedir(), ".exe-os", "crdt-state.bin");
2564
+ DEFAULT_STATE_PATH = path6.join(homedir(), ".exe-os", "crdt-state.bin");
1950
2565
  _statePathOverride = null;
1951
2566
  doc = null;
1952
2567
  }
@@ -1963,13 +2578,13 @@ __export(keychain_exports, {
1963
2578
  });
1964
2579
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1965
2580
  import { existsSync as existsSync6 } from "fs";
1966
- import path6 from "path";
1967
- import os4 from "os";
2581
+ import path7 from "path";
2582
+ import os5 from "os";
1968
2583
  function getKeyDir() {
1969
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os4.homedir(), ".exe-os");
2584
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
1970
2585
  }
1971
2586
  function getKeyPath() {
1972
- return path6.join(getKeyDir(), "master.key");
2587
+ return path7.join(getKeyDir(), "master.key");
1973
2588
  }
1974
2589
  async function tryKeytar() {
1975
2590
  try {
@@ -1992,7 +2607,7 @@ async function getMasterKey() {
1992
2607
  const keyPath = getKeyPath();
1993
2608
  if (!existsSync6(keyPath)) {
1994
2609
  process.stderr.write(
1995
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2610
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
1996
2611
  `
1997
2612
  );
1998
2613
  return null;
@@ -2079,7 +2694,7 @@ var init_keychain = __esm({
2079
2694
  init_database();
2080
2695
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
2081
2696
  import crypto2 from "crypto";
2082
- import path7 from "path";
2697
+ import path8 from "path";
2083
2698
  import { homedir as homedir2 } from "os";
2084
2699
 
2085
2700
  // src/lib/crypto.ts
@@ -2147,13 +2762,13 @@ function decompress(input) {
2147
2762
  init_config();
2148
2763
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync } from "fs";
2149
2764
  import { randomUUID as randomUUID2 } from "crypto";
2150
- import path4 from "path";
2765
+ import path5 from "path";
2151
2766
  import { jwtVerify, importSPKI } from "jose";
2152
- var LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
2153
- var CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
2154
- var DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
2767
+ var LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
2768
+ var CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
2769
+ var DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
2155
2770
  function loadDeviceId() {
2156
- const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
2771
+ const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
2157
2772
  try {
2158
2773
  if (existsSync4(deviceJsonPath)) {
2159
2774
  const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
@@ -2183,7 +2798,7 @@ function sqlSafe(v) {
2183
2798
  }
2184
2799
  function logError(msg) {
2185
2800
  try {
2186
- const logPath = path7.join(homedir2(), ".exe-os", "workers.log");
2801
+ const logPath = path8.join(homedir2(), ".exe-os", "workers.log");
2187
2802
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
2188
2803
  `);
2189
2804
  } catch {
@@ -2192,7 +2807,7 @@ function logError(msg) {
2192
2807
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
2193
2808
  var FETCH_TIMEOUT_MS = 3e4;
2194
2809
  var PUSH_BATCH_SIZE = 5e3;
2195
- var ROSTER_LOCK_PATH = path7.join(EXE_AI_DIR, "roster-merge.lock");
2810
+ var ROSTER_LOCK_PATH = path8.join(EXE_AI_DIR, "roster-merge.lock");
2196
2811
  var LOCK_STALE_MS = 3e4;
2197
2812
  async function withRosterLock(fn) {
2198
2813
  try {
@@ -2583,7 +3198,7 @@ async function cloudSync(config) {
2583
3198
  try {
2584
3199
  const employees = await loadEmployees();
2585
3200
  rosterResult.employees = employees.length;
2586
- const idDir = path7.join(EXE_AI_DIR, "identity");
3201
+ const idDir = path8.join(EXE_AI_DIR, "identity");
2587
3202
  if (existsSync7(idDir)) {
2588
3203
  rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
2589
3204
  }
@@ -2602,7 +3217,7 @@ async function cloudSync(config) {
2602
3217
  roster: rosterResult
2603
3218
  };
2604
3219
  }
2605
- var ROSTER_DELETIONS_PATH = path7.join(EXE_AI_DIR, "roster-deletions.json");
3220
+ var ROSTER_DELETIONS_PATH = path8.join(EXE_AI_DIR, "roster-deletions.json");
2606
3221
  function recordRosterDeletion(name) {
2607
3222
  let deletions = [];
2608
3223
  try {
@@ -2625,9 +3240,9 @@ function consumeRosterDeletions() {
2625
3240
  }
2626
3241
  }
2627
3242
  function buildRosterBlob(paths) {
2628
- const rosterPath = paths?.rosterPath ?? path7.join(EXE_AI_DIR, "exe-employees.json");
2629
- const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
2630
- const configPath = paths?.configPath ?? path7.join(EXE_AI_DIR, "config.json");
3243
+ const rosterPath = paths?.rosterPath ?? path8.join(EXE_AI_DIR, "exe-employees.json");
3244
+ const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
3245
+ const configPath = paths?.configPath ?? path8.join(EXE_AI_DIR, "config.json");
2631
3246
  let roster = [];
2632
3247
  if (existsSync7(rosterPath)) {
2633
3248
  try {
@@ -2639,7 +3254,7 @@ function buildRosterBlob(paths) {
2639
3254
  if (existsSync7(identityDir)) {
2640
3255
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
2641
3256
  try {
2642
- identities[file] = readFileSync6(path7.join(identityDir, file), "utf-8");
3257
+ identities[file] = readFileSync6(path8.join(identityDir, file), "utf-8");
2643
3258
  } catch {
2644
3259
  }
2645
3260
  }
@@ -2652,7 +3267,7 @@ function buildRosterBlob(paths) {
2652
3267
  }
2653
3268
  }
2654
3269
  let agentConfig;
2655
- const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
3270
+ const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
2656
3271
  if (existsSync7(agentConfigPath)) {
2657
3272
  try {
2658
3273
  agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
@@ -2731,7 +3346,7 @@ async function cloudPullRoster(config) {
2731
3346
  }
2732
3347
  }
2733
3348
  function mergeConfig(remoteConfig, configPath) {
2734
- const cfgPath = configPath ?? path7.join(EXE_AI_DIR, "config.json");
3349
+ const cfgPath = configPath ?? path8.join(EXE_AI_DIR, "config.json");
2735
3350
  let local = {};
2736
3351
  if (existsSync7(cfgPath)) {
2737
3352
  try {
@@ -2740,14 +3355,14 @@ function mergeConfig(remoteConfig, configPath) {
2740
3355
  }
2741
3356
  }
2742
3357
  const merged = { ...remoteConfig, ...local };
2743
- const dir = path7.dirname(cfgPath);
3358
+ const dir = path8.dirname(cfgPath);
2744
3359
  if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2745
3360
  writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2746
3361
  }
2747
3362
  async function mergeRosterFromRemote(remote, paths) {
2748
3363
  return withRosterLock(async () => {
2749
3364
  const rosterPath = paths?.rosterPath ?? void 0;
2750
- const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
3365
+ const identityDir = paths?.identityDir ?? path8.join(EXE_AI_DIR, "identity");
2751
3366
  const localEmployees = await loadEmployees(rosterPath);
2752
3367
  const localNames = new Set(localEmployees.map((e) => e.name));
2753
3368
  let added = 0;
@@ -2769,7 +3384,7 @@ async function mergeRosterFromRemote(remote, paths) {
2769
3384
  const remoteIdentity = remote.identities[matchedKey];
2770
3385
  if (remoteIdentity) {
2771
3386
  if (!existsSync7(identityDir)) mkdirSync3(identityDir, { recursive: true });
2772
- const idPath = path7.join(identityDir, `${remoteEmp.name}.md`);
3387
+ const idPath = path8.join(identityDir, `${remoteEmp.name}.md`);
2773
3388
  let localIdentity = null;
2774
3389
  try {
2775
3390
  localIdentity = existsSync7(idPath) ? readFileSync6(idPath, "utf-8") : null;
@@ -2802,7 +3417,7 @@ async function mergeRosterFromRemote(remote, paths) {
2802
3417
  }
2803
3418
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
2804
3419
  try {
2805
- const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
3420
+ const agentConfigPath = path8.join(EXE_AI_DIR, "agent-config.json");
2806
3421
  let local = {};
2807
3422
  if (existsSync7(agentConfigPath)) {
2808
3423
  try {