@askexenow/exe-os 0.9.7 → 0.9.9

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +953 -105
  2. package/dist/bin/backfill-responses.js +952 -104
  3. package/dist/bin/backfill-vectors.js +956 -108
  4. package/dist/bin/cleanup-stale-review-tasks.js +802 -58
  5. package/dist/bin/cli.js +2292 -1070
  6. package/dist/bin/exe-agent-config.js +157 -101
  7. package/dist/bin/exe-agent.js +55 -29
  8. package/dist/bin/exe-assign.js +940 -92
  9. package/dist/bin/exe-boot.js +1424 -442
  10. package/dist/bin/exe-call.js +240 -141
  11. package/dist/bin/exe-cloud.js +198 -70
  12. package/dist/bin/exe-dispatch.js +951 -192
  13. package/dist/bin/exe-doctor.js +791 -51
  14. package/dist/bin/exe-export-behaviors.js +790 -42
  15. package/dist/bin/exe-forget.js +771 -31
  16. package/dist/bin/exe-gateway.js +1592 -521
  17. package/dist/bin/exe-heartbeat.js +850 -109
  18. package/dist/bin/exe-kill.js +783 -35
  19. package/dist/bin/exe-launch-agent.js +1030 -107
  20. package/dist/bin/exe-link.js +916 -110
  21. package/dist/bin/exe-new-employee.js +526 -217
  22. package/dist/bin/exe-pending-messages.js +1046 -62
  23. package/dist/bin/exe-pending-notifications.js +1318 -111
  24. package/dist/bin/exe-pending-reviews.js +1040 -72
  25. package/dist/bin/exe-rename.js +772 -59
  26. package/dist/bin/exe-review.js +772 -32
  27. package/dist/bin/exe-search.js +982 -128
  28. package/dist/bin/exe-session-cleanup.js +1180 -306
  29. package/dist/bin/exe-settings.js +185 -105
  30. package/dist/bin/exe-start-codex.js +886 -132
  31. package/dist/bin/exe-start-opencode.js +873 -119
  32. package/dist/bin/exe-status.js +803 -59
  33. package/dist/bin/exe-team.js +772 -32
  34. package/dist/bin/git-sweep.js +1046 -223
  35. package/dist/bin/graph-backfill.js +779 -31
  36. package/dist/bin/graph-export.js +785 -37
  37. package/dist/bin/install.js +632 -200
  38. package/dist/bin/scan-tasks.js +1055 -232
  39. package/dist/bin/setup.js +1419 -320
  40. package/dist/bin/shard-migrate.js +783 -35
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +782 -34
  43. package/dist/gateway/index.js +1444 -449
  44. package/dist/hooks/bug-report-worker.js +1141 -269
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +1044 -221
  47. package/dist/hooks/error-recall.js +989 -135
  48. package/dist/hooks/exe-heartbeat-hook.js +99 -75
  49. package/dist/hooks/ingest-worker.js +4176 -3226
  50. package/dist/hooks/ingest.js +920 -168
  51. package/dist/hooks/instructions-loaded.js +874 -70
  52. package/dist/hooks/notification.js +860 -56
  53. package/dist/hooks/post-compact.js +881 -73
  54. package/dist/hooks/pre-compact.js +1050 -227
  55. package/dist/hooks/pre-tool-use.js +1084 -159
  56. package/dist/hooks/prompt-ingest-worker.js +1089 -164
  57. package/dist/hooks/prompt-submit.js +1469 -515
  58. package/dist/hooks/response-ingest-worker.js +1104 -179
  59. package/dist/hooks/session-end.js +1085 -251
  60. package/dist/hooks/session-start.js +1241 -231
  61. package/dist/hooks/stop.js +935 -109
  62. package/dist/hooks/subagent-stop.js +881 -73
  63. package/dist/hooks/summary-worker.js +1323 -307
  64. package/dist/index.js +1449 -452
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +909 -115
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +42 -9
  69. package/dist/lib/database.js +739 -33
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +2359 -0
  72. package/dist/lib/device-registry.js +760 -47
  73. package/dist/lib/embedder.js +201 -73
  74. package/dist/lib/employee-templates.js +30 -4
  75. package/dist/lib/employees.js +290 -86
  76. package/dist/lib/exe-daemon-client.js +187 -83
  77. package/dist/lib/exe-daemon.js +1696 -616
  78. package/dist/lib/hybrid-search.js +982 -128
  79. package/dist/lib/identity.js +43 -13
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +167 -80
  82. package/dist/lib/reminders.js +35 -5
  83. package/dist/lib/schedules.js +772 -32
  84. package/dist/lib/skill-learning.js +54 -7
  85. package/dist/lib/store.js +779 -31
  86. package/dist/lib/task-router.js +94 -73
  87. package/dist/lib/tasks.js +298 -225
  88. package/dist/lib/tmux-routing.js +246 -172
  89. package/dist/lib/token-spend.js +52 -14
  90. package/dist/mcp/server.js +2893 -850
  91. package/dist/mcp/tools/complete-reminder.js +35 -5
  92. package/dist/mcp/tools/create-reminder.js +35 -5
  93. package/dist/mcp/tools/create-task.js +507 -323
  94. package/dist/mcp/tools/deactivate-behavior.js +40 -10
  95. package/dist/mcp/tools/list-reminders.js +35 -5
  96. package/dist/mcp/tools/list-tasks.js +277 -104
  97. package/dist/mcp/tools/send-message.js +129 -56
  98. package/dist/mcp/tools/update-task.js +1864 -188
  99. package/dist/runtime/index.js +1083 -259
  100. package/dist/tui/App.js +1501 -434
  101. package/package.json +3 -2
@@ -70,9 +70,34 @@ var init_db_retry = __esm({
70
70
  }
71
71
  });
72
72
 
73
+ // src/lib/secure-files.ts
74
+ import { chmodSync, existsSync, mkdirSync } from "fs";
75
+ import { chmod, mkdir } from "fs/promises";
76
+ async function ensurePrivateDir(dirPath) {
77
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
78
+ try {
79
+ await chmod(dirPath, PRIVATE_DIR_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ async function enforcePrivateFile(filePath) {
84
+ try {
85
+ await chmod(filePath, PRIVATE_FILE_MODE);
86
+ } catch {
87
+ }
88
+ }
89
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
90
+ var init_secure_files = __esm({
91
+ "src/lib/secure-files.ts"() {
92
+ "use strict";
93
+ PRIVATE_DIR_MODE = 448;
94
+ PRIVATE_FILE_MODE = 384;
95
+ }
96
+ });
97
+
73
98
  // src/lib/config.ts
74
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
75
- import { readFileSync, existsSync, renameSync } from "fs";
99
+ import { readFile, writeFile } from "fs/promises";
100
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
76
101
  import path from "path";
77
102
  import os from "os";
78
103
  function resolveDataDir() {
@@ -80,7 +105,7 @@ function resolveDataDir() {
80
105
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
81
106
  const newDir = path.join(os.homedir(), ".exe-os");
82
107
  const legacyDir = path.join(os.homedir(), ".exe-mem");
83
- if (!existsSync(newDir) && existsSync(legacyDir)) {
108
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
84
109
  try {
85
110
  renameSync(legacyDir, newDir);
86
111
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -143,9 +168,9 @@ function normalizeAutoUpdate(raw) {
143
168
  }
144
169
  async function loadConfig() {
145
170
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
146
- await mkdir(dir, { recursive: true });
171
+ await ensurePrivateDir(dir);
147
172
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
173
+ if (!existsSync2(configPath)) {
149
174
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
175
  }
151
176
  const raw = await readFile(configPath, "utf-8");
@@ -158,6 +183,7 @@ async function loadConfig() {
158
183
  `);
159
184
  try {
160
185
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
186
+ await enforcePrivateFile(configPath);
161
187
  } catch {
162
188
  }
163
189
  }
@@ -177,6 +203,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
177
203
  var init_config = __esm({
178
204
  "src/lib/config.ts"() {
179
205
  "use strict";
206
+ init_secure_files();
180
207
  EXE_AI_DIR = resolveDataDir();
181
208
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
182
209
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -255,7 +282,7 @@ var init_config = __esm({
255
282
 
256
283
  // src/lib/employees.ts
257
284
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
258
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
285
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
259
286
  import { execSync } from "child_process";
260
287
  import path2 from "path";
261
288
  import os2 from "os";
@@ -272,7 +299,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
272
299
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
273
300
  }
274
301
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
275
- if (!existsSync2(employeesPath)) return [];
302
+ if (!existsSync3(employeesPath)) return [];
276
303
  try {
277
304
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
278
305
  } catch {
@@ -290,7 +317,7 @@ function baseAgentName(name, employees) {
290
317
  if (getEmployee(roster, base)) return base;
291
318
  return name;
292
319
  }
293
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
320
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
294
321
  var init_employees = __esm({
295
322
  "src/lib/employees.ts"() {
296
323
  "use strict";
@@ -298,12 +325,609 @@ var init_employees = __esm({
298
325
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
299
326
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
300
327
  COORDINATOR_ROLE = "COO";
328
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
329
+ }
330
+ });
331
+
332
+ // src/lib/database-adapter.ts
333
+ import os3 from "os";
334
+ import path3 from "path";
335
+ import { createRequire } from "module";
336
+ import { pathToFileURL } from "url";
337
+ function quotedIdentifier(identifier) {
338
+ return `"${identifier.replace(/"/g, '""')}"`;
339
+ }
340
+ function unqualifiedTableName(name) {
341
+ const raw = name.trim().replace(/^"|"$/g, "");
342
+ const parts = raw.split(".");
343
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
344
+ }
345
+ function stripTrailingSemicolon(sql) {
346
+ return sql.trim().replace(/;+\s*$/u, "");
347
+ }
348
+ function appendClause(sql, clause) {
349
+ const trimmed = stripTrailingSemicolon(sql);
350
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
351
+ if (!returningMatch) {
352
+ return `${trimmed}${clause}`;
353
+ }
354
+ const idx = returningMatch.index;
355
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
356
+ }
357
+ function normalizeStatement(stmt) {
358
+ if (typeof stmt === "string") {
359
+ return { kind: "positional", sql: stmt, args: [] };
360
+ }
361
+ const sql = stmt.sql;
362
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
363
+ return { kind: "positional", sql, args: stmt.args ?? [] };
364
+ }
365
+ return { kind: "named", sql, args: stmt.args };
366
+ }
367
+ function rewriteBooleanLiterals(sql) {
368
+ let out = sql;
369
+ for (const column of BOOLEAN_COLUMN_NAMES) {
370
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
371
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
372
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
373
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
374
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
375
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
376
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
377
+ }
378
+ return out;
379
+ }
380
+ function rewriteInsertOrIgnore(sql) {
381
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
382
+ return sql;
383
+ }
384
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
385
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
386
+ }
387
+ function rewriteInsertOrReplace(sql) {
388
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
389
+ if (!match) {
390
+ return sql;
391
+ }
392
+ const rawTable = match[1];
393
+ const rawColumns = match[2];
394
+ const remainder = match[3];
395
+ const tableName = unqualifiedTableName(rawTable);
396
+ const conflictKeys = UPSERT_KEYS[tableName];
397
+ if (!conflictKeys?.length) {
398
+ return sql;
399
+ }
400
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
401
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
402
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
403
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
404
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
405
+ }
406
+ function rewriteSql(sql) {
407
+ let out = sql;
408
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
409
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
410
+ out = rewriteBooleanLiterals(out);
411
+ out = rewriteInsertOrReplace(out);
412
+ out = rewriteInsertOrIgnore(out);
413
+ return stripTrailingSemicolon(out);
414
+ }
415
+ function toBoolean(value) {
416
+ if (value === null || value === void 0) return value;
417
+ if (typeof value === "boolean") return value;
418
+ if (typeof value === "number") return value !== 0;
419
+ if (typeof value === "bigint") return value !== 0n;
420
+ if (typeof value === "string") {
421
+ const normalized = value.trim().toLowerCase();
422
+ if (normalized === "0" || normalized === "false") return false;
423
+ if (normalized === "1" || normalized === "true") return true;
424
+ }
425
+ return Boolean(value);
426
+ }
427
+ function countQuestionMarks(sql, end) {
428
+ let count = 0;
429
+ let inSingle = false;
430
+ let inDouble = false;
431
+ let inLineComment = false;
432
+ let inBlockComment = false;
433
+ for (let i = 0; i < end; i++) {
434
+ const ch = sql[i];
435
+ const next = sql[i + 1];
436
+ if (inLineComment) {
437
+ if (ch === "\n") inLineComment = false;
438
+ continue;
439
+ }
440
+ if (inBlockComment) {
441
+ if (ch === "*" && next === "/") {
442
+ inBlockComment = false;
443
+ i += 1;
444
+ }
445
+ continue;
446
+ }
447
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
448
+ inLineComment = true;
449
+ i += 1;
450
+ continue;
451
+ }
452
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
453
+ inBlockComment = true;
454
+ i += 1;
455
+ continue;
456
+ }
457
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
458
+ inSingle = !inSingle;
459
+ continue;
460
+ }
461
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
462
+ inDouble = !inDouble;
463
+ continue;
464
+ }
465
+ if (!inSingle && !inDouble && ch === "?") {
466
+ count += 1;
467
+ }
468
+ }
469
+ return count;
470
+ }
471
+ function findBooleanPlaceholderIndexes(sql) {
472
+ const indexes = /* @__PURE__ */ new Set();
473
+ for (const column of BOOLEAN_COLUMN_NAMES) {
474
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
475
+ for (const match of sql.matchAll(pattern)) {
476
+ const matchText = match[0];
477
+ const qIndex = match.index + matchText.lastIndexOf("?");
478
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
479
+ }
480
+ }
481
+ return indexes;
482
+ }
483
+ function coerceInsertBooleanArgs(sql, args) {
484
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
485
+ if (!match) return;
486
+ const rawTable = match[1];
487
+ const rawColumns = match[2];
488
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
489
+ if (!boolColumns?.size) return;
490
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
491
+ for (const [index, column] of columns.entries()) {
492
+ if (boolColumns.has(column) && index < args.length) {
493
+ args[index] = toBoolean(args[index]);
494
+ }
495
+ }
496
+ }
497
+ function coerceUpdateBooleanArgs(sql, args) {
498
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
499
+ if (!match) return;
500
+ const rawTable = match[1];
501
+ const setClause = match[2];
502
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
503
+ if (!boolColumns?.size) return;
504
+ const assignments = setClause.split(",");
505
+ let placeholderIndex = 0;
506
+ for (const assignment of assignments) {
507
+ if (!assignment.includes("?")) continue;
508
+ placeholderIndex += 1;
509
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
510
+ if (colMatch && boolColumns.has(colMatch[1])) {
511
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
512
+ }
513
+ }
514
+ }
515
+ function coerceBooleanArgs(sql, args) {
516
+ const nextArgs = [...args];
517
+ coerceInsertBooleanArgs(sql, nextArgs);
518
+ coerceUpdateBooleanArgs(sql, nextArgs);
519
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
520
+ for (const index of placeholderIndexes) {
521
+ if (index > 0 && index <= nextArgs.length) {
522
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
523
+ }
524
+ }
525
+ return nextArgs;
526
+ }
527
+ function convertQuestionMarksToDollarParams(sql) {
528
+ let out = "";
529
+ let placeholder = 0;
530
+ let inSingle = false;
531
+ let inDouble = false;
532
+ let inLineComment = false;
533
+ let inBlockComment = false;
534
+ for (let i = 0; i < sql.length; i++) {
535
+ const ch = sql[i];
536
+ const next = sql[i + 1];
537
+ if (inLineComment) {
538
+ out += ch;
539
+ if (ch === "\n") inLineComment = false;
540
+ continue;
541
+ }
542
+ if (inBlockComment) {
543
+ out += ch;
544
+ if (ch === "*" && next === "/") {
545
+ out += next;
546
+ inBlockComment = false;
547
+ i += 1;
548
+ }
549
+ continue;
550
+ }
551
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
552
+ out += ch + next;
553
+ inLineComment = true;
554
+ i += 1;
555
+ continue;
556
+ }
557
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
558
+ out += ch + next;
559
+ inBlockComment = true;
560
+ i += 1;
561
+ continue;
562
+ }
563
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
564
+ inSingle = !inSingle;
565
+ out += ch;
566
+ continue;
567
+ }
568
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
569
+ inDouble = !inDouble;
570
+ out += ch;
571
+ continue;
572
+ }
573
+ if (!inSingle && !inDouble && ch === "?") {
574
+ placeholder += 1;
575
+ out += `$${placeholder}`;
576
+ continue;
577
+ }
578
+ out += ch;
579
+ }
580
+ return out;
581
+ }
582
+ function translateStatementForPostgres(stmt) {
583
+ const normalized = normalizeStatement(stmt);
584
+ if (normalized.kind === "named") {
585
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
586
+ }
587
+ const rewrittenSql = rewriteSql(normalized.sql);
588
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
589
+ return {
590
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
591
+ args: coercedArgs
592
+ };
593
+ }
594
+ function shouldBypassPostgres(stmt) {
595
+ const normalized = normalizeStatement(stmt);
596
+ if (normalized.kind === "named") {
597
+ return true;
598
+ }
599
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
600
+ }
601
+ function shouldFallbackOnError(error) {
602
+ const message = error instanceof Error ? error.message : String(error);
603
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
604
+ }
605
+ function isReadQuery(sql) {
606
+ const trimmed = sql.trimStart();
607
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
608
+ }
609
+ function buildRow(row, columns) {
610
+ const values = columns.map((column) => row[column]);
611
+ return Object.assign(values, row);
612
+ }
613
+ function buildResultSet(rows, rowsAffected = 0) {
614
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
615
+ const resultRows = rows.map((row) => buildRow(row, columns));
616
+ return {
617
+ columns,
618
+ columnTypes: columns.map(() => ""),
619
+ rows: resultRows,
620
+ rowsAffected,
621
+ lastInsertRowid: void 0,
622
+ toJSON() {
623
+ return {
624
+ columns,
625
+ columnTypes: columns.map(() => ""),
626
+ rows,
627
+ rowsAffected,
628
+ lastInsertRowid: void 0
629
+ };
630
+ }
631
+ };
632
+ }
633
+ async function loadPrismaClient() {
634
+ if (!prismaClientPromise) {
635
+ prismaClientPromise = (async () => {
636
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
637
+ if (explicitPath) {
638
+ const module2 = await import(pathToFileURL(explicitPath).href);
639
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
640
+ if (!PrismaClient2) {
641
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
642
+ }
643
+ return new PrismaClient2();
644
+ }
645
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
646
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
647
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
648
+ const module = await import(pathToFileURL(prismaEntry).href);
649
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
650
+ if (!PrismaClient) {
651
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
652
+ }
653
+ return new PrismaClient();
654
+ })();
655
+ }
656
+ return prismaClientPromise;
657
+ }
658
+ async function ensureCompatibilityViews(prisma) {
659
+ if (!compatibilityBootstrapPromise) {
660
+ compatibilityBootstrapPromise = (async () => {
661
+ for (const mapping of VIEW_MAPPINGS) {
662
+ const relation = mapping.source.replace(/"/g, "");
663
+ const rows = await prisma.$queryRawUnsafe(
664
+ "SELECT to_regclass($1) AS regclass",
665
+ relation
666
+ );
667
+ if (!rows[0]?.regclass) {
668
+ continue;
669
+ }
670
+ await prisma.$executeRawUnsafe(
671
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
672
+ );
673
+ }
674
+ })();
675
+ }
676
+ return compatibilityBootstrapPromise;
677
+ }
678
+ async function executeOnPrisma(executor, stmt) {
679
+ const translated = translateStatementForPostgres(stmt);
680
+ if (isReadQuery(translated.sql)) {
681
+ const rows = await executor.$queryRawUnsafe(
682
+ translated.sql,
683
+ ...translated.args
684
+ );
685
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
686
+ }
687
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
688
+ return buildResultSet([], rowsAffected);
689
+ }
690
+ function splitSqlStatements(sql) {
691
+ const parts = [];
692
+ let current = "";
693
+ let inSingle = false;
694
+ let inDouble = false;
695
+ let inLineComment = false;
696
+ let inBlockComment = false;
697
+ for (let i = 0; i < sql.length; i++) {
698
+ const ch = sql[i];
699
+ const next = sql[i + 1];
700
+ if (inLineComment) {
701
+ current += ch;
702
+ if (ch === "\n") inLineComment = false;
703
+ continue;
704
+ }
705
+ if (inBlockComment) {
706
+ current += ch;
707
+ if (ch === "*" && next === "/") {
708
+ current += next;
709
+ inBlockComment = false;
710
+ i += 1;
711
+ }
712
+ continue;
713
+ }
714
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
715
+ current += ch + next;
716
+ inLineComment = true;
717
+ i += 1;
718
+ continue;
719
+ }
720
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
721
+ current += ch + next;
722
+ inBlockComment = true;
723
+ i += 1;
724
+ continue;
725
+ }
726
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
727
+ inSingle = !inSingle;
728
+ current += ch;
729
+ continue;
730
+ }
731
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
732
+ inDouble = !inDouble;
733
+ current += ch;
734
+ continue;
735
+ }
736
+ if (!inSingle && !inDouble && ch === ";") {
737
+ if (current.trim()) {
738
+ parts.push(current.trim());
739
+ }
740
+ current = "";
741
+ continue;
742
+ }
743
+ current += ch;
744
+ }
745
+ if (current.trim()) {
746
+ parts.push(current.trim());
747
+ }
748
+ return parts;
749
+ }
750
+ async function createPrismaDbAdapter(fallbackClient) {
751
+ const prisma = await loadPrismaClient();
752
+ await ensureCompatibilityViews(prisma);
753
+ let closed = false;
754
+ let adapter;
755
+ const fallbackExecute = async (stmt, error) => {
756
+ if (!fallbackClient) {
757
+ if (error) throw error;
758
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
759
+ }
760
+ if (error) {
761
+ process.stderr.write(
762
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
763
+ `
764
+ );
765
+ }
766
+ return fallbackClient.execute(stmt);
767
+ };
768
+ adapter = {
769
+ async execute(stmt) {
770
+ if (shouldBypassPostgres(stmt)) {
771
+ return fallbackExecute(stmt);
772
+ }
773
+ try {
774
+ return await executeOnPrisma(prisma, stmt);
775
+ } catch (error) {
776
+ if (shouldFallbackOnError(error)) {
777
+ return fallbackExecute(stmt, error);
778
+ }
779
+ throw error;
780
+ }
781
+ },
782
+ async batch(stmts, mode) {
783
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
784
+ if (!fallbackClient) {
785
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
786
+ }
787
+ return fallbackClient.batch(stmts, mode);
788
+ }
789
+ try {
790
+ if (prisma.$transaction) {
791
+ return await prisma.$transaction(async (tx) => {
792
+ const results2 = [];
793
+ for (const stmt of stmts) {
794
+ results2.push(await executeOnPrisma(tx, stmt));
795
+ }
796
+ return results2;
797
+ });
798
+ }
799
+ const results = [];
800
+ for (const stmt of stmts) {
801
+ results.push(await executeOnPrisma(prisma, stmt));
802
+ }
803
+ return results;
804
+ } catch (error) {
805
+ if (fallbackClient && shouldFallbackOnError(error)) {
806
+ process.stderr.write(
807
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
808
+ `
809
+ );
810
+ return fallbackClient.batch(stmts, mode);
811
+ }
812
+ throw error;
813
+ }
814
+ },
815
+ async migrate(stmts) {
816
+ if (fallbackClient) {
817
+ return fallbackClient.migrate(stmts);
818
+ }
819
+ return adapter.batch(stmts, "deferred");
820
+ },
821
+ async transaction(mode) {
822
+ if (!fallbackClient) {
823
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
824
+ }
825
+ return fallbackClient.transaction(mode);
826
+ },
827
+ async executeMultiple(sql) {
828
+ if (fallbackClient && shouldBypassPostgres(sql)) {
829
+ return fallbackClient.executeMultiple(sql);
830
+ }
831
+ for (const statement of splitSqlStatements(sql)) {
832
+ await adapter.execute(statement);
833
+ }
834
+ },
835
+ async sync() {
836
+ if (fallbackClient) {
837
+ return fallbackClient.sync();
838
+ }
839
+ return { frame_no: 0, frames_synced: 0 };
840
+ },
841
+ close() {
842
+ closed = true;
843
+ prismaClientPromise = null;
844
+ compatibilityBootstrapPromise = null;
845
+ void prisma.$disconnect?.();
846
+ },
847
+ get closed() {
848
+ return closed;
849
+ },
850
+ get protocol() {
851
+ return "prisma-postgres";
852
+ }
853
+ };
854
+ return adapter;
855
+ }
856
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
857
+ var init_database_adapter = __esm({
858
+ "src/lib/database-adapter.ts"() {
859
+ "use strict";
860
+ VIEW_MAPPINGS = [
861
+ { view: "memories", source: "memory.memory_records" },
862
+ { view: "tasks", source: "memory.tasks" },
863
+ { view: "behaviors", source: "memory.behaviors" },
864
+ { view: "entities", source: "memory.entities" },
865
+ { view: "relationships", source: "memory.relationships" },
866
+ { view: "entity_memories", source: "memory.entity_memories" },
867
+ { view: "entity_aliases", source: "memory.entity_aliases" },
868
+ { view: "notifications", source: "memory.notifications" },
869
+ { view: "messages", source: "memory.messages" },
870
+ { view: "users", source: "wiki.users" },
871
+ { view: "workspaces", source: "wiki.workspaces" },
872
+ { view: "workspace_users", source: "wiki.workspace_users" },
873
+ { view: "documents", source: "wiki.workspace_documents" },
874
+ { view: "chats", source: "wiki.workspace_chats" }
875
+ ];
876
+ UPSERT_KEYS = {
877
+ memories: ["id"],
878
+ tasks: ["id"],
879
+ behaviors: ["id"],
880
+ entities: ["id"],
881
+ relationships: ["id"],
882
+ entity_aliases: ["alias"],
883
+ notifications: ["id"],
884
+ messages: ["id"],
885
+ users: ["id"],
886
+ workspaces: ["id"],
887
+ workspace_users: ["id"],
888
+ documents: ["id"],
889
+ chats: ["id"]
890
+ };
891
+ BOOLEAN_COLUMNS_BY_TABLE = {
892
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
893
+ behaviors: /* @__PURE__ */ new Set(["active"]),
894
+ notifications: /* @__PURE__ */ new Set(["read"]),
895
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
896
+ };
897
+ BOOLEAN_COLUMN_NAMES = new Set(
898
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
899
+ );
900
+ IMMEDIATE_FALLBACK_PATTERNS = [
901
+ /\bPRAGMA\b/i,
902
+ /\bsqlite_master\b/i,
903
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
904
+ /\bMATCH\b/i,
905
+ /\bvector_distance_cos\s*\(/i,
906
+ /\bjson_extract\s*\(/i,
907
+ /\bjulianday\s*\(/i,
908
+ /\bstrftime\s*\(/i,
909
+ /\blast_insert_rowid\s*\(/i
910
+ ];
911
+ prismaClientPromise = null;
912
+ compatibilityBootstrapPromise = null;
301
913
  }
302
914
  });
303
915
 
304
916
  // src/lib/database.ts
305
917
  import { createClient } from "@libsql/client";
306
918
  async function initDatabase(config) {
919
+ if (_walCheckpointTimer) {
920
+ clearInterval(_walCheckpointTimer);
921
+ _walCheckpointTimer = null;
922
+ }
923
+ if (_daemonClient) {
924
+ _daemonClient.close();
925
+ _daemonClient = null;
926
+ }
927
+ if (_adapterClient && _adapterClient !== _resilientClient) {
928
+ _adapterClient.close();
929
+ }
930
+ _adapterClient = null;
307
931
  if (_client) {
308
932
  _client.close();
309
933
  _client = null;
@@ -317,6 +941,7 @@ async function initDatabase(config) {
317
941
  }
318
942
  _client = createClient(opts);
319
943
  _resilientClient = wrapWithRetry(_client);
944
+ _adapterClient = _resilientClient;
320
945
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
321
946
  });
322
947
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -327,11 +952,17 @@ async function initDatabase(config) {
327
952
  });
328
953
  }, 3e4);
329
954
  _walCheckpointTimer.unref();
955
+ if (process.env.DATABASE_URL) {
956
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
957
+ }
330
958
  }
331
959
  function getClient() {
332
- if (!_resilientClient) {
960
+ if (!_adapterClient) {
333
961
  throw new Error("Database client not initialized. Call initDatabase() first.");
334
962
  }
963
+ if (process.env.DATABASE_URL) {
964
+ return _adapterClient;
965
+ }
335
966
  if (process.env.EXE_IS_DAEMON === "1") {
336
967
  return _resilientClient;
337
968
  }
@@ -624,6 +1255,7 @@ async function ensureSchema() {
624
1255
  project TEXT NOT NULL,
625
1256
  summary TEXT NOT NULL,
626
1257
  task_file TEXT,
1258
+ session_scope TEXT,
627
1259
  read INTEGER NOT NULL DEFAULT 0,
628
1260
  created_at TEXT NOT NULL
629
1261
  );
@@ -632,7 +1264,7 @@ async function ensureSchema() {
632
1264
  ON notifications(read);
633
1265
 
634
1266
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
635
- ON notifications(agent_id);
1267
+ ON notifications(agent_id, session_scope);
636
1268
 
637
1269
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
638
1270
  ON notifications(task_file);
@@ -670,6 +1302,7 @@ async function ensureSchema() {
670
1302
  target_agent TEXT NOT NULL,
671
1303
  target_project TEXT,
672
1304
  target_device TEXT NOT NULL DEFAULT 'local',
1305
+ session_scope TEXT,
673
1306
  content TEXT NOT NULL,
674
1307
  priority TEXT DEFAULT 'normal',
675
1308
  status TEXT DEFAULT 'pending',
@@ -683,10 +1316,31 @@ async function ensureSchema() {
683
1316
  );
684
1317
 
685
1318
  CREATE INDEX IF NOT EXISTS idx_messages_target
686
- ON messages(target_agent, status);
1319
+ ON messages(target_agent, session_scope, status);
687
1320
 
688
1321
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
689
- ON messages(target_agent, from_agent, server_seq);
1322
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1323
+ `);
1324
+ try {
1325
+ await client.execute({
1326
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1327
+ args: []
1328
+ });
1329
+ } catch {
1330
+ }
1331
+ try {
1332
+ await client.execute({
1333
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1334
+ args: []
1335
+ });
1336
+ } catch {
1337
+ }
1338
+ await client.executeMultiple(`
1339
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1340
+ ON notifications(agent_id, session_scope, read, created_at);
1341
+
1342
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1343
+ ON messages(target_agent, session_scope, status, created_at);
690
1344
  `);
691
1345
  try {
692
1346
  await client.execute({
@@ -1270,28 +1924,45 @@ async function ensureSchema() {
1270
1924
  } catch {
1271
1925
  }
1272
1926
  }
1927
+ try {
1928
+ await client.execute({
1929
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
1930
+ args: []
1931
+ });
1932
+ } catch {
1933
+ }
1273
1934
  }
1274
1935
  async function disposeDatabase() {
1936
+ if (_walCheckpointTimer) {
1937
+ clearInterval(_walCheckpointTimer);
1938
+ _walCheckpointTimer = null;
1939
+ }
1275
1940
  if (_daemonClient) {
1276
1941
  _daemonClient.close();
1277
1942
  _daemonClient = null;
1278
1943
  }
1944
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1945
+ _adapterClient.close();
1946
+ }
1947
+ _adapterClient = null;
1279
1948
  if (_client) {
1280
1949
  _client.close();
1281
1950
  _client = null;
1282
1951
  _resilientClient = null;
1283
1952
  }
1284
1953
  }
1285
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
1954
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1286
1955
  var init_database = __esm({
1287
1956
  "src/lib/database.ts"() {
1288
1957
  "use strict";
1289
1958
  init_db_retry();
1290
1959
  init_employees();
1960
+ init_database_adapter();
1291
1961
  _client = null;
1292
1962
  _resilientClient = null;
1293
1963
  _walCheckpointTimer = null;
1294
1964
  _daemonClient = null;
1965
+ _adapterClient = null;
1295
1966
  initTurso = initDatabase;
1296
1967
  disposeTurso = disposeDatabase;
1297
1968
  }
@@ -1302,6 +1973,7 @@ var shard_manager_exports = {};
1302
1973
  __export(shard_manager_exports, {
1303
1974
  disposeShards: () => disposeShards,
1304
1975
  ensureShardSchema: () => ensureShardSchema,
1976
+ getOpenShardCount: () => getOpenShardCount,
1305
1977
  getReadyShardClient: () => getReadyShardClient,
1306
1978
  getShardClient: () => getShardClient,
1307
1979
  getShardsDir: () => getShardsDir,
@@ -1310,15 +1982,18 @@ __export(shard_manager_exports, {
1310
1982
  listShards: () => listShards,
1311
1983
  shardExists: () => shardExists
1312
1984
  });
1313
- import path4 from "path";
1314
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1985
+ import path5 from "path";
1986
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1315
1987
  import { createClient as createClient2 } from "@libsql/client";
1316
1988
  function initShardManager(encryptionKey) {
1317
1989
  _encryptionKey = encryptionKey;
1318
- if (!existsSync4(SHARDS_DIR)) {
1319
- mkdirSync(SHARDS_DIR, { recursive: true });
1990
+ if (!existsSync5(SHARDS_DIR)) {
1991
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1320
1992
  }
1321
1993
  _shardingEnabled = true;
1994
+ if (_evictionTimer) clearInterval(_evictionTimer);
1995
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
1996
+ _evictionTimer.unref();
1322
1997
  }
1323
1998
  function isShardingEnabled() {
1324
1999
  return _shardingEnabled;
@@ -1335,21 +2010,28 @@ function getShardClient(projectName) {
1335
2010
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1336
2011
  }
1337
2012
  const cached = _shards.get(safeName);
1338
- if (cached) return cached;
1339
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
2013
+ if (cached) {
2014
+ _shardLastAccess.set(safeName, Date.now());
2015
+ return cached;
2016
+ }
2017
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2018
+ evictLRU();
2019
+ }
2020
+ const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1340
2021
  const client = createClient2({
1341
2022
  url: `file:${dbPath}`,
1342
2023
  encryptionKey: _encryptionKey
1343
2024
  });
1344
2025
  _shards.set(safeName, client);
2026
+ _shardLastAccess.set(safeName, Date.now());
1345
2027
  return client;
1346
2028
  }
1347
2029
  function shardExists(projectName) {
1348
2030
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1349
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
2031
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1350
2032
  }
1351
2033
  function listShards() {
1352
- if (!existsSync4(SHARDS_DIR)) return [];
2034
+ if (!existsSync5(SHARDS_DIR)) return [];
1353
2035
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1354
2036
  }
1355
2037
  async function ensureShardSchema(client) {
@@ -1401,6 +2083,8 @@ async function ensureShardSchema(client) {
1401
2083
  for (const col of [
1402
2084
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
1403
2085
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2086
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2087
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
1404
2088
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
1405
2089
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
1406
2090
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -1423,7 +2107,23 @@ async function ensureShardSchema(client) {
1423
2107
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1424
2108
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1425
2109
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1426
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2110
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2111
+ // Metadata enrichment columns (must match database.ts)
2112
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2113
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2114
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2115
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2116
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2117
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2118
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2119
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2120
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2121
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2122
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2123
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2124
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2125
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2126
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1427
2127
  ]) {
1428
2128
  try {
1429
2129
  await client.execute(col);
@@ -1522,21 +2222,69 @@ async function getReadyShardClient(projectName) {
1522
2222
  await ensureShardSchema(client);
1523
2223
  return client;
1524
2224
  }
2225
+ function evictLRU() {
2226
+ let oldest = null;
2227
+ let oldestTime = Infinity;
2228
+ for (const [name, time] of _shardLastAccess) {
2229
+ if (time < oldestTime) {
2230
+ oldestTime = time;
2231
+ oldest = name;
2232
+ }
2233
+ }
2234
+ if (oldest) {
2235
+ const client = _shards.get(oldest);
2236
+ if (client) {
2237
+ client.close();
2238
+ }
2239
+ _shards.delete(oldest);
2240
+ _shardLastAccess.delete(oldest);
2241
+ }
2242
+ }
2243
+ function evictIdleShards() {
2244
+ const now = Date.now();
2245
+ const toEvict = [];
2246
+ for (const [name, lastAccess] of _shardLastAccess) {
2247
+ if (now - lastAccess > SHARD_IDLE_MS) {
2248
+ toEvict.push(name);
2249
+ }
2250
+ }
2251
+ for (const name of toEvict) {
2252
+ const client = _shards.get(name);
2253
+ if (client) {
2254
+ client.close();
2255
+ }
2256
+ _shards.delete(name);
2257
+ _shardLastAccess.delete(name);
2258
+ }
2259
+ }
2260
+ function getOpenShardCount() {
2261
+ return _shards.size;
2262
+ }
1525
2263
  function disposeShards() {
2264
+ if (_evictionTimer) {
2265
+ clearInterval(_evictionTimer);
2266
+ _evictionTimer = null;
2267
+ }
1526
2268
  for (const [, client] of _shards) {
1527
2269
  client.close();
1528
2270
  }
1529
2271
  _shards.clear();
2272
+ _shardLastAccess.clear();
1530
2273
  _shardingEnabled = false;
1531
2274
  _encryptionKey = null;
1532
2275
  }
1533
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2276
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
1534
2277
  var init_shard_manager = __esm({
1535
2278
  "src/lib/shard-manager.ts"() {
1536
2279
  "use strict";
1537
2280
  init_config();
1538
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2281
+ SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2282
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2283
+ MAX_OPEN_SHARDS = 10;
2284
+ EVICTION_INTERVAL_MS = 60 * 1e3;
1539
2285
  _shards = /* @__PURE__ */ new Map();
2286
+ _shardLastAccess = /* @__PURE__ */ new Map();
2287
+ _evictionTimer = null;
1540
2288
  _encryptionKey = null;
1541
2289
  _shardingEnabled = false;
1542
2290
  }
@@ -1729,6 +2477,32 @@ ${p.content}`).join("\n\n");
1729
2477
  }
1730
2478
  });
1731
2479
 
2480
+ // src/lib/runtime-table.ts
2481
+ var RUNTIME_TABLE;
2482
+ var init_runtime_table = __esm({
2483
+ "src/lib/runtime-table.ts"() {
2484
+ "use strict";
2485
+ RUNTIME_TABLE = {
2486
+ codex: {
2487
+ binary: "codex",
2488
+ launchMode: "interactive",
2489
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2490
+ inlineFlag: "--no-alt-screen",
2491
+ apiKeyEnv: "OPENAI_API_KEY",
2492
+ defaultModel: "gpt-5.4"
2493
+ },
2494
+ opencode: {
2495
+ binary: "opencode",
2496
+ launchMode: "exec",
2497
+ autoApproveFlag: "--dangerously-skip-permissions",
2498
+ inlineFlag: "",
2499
+ apiKeyEnv: "ANTHROPIC_API_KEY",
2500
+ defaultModel: "anthropic/claude-sonnet-4-6"
2501
+ }
2502
+ };
2503
+ }
2504
+ });
2505
+
1732
2506
  // src/lib/session-key.ts
1733
2507
  import { execSync as execSync2 } from "child_process";
1734
2508
  function normalizeCommand(command) {
@@ -1807,9 +2581,9 @@ __export(active_agent_exports, {
1807
2581
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
1808
2582
  writeActiveAgent: () => writeActiveAgent
1809
2583
  });
1810
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2584
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
1811
2585
  import { execSync as execSync3 } from "child_process";
1812
- import path6 from "path";
2586
+ import path7 from "path";
1813
2587
  function isNameWithOptionalInstance(candidate, baseName) {
1814
2588
  if (candidate === baseName) return true;
1815
2589
  if (!candidate.startsWith(baseName)) return false;
@@ -1853,11 +2627,11 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
1853
2627
  return null;
1854
2628
  }
1855
2629
  function getMarkerPath() {
1856
- return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
2630
+ return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1857
2631
  }
1858
2632
  function writeActiveAgent(agentId, agentRole) {
1859
2633
  try {
1860
- mkdirSync3(CACHE_DIR, { recursive: true });
2634
+ mkdirSync4(CACHE_DIR, { recursive: true });
1861
2635
  writeFileSync3(
1862
2636
  getMarkerPath(),
1863
2637
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
@@ -1922,14 +2696,14 @@ function getAllActiveAgents() {
1922
2696
  const key = file.slice("active-agent-".length, -".json".length);
1923
2697
  if (key === "undefined") continue;
1924
2698
  try {
1925
- const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
2699
+ const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
1926
2700
  const data = JSON.parse(raw);
1927
2701
  if (!data.agentId) continue;
1928
2702
  if (data.startedAt) {
1929
2703
  const age = Date.now() - new Date(data.startedAt).getTime();
1930
2704
  if (age > STALE_MS) {
1931
2705
  try {
1932
- unlinkSync3(path6.join(CACHE_DIR, file));
2706
+ unlinkSync3(path7.join(CACHE_DIR, file));
1933
2707
  } catch {
1934
2708
  }
1935
2709
  continue;
@@ -1952,11 +2726,11 @@ function getAllActiveAgents() {
1952
2726
  function cleanupSessionMarkers() {
1953
2727
  const key = getSessionKey();
1954
2728
  try {
1955
- unlinkSync3(path6.join(CACHE_DIR, `active-agent-${key}.json`));
2729
+ unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
1956
2730
  } catch {
1957
2731
  }
1958
2732
  try {
1959
- unlinkSync3(path6.join(CACHE_DIR, "active-agent-undefined.json"));
2733
+ unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
1960
2734
  } catch {
1961
2735
  }
1962
2736
  }
@@ -1967,18 +2741,18 @@ var init_active_agent = __esm({
1967
2741
  init_config();
1968
2742
  init_session_key();
1969
2743
  init_employees();
1970
- CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
2744
+ CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
1971
2745
  STALE_MS = 24 * 60 * 60 * 1e3;
1972
2746
  }
1973
2747
  });
1974
2748
 
1975
2749
  // src/lib/agent-symlinks.ts
1976
- import os5 from "os";
1977
- import path7 from "path";
2750
+ import os6 from "os";
2751
+ import path8 from "path";
1978
2752
  import {
1979
- existsSync as existsSync6,
2753
+ existsSync as existsSync7,
1980
2754
  lstatSync,
1981
- mkdirSync as mkdirSync4,
2755
+ mkdirSync as mkdirSync5,
1982
2756
  readlinkSync as readlinkSync2,
1983
2757
  symlinkSync as symlinkSync2
1984
2758
  } from "fs";
@@ -2004,12 +2778,12 @@ var init_mcp_prefix = __esm({
2004
2778
  });
2005
2779
 
2006
2780
  // src/lib/preferences.ts
2007
- import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
2008
- import path8 from "path";
2009
- import os6 from "os";
2010
- function loadPreferences(homeDir = os6.homedir()) {
2011
- const configPath = path8.join(homeDir, ".exe-os", "config.json");
2012
- if (!existsSync7(configPath)) return {};
2781
+ import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
2782
+ import path9 from "path";
2783
+ import os7 from "os";
2784
+ function loadPreferences(homeDir = os7.homedir()) {
2785
+ const configPath = path9.join(homeDir, ".exe-os", "config.json");
2786
+ if (!existsSync8(configPath)) return {};
2013
2787
  try {
2014
2788
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
2015
2789
  return config.preferences ?? {};
@@ -2020,32 +2794,33 @@ function loadPreferences(homeDir = os6.homedir()) {
2020
2794
  var init_preferences = __esm({
2021
2795
  "src/lib/preferences.ts"() {
2022
2796
  "use strict";
2797
+ init_secure_files();
2023
2798
  }
2024
2799
  });
2025
2800
 
2026
2801
  // src/adapters/claude/installer.ts
2027
2802
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
2028
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2029
- import path9 from "path";
2030
- import os7 from "os";
2803
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2804
+ import path10 from "path";
2805
+ import os8 from "os";
2031
2806
  import { execSync as execSync4 } from "child_process";
2032
2807
  import { fileURLToPath } from "url";
2033
2808
  function resolvePackageRoot() {
2034
2809
  const thisFile = fileURLToPath(import.meta.url);
2035
- let dir = path9.dirname(thisFile);
2036
- const root = path9.parse(dir).root;
2810
+ let dir = path10.dirname(thisFile);
2811
+ const root = path10.parse(dir).root;
2037
2812
  while (dir !== root) {
2038
- const pkgPath = path9.join(dir, "package.json");
2039
- if (existsSync8(pkgPath)) {
2813
+ const pkgPath = path10.join(dir, "package.json");
2814
+ if (existsSync9(pkgPath)) {
2040
2815
  try {
2041
2816
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2042
2817
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
2043
2818
  } catch {
2044
2819
  }
2045
2820
  }
2046
- dir = path9.dirname(dir);
2821
+ dir = path10.dirname(dir);
2047
2822
  }
2048
- return path9.resolve(path9.dirname(thisFile), "..", "..", "..");
2823
+ return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
2049
2824
  }
2050
2825
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
2051
2826
  var init_installer = __esm({
@@ -2079,19 +2854,19 @@ __export(installer_exports, {
2079
2854
  verifyCodexHooks: () => verifyCodexHooks
2080
2855
  });
2081
2856
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2082
- import { existsSync as existsSync9 } from "fs";
2083
- import path10 from "path";
2084
- import os8 from "os";
2085
- async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2086
- const codexDir = path10.join(homeDir, ".codex");
2087
- const hooksPath = path10.join(codexDir, "hooks.json");
2088
- const logsDir = path10.join(homeDir, ".exe-os", "logs");
2089
- const hookLogPath = path10.join(logsDir, "hooks.log");
2857
+ import { existsSync as existsSync10 } from "fs";
2858
+ import path11 from "path";
2859
+ import os9 from "os";
2860
+ async function mergeCodexHooks(packageRoot, homeDir = os9.homedir()) {
2861
+ const codexDir = path11.join(homeDir, ".codex");
2862
+ const hooksPath = path11.join(codexDir, "hooks.json");
2863
+ const logsDir = path11.join(homeDir, ".exe-os", "logs");
2864
+ const hookLogPath = path11.join(logsDir, "hooks.log");
2090
2865
  const logSuffix = ` 2>> "${hookLogPath}"`;
2091
2866
  await mkdir5(codexDir, { recursive: true });
2092
2867
  await mkdir5(logsDir, { recursive: true });
2093
2868
  let hooksJson = {};
2094
- if (existsSync9(hooksPath)) {
2869
+ if (existsSync10(hooksPath)) {
2095
2870
  try {
2096
2871
  hooksJson = JSON.parse(await readFile5(hooksPath, "utf-8"));
2097
2872
  } catch {
@@ -2108,7 +2883,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2108
2883
  hooks: [
2109
2884
  {
2110
2885
  type: "command",
2111
- command: `node "${path10.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
2886
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
2112
2887
  timeout: 30,
2113
2888
  statusMessage: "exe-os: loading memory brief"
2114
2889
  }
@@ -2123,11 +2898,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2123
2898
  hooks: [
2124
2899
  {
2125
2900
  type: "command",
2126
- command: `node "${path10.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
2901
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
2127
2902
  },
2128
2903
  {
2129
2904
  type: "command",
2130
- command: `node "${path10.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
2905
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
2131
2906
  }
2132
2907
  ]
2133
2908
  },
@@ -2139,11 +2914,11 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2139
2914
  hooks: [
2140
2915
  {
2141
2916
  type: "command",
2142
- command: `node "${path10.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
2917
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
2143
2918
  },
2144
2919
  {
2145
2920
  type: "command",
2146
- command: `node "${path10.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
2921
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
2147
2922
  timeout: 5
2148
2923
  }
2149
2924
  ]
@@ -2156,7 +2931,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2156
2931
  hooks: [
2157
2932
  {
2158
2933
  type: "command",
2159
- command: `node "${path10.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
2934
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
2160
2935
  }
2161
2936
  ]
2162
2937
  },
@@ -2169,7 +2944,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2169
2944
  hooks: [
2170
2945
  {
2171
2946
  type: "command",
2172
- command: `node "${path10.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
2947
+ command: `node "${path11.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
2173
2948
  }
2174
2949
  ]
2175
2950
  },
@@ -2200,15 +2975,15 @@ async function mergeCodexHooks(packageRoot, homeDir = os8.homedir()) {
2200
2975
  await writeFile5(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
2201
2976
  return { added, skipped };
2202
2977
  }
2203
- function verifyCodexHooks(homeDir = os8.homedir()) {
2204
- const hooksPath = path10.join(homeDir, ".codex", "hooks.json");
2205
- if (!existsSync9(hooksPath)) return false;
2978
+ function verifyCodexHooks(homeDir = os9.homedir()) {
2979
+ const hooksPath = path11.join(homeDir, ".codex", "hooks.json");
2980
+ if (!existsSync10(hooksPath)) return false;
2206
2981
  try {
2207
2982
  const hooksJson = JSON.parse(
2208
2983
  __require("fs").readFileSync(hooksPath, "utf-8")
2209
2984
  );
2210
2985
  if (!hooksJson.hooks) return false;
2211
- const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop"];
2986
+ const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
2212
2987
  for (const event of required) {
2213
2988
  const groups = hooksJson.hooks[event];
2214
2989
  if (!groups || !groups.some(
@@ -2222,14 +2997,14 @@ function verifyCodexHooks(homeDir = os8.homedir()) {
2222
2997
  return false;
2223
2998
  }
2224
2999
  }
2225
- async function installCodexStatusLine(homeDir = os8.homedir()) {
3000
+ async function installCodexStatusLine(homeDir = os9.homedir()) {
2226
3001
  const prefs = loadPreferences(homeDir);
2227
3002
  if (prefs.codexStatusLine === false) return "opted-out";
2228
- const codexDir = path10.join(homeDir, ".codex");
2229
- const configPath = path10.join(codexDir, "config.toml");
3003
+ const codexDir = path11.join(homeDir, ".codex");
3004
+ const configPath = path11.join(codexDir, "config.toml");
2230
3005
  await mkdir5(codexDir, { recursive: true });
2231
3006
  let content = "";
2232
- if (existsSync9(configPath)) {
3007
+ if (existsSync10(configPath)) {
2233
3008
  content = await readFile5(configPath, "utf-8");
2234
3009
  if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
2235
3010
  return "already-configured";
@@ -2277,10 +3052,10 @@ var init_installer2 = __esm({
2277
3052
  });
2278
3053
 
2279
3054
  // src/bin/exe-start-codex.ts
2280
- import os9 from "os";
2281
- import path11 from "path";
3055
+ import os10 from "os";
3056
+ import path12 from "path";
2282
3057
  import {
2283
- existsSync as existsSync10,
3058
+ existsSync as existsSync11,
2284
3059
  readFileSync as readFileSync6,
2285
3060
  writeFileSync as writeFileSync6,
2286
3061
  mkdirSync as mkdirSync7,
@@ -2294,16 +3069,16 @@ init_database();
2294
3069
 
2295
3070
  // src/lib/keychain.ts
2296
3071
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2297
- import { existsSync as existsSync3 } from "fs";
2298
- import path3 from "path";
2299
- import os3 from "os";
3072
+ import { existsSync as existsSync4 } from "fs";
3073
+ import path4 from "path";
3074
+ import os4 from "os";
2300
3075
  var SERVICE = "exe-mem";
2301
3076
  var ACCOUNT = "master-key";
2302
3077
  function getKeyDir() {
2303
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
3078
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
2304
3079
  }
2305
3080
  function getKeyPath() {
2306
- return path3.join(getKeyDir(), "master.key");
3081
+ return path4.join(getKeyDir(), "master.key");
2307
3082
  }
2308
3083
  async function tryKeytar() {
2309
3084
  try {
@@ -2324,9 +3099,9 @@ async function getMasterKey() {
2324
3099
  }
2325
3100
  }
2326
3101
  const keyPath = getKeyPath();
2327
- if (!existsSync3(keyPath)) {
3102
+ if (!existsSync4(keyPath)) {
2328
3103
  process.stderr.write(
2329
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3104
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2330
3105
  `
2331
3106
  );
2332
3107
  return null;
@@ -2637,11 +3412,11 @@ function vectorToBlob(vector) {
2637
3412
  }
2638
3413
 
2639
3414
  // src/lib/behaviors-export.ts
2640
- import os4 from "os";
2641
- import path5 from "path";
3415
+ import os5 from "os";
3416
+ import path6 from "path";
2642
3417
  import {
2643
- existsSync as existsSync5,
2644
- mkdirSync as mkdirSync2,
3418
+ existsSync as existsSync6,
3419
+ mkdirSync as mkdirSync3,
2645
3420
  readdirSync as readdirSync2,
2646
3421
  statSync,
2647
3422
  unlinkSync as unlinkSync2,
@@ -2680,15 +3455,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2680
3455
  }
2681
3456
 
2682
3457
  // src/lib/behaviors-export.ts
2683
- var BEHAVIORS_EXPORT_DIR = path5.join(
2684
- os4.homedir(),
3458
+ var BEHAVIORS_EXPORT_DIR = path6.join(
3459
+ os5.homedir(),
2685
3460
  ".exe-os",
2686
3461
  "behaviors-export"
2687
3462
  );
2688
3463
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
2689
3464
  var EXPORT_BEHAVIOR_LIMIT = 30;
2690
3465
  function sweepStaleBehaviorExports(now = Date.now()) {
2691
- if (!existsSync5(BEHAVIORS_EXPORT_DIR)) return;
3466
+ if (!existsSync6(BEHAVIORS_EXPORT_DIR)) return;
2692
3467
  let entries;
2693
3468
  try {
2694
3469
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -2696,7 +3471,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
2696
3471
  return;
2697
3472
  }
2698
3473
  for (const entry of entries) {
2699
- const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
3474
+ const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
2700
3475
  try {
2701
3476
  const stat = statSync(filePath);
2702
3477
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -2729,16 +3504,16 @@ function renderBehaviorExport(behaviors) {
2729
3504
  }
2730
3505
  function exportFilePath(agentId, projectName, sessionKey) {
2731
3506
  if (!sessionKey) {
2732
- return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
3507
+ return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
2733
3508
  }
2734
3509
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2735
- return path5.join(
3510
+ return path6.join(
2736
3511
  BEHAVIORS_EXPORT_DIR,
2737
3512
  `${agentId}-${safeProject}-${sessionKey}.md`
2738
3513
  );
2739
3514
  }
2740
3515
  async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2741
- mkdirSync2(BEHAVIORS_EXPORT_DIR, { recursive: true });
3516
+ mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
2742
3517
  sweepStaleBehaviorExports();
2743
3518
  const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
2744
3519
  if (behaviors.length === 0) return null;
@@ -2750,28 +3525,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2750
3525
 
2751
3526
  // src/bin/exe-start-codex.ts
2752
3527
  init_employees();
2753
-
2754
- // src/lib/runtime-table.ts
2755
- var RUNTIME_TABLE = {
2756
- codex: {
2757
- binary: "codex",
2758
- launchMode: "interactive",
2759
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2760
- inlineFlag: "--no-alt-screen",
2761
- apiKeyEnv: "OPENAI_API_KEY",
2762
- defaultModel: "gpt-5.4"
2763
- },
2764
- opencode: {
2765
- binary: "opencode",
2766
- launchMode: "exec",
2767
- autoApproveFlag: "--dangerously-skip-permissions",
2768
- inlineFlag: "",
2769
- apiKeyEnv: "ANTHROPIC_API_KEY",
2770
- defaultModel: "anthropic/claude-sonnet-4-6"
2771
- }
2772
- };
2773
-
2774
- // src/bin/exe-start-codex.ts
3528
+ init_runtime_table();
2775
3529
  var CODEX = RUNTIME_TABLE.codex;
2776
3530
  var BOOT_INSTRUCTIONS = `
2777
3531
  ---
@@ -2782,7 +3536,7 @@ When done with a task: call update_task with status "done".
2782
3536
  Always call store_memory to persist important findings.
2783
3537
  `;
2784
3538
  function resolveAgent(argv) {
2785
- const invokedAs = path11.basename(argv[1] ?? "");
3539
+ const invokedAs = path12.basename(argv[1] ?? "");
2786
3540
  if (invokedAs && invokedAs !== "exe-start-codex" && !invokedAs.endsWith(".js")) {
2787
3541
  const agent2 = invokedAs.replace(/-codex$/, "").toLowerCase();
2788
3542
  return { agent: agent2, passthrough: argv.slice(2) };
@@ -2808,9 +3562,9 @@ function resolveAgent(argv) {
2808
3562
  return { agent, passthrough, sessionName };
2809
3563
  }
2810
3564
  function loadIdentity(agent) {
2811
- const dir = path11.join(os9.homedir(), ".exe-os", "identity");
2812
- const exact = path11.join(dir, `${agent}.md`);
2813
- if (existsSync10(exact)) {
3565
+ const dir = path12.join(os10.homedir(), ".exe-os", "identity");
3566
+ const exact = path12.join(dir, `${agent}.md`);
3567
+ if (existsSync11(exact)) {
2814
3568
  const content = readFileSync6(exact, "utf-8").trim();
2815
3569
  if (content) return content;
2816
3570
  }
@@ -2818,13 +3572,13 @@ function loadIdentity(agent) {
2818
3572
  const files = readdirSync4(dir);
2819
3573
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
2820
3574
  if (match) {
2821
- const content = readFileSync6(path11.join(dir, match), "utf-8").trim();
3575
+ const content = readFileSync6(path12.join(dir, match), "utf-8").trim();
2822
3576
  if (content) return content;
2823
3577
  }
2824
3578
  } catch {
2825
3579
  }
2826
3580
  try {
2827
- const rosterPath = path11.join(os9.homedir(), ".exe-os", "exe-employees.json");
3581
+ const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
2828
3582
  const roster = JSON.parse(readFileSync6(rosterPath, "utf8"));
2829
3583
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
2830
3584
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
@@ -2835,17 +3589,17 @@ function loadIdentity(agent) {
2835
3589
  return null;
2836
3590
  }
2837
3591
  function writePromptFile(agent, identity, behaviorsPath) {
2838
- const promptDir = path11.join(os9.homedir(), ".exe-os", "codex-prompt");
3592
+ const promptDir = path12.join(os10.homedir(), ".exe-os", "codex-prompt");
2839
3593
  mkdirSync7(promptDir, { recursive: true });
2840
3594
  let prompt = identity;
2841
- if (behaviorsPath && existsSync10(behaviorsPath)) {
3595
+ if (behaviorsPath && existsSync11(behaviorsPath)) {
2842
3596
  const behaviors = readFileSync6(behaviorsPath, "utf-8").trim();
2843
3597
  if (behaviors) {
2844
3598
  prompt += "\n\n" + behaviors;
2845
3599
  }
2846
3600
  }
2847
3601
  prompt += "\n" + BOOT_INSTRUCTIONS;
2848
- const outPath = path11.join(promptDir, `${agent}.md`);
3602
+ const outPath = path12.join(promptDir, `${agent}.md`);
2849
3603
  writeFileSync6(outPath, prompt, "utf-8");
2850
3604
  return outPath;
2851
3605
  }
@@ -2926,7 +3680,7 @@ async function main() {
2926
3680
  const empRole = (() => {
2927
3681
  try {
2928
3682
  const emps = readFileSync6(
2929
- path11.join(os9.homedir(), ".exe-os", "exe-employees.json"),
3683
+ path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
2930
3684
  "utf-8"
2931
3685
  );
2932
3686
  const found = JSON.parse(emps).find(
@@ -2963,14 +3717,14 @@ async function main() {
2963
3717
  if (WORKTREE_ROLES.has(empRole)) {
2964
3718
  try {
2965
3719
  const { execSync: es } = await import("child_process");
2966
- const worktreeDir = path11.join(process.cwd(), ".worktrees", worktreeName);
3720
+ const worktreeDir = path12.join(process.cwd(), ".worktrees", worktreeName);
2967
3721
  const branchName = `${worktreeName}/codex-${Date.now()}`;
2968
- if (existsSync10(worktreeDir)) {
3722
+ if (existsSync11(worktreeDir)) {
2969
3723
  worktreePath = worktreeDir;
2970
3724
  process.stderr.write(`[exe-start-codex] Reusing worktree at ${worktreeDir}
2971
3725
  `);
2972
3726
  } else {
2973
- mkdirSync7(path11.dirname(worktreeDir), { recursive: true });
3727
+ mkdirSync7(path12.dirname(worktreeDir), { recursive: true });
2974
3728
  es(`git worktree add "${worktreeDir}" -b "${branchName}" HEAD`, {
2975
3729
  encoding: "utf-8",
2976
3730
  timeout: 3e4