@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
@@ -64,9 +64,34 @@ var init_db_retry = __esm({
64
64
  }
65
65
  });
66
66
 
67
+ // src/lib/secure-files.ts
68
+ import { chmodSync, existsSync, mkdirSync } from "fs";
69
+ import { chmod, mkdir } from "fs/promises";
70
+ async function ensurePrivateDir(dirPath) {
71
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
72
+ try {
73
+ await chmod(dirPath, PRIVATE_DIR_MODE);
74
+ } catch {
75
+ }
76
+ }
77
+ async function enforcePrivateFile(filePath) {
78
+ try {
79
+ await chmod(filePath, PRIVATE_FILE_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
84
+ var init_secure_files = __esm({
85
+ "src/lib/secure-files.ts"() {
86
+ "use strict";
87
+ PRIVATE_DIR_MODE = 448;
88
+ PRIVATE_FILE_MODE = 384;
89
+ }
90
+ });
91
+
67
92
  // src/lib/config.ts
68
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
69
- import { readFileSync, existsSync, renameSync } from "fs";
93
+ import { readFile, writeFile } from "fs/promises";
94
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
70
95
  import path from "path";
71
96
  import os from "os";
72
97
  function resolveDataDir() {
@@ -74,7 +99,7 @@ function resolveDataDir() {
74
99
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
75
100
  const newDir = path.join(os.homedir(), ".exe-os");
76
101
  const legacyDir = path.join(os.homedir(), ".exe-mem");
77
- if (!existsSync(newDir) && existsSync(legacyDir)) {
102
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
78
103
  try {
79
104
  renameSync(legacyDir, newDir);
80
105
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -137,9 +162,9 @@ function normalizeAutoUpdate(raw) {
137
162
  }
138
163
  async function loadConfig() {
139
164
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
140
- await mkdir(dir, { recursive: true });
165
+ await ensurePrivateDir(dir);
141
166
  const configPath = path.join(dir, "config.json");
142
- if (!existsSync(configPath)) {
167
+ if (!existsSync2(configPath)) {
143
168
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
144
169
  }
145
170
  const raw = await readFile(configPath, "utf-8");
@@ -152,6 +177,7 @@ async function loadConfig() {
152
177
  `);
153
178
  try {
154
179
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
180
+ await enforcePrivateFile(configPath);
155
181
  } catch {
156
182
  }
157
183
  }
@@ -171,6 +197,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
171
197
  var init_config = __esm({
172
198
  "src/lib/config.ts"() {
173
199
  "use strict";
200
+ init_secure_files();
174
201
  EXE_AI_DIR = resolveDataDir();
175
202
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
176
203
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -249,7 +276,7 @@ var init_config = __esm({
249
276
 
250
277
  // src/lib/employees.ts
251
278
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
252
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
279
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
253
280
  import { execSync } from "child_process";
254
281
  import path2 from "path";
255
282
  import os2 from "os";
@@ -266,7 +293,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
266
293
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
267
294
  }
268
295
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
269
- if (!existsSync2(employeesPath)) return [];
296
+ if (!existsSync3(employeesPath)) return [];
270
297
  try {
271
298
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
272
299
  } catch {
@@ -284,7 +311,7 @@ function baseAgentName(name, employees) {
284
311
  if (getEmployee(roster, base)) return base;
285
312
  return name;
286
313
  }
287
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
314
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
288
315
  var init_employees = __esm({
289
316
  "src/lib/employees.ts"() {
290
317
  "use strict";
@@ -292,12 +319,609 @@ var init_employees = __esm({
292
319
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
293
320
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
294
321
  COORDINATOR_ROLE = "COO";
322
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
323
+ }
324
+ });
325
+
326
+ // src/lib/database-adapter.ts
327
+ import os3 from "os";
328
+ import path3 from "path";
329
+ import { createRequire } from "module";
330
+ import { pathToFileURL } from "url";
331
+ function quotedIdentifier(identifier) {
332
+ return `"${identifier.replace(/"/g, '""')}"`;
333
+ }
334
+ function unqualifiedTableName(name) {
335
+ const raw = name.trim().replace(/^"|"$/g, "");
336
+ const parts = raw.split(".");
337
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
338
+ }
339
+ function stripTrailingSemicolon(sql) {
340
+ return sql.trim().replace(/;+\s*$/u, "");
341
+ }
342
+ function appendClause(sql, clause) {
343
+ const trimmed = stripTrailingSemicolon(sql);
344
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
345
+ if (!returningMatch) {
346
+ return `${trimmed}${clause}`;
347
+ }
348
+ const idx = returningMatch.index;
349
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
350
+ }
351
+ function normalizeStatement(stmt) {
352
+ if (typeof stmt === "string") {
353
+ return { kind: "positional", sql: stmt, args: [] };
354
+ }
355
+ const sql = stmt.sql;
356
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
357
+ return { kind: "positional", sql, args: stmt.args ?? [] };
358
+ }
359
+ return { kind: "named", sql, args: stmt.args };
360
+ }
361
+ function rewriteBooleanLiterals(sql) {
362
+ let out = sql;
363
+ for (const column of BOOLEAN_COLUMN_NAMES) {
364
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
365
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
366
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
367
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
368
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
369
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
370
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
371
+ }
372
+ return out;
373
+ }
374
+ function rewriteInsertOrIgnore(sql) {
375
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
376
+ return sql;
377
+ }
378
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
379
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
380
+ }
381
+ function rewriteInsertOrReplace(sql) {
382
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
383
+ if (!match) {
384
+ return sql;
385
+ }
386
+ const rawTable = match[1];
387
+ const rawColumns = match[2];
388
+ const remainder = match[3];
389
+ const tableName = unqualifiedTableName(rawTable);
390
+ const conflictKeys = UPSERT_KEYS[tableName];
391
+ if (!conflictKeys?.length) {
392
+ return sql;
393
+ }
394
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
395
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
396
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
397
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
398
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
399
+ }
400
+ function rewriteSql(sql) {
401
+ let out = sql;
402
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
403
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
404
+ out = rewriteBooleanLiterals(out);
405
+ out = rewriteInsertOrReplace(out);
406
+ out = rewriteInsertOrIgnore(out);
407
+ return stripTrailingSemicolon(out);
408
+ }
409
+ function toBoolean(value) {
410
+ if (value === null || value === void 0) return value;
411
+ if (typeof value === "boolean") return value;
412
+ if (typeof value === "number") return value !== 0;
413
+ if (typeof value === "bigint") return value !== 0n;
414
+ if (typeof value === "string") {
415
+ const normalized = value.trim().toLowerCase();
416
+ if (normalized === "0" || normalized === "false") return false;
417
+ if (normalized === "1" || normalized === "true") return true;
418
+ }
419
+ return Boolean(value);
420
+ }
421
+ function countQuestionMarks(sql, end) {
422
+ let count = 0;
423
+ let inSingle = false;
424
+ let inDouble = false;
425
+ let inLineComment = false;
426
+ let inBlockComment = false;
427
+ for (let i = 0; i < end; i++) {
428
+ const ch = sql[i];
429
+ const next = sql[i + 1];
430
+ if (inLineComment) {
431
+ if (ch === "\n") inLineComment = false;
432
+ continue;
433
+ }
434
+ if (inBlockComment) {
435
+ if (ch === "*" && next === "/") {
436
+ inBlockComment = false;
437
+ i += 1;
438
+ }
439
+ continue;
440
+ }
441
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
442
+ inLineComment = true;
443
+ i += 1;
444
+ continue;
445
+ }
446
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
447
+ inBlockComment = true;
448
+ i += 1;
449
+ continue;
450
+ }
451
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
452
+ inSingle = !inSingle;
453
+ continue;
454
+ }
455
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
456
+ inDouble = !inDouble;
457
+ continue;
458
+ }
459
+ if (!inSingle && !inDouble && ch === "?") {
460
+ count += 1;
461
+ }
462
+ }
463
+ return count;
464
+ }
465
+ function findBooleanPlaceholderIndexes(sql) {
466
+ const indexes = /* @__PURE__ */ new Set();
467
+ for (const column of BOOLEAN_COLUMN_NAMES) {
468
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
469
+ for (const match of sql.matchAll(pattern)) {
470
+ const matchText = match[0];
471
+ const qIndex = match.index + matchText.lastIndexOf("?");
472
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
473
+ }
474
+ }
475
+ return indexes;
476
+ }
477
+ function coerceInsertBooleanArgs(sql, args) {
478
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
479
+ if (!match) return;
480
+ const rawTable = match[1];
481
+ const rawColumns = match[2];
482
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
483
+ if (!boolColumns?.size) return;
484
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
485
+ for (const [index, column] of columns.entries()) {
486
+ if (boolColumns.has(column) && index < args.length) {
487
+ args[index] = toBoolean(args[index]);
488
+ }
489
+ }
490
+ }
491
+ function coerceUpdateBooleanArgs(sql, args) {
492
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
493
+ if (!match) return;
494
+ const rawTable = match[1];
495
+ const setClause = match[2];
496
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
497
+ if (!boolColumns?.size) return;
498
+ const assignments = setClause.split(",");
499
+ let placeholderIndex = 0;
500
+ for (const assignment of assignments) {
501
+ if (!assignment.includes("?")) continue;
502
+ placeholderIndex += 1;
503
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
504
+ if (colMatch && boolColumns.has(colMatch[1])) {
505
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
506
+ }
507
+ }
508
+ }
509
+ function coerceBooleanArgs(sql, args) {
510
+ const nextArgs = [...args];
511
+ coerceInsertBooleanArgs(sql, nextArgs);
512
+ coerceUpdateBooleanArgs(sql, nextArgs);
513
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
514
+ for (const index of placeholderIndexes) {
515
+ if (index > 0 && index <= nextArgs.length) {
516
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
517
+ }
518
+ }
519
+ return nextArgs;
520
+ }
521
+ function convertQuestionMarksToDollarParams(sql) {
522
+ let out = "";
523
+ let placeholder = 0;
524
+ let inSingle = false;
525
+ let inDouble = false;
526
+ let inLineComment = false;
527
+ let inBlockComment = false;
528
+ for (let i = 0; i < sql.length; i++) {
529
+ const ch = sql[i];
530
+ const next = sql[i + 1];
531
+ if (inLineComment) {
532
+ out += ch;
533
+ if (ch === "\n") inLineComment = false;
534
+ continue;
535
+ }
536
+ if (inBlockComment) {
537
+ out += ch;
538
+ if (ch === "*" && next === "/") {
539
+ out += next;
540
+ inBlockComment = false;
541
+ i += 1;
542
+ }
543
+ continue;
544
+ }
545
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
546
+ out += ch + next;
547
+ inLineComment = true;
548
+ i += 1;
549
+ continue;
550
+ }
551
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
552
+ out += ch + next;
553
+ inBlockComment = true;
554
+ i += 1;
555
+ continue;
556
+ }
557
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
558
+ inSingle = !inSingle;
559
+ out += ch;
560
+ continue;
561
+ }
562
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
563
+ inDouble = !inDouble;
564
+ out += ch;
565
+ continue;
566
+ }
567
+ if (!inSingle && !inDouble && ch === "?") {
568
+ placeholder += 1;
569
+ out += `$${placeholder}`;
570
+ continue;
571
+ }
572
+ out += ch;
573
+ }
574
+ return out;
575
+ }
576
+ function translateStatementForPostgres(stmt) {
577
+ const normalized = normalizeStatement(stmt);
578
+ if (normalized.kind === "named") {
579
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
580
+ }
581
+ const rewrittenSql = rewriteSql(normalized.sql);
582
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
583
+ return {
584
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
585
+ args: coercedArgs
586
+ };
587
+ }
588
+ function shouldBypassPostgres(stmt) {
589
+ const normalized = normalizeStatement(stmt);
590
+ if (normalized.kind === "named") {
591
+ return true;
592
+ }
593
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
594
+ }
595
+ function shouldFallbackOnError(error) {
596
+ const message = error instanceof Error ? error.message : String(error);
597
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
598
+ }
599
+ function isReadQuery(sql) {
600
+ const trimmed = sql.trimStart();
601
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
602
+ }
603
+ function buildRow(row, columns) {
604
+ const values = columns.map((column) => row[column]);
605
+ return Object.assign(values, row);
606
+ }
607
+ function buildResultSet(rows, rowsAffected = 0) {
608
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
609
+ const resultRows = rows.map((row) => buildRow(row, columns));
610
+ return {
611
+ columns,
612
+ columnTypes: columns.map(() => ""),
613
+ rows: resultRows,
614
+ rowsAffected,
615
+ lastInsertRowid: void 0,
616
+ toJSON() {
617
+ return {
618
+ columns,
619
+ columnTypes: columns.map(() => ""),
620
+ rows,
621
+ rowsAffected,
622
+ lastInsertRowid: void 0
623
+ };
624
+ }
625
+ };
626
+ }
627
+ async function loadPrismaClient() {
628
+ if (!prismaClientPromise) {
629
+ prismaClientPromise = (async () => {
630
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
631
+ if (explicitPath) {
632
+ const module2 = await import(pathToFileURL(explicitPath).href);
633
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
634
+ if (!PrismaClient2) {
635
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
636
+ }
637
+ return new PrismaClient2();
638
+ }
639
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
640
+ const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
641
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
642
+ const module = await import(pathToFileURL(prismaEntry).href);
643
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
644
+ if (!PrismaClient) {
645
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
646
+ }
647
+ return new PrismaClient();
648
+ })();
649
+ }
650
+ return prismaClientPromise;
651
+ }
652
+ async function ensureCompatibilityViews(prisma) {
653
+ if (!compatibilityBootstrapPromise) {
654
+ compatibilityBootstrapPromise = (async () => {
655
+ for (const mapping of VIEW_MAPPINGS) {
656
+ const relation = mapping.source.replace(/"/g, "");
657
+ const rows = await prisma.$queryRawUnsafe(
658
+ "SELECT to_regclass($1) AS regclass",
659
+ relation
660
+ );
661
+ if (!rows[0]?.regclass) {
662
+ continue;
663
+ }
664
+ await prisma.$executeRawUnsafe(
665
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
666
+ );
667
+ }
668
+ })();
669
+ }
670
+ return compatibilityBootstrapPromise;
671
+ }
672
+ async function executeOnPrisma(executor, stmt) {
673
+ const translated = translateStatementForPostgres(stmt);
674
+ if (isReadQuery(translated.sql)) {
675
+ const rows = await executor.$queryRawUnsafe(
676
+ translated.sql,
677
+ ...translated.args
678
+ );
679
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
680
+ }
681
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
682
+ return buildResultSet([], rowsAffected);
683
+ }
684
+ function splitSqlStatements(sql) {
685
+ const parts = [];
686
+ let current = "";
687
+ let inSingle = false;
688
+ let inDouble = false;
689
+ let inLineComment = false;
690
+ let inBlockComment = false;
691
+ for (let i = 0; i < sql.length; i++) {
692
+ const ch = sql[i];
693
+ const next = sql[i + 1];
694
+ if (inLineComment) {
695
+ current += ch;
696
+ if (ch === "\n") inLineComment = false;
697
+ continue;
698
+ }
699
+ if (inBlockComment) {
700
+ current += ch;
701
+ if (ch === "*" && next === "/") {
702
+ current += next;
703
+ inBlockComment = false;
704
+ i += 1;
705
+ }
706
+ continue;
707
+ }
708
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
709
+ current += ch + next;
710
+ inLineComment = true;
711
+ i += 1;
712
+ continue;
713
+ }
714
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
715
+ current += ch + next;
716
+ inBlockComment = true;
717
+ i += 1;
718
+ continue;
719
+ }
720
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
721
+ inSingle = !inSingle;
722
+ current += ch;
723
+ continue;
724
+ }
725
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
726
+ inDouble = !inDouble;
727
+ current += ch;
728
+ continue;
729
+ }
730
+ if (!inSingle && !inDouble && ch === ";") {
731
+ if (current.trim()) {
732
+ parts.push(current.trim());
733
+ }
734
+ current = "";
735
+ continue;
736
+ }
737
+ current += ch;
738
+ }
739
+ if (current.trim()) {
740
+ parts.push(current.trim());
741
+ }
742
+ return parts;
743
+ }
744
+ async function createPrismaDbAdapter(fallbackClient) {
745
+ const prisma = await loadPrismaClient();
746
+ await ensureCompatibilityViews(prisma);
747
+ let closed = false;
748
+ let adapter;
749
+ const fallbackExecute = async (stmt, error) => {
750
+ if (!fallbackClient) {
751
+ if (error) throw error;
752
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
753
+ }
754
+ if (error) {
755
+ process.stderr.write(
756
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
757
+ `
758
+ );
759
+ }
760
+ return fallbackClient.execute(stmt);
761
+ };
762
+ adapter = {
763
+ async execute(stmt) {
764
+ if (shouldBypassPostgres(stmt)) {
765
+ return fallbackExecute(stmt);
766
+ }
767
+ try {
768
+ return await executeOnPrisma(prisma, stmt);
769
+ } catch (error) {
770
+ if (shouldFallbackOnError(error)) {
771
+ return fallbackExecute(stmt, error);
772
+ }
773
+ throw error;
774
+ }
775
+ },
776
+ async batch(stmts, mode) {
777
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
778
+ if (!fallbackClient) {
779
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
780
+ }
781
+ return fallbackClient.batch(stmts, mode);
782
+ }
783
+ try {
784
+ if (prisma.$transaction) {
785
+ return await prisma.$transaction(async (tx) => {
786
+ const results2 = [];
787
+ for (const stmt of stmts) {
788
+ results2.push(await executeOnPrisma(tx, stmt));
789
+ }
790
+ return results2;
791
+ });
792
+ }
793
+ const results = [];
794
+ for (const stmt of stmts) {
795
+ results.push(await executeOnPrisma(prisma, stmt));
796
+ }
797
+ return results;
798
+ } catch (error) {
799
+ if (fallbackClient && shouldFallbackOnError(error)) {
800
+ process.stderr.write(
801
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
802
+ `
803
+ );
804
+ return fallbackClient.batch(stmts, mode);
805
+ }
806
+ throw error;
807
+ }
808
+ },
809
+ async migrate(stmts) {
810
+ if (fallbackClient) {
811
+ return fallbackClient.migrate(stmts);
812
+ }
813
+ return adapter.batch(stmts, "deferred");
814
+ },
815
+ async transaction(mode) {
816
+ if (!fallbackClient) {
817
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
818
+ }
819
+ return fallbackClient.transaction(mode);
820
+ },
821
+ async executeMultiple(sql) {
822
+ if (fallbackClient && shouldBypassPostgres(sql)) {
823
+ return fallbackClient.executeMultiple(sql);
824
+ }
825
+ for (const statement of splitSqlStatements(sql)) {
826
+ await adapter.execute(statement);
827
+ }
828
+ },
829
+ async sync() {
830
+ if (fallbackClient) {
831
+ return fallbackClient.sync();
832
+ }
833
+ return { frame_no: 0, frames_synced: 0 };
834
+ },
835
+ close() {
836
+ closed = true;
837
+ prismaClientPromise = null;
838
+ compatibilityBootstrapPromise = null;
839
+ void prisma.$disconnect?.();
840
+ },
841
+ get closed() {
842
+ return closed;
843
+ },
844
+ get protocol() {
845
+ return "prisma-postgres";
846
+ }
847
+ };
848
+ return adapter;
849
+ }
850
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
851
+ var init_database_adapter = __esm({
852
+ "src/lib/database-adapter.ts"() {
853
+ "use strict";
854
+ VIEW_MAPPINGS = [
855
+ { view: "memories", source: "memory.memory_records" },
856
+ { view: "tasks", source: "memory.tasks" },
857
+ { view: "behaviors", source: "memory.behaviors" },
858
+ { view: "entities", source: "memory.entities" },
859
+ { view: "relationships", source: "memory.relationships" },
860
+ { view: "entity_memories", source: "memory.entity_memories" },
861
+ { view: "entity_aliases", source: "memory.entity_aliases" },
862
+ { view: "notifications", source: "memory.notifications" },
863
+ { view: "messages", source: "memory.messages" },
864
+ { view: "users", source: "wiki.users" },
865
+ { view: "workspaces", source: "wiki.workspaces" },
866
+ { view: "workspace_users", source: "wiki.workspace_users" },
867
+ { view: "documents", source: "wiki.workspace_documents" },
868
+ { view: "chats", source: "wiki.workspace_chats" }
869
+ ];
870
+ UPSERT_KEYS = {
871
+ memories: ["id"],
872
+ tasks: ["id"],
873
+ behaviors: ["id"],
874
+ entities: ["id"],
875
+ relationships: ["id"],
876
+ entity_aliases: ["alias"],
877
+ notifications: ["id"],
878
+ messages: ["id"],
879
+ users: ["id"],
880
+ workspaces: ["id"],
881
+ workspace_users: ["id"],
882
+ documents: ["id"],
883
+ chats: ["id"]
884
+ };
885
+ BOOLEAN_COLUMNS_BY_TABLE = {
886
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
887
+ behaviors: /* @__PURE__ */ new Set(["active"]),
888
+ notifications: /* @__PURE__ */ new Set(["read"]),
889
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
890
+ };
891
+ BOOLEAN_COLUMN_NAMES = new Set(
892
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
893
+ );
894
+ IMMEDIATE_FALLBACK_PATTERNS = [
895
+ /\bPRAGMA\b/i,
896
+ /\bsqlite_master\b/i,
897
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
898
+ /\bMATCH\b/i,
899
+ /\bvector_distance_cos\s*\(/i,
900
+ /\bjson_extract\s*\(/i,
901
+ /\bjulianday\s*\(/i,
902
+ /\bstrftime\s*\(/i,
903
+ /\blast_insert_rowid\s*\(/i
904
+ ];
905
+ prismaClientPromise = null;
906
+ compatibilityBootstrapPromise = null;
295
907
  }
296
908
  });
297
909
 
298
910
  // src/lib/database.ts
299
911
  import { createClient } from "@libsql/client";
300
912
  async function initDatabase(config) {
913
+ if (_walCheckpointTimer) {
914
+ clearInterval(_walCheckpointTimer);
915
+ _walCheckpointTimer = null;
916
+ }
917
+ if (_daemonClient) {
918
+ _daemonClient.close();
919
+ _daemonClient = null;
920
+ }
921
+ if (_adapterClient && _adapterClient !== _resilientClient) {
922
+ _adapterClient.close();
923
+ }
924
+ _adapterClient = null;
301
925
  if (_client) {
302
926
  _client.close();
303
927
  _client = null;
@@ -311,6 +935,7 @@ async function initDatabase(config) {
311
935
  }
312
936
  _client = createClient(opts);
313
937
  _resilientClient = wrapWithRetry(_client);
938
+ _adapterClient = _resilientClient;
314
939
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
315
940
  });
316
941
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -321,11 +946,17 @@ async function initDatabase(config) {
321
946
  });
322
947
  }, 3e4);
323
948
  _walCheckpointTimer.unref();
949
+ if (process.env.DATABASE_URL) {
950
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
951
+ }
324
952
  }
325
953
  function getClient() {
326
- if (!_resilientClient) {
954
+ if (!_adapterClient) {
327
955
  throw new Error("Database client not initialized. Call initDatabase() first.");
328
956
  }
957
+ if (process.env.DATABASE_URL) {
958
+ return _adapterClient;
959
+ }
329
960
  if (process.env.EXE_IS_DAEMON === "1") {
330
961
  return _resilientClient;
331
962
  }
@@ -618,6 +1249,7 @@ async function ensureSchema() {
618
1249
  project TEXT NOT NULL,
619
1250
  summary TEXT NOT NULL,
620
1251
  task_file TEXT,
1252
+ session_scope TEXT,
621
1253
  read INTEGER NOT NULL DEFAULT 0,
622
1254
  created_at TEXT NOT NULL
623
1255
  );
@@ -626,7 +1258,7 @@ async function ensureSchema() {
626
1258
  ON notifications(read);
627
1259
 
628
1260
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
629
- ON notifications(agent_id);
1261
+ ON notifications(agent_id, session_scope);
630
1262
 
631
1263
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
632
1264
  ON notifications(task_file);
@@ -664,6 +1296,7 @@ async function ensureSchema() {
664
1296
  target_agent TEXT NOT NULL,
665
1297
  target_project TEXT,
666
1298
  target_device TEXT NOT NULL DEFAULT 'local',
1299
+ session_scope TEXT,
667
1300
  content TEXT NOT NULL,
668
1301
  priority TEXT DEFAULT 'normal',
669
1302
  status TEXT DEFAULT 'pending',
@@ -677,10 +1310,31 @@ async function ensureSchema() {
677
1310
  );
678
1311
 
679
1312
  CREATE INDEX IF NOT EXISTS idx_messages_target
680
- ON messages(target_agent, status);
1313
+ ON messages(target_agent, session_scope, status);
681
1314
 
682
1315
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
683
- ON messages(target_agent, from_agent, server_seq);
1316
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1317
+ `);
1318
+ try {
1319
+ await client.execute({
1320
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1321
+ args: []
1322
+ });
1323
+ } catch {
1324
+ }
1325
+ try {
1326
+ await client.execute({
1327
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1328
+ args: []
1329
+ });
1330
+ } catch {
1331
+ }
1332
+ await client.executeMultiple(`
1333
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1334
+ ON notifications(agent_id, session_scope, read, created_at);
1335
+
1336
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1337
+ ON messages(target_agent, session_scope, status, created_at);
684
1338
  `);
685
1339
  try {
686
1340
  await client.execute({
@@ -1264,28 +1918,45 @@ async function ensureSchema() {
1264
1918
  } catch {
1265
1919
  }
1266
1920
  }
1921
+ try {
1922
+ await client.execute({
1923
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
1924
+ args: []
1925
+ });
1926
+ } catch {
1927
+ }
1267
1928
  }
1268
1929
  async function disposeDatabase() {
1930
+ if (_walCheckpointTimer) {
1931
+ clearInterval(_walCheckpointTimer);
1932
+ _walCheckpointTimer = null;
1933
+ }
1269
1934
  if (_daemonClient) {
1270
1935
  _daemonClient.close();
1271
1936
  _daemonClient = null;
1272
1937
  }
1938
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1939
+ _adapterClient.close();
1940
+ }
1941
+ _adapterClient = null;
1273
1942
  if (_client) {
1274
1943
  _client.close();
1275
1944
  _client = null;
1276
1945
  _resilientClient = null;
1277
1946
  }
1278
1947
  }
1279
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
1948
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1280
1949
  var init_database = __esm({
1281
1950
  "src/lib/database.ts"() {
1282
1951
  "use strict";
1283
1952
  init_db_retry();
1284
1953
  init_employees();
1954
+ init_database_adapter();
1285
1955
  _client = null;
1286
1956
  _resilientClient = null;
1287
1957
  _walCheckpointTimer = null;
1288
1958
  _daemonClient = null;
1959
+ _adapterClient = null;
1289
1960
  initTurso = initDatabase;
1290
1961
  disposeTurso = disposeDatabase;
1291
1962
  }
@@ -1296,6 +1967,7 @@ var shard_manager_exports = {};
1296
1967
  __export(shard_manager_exports, {
1297
1968
  disposeShards: () => disposeShards,
1298
1969
  ensureShardSchema: () => ensureShardSchema,
1970
+ getOpenShardCount: () => getOpenShardCount,
1299
1971
  getReadyShardClient: () => getReadyShardClient,
1300
1972
  getShardClient: () => getShardClient,
1301
1973
  getShardsDir: () => getShardsDir,
@@ -1304,15 +1976,18 @@ __export(shard_manager_exports, {
1304
1976
  listShards: () => listShards,
1305
1977
  shardExists: () => shardExists
1306
1978
  });
1307
- import path4 from "path";
1308
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1979
+ import path5 from "path";
1980
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1309
1981
  import { createClient as createClient2 } from "@libsql/client";
1310
1982
  function initShardManager(encryptionKey) {
1311
1983
  _encryptionKey = encryptionKey;
1312
- if (!existsSync4(SHARDS_DIR)) {
1313
- mkdirSync(SHARDS_DIR, { recursive: true });
1984
+ if (!existsSync5(SHARDS_DIR)) {
1985
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1314
1986
  }
1315
1987
  _shardingEnabled = true;
1988
+ if (_evictionTimer) clearInterval(_evictionTimer);
1989
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
1990
+ _evictionTimer.unref();
1316
1991
  }
1317
1992
  function isShardingEnabled() {
1318
1993
  return _shardingEnabled;
@@ -1329,21 +2004,28 @@ function getShardClient(projectName) {
1329
2004
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1330
2005
  }
1331
2006
  const cached = _shards.get(safeName);
1332
- if (cached) return cached;
1333
- const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
2007
+ if (cached) {
2008
+ _shardLastAccess.set(safeName, Date.now());
2009
+ return cached;
2010
+ }
2011
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2012
+ evictLRU();
2013
+ }
2014
+ const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1334
2015
  const client = createClient2({
1335
2016
  url: `file:${dbPath}`,
1336
2017
  encryptionKey: _encryptionKey
1337
2018
  });
1338
2019
  _shards.set(safeName, client);
2020
+ _shardLastAccess.set(safeName, Date.now());
1339
2021
  return client;
1340
2022
  }
1341
2023
  function shardExists(projectName) {
1342
2024
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1343
- return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
2025
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1344
2026
  }
1345
2027
  function listShards() {
1346
- if (!existsSync4(SHARDS_DIR)) return [];
2028
+ if (!existsSync5(SHARDS_DIR)) return [];
1347
2029
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1348
2030
  }
1349
2031
  async function ensureShardSchema(client) {
@@ -1395,6 +2077,8 @@ async function ensureShardSchema(client) {
1395
2077
  for (const col of [
1396
2078
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
1397
2079
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2080
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2081
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
1398
2082
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
1399
2083
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
1400
2084
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -1417,7 +2101,23 @@ async function ensureShardSchema(client) {
1417
2101
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1418
2102
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1419
2103
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1420
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
2104
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
2105
+ // Metadata enrichment columns (must match database.ts)
2106
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
2107
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
2108
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
2109
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
2110
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
2111
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
2112
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
2113
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
2114
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
2115
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
2116
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
2117
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
2118
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
2119
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
2120
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
1421
2121
  ]) {
1422
2122
  try {
1423
2123
  await client.execute(col);
@@ -1516,21 +2216,69 @@ async function getReadyShardClient(projectName) {
1516
2216
  await ensureShardSchema(client);
1517
2217
  return client;
1518
2218
  }
2219
+ function evictLRU() {
2220
+ let oldest = null;
2221
+ let oldestTime = Infinity;
2222
+ for (const [name, time] of _shardLastAccess) {
2223
+ if (time < oldestTime) {
2224
+ oldestTime = time;
2225
+ oldest = name;
2226
+ }
2227
+ }
2228
+ if (oldest) {
2229
+ const client = _shards.get(oldest);
2230
+ if (client) {
2231
+ client.close();
2232
+ }
2233
+ _shards.delete(oldest);
2234
+ _shardLastAccess.delete(oldest);
2235
+ }
2236
+ }
2237
+ function evictIdleShards() {
2238
+ const now = Date.now();
2239
+ const toEvict = [];
2240
+ for (const [name, lastAccess] of _shardLastAccess) {
2241
+ if (now - lastAccess > SHARD_IDLE_MS) {
2242
+ toEvict.push(name);
2243
+ }
2244
+ }
2245
+ for (const name of toEvict) {
2246
+ const client = _shards.get(name);
2247
+ if (client) {
2248
+ client.close();
2249
+ }
2250
+ _shards.delete(name);
2251
+ _shardLastAccess.delete(name);
2252
+ }
2253
+ }
2254
+ function getOpenShardCount() {
2255
+ return _shards.size;
2256
+ }
1519
2257
  function disposeShards() {
2258
+ if (_evictionTimer) {
2259
+ clearInterval(_evictionTimer);
2260
+ _evictionTimer = null;
2261
+ }
1520
2262
  for (const [, client] of _shards) {
1521
2263
  client.close();
1522
2264
  }
1523
2265
  _shards.clear();
2266
+ _shardLastAccess.clear();
1524
2267
  _shardingEnabled = false;
1525
2268
  _encryptionKey = null;
1526
2269
  }
1527
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2270
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
1528
2271
  var init_shard_manager = __esm({
1529
2272
  "src/lib/shard-manager.ts"() {
1530
2273
  "use strict";
1531
2274
  init_config();
1532
- SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
2275
+ SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2276
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2277
+ MAX_OPEN_SHARDS = 10;
2278
+ EVICTION_INTERVAL_MS = 60 * 1e3;
1533
2279
  _shards = /* @__PURE__ */ new Map();
2280
+ _shardLastAccess = /* @__PURE__ */ new Map();
2281
+ _evictionTimer = null;
1534
2282
  _encryptionKey = null;
1535
2283
  _shardingEnabled = false;
1536
2284
  }
@@ -1723,6 +2471,32 @@ ${p.content}`).join("\n\n");
1723
2471
  }
1724
2472
  });
1725
2473
 
2474
+ // src/lib/runtime-table.ts
2475
+ var RUNTIME_TABLE;
2476
+ var init_runtime_table = __esm({
2477
+ "src/lib/runtime-table.ts"() {
2478
+ "use strict";
2479
+ RUNTIME_TABLE = {
2480
+ codex: {
2481
+ binary: "codex",
2482
+ launchMode: "interactive",
2483
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2484
+ inlineFlag: "--no-alt-screen",
2485
+ apiKeyEnv: "OPENAI_API_KEY",
2486
+ defaultModel: "gpt-5.4"
2487
+ },
2488
+ opencode: {
2489
+ binary: "opencode",
2490
+ launchMode: "exec",
2491
+ autoApproveFlag: "--dangerously-skip-permissions",
2492
+ inlineFlag: "",
2493
+ apiKeyEnv: "ANTHROPIC_API_KEY",
2494
+ defaultModel: "anthropic/claude-sonnet-4-6"
2495
+ }
2496
+ };
2497
+ }
2498
+ });
2499
+
1726
2500
  // src/lib/session-key.ts
1727
2501
  import { execSync as execSync2 } from "child_process";
1728
2502
  function normalizeCommand(command) {
@@ -1801,9 +2575,9 @@ __export(active_agent_exports, {
1801
2575
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
1802
2576
  writeActiveAgent: () => writeActiveAgent
1803
2577
  });
1804
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2578
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
1805
2579
  import { execSync as execSync3 } from "child_process";
1806
- import path6 from "path";
2580
+ import path7 from "path";
1807
2581
  function isNameWithOptionalInstance(candidate, baseName) {
1808
2582
  if (candidate === baseName) return true;
1809
2583
  if (!candidate.startsWith(baseName)) return false;
@@ -1847,11 +2621,11 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
1847
2621
  return null;
1848
2622
  }
1849
2623
  function getMarkerPath() {
1850
- return path6.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
2624
+ return path7.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1851
2625
  }
1852
2626
  function writeActiveAgent(agentId, agentRole) {
1853
2627
  try {
1854
- mkdirSync3(CACHE_DIR, { recursive: true });
2628
+ mkdirSync4(CACHE_DIR, { recursive: true });
1855
2629
  writeFileSync3(
1856
2630
  getMarkerPath(),
1857
2631
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
@@ -1916,14 +2690,14 @@ function getAllActiveAgents() {
1916
2690
  const key = file.slice("active-agent-".length, -".json".length);
1917
2691
  if (key === "undefined") continue;
1918
2692
  try {
1919
- const raw = readFileSync3(path6.join(CACHE_DIR, file), "utf8");
2693
+ const raw = readFileSync3(path7.join(CACHE_DIR, file), "utf8");
1920
2694
  const data = JSON.parse(raw);
1921
2695
  if (!data.agentId) continue;
1922
2696
  if (data.startedAt) {
1923
2697
  const age = Date.now() - new Date(data.startedAt).getTime();
1924
2698
  if (age > STALE_MS) {
1925
2699
  try {
1926
- unlinkSync3(path6.join(CACHE_DIR, file));
2700
+ unlinkSync3(path7.join(CACHE_DIR, file));
1927
2701
  } catch {
1928
2702
  }
1929
2703
  continue;
@@ -1946,11 +2720,11 @@ function getAllActiveAgents() {
1946
2720
  function cleanupSessionMarkers() {
1947
2721
  const key = getSessionKey();
1948
2722
  try {
1949
- unlinkSync3(path6.join(CACHE_DIR, `active-agent-${key}.json`));
2723
+ unlinkSync3(path7.join(CACHE_DIR, `active-agent-${key}.json`));
1950
2724
  } catch {
1951
2725
  }
1952
2726
  try {
1953
- unlinkSync3(path6.join(CACHE_DIR, "active-agent-undefined.json"));
2727
+ unlinkSync3(path7.join(CACHE_DIR, "active-agent-undefined.json"));
1954
2728
  } catch {
1955
2729
  }
1956
2730
  }
@@ -1961,18 +2735,18 @@ var init_active_agent = __esm({
1961
2735
  init_config();
1962
2736
  init_session_key();
1963
2737
  init_employees();
1964
- CACHE_DIR = path6.join(EXE_AI_DIR, "session-cache");
2738
+ CACHE_DIR = path7.join(EXE_AI_DIR, "session-cache");
1965
2739
  STALE_MS = 24 * 60 * 60 * 1e3;
1966
2740
  }
1967
2741
  });
1968
2742
 
1969
2743
  // src/lib/agent-symlinks.ts
1970
- import os5 from "os";
1971
- import path7 from "path";
2744
+ import os6 from "os";
2745
+ import path8 from "path";
1972
2746
  import {
1973
- existsSync as existsSync6,
2747
+ existsSync as existsSync7,
1974
2748
  lstatSync,
1975
- mkdirSync as mkdirSync4,
2749
+ mkdirSync as mkdirSync5,
1976
2750
  readlinkSync as readlinkSync2,
1977
2751
  symlinkSync as symlinkSync2
1978
2752
  } from "fs";
@@ -1998,38 +2772,39 @@ var init_mcp_prefix = __esm({
1998
2772
  });
1999
2773
 
2000
2774
  // src/lib/preferences.ts
2001
- import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
2002
- import path8 from "path";
2003
- import os6 from "os";
2775
+ import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
2776
+ import path9 from "path";
2777
+ import os7 from "os";
2004
2778
  var init_preferences = __esm({
2005
2779
  "src/lib/preferences.ts"() {
2006
2780
  "use strict";
2781
+ init_secure_files();
2007
2782
  }
2008
2783
  });
2009
2784
 
2010
2785
  // src/adapters/claude/installer.ts
2011
2786
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
2012
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2013
- import path9 from "path";
2014
- import os7 from "os";
2787
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2788
+ import path10 from "path";
2789
+ import os8 from "os";
2015
2790
  import { execSync as execSync4 } from "child_process";
2016
2791
  import { fileURLToPath } from "url";
2017
2792
  function resolvePackageRoot() {
2018
2793
  const thisFile = fileURLToPath(import.meta.url);
2019
- let dir = path9.dirname(thisFile);
2020
- const root = path9.parse(dir).root;
2794
+ let dir = path10.dirname(thisFile);
2795
+ const root = path10.parse(dir).root;
2021
2796
  while (dir !== root) {
2022
- const pkgPath = path9.join(dir, "package.json");
2023
- if (existsSync8(pkgPath)) {
2797
+ const pkgPath = path10.join(dir, "package.json");
2798
+ if (existsSync9(pkgPath)) {
2024
2799
  try {
2025
2800
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2026
2801
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
2027
2802
  } catch {
2028
2803
  }
2029
2804
  }
2030
- dir = path9.dirname(dir);
2805
+ dir = path10.dirname(dir);
2031
2806
  }
2032
- return path9.resolve(path9.dirname(thisFile), "..", "..", "..");
2807
+ return path10.resolve(path10.dirname(thisFile), "..", "..", "..");
2033
2808
  }
2034
2809
  var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
2035
2810
  var init_installer = __esm({
@@ -2247,15 +3022,15 @@ __export(installer_exports, {
2247
3022
  verifyOpenCodeHooks: () => verifyOpenCodeHooks
2248
3023
  });
2249
3024
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2250
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
2251
- import path10 from "path";
2252
- import os8 from "os";
2253
- async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2254
- const configDir = path10.join(homeDir, ".config", "opencode");
2255
- const configPath = path10.join(configDir, "opencode.json");
3025
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
3026
+ import path11 from "path";
3027
+ import os9 from "os";
3028
+ async function registerOpenCodeMcp(packageRoot, homeDir = os9.homedir()) {
3029
+ const configDir = path11.join(homeDir, ".config", "opencode");
3030
+ const configPath = path11.join(configDir, "opencode.json");
2256
3031
  await mkdir5(configDir, { recursive: true });
2257
3032
  let config = {};
2258
- if (existsSync9(configPath)) {
3033
+ if (existsSync10(configPath)) {
2259
3034
  try {
2260
3035
  config = JSON.parse(await readFile5(configPath, "utf-8"));
2261
3036
  } catch {
@@ -2267,7 +3042,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2267
3042
  }
2268
3043
  const newEntry = {
2269
3044
  type: "local",
2270
- command: ["node", path10.join(packageRoot, "dist", "mcp", "server.js")],
3045
+ command: ["node", path11.join(packageRoot, "dist", "mcp", "server.js")],
2271
3046
  enabled: true
2272
3047
  };
2273
3048
  const current = config.mcp["exe-os"];
@@ -2281,15 +3056,15 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os8.homedir()) {
2281
3056
  await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n");
2282
3057
  return true;
2283
3058
  }
2284
- async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
2285
- const pluginDir = path10.join(homeDir, ".config", "opencode", "plugins");
2286
- const pluginPath = path10.join(pluginDir, "exe-os.mjs");
3059
+ async function installOpenCodePlugin(packageRoot, homeDir = os9.homedir()) {
3060
+ const pluginDir = path11.join(homeDir, ".config", "opencode", "plugins");
3061
+ const pluginPath = path11.join(pluginDir, "exe-os.mjs");
2287
3062
  await mkdir5(pluginDir, { recursive: true });
2288
3063
  const pluginContent = PLUGIN_TEMPLATE.replace(
2289
3064
  /__PACKAGE_ROOT__/g,
2290
3065
  packageRoot.replace(/\\/g, "\\\\")
2291
3066
  );
2292
- if (existsSync9(pluginPath)) {
3067
+ if (existsSync10(pluginPath)) {
2293
3068
  const existing = await readFile5(pluginPath, "utf-8");
2294
3069
  if (existing === pluginContent) {
2295
3070
  return false;
@@ -2298,17 +3073,17 @@ async function installOpenCodePlugin(packageRoot, homeDir = os8.homedir()) {
2298
3073
  await writeFile5(pluginPath, pluginContent);
2299
3074
  return true;
2300
3075
  }
2301
- function verifyOpenCodeHooks(homeDir = os8.homedir()) {
2302
- const configPath = path10.join(homeDir, ".config", "opencode", "opencode.json");
2303
- const pluginPath = path10.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
2304
- if (!existsSync9(configPath)) return false;
3076
+ function verifyOpenCodeHooks(homeDir = os9.homedir()) {
3077
+ const configPath = path11.join(homeDir, ".config", "opencode", "opencode.json");
3078
+ const pluginPath = path11.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
3079
+ if (!existsSync10(configPath)) return false;
2305
3080
  try {
2306
3081
  const config = JSON.parse(readFileSync6(configPath, "utf-8"));
2307
3082
  if (!config.mcp?.["exe-os"]?.enabled) return false;
2308
3083
  } catch {
2309
3084
  return false;
2310
3085
  }
2311
- if (!existsSync9(pluginPath)) return false;
3086
+ if (!existsSync10(pluginPath)) return false;
2312
3087
  return true;
2313
3088
  }
2314
3089
  async function runOpenCodeInstaller(homeDir) {
@@ -2333,10 +3108,10 @@ var init_installer2 = __esm({
2333
3108
  });
2334
3109
 
2335
3110
  // src/bin/exe-start-opencode.ts
2336
- import os9 from "os";
2337
- import path11 from "path";
3111
+ import os10 from "os";
3112
+ import path12 from "path";
2338
3113
  import {
2339
- existsSync as existsSync10,
3114
+ existsSync as existsSync11,
2340
3115
  readFileSync as readFileSync7,
2341
3116
  writeFileSync as writeFileSync6,
2342
3117
  mkdirSync as mkdirSync7,
@@ -2350,16 +3125,16 @@ init_database();
2350
3125
 
2351
3126
  // src/lib/keychain.ts
2352
3127
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2353
- import { existsSync as existsSync3 } from "fs";
2354
- import path3 from "path";
2355
- import os3 from "os";
3128
+ import { existsSync as existsSync4 } from "fs";
3129
+ import path4 from "path";
3130
+ import os4 from "os";
2356
3131
  var SERVICE = "exe-mem";
2357
3132
  var ACCOUNT = "master-key";
2358
3133
  function getKeyDir() {
2359
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
3134
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
2360
3135
  }
2361
3136
  function getKeyPath() {
2362
- return path3.join(getKeyDir(), "master.key");
3137
+ return path4.join(getKeyDir(), "master.key");
2363
3138
  }
2364
3139
  async function tryKeytar() {
2365
3140
  try {
@@ -2380,9 +3155,9 @@ async function getMasterKey() {
2380
3155
  }
2381
3156
  }
2382
3157
  const keyPath = getKeyPath();
2383
- if (!existsSync3(keyPath)) {
3158
+ if (!existsSync4(keyPath)) {
2384
3159
  process.stderr.write(
2385
- `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3160
+ `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2386
3161
  `
2387
3162
  );
2388
3163
  return null;
@@ -2693,11 +3468,11 @@ function vectorToBlob(vector) {
2693
3468
  }
2694
3469
 
2695
3470
  // src/lib/behaviors-export.ts
2696
- import os4 from "os";
2697
- import path5 from "path";
3471
+ import os5 from "os";
3472
+ import path6 from "path";
2698
3473
  import {
2699
- existsSync as existsSync5,
2700
- mkdirSync as mkdirSync2,
3474
+ existsSync as existsSync6,
3475
+ mkdirSync as mkdirSync3,
2701
3476
  readdirSync as readdirSync2,
2702
3477
  statSync,
2703
3478
  unlinkSync as unlinkSync2,
@@ -2736,15 +3511,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2736
3511
  }
2737
3512
 
2738
3513
  // src/lib/behaviors-export.ts
2739
- var BEHAVIORS_EXPORT_DIR = path5.join(
2740
- os4.homedir(),
3514
+ var BEHAVIORS_EXPORT_DIR = path6.join(
3515
+ os5.homedir(),
2741
3516
  ".exe-os",
2742
3517
  "behaviors-export"
2743
3518
  );
2744
3519
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
2745
3520
  var EXPORT_BEHAVIOR_LIMIT = 30;
2746
3521
  function sweepStaleBehaviorExports(now = Date.now()) {
2747
- if (!existsSync5(BEHAVIORS_EXPORT_DIR)) return;
3522
+ if (!existsSync6(BEHAVIORS_EXPORT_DIR)) return;
2748
3523
  let entries;
2749
3524
  try {
2750
3525
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -2752,7 +3527,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
2752
3527
  return;
2753
3528
  }
2754
3529
  for (const entry of entries) {
2755
- const filePath = path5.join(BEHAVIORS_EXPORT_DIR, entry);
3530
+ const filePath = path6.join(BEHAVIORS_EXPORT_DIR, entry);
2756
3531
  try {
2757
3532
  const stat = statSync(filePath);
2758
3533
  if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
@@ -2785,16 +3560,16 @@ function renderBehaviorExport(behaviors) {
2785
3560
  }
2786
3561
  function exportFilePath(agentId, projectName, sessionKey) {
2787
3562
  if (!sessionKey) {
2788
- return path5.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
3563
+ return path6.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
2789
3564
  }
2790
3565
  const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2791
- return path5.join(
3566
+ return path6.join(
2792
3567
  BEHAVIORS_EXPORT_DIR,
2793
3568
  `${agentId}-${safeProject}-${sessionKey}.md`
2794
3569
  );
2795
3570
  }
2796
3571
  async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2797
- mkdirSync2(BEHAVIORS_EXPORT_DIR, { recursive: true });
3572
+ mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
2798
3573
  sweepStaleBehaviorExports();
2799
3574
  const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
2800
3575
  if (behaviors.length === 0) return null;
@@ -2806,28 +3581,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
2806
3581
 
2807
3582
  // src/bin/exe-start-opencode.ts
2808
3583
  init_employees();
2809
-
2810
- // src/lib/runtime-table.ts
2811
- var RUNTIME_TABLE = {
2812
- codex: {
2813
- binary: "codex",
2814
- launchMode: "interactive",
2815
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
2816
- inlineFlag: "--no-alt-screen",
2817
- apiKeyEnv: "OPENAI_API_KEY",
2818
- defaultModel: "gpt-5.4"
2819
- },
2820
- opencode: {
2821
- binary: "opencode",
2822
- launchMode: "exec",
2823
- autoApproveFlag: "--dangerously-skip-permissions",
2824
- inlineFlag: "",
2825
- apiKeyEnv: "ANTHROPIC_API_KEY",
2826
- defaultModel: "anthropic/claude-sonnet-4-6"
2827
- }
2828
- };
2829
-
2830
- // src/bin/exe-start-opencode.ts
3584
+ init_runtime_table();
2831
3585
  var OC = RUNTIME_TABLE.opencode;
2832
3586
  var BOOT_INSTRUCTIONS = `
2833
3587
  ---
@@ -2838,7 +3592,7 @@ When done with a task: call update_task with status "done".
2838
3592
  Always call store_memory to persist important findings.
2839
3593
  `;
2840
3594
  function resolveAgent(argv) {
2841
- const invokedAs = path11.basename(argv[1] ?? "");
3595
+ const invokedAs = path12.basename(argv[1] ?? "");
2842
3596
  if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
2843
3597
  const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
2844
3598
  return { agent: agent2, passthrough: argv.slice(2) };
@@ -2855,9 +3609,9 @@ function resolveAgent(argv) {
2855
3609
  return { agent, passthrough };
2856
3610
  }
2857
3611
  function loadIdentity(agent) {
2858
- const dir = path11.join(os9.homedir(), ".exe-os", "identity");
2859
- const exact = path11.join(dir, `${agent}.md`);
2860
- if (existsSync10(exact)) {
3612
+ const dir = path12.join(os10.homedir(), ".exe-os", "identity");
3613
+ const exact = path12.join(dir, `${agent}.md`);
3614
+ if (existsSync11(exact)) {
2861
3615
  const content = readFileSync7(exact, "utf-8").trim();
2862
3616
  if (content) return content;
2863
3617
  }
@@ -2865,13 +3619,13 @@ function loadIdentity(agent) {
2865
3619
  const files = readdirSync4(dir);
2866
3620
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
2867
3621
  if (match) {
2868
- const content = readFileSync7(path11.join(dir, match), "utf-8").trim();
3622
+ const content = readFileSync7(path12.join(dir, match), "utf-8").trim();
2869
3623
  if (content) return content;
2870
3624
  }
2871
3625
  } catch {
2872
3626
  }
2873
3627
  try {
2874
- const rosterPath = path11.join(os9.homedir(), ".exe-os", "exe-employees.json");
3628
+ const rosterPath = path12.join(os10.homedir(), ".exe-os", "exe-employees.json");
2875
3629
  const roster = JSON.parse(readFileSync7(rosterPath, "utf8"));
2876
3630
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
2877
3631
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
@@ -2882,17 +3636,17 @@ function loadIdentity(agent) {
2882
3636
  return null;
2883
3637
  }
2884
3638
  function writeAgentFile(agent, identity, behaviorsPath) {
2885
- const agentDir = path11.join(os9.homedir(), ".config", "opencode", "agents");
3639
+ const agentDir = path12.join(os10.homedir(), ".config", "opencode", "agents");
2886
3640
  mkdirSync7(agentDir, { recursive: true });
2887
3641
  let content = identity;
2888
- if (behaviorsPath && existsSync10(behaviorsPath)) {
3642
+ if (behaviorsPath && existsSync11(behaviorsPath)) {
2889
3643
  const behaviors = readFileSync7(behaviorsPath, "utf-8").trim();
2890
3644
  if (behaviors) {
2891
3645
  content += "\n\n" + behaviors;
2892
3646
  }
2893
3647
  }
2894
3648
  content += "\n" + BOOT_INSTRUCTIONS;
2895
- const outPath = path11.join(agentDir, `${agent}.md`);
3649
+ const outPath = path12.join(agentDir, `${agent}.md`);
2896
3650
  writeFileSync6(outPath, content, "utf-8");
2897
3651
  return outPath;
2898
3652
  }
@@ -2955,7 +3709,7 @@ async function main() {
2955
3709
  const empRole = (() => {
2956
3710
  try {
2957
3711
  const emps = readFileSync7(
2958
- path11.join(os9.homedir(), ".exe-os", "exe-employees.json"),
3712
+ path12.join(os10.homedir(), ".exe-os", "exe-employees.json"),
2959
3713
  "utf-8"
2960
3714
  );
2961
3715
  const found = JSON.parse(emps).find(