@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
@@ -25,9 +25,47 @@ var __copyProps = (to, from, except, desc) => {
25
25
  };
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
 
28
+ // src/lib/secure-files.ts
29
+ import { chmodSync, existsSync, mkdirSync } from "fs";
30
+ import { chmod, mkdir } from "fs/promises";
31
+ async function ensurePrivateDir(dirPath) {
32
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
33
+ try {
34
+ await chmod(dirPath, PRIVATE_DIR_MODE);
35
+ } catch {
36
+ }
37
+ }
38
+ function ensurePrivateDirSync(dirPath) {
39
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
40
+ try {
41
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
42
+ } catch {
43
+ }
44
+ }
45
+ async function enforcePrivateFile(filePath) {
46
+ try {
47
+ await chmod(filePath, PRIVATE_FILE_MODE);
48
+ } catch {
49
+ }
50
+ }
51
+ function enforcePrivateFileSync(filePath) {
52
+ try {
53
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
54
+ } catch {
55
+ }
56
+ }
57
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
58
+ var init_secure_files = __esm({
59
+ "src/lib/secure-files.ts"() {
60
+ "use strict";
61
+ PRIVATE_DIR_MODE = 448;
62
+ PRIVATE_FILE_MODE = 384;
63
+ }
64
+ });
65
+
28
66
  // src/lib/config.ts
29
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
30
- import { readFileSync, existsSync, renameSync } from "fs";
67
+ import { readFile, writeFile } from "fs/promises";
68
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
31
69
  import path from "path";
32
70
  import os from "os";
33
71
  function resolveDataDir() {
@@ -35,7 +73,7 @@ function resolveDataDir() {
35
73
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
36
74
  const newDir = path.join(os.homedir(), ".exe-os");
37
75
  const legacyDir = path.join(os.homedir(), ".exe-mem");
38
- if (!existsSync(newDir) && existsSync(legacyDir)) {
76
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
39
77
  try {
40
78
  renameSync(legacyDir, newDir);
41
79
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -98,9 +136,9 @@ function normalizeAutoUpdate(raw) {
98
136
  }
99
137
  async function loadConfig() {
100
138
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
101
- await mkdir(dir, { recursive: true });
139
+ await ensurePrivateDir(dir);
102
140
  const configPath = path.join(dir, "config.json");
103
- if (!existsSync(configPath)) {
141
+ if (!existsSync2(configPath)) {
104
142
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
105
143
  }
106
144
  const raw = await readFile(configPath, "utf-8");
@@ -113,6 +151,7 @@ async function loadConfig() {
113
151
  `);
114
152
  try {
115
153
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
154
+ await enforcePrivateFile(configPath);
116
155
  } catch {
117
156
  }
118
157
  }
@@ -132,6 +171,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
132
171
  var init_config = __esm({
133
172
  "src/lib/config.ts"() {
134
173
  "use strict";
174
+ init_secure_files();
135
175
  EXE_AI_DIR = resolveDataDir();
136
176
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
137
177
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -278,7 +318,7 @@ var init_session_key = __esm({
278
318
 
279
319
  // src/lib/employees.ts
280
320
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
281
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
321
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
282
322
  import { execSync as execSync2 } from "child_process";
283
323
  import path2 from "path";
284
324
  import os2 from "os";
@@ -299,7 +339,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
299
339
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
300
340
  }
301
341
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
302
- if (!existsSync2(employeesPath)) return [];
342
+ if (!existsSync3(employeesPath)) return [];
303
343
  try {
304
344
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
305
345
  } catch {
@@ -323,7 +363,7 @@ function isMultiInstance(agentName, employees) {
323
363
  if (!emp) return false;
324
364
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
325
365
  }
326
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
366
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
327
367
  var init_employees = __esm({
328
368
  "src/lib/employees.ts"() {
329
369
  "use strict";
@@ -332,17 +372,18 @@ var init_employees = __esm({
332
372
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
333
373
  COORDINATOR_ROLE = "COO";
334
374
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
375
+ IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
335
376
  }
336
377
  });
337
378
 
338
379
  // src/lib/session-registry.ts
339
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
380
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
340
381
  import path4 from "path";
341
382
  import os3 from "os";
342
383
  function registerSession(entry) {
343
384
  const dir = path4.dirname(REGISTRY_PATH);
344
- if (!existsSync3(dir)) {
345
- mkdirSync2(dir, { recursive: true });
385
+ if (!existsSync4(dir)) {
386
+ mkdirSync3(dir, { recursive: true });
346
387
  }
347
388
  const sessions = listSessions();
348
389
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -577,10 +618,10 @@ var init_runtime_table = __esm({
577
618
  });
578
619
 
579
620
  // src/lib/agent-config.ts
580
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
621
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5 } from "fs";
581
622
  import path5 from "path";
582
623
  function loadAgentConfig() {
583
- if (!existsSync4(AGENT_CONFIG_PATH)) return {};
624
+ if (!existsSync5(AGENT_CONFIG_PATH)) return {};
584
625
  try {
585
626
  return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
586
627
  } catch {
@@ -601,6 +642,7 @@ var init_agent_config = __esm({
601
642
  "use strict";
602
643
  init_config();
603
644
  init_runtime_table();
645
+ init_secure_files();
604
646
  AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
605
647
  DEFAULT_MODELS = {
606
648
  claude: "claude-opus-4",
@@ -619,16 +661,16 @@ __export(intercom_queue_exports, {
619
661
  queueIntercom: () => queueIntercom,
620
662
  readQueue: () => readQueue
621
663
  });
622
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
664
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
623
665
  import path6 from "path";
624
666
  import os4 from "os";
625
667
  function ensureDir() {
626
668
  const dir = path6.dirname(QUEUE_PATH);
627
- if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
669
+ if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
628
670
  }
629
671
  function readQueue() {
630
672
  try {
631
- if (!existsSync5(QUEUE_PATH)) return [];
673
+ if (!existsSync6(QUEUE_PATH)) return [];
632
674
  return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
633
675
  } catch {
634
676
  return [];
@@ -791,13 +833,634 @@ var init_db_retry = __esm({
791
833
  }
792
834
  });
793
835
 
836
+ // src/lib/database-adapter.ts
837
+ import os5 from "os";
838
+ import path7 from "path";
839
+ import { createRequire } from "module";
840
+ import { pathToFileURL } from "url";
841
+ function quotedIdentifier(identifier) {
842
+ return `"${identifier.replace(/"/g, '""')}"`;
843
+ }
844
+ function unqualifiedTableName(name) {
845
+ const raw = name.trim().replace(/^"|"$/g, "");
846
+ const parts = raw.split(".");
847
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
848
+ }
849
+ function stripTrailingSemicolon(sql) {
850
+ return sql.trim().replace(/;+\s*$/u, "");
851
+ }
852
+ function appendClause(sql, clause) {
853
+ const trimmed = stripTrailingSemicolon(sql);
854
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
855
+ if (!returningMatch) {
856
+ return `${trimmed}${clause}`;
857
+ }
858
+ const idx = returningMatch.index;
859
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
860
+ }
861
+ function normalizeStatement(stmt) {
862
+ if (typeof stmt === "string") {
863
+ return { kind: "positional", sql: stmt, args: [] };
864
+ }
865
+ const sql = stmt.sql;
866
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
867
+ return { kind: "positional", sql, args: stmt.args ?? [] };
868
+ }
869
+ return { kind: "named", sql, args: stmt.args };
870
+ }
871
+ function rewriteBooleanLiterals(sql) {
872
+ let out = sql;
873
+ for (const column of BOOLEAN_COLUMN_NAMES) {
874
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
875
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
876
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
877
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
878
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
879
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
880
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
881
+ }
882
+ return out;
883
+ }
884
+ function rewriteInsertOrIgnore(sql) {
885
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
886
+ return sql;
887
+ }
888
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
889
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
890
+ }
891
+ function rewriteInsertOrReplace(sql) {
892
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
893
+ if (!match) {
894
+ return sql;
895
+ }
896
+ const rawTable = match[1];
897
+ const rawColumns = match[2];
898
+ const remainder = match[3];
899
+ const tableName = unqualifiedTableName(rawTable);
900
+ const conflictKeys = UPSERT_KEYS[tableName];
901
+ if (!conflictKeys?.length) {
902
+ return sql;
903
+ }
904
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
905
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
906
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
907
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
908
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
909
+ }
910
+ function rewriteSql(sql) {
911
+ let out = sql;
912
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
913
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
914
+ out = rewriteBooleanLiterals(out);
915
+ out = rewriteInsertOrReplace(out);
916
+ out = rewriteInsertOrIgnore(out);
917
+ return stripTrailingSemicolon(out);
918
+ }
919
+ function toBoolean(value) {
920
+ if (value === null || value === void 0) return value;
921
+ if (typeof value === "boolean") return value;
922
+ if (typeof value === "number") return value !== 0;
923
+ if (typeof value === "bigint") return value !== 0n;
924
+ if (typeof value === "string") {
925
+ const normalized = value.trim().toLowerCase();
926
+ if (normalized === "0" || normalized === "false") return false;
927
+ if (normalized === "1" || normalized === "true") return true;
928
+ }
929
+ return Boolean(value);
930
+ }
931
+ function countQuestionMarks(sql, end) {
932
+ let count = 0;
933
+ let inSingle = false;
934
+ let inDouble = false;
935
+ let inLineComment = false;
936
+ let inBlockComment = false;
937
+ for (let i = 0; i < end; i++) {
938
+ const ch = sql[i];
939
+ const next = sql[i + 1];
940
+ if (inLineComment) {
941
+ if (ch === "\n") inLineComment = false;
942
+ continue;
943
+ }
944
+ if (inBlockComment) {
945
+ if (ch === "*" && next === "/") {
946
+ inBlockComment = false;
947
+ i += 1;
948
+ }
949
+ continue;
950
+ }
951
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
952
+ inLineComment = true;
953
+ i += 1;
954
+ continue;
955
+ }
956
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
957
+ inBlockComment = true;
958
+ i += 1;
959
+ continue;
960
+ }
961
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
962
+ inSingle = !inSingle;
963
+ continue;
964
+ }
965
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
966
+ inDouble = !inDouble;
967
+ continue;
968
+ }
969
+ if (!inSingle && !inDouble && ch === "?") {
970
+ count += 1;
971
+ }
972
+ }
973
+ return count;
974
+ }
975
+ function findBooleanPlaceholderIndexes(sql) {
976
+ const indexes = /* @__PURE__ */ new Set();
977
+ for (const column of BOOLEAN_COLUMN_NAMES) {
978
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
979
+ for (const match of sql.matchAll(pattern)) {
980
+ const matchText = match[0];
981
+ const qIndex = match.index + matchText.lastIndexOf("?");
982
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
983
+ }
984
+ }
985
+ return indexes;
986
+ }
987
+ function coerceInsertBooleanArgs(sql, args) {
988
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
989
+ if (!match) return;
990
+ const rawTable = match[1];
991
+ const rawColumns = match[2];
992
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
993
+ if (!boolColumns?.size) return;
994
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
995
+ for (const [index, column] of columns.entries()) {
996
+ if (boolColumns.has(column) && index < args.length) {
997
+ args[index] = toBoolean(args[index]);
998
+ }
999
+ }
1000
+ }
1001
+ function coerceUpdateBooleanArgs(sql, args) {
1002
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
1003
+ if (!match) return;
1004
+ const rawTable = match[1];
1005
+ const setClause = match[2];
1006
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1007
+ if (!boolColumns?.size) return;
1008
+ const assignments = setClause.split(",");
1009
+ let placeholderIndex = 0;
1010
+ for (const assignment of assignments) {
1011
+ if (!assignment.includes("?")) continue;
1012
+ placeholderIndex += 1;
1013
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
1014
+ if (colMatch && boolColumns.has(colMatch[1])) {
1015
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
1016
+ }
1017
+ }
1018
+ }
1019
+ function coerceBooleanArgs(sql, args) {
1020
+ const nextArgs = [...args];
1021
+ coerceInsertBooleanArgs(sql, nextArgs);
1022
+ coerceUpdateBooleanArgs(sql, nextArgs);
1023
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
1024
+ for (const index of placeholderIndexes) {
1025
+ if (index > 0 && index <= nextArgs.length) {
1026
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
1027
+ }
1028
+ }
1029
+ return nextArgs;
1030
+ }
1031
+ function convertQuestionMarksToDollarParams(sql) {
1032
+ let out = "";
1033
+ let placeholder = 0;
1034
+ let inSingle = false;
1035
+ let inDouble = false;
1036
+ let inLineComment = false;
1037
+ let inBlockComment = false;
1038
+ for (let i = 0; i < sql.length; i++) {
1039
+ const ch = sql[i];
1040
+ const next = sql[i + 1];
1041
+ if (inLineComment) {
1042
+ out += ch;
1043
+ if (ch === "\n") inLineComment = false;
1044
+ continue;
1045
+ }
1046
+ if (inBlockComment) {
1047
+ out += ch;
1048
+ if (ch === "*" && next === "/") {
1049
+ out += next;
1050
+ inBlockComment = false;
1051
+ i += 1;
1052
+ }
1053
+ continue;
1054
+ }
1055
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1056
+ out += ch + next;
1057
+ inLineComment = true;
1058
+ i += 1;
1059
+ continue;
1060
+ }
1061
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1062
+ out += ch + next;
1063
+ inBlockComment = true;
1064
+ i += 1;
1065
+ continue;
1066
+ }
1067
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1068
+ inSingle = !inSingle;
1069
+ out += ch;
1070
+ continue;
1071
+ }
1072
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1073
+ inDouble = !inDouble;
1074
+ out += ch;
1075
+ continue;
1076
+ }
1077
+ if (!inSingle && !inDouble && ch === "?") {
1078
+ placeholder += 1;
1079
+ out += `$${placeholder}`;
1080
+ continue;
1081
+ }
1082
+ out += ch;
1083
+ }
1084
+ return out;
1085
+ }
1086
+ function translateStatementForPostgres(stmt) {
1087
+ const normalized = normalizeStatement(stmt);
1088
+ if (normalized.kind === "named") {
1089
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1090
+ }
1091
+ const rewrittenSql = rewriteSql(normalized.sql);
1092
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1093
+ return {
1094
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1095
+ args: coercedArgs
1096
+ };
1097
+ }
1098
+ function shouldBypassPostgres(stmt) {
1099
+ const normalized = normalizeStatement(stmt);
1100
+ if (normalized.kind === "named") {
1101
+ return true;
1102
+ }
1103
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1104
+ }
1105
+ function shouldFallbackOnError(error) {
1106
+ const message = error instanceof Error ? error.message : String(error);
1107
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1108
+ }
1109
+ function isReadQuery(sql) {
1110
+ const trimmed = sql.trimStart();
1111
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1112
+ }
1113
+ function buildRow(row, columns) {
1114
+ const values = columns.map((column) => row[column]);
1115
+ return Object.assign(values, row);
1116
+ }
1117
+ function buildResultSet(rows, rowsAffected = 0) {
1118
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1119
+ const resultRows = rows.map((row) => buildRow(row, columns));
1120
+ return {
1121
+ columns,
1122
+ columnTypes: columns.map(() => ""),
1123
+ rows: resultRows,
1124
+ rowsAffected,
1125
+ lastInsertRowid: void 0,
1126
+ toJSON() {
1127
+ return {
1128
+ columns,
1129
+ columnTypes: columns.map(() => ""),
1130
+ rows,
1131
+ rowsAffected,
1132
+ lastInsertRowid: void 0
1133
+ };
1134
+ }
1135
+ };
1136
+ }
1137
+ async function loadPrismaClient() {
1138
+ if (!prismaClientPromise) {
1139
+ prismaClientPromise = (async () => {
1140
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1141
+ if (explicitPath) {
1142
+ const module2 = await import(pathToFileURL(explicitPath).href);
1143
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1144
+ if (!PrismaClient2) {
1145
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1146
+ }
1147
+ return new PrismaClient2();
1148
+ }
1149
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
1150
+ const requireFromExeDb = createRequire(path7.join(exeDbRoot, "package.json"));
1151
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1152
+ const module = await import(pathToFileURL(prismaEntry).href);
1153
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1154
+ if (!PrismaClient) {
1155
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1156
+ }
1157
+ return new PrismaClient();
1158
+ })();
1159
+ }
1160
+ return prismaClientPromise;
1161
+ }
1162
+ async function ensureCompatibilityViews(prisma) {
1163
+ if (!compatibilityBootstrapPromise) {
1164
+ compatibilityBootstrapPromise = (async () => {
1165
+ for (const mapping of VIEW_MAPPINGS) {
1166
+ const relation = mapping.source.replace(/"/g, "");
1167
+ const rows = await prisma.$queryRawUnsafe(
1168
+ "SELECT to_regclass($1) AS regclass",
1169
+ relation
1170
+ );
1171
+ if (!rows[0]?.regclass) {
1172
+ continue;
1173
+ }
1174
+ await prisma.$executeRawUnsafe(
1175
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1176
+ );
1177
+ }
1178
+ })();
1179
+ }
1180
+ return compatibilityBootstrapPromise;
1181
+ }
1182
+ async function executeOnPrisma(executor, stmt) {
1183
+ const translated = translateStatementForPostgres(stmt);
1184
+ if (isReadQuery(translated.sql)) {
1185
+ const rows = await executor.$queryRawUnsafe(
1186
+ translated.sql,
1187
+ ...translated.args
1188
+ );
1189
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1190
+ }
1191
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1192
+ return buildResultSet([], rowsAffected);
1193
+ }
1194
+ function splitSqlStatements(sql) {
1195
+ const parts = [];
1196
+ let current = "";
1197
+ let inSingle = false;
1198
+ let inDouble = false;
1199
+ let inLineComment = false;
1200
+ let inBlockComment = false;
1201
+ for (let i = 0; i < sql.length; i++) {
1202
+ const ch = sql[i];
1203
+ const next = sql[i + 1];
1204
+ if (inLineComment) {
1205
+ current += ch;
1206
+ if (ch === "\n") inLineComment = false;
1207
+ continue;
1208
+ }
1209
+ if (inBlockComment) {
1210
+ current += ch;
1211
+ if (ch === "*" && next === "/") {
1212
+ current += next;
1213
+ inBlockComment = false;
1214
+ i += 1;
1215
+ }
1216
+ continue;
1217
+ }
1218
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1219
+ current += ch + next;
1220
+ inLineComment = true;
1221
+ i += 1;
1222
+ continue;
1223
+ }
1224
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1225
+ current += ch + next;
1226
+ inBlockComment = true;
1227
+ i += 1;
1228
+ continue;
1229
+ }
1230
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1231
+ inSingle = !inSingle;
1232
+ current += ch;
1233
+ continue;
1234
+ }
1235
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1236
+ inDouble = !inDouble;
1237
+ current += ch;
1238
+ continue;
1239
+ }
1240
+ if (!inSingle && !inDouble && ch === ";") {
1241
+ if (current.trim()) {
1242
+ parts.push(current.trim());
1243
+ }
1244
+ current = "";
1245
+ continue;
1246
+ }
1247
+ current += ch;
1248
+ }
1249
+ if (current.trim()) {
1250
+ parts.push(current.trim());
1251
+ }
1252
+ return parts;
1253
+ }
1254
+ async function createPrismaDbAdapter(fallbackClient) {
1255
+ const prisma = await loadPrismaClient();
1256
+ await ensureCompatibilityViews(prisma);
1257
+ let closed = false;
1258
+ let adapter;
1259
+ const fallbackExecute = async (stmt, error) => {
1260
+ if (!fallbackClient) {
1261
+ if (error) throw error;
1262
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1263
+ }
1264
+ if (error) {
1265
+ process.stderr.write(
1266
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1267
+ `
1268
+ );
1269
+ }
1270
+ return fallbackClient.execute(stmt);
1271
+ };
1272
+ adapter = {
1273
+ async execute(stmt) {
1274
+ if (shouldBypassPostgres(stmt)) {
1275
+ return fallbackExecute(stmt);
1276
+ }
1277
+ try {
1278
+ return await executeOnPrisma(prisma, stmt);
1279
+ } catch (error) {
1280
+ if (shouldFallbackOnError(error)) {
1281
+ return fallbackExecute(stmt, error);
1282
+ }
1283
+ throw error;
1284
+ }
1285
+ },
1286
+ async batch(stmts, mode) {
1287
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1288
+ if (!fallbackClient) {
1289
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1290
+ }
1291
+ return fallbackClient.batch(stmts, mode);
1292
+ }
1293
+ try {
1294
+ if (prisma.$transaction) {
1295
+ return await prisma.$transaction(async (tx) => {
1296
+ const results2 = [];
1297
+ for (const stmt of stmts) {
1298
+ results2.push(await executeOnPrisma(tx, stmt));
1299
+ }
1300
+ return results2;
1301
+ });
1302
+ }
1303
+ const results = [];
1304
+ for (const stmt of stmts) {
1305
+ results.push(await executeOnPrisma(prisma, stmt));
1306
+ }
1307
+ return results;
1308
+ } catch (error) {
1309
+ if (fallbackClient && shouldFallbackOnError(error)) {
1310
+ process.stderr.write(
1311
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1312
+ `
1313
+ );
1314
+ return fallbackClient.batch(stmts, mode);
1315
+ }
1316
+ throw error;
1317
+ }
1318
+ },
1319
+ async migrate(stmts) {
1320
+ if (fallbackClient) {
1321
+ return fallbackClient.migrate(stmts);
1322
+ }
1323
+ return adapter.batch(stmts, "deferred");
1324
+ },
1325
+ async transaction(mode) {
1326
+ if (!fallbackClient) {
1327
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1328
+ }
1329
+ return fallbackClient.transaction(mode);
1330
+ },
1331
+ async executeMultiple(sql) {
1332
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1333
+ return fallbackClient.executeMultiple(sql);
1334
+ }
1335
+ for (const statement of splitSqlStatements(sql)) {
1336
+ await adapter.execute(statement);
1337
+ }
1338
+ },
1339
+ async sync() {
1340
+ if (fallbackClient) {
1341
+ return fallbackClient.sync();
1342
+ }
1343
+ return { frame_no: 0, frames_synced: 0 };
1344
+ },
1345
+ close() {
1346
+ closed = true;
1347
+ prismaClientPromise = null;
1348
+ compatibilityBootstrapPromise = null;
1349
+ void prisma.$disconnect?.();
1350
+ },
1351
+ get closed() {
1352
+ return closed;
1353
+ },
1354
+ get protocol() {
1355
+ return "prisma-postgres";
1356
+ }
1357
+ };
1358
+ return adapter;
1359
+ }
1360
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1361
+ var init_database_adapter = __esm({
1362
+ "src/lib/database-adapter.ts"() {
1363
+ "use strict";
1364
+ VIEW_MAPPINGS = [
1365
+ { view: "memories", source: "memory.memory_records" },
1366
+ { view: "tasks", source: "memory.tasks" },
1367
+ { view: "behaviors", source: "memory.behaviors" },
1368
+ { view: "entities", source: "memory.entities" },
1369
+ { view: "relationships", source: "memory.relationships" },
1370
+ { view: "entity_memories", source: "memory.entity_memories" },
1371
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1372
+ { view: "notifications", source: "memory.notifications" },
1373
+ { view: "messages", source: "memory.messages" },
1374
+ { view: "users", source: "wiki.users" },
1375
+ { view: "workspaces", source: "wiki.workspaces" },
1376
+ { view: "workspace_users", source: "wiki.workspace_users" },
1377
+ { view: "documents", source: "wiki.workspace_documents" },
1378
+ { view: "chats", source: "wiki.workspace_chats" }
1379
+ ];
1380
+ UPSERT_KEYS = {
1381
+ memories: ["id"],
1382
+ tasks: ["id"],
1383
+ behaviors: ["id"],
1384
+ entities: ["id"],
1385
+ relationships: ["id"],
1386
+ entity_aliases: ["alias"],
1387
+ notifications: ["id"],
1388
+ messages: ["id"],
1389
+ users: ["id"],
1390
+ workspaces: ["id"],
1391
+ workspace_users: ["id"],
1392
+ documents: ["id"],
1393
+ chats: ["id"]
1394
+ };
1395
+ BOOLEAN_COLUMNS_BY_TABLE = {
1396
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1397
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1398
+ notifications: /* @__PURE__ */ new Set(["read"]),
1399
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1400
+ };
1401
+ BOOLEAN_COLUMN_NAMES = new Set(
1402
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1403
+ );
1404
+ IMMEDIATE_FALLBACK_PATTERNS = [
1405
+ /\bPRAGMA\b/i,
1406
+ /\bsqlite_master\b/i,
1407
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1408
+ /\bMATCH\b/i,
1409
+ /\bvector_distance_cos\s*\(/i,
1410
+ /\bjson_extract\s*\(/i,
1411
+ /\bjulianday\s*\(/i,
1412
+ /\bstrftime\s*\(/i,
1413
+ /\blast_insert_rowid\s*\(/i
1414
+ ];
1415
+ prismaClientPromise = null;
1416
+ compatibilityBootstrapPromise = null;
1417
+ }
1418
+ });
1419
+
1420
+ // src/lib/daemon-auth.ts
1421
+ import crypto from "crypto";
1422
+ import path8 from "path";
1423
+ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
1424
+ function normalizeToken(token) {
1425
+ if (!token) return null;
1426
+ const trimmed = token.trim();
1427
+ return trimmed.length > 0 ? trimmed : null;
1428
+ }
1429
+ function readDaemonToken() {
1430
+ try {
1431
+ if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
1432
+ return normalizeToken(readFileSync7(DAEMON_TOKEN_PATH, "utf8"));
1433
+ } catch {
1434
+ return null;
1435
+ }
1436
+ }
1437
+ function ensureDaemonToken(seed) {
1438
+ const existing = readDaemonToken();
1439
+ if (existing) return existing;
1440
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1441
+ ensurePrivateDirSync(EXE_AI_DIR);
1442
+ writeFileSync6(DAEMON_TOKEN_PATH, `${token}
1443
+ `, "utf8");
1444
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1445
+ return token;
1446
+ }
1447
+ var DAEMON_TOKEN_PATH;
1448
+ var init_daemon_auth = __esm({
1449
+ "src/lib/daemon-auth.ts"() {
1450
+ "use strict";
1451
+ init_config();
1452
+ init_secure_files();
1453
+ DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
1454
+ }
1455
+ });
1456
+
794
1457
  // src/lib/exe-daemon-client.ts
795
1458
  import net from "net";
796
- import os5 from "os";
1459
+ import os6 from "os";
797
1460
  import { spawn } from "child_process";
798
1461
  import { randomUUID } from "crypto";
799
- import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
800
- import path7 from "path";
1462
+ import { existsSync as existsSync8, unlinkSync as unlinkSync3, readFileSync as readFileSync8, openSync, closeSync, statSync } from "fs";
1463
+ import path9 from "path";
801
1464
  import { fileURLToPath } from "url";
802
1465
  function handleData(chunk) {
803
1466
  _buffer += chunk.toString();
@@ -825,9 +1488,9 @@ function handleData(chunk) {
825
1488
  }
826
1489
  }
827
1490
  function cleanupStaleFiles() {
828
- if (existsSync6(PID_PATH)) {
1491
+ if (existsSync8(PID_PATH)) {
829
1492
  try {
830
- const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1493
+ const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
831
1494
  if (pid > 0) {
832
1495
  try {
833
1496
  process.kill(pid, 0);
@@ -848,17 +1511,17 @@ function cleanupStaleFiles() {
848
1511
  }
849
1512
  }
850
1513
  function findPackageRoot() {
851
- let dir = path7.dirname(fileURLToPath(import.meta.url));
852
- const { root } = path7.parse(dir);
1514
+ let dir = path9.dirname(fileURLToPath(import.meta.url));
1515
+ const { root } = path9.parse(dir);
853
1516
  while (dir !== root) {
854
- if (existsSync6(path7.join(dir, "package.json"))) return dir;
855
- dir = path7.dirname(dir);
1517
+ if (existsSync8(path9.join(dir, "package.json"))) return dir;
1518
+ dir = path9.dirname(dir);
856
1519
  }
857
1520
  return null;
858
1521
  }
859
1522
  function spawnDaemon() {
860
- const freeGB = os5.freemem() / (1024 * 1024 * 1024);
861
- const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1523
+ const freeGB = os6.freemem() / (1024 * 1024 * 1024);
1524
+ const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
862
1525
  if (totalGB <= 8) {
863
1526
  process.stderr.write(
864
1527
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -878,16 +1541,17 @@ function spawnDaemon() {
878
1541
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
879
1542
  return;
880
1543
  }
881
- const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
882
- if (!existsSync6(daemonPath)) {
1544
+ const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1545
+ if (!existsSync8(daemonPath)) {
883
1546
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
884
1547
  `);
885
1548
  return;
886
1549
  }
887
1550
  const resolvedPath = daemonPath;
1551
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
888
1552
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
889
1553
  `);
890
- const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
1554
+ const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
891
1555
  let stderrFd = "ignore";
892
1556
  try {
893
1557
  stderrFd = openSync(logPath, "a");
@@ -905,7 +1569,8 @@ function spawnDaemon() {
905
1569
  TMUX_PANE: void 0,
906
1570
  // Prevents resolveExeSession() from scoping to one session
907
1571
  EXE_DAEMON_SOCK: SOCKET_PATH,
908
- EXE_DAEMON_PID: PID_PATH
1572
+ EXE_DAEMON_PID: PID_PATH,
1573
+ [DAEMON_TOKEN_ENV]: daemonToken
909
1574
  }
910
1575
  });
911
1576
  child.unref();
@@ -1012,13 +1677,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1012
1677
  return;
1013
1678
  }
1014
1679
  const id = randomUUID();
1680
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1015
1681
  const timer = setTimeout(() => {
1016
1682
  _pending.delete(id);
1017
1683
  resolve({ error: "Request timeout" });
1018
1684
  }, timeoutMs);
1019
1685
  _pending.set(id, { resolve, timer });
1020
1686
  try {
1021
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1687
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1022
1688
  } catch {
1023
1689
  clearTimeout(timer);
1024
1690
  _pending.delete(id);
@@ -1029,17 +1695,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1029
1695
  function isClientConnected() {
1030
1696
  return _connected;
1031
1697
  }
1032
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1698
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1033
1699
  var init_exe_daemon_client = __esm({
1034
1700
  "src/lib/exe-daemon-client.ts"() {
1035
1701
  "use strict";
1036
1702
  init_config();
1037
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
1038
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
1039
- SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
1703
+ init_daemon_auth();
1704
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
1705
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
1706
+ SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
1040
1707
  SPAWN_LOCK_STALE_MS = 3e4;
1041
1708
  CONNECT_TIMEOUT_MS = 15e3;
1042
1709
  REQUEST_TIMEOUT_MS = 3e4;
1710
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1043
1711
  _socket = null;
1044
1712
  _connected = false;
1045
1713
  _buffer = "";
@@ -1118,7 +1786,7 @@ __export(db_daemon_client_exports, {
1118
1786
  createDaemonDbClient: () => createDaemonDbClient,
1119
1787
  initDaemonDbClient: () => initDaemonDbClient
1120
1788
  });
1121
- function normalizeStatement(stmt) {
1789
+ function normalizeStatement2(stmt) {
1122
1790
  if (typeof stmt === "string") {
1123
1791
  return { sql: stmt, args: [] };
1124
1792
  }
@@ -1142,7 +1810,7 @@ function createDaemonDbClient(fallbackClient) {
1142
1810
  if (!_useDaemon || !isClientConnected()) {
1143
1811
  return fallbackClient.execute(stmt);
1144
1812
  }
1145
- const { sql, args } = normalizeStatement(stmt);
1813
+ const { sql, args } = normalizeStatement2(stmt);
1146
1814
  const response = await sendDaemonRequest({
1147
1815
  type: "db-execute",
1148
1816
  sql,
@@ -1167,7 +1835,7 @@ function createDaemonDbClient(fallbackClient) {
1167
1835
  if (!_useDaemon || !isClientConnected()) {
1168
1836
  return fallbackClient.batch(stmts, mode);
1169
1837
  }
1170
- const statements = stmts.map(normalizeStatement);
1838
+ const statements = stmts.map(normalizeStatement2);
1171
1839
  const response = await sendDaemonRequest({
1172
1840
  type: "db-batch",
1173
1841
  statements,
@@ -1262,6 +1930,18 @@ __export(database_exports, {
1262
1930
  });
1263
1931
  import { createClient } from "@libsql/client";
1264
1932
  async function initDatabase(config) {
1933
+ if (_walCheckpointTimer) {
1934
+ clearInterval(_walCheckpointTimer);
1935
+ _walCheckpointTimer = null;
1936
+ }
1937
+ if (_daemonClient) {
1938
+ _daemonClient.close();
1939
+ _daemonClient = null;
1940
+ }
1941
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1942
+ _adapterClient.close();
1943
+ }
1944
+ _adapterClient = null;
1265
1945
  if (_client) {
1266
1946
  _client.close();
1267
1947
  _client = null;
@@ -1275,6 +1955,7 @@ async function initDatabase(config) {
1275
1955
  }
1276
1956
  _client = createClient(opts);
1277
1957
  _resilientClient = wrapWithRetry(_client);
1958
+ _adapterClient = _resilientClient;
1278
1959
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1279
1960
  });
1280
1961
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1285,14 +1966,20 @@ async function initDatabase(config) {
1285
1966
  });
1286
1967
  }, 3e4);
1287
1968
  _walCheckpointTimer.unref();
1969
+ if (process.env.DATABASE_URL) {
1970
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1971
+ }
1288
1972
  }
1289
1973
  function isInitialized() {
1290
- return _client !== null;
1974
+ return _adapterClient !== null || _client !== null;
1291
1975
  }
1292
1976
  function getClient() {
1293
- if (!_resilientClient) {
1977
+ if (!_adapterClient) {
1294
1978
  throw new Error("Database client not initialized. Call initDatabase() first.");
1295
1979
  }
1980
+ if (process.env.DATABASE_URL) {
1981
+ return _adapterClient;
1982
+ }
1296
1983
  if (process.env.EXE_IS_DAEMON === "1") {
1297
1984
  return _resilientClient;
1298
1985
  }
@@ -1302,6 +1989,7 @@ function getClient() {
1302
1989
  return _resilientClient;
1303
1990
  }
1304
1991
  async function initDaemonClient() {
1992
+ if (process.env.DATABASE_URL) return;
1305
1993
  if (process.env.EXE_IS_DAEMON === "1") return;
1306
1994
  if (!_resilientClient) return;
1307
1995
  try {
@@ -1598,6 +2286,7 @@ async function ensureSchema() {
1598
2286
  project TEXT NOT NULL,
1599
2287
  summary TEXT NOT NULL,
1600
2288
  task_file TEXT,
2289
+ session_scope TEXT,
1601
2290
  read INTEGER NOT NULL DEFAULT 0,
1602
2291
  created_at TEXT NOT NULL
1603
2292
  );
@@ -1606,7 +2295,7 @@ async function ensureSchema() {
1606
2295
  ON notifications(read);
1607
2296
 
1608
2297
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1609
- ON notifications(agent_id);
2298
+ ON notifications(agent_id, session_scope);
1610
2299
 
1611
2300
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1612
2301
  ON notifications(task_file);
@@ -1644,6 +2333,7 @@ async function ensureSchema() {
1644
2333
  target_agent TEXT NOT NULL,
1645
2334
  target_project TEXT,
1646
2335
  target_device TEXT NOT NULL DEFAULT 'local',
2336
+ session_scope TEXT,
1647
2337
  content TEXT NOT NULL,
1648
2338
  priority TEXT DEFAULT 'normal',
1649
2339
  status TEXT DEFAULT 'pending',
@@ -1657,10 +2347,31 @@ async function ensureSchema() {
1657
2347
  );
1658
2348
 
1659
2349
  CREATE INDEX IF NOT EXISTS idx_messages_target
1660
- ON messages(target_agent, status);
2350
+ ON messages(target_agent, session_scope, status);
1661
2351
 
1662
2352
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1663
- ON messages(target_agent, from_agent, server_seq);
2353
+ ON messages(target_agent, session_scope, from_agent, server_seq);
2354
+ `);
2355
+ try {
2356
+ await client.execute({
2357
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2358
+ args: []
2359
+ });
2360
+ } catch {
2361
+ }
2362
+ try {
2363
+ await client.execute({
2364
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2365
+ args: []
2366
+ });
2367
+ } catch {
2368
+ }
2369
+ await client.executeMultiple(`
2370
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
2371
+ ON notifications(agent_id, session_scope, read, created_at);
2372
+
2373
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
2374
+ ON messages(target_agent, session_scope, status, created_at);
1664
2375
  `);
1665
2376
  try {
1666
2377
  await client.execute({
@@ -2244,46 +2955,66 @@ async function ensureSchema() {
2244
2955
  } catch {
2245
2956
  }
2246
2957
  }
2958
+ try {
2959
+ await client.execute({
2960
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2961
+ args: []
2962
+ });
2963
+ } catch {
2964
+ }
2247
2965
  }
2248
2966
  async function disposeDatabase() {
2967
+ if (_walCheckpointTimer) {
2968
+ clearInterval(_walCheckpointTimer);
2969
+ _walCheckpointTimer = null;
2970
+ }
2249
2971
  if (_daemonClient) {
2250
2972
  _daemonClient.close();
2251
2973
  _daemonClient = null;
2252
2974
  }
2975
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2976
+ _adapterClient.close();
2977
+ }
2978
+ _adapterClient = null;
2253
2979
  if (_client) {
2254
2980
  _client.close();
2255
2981
  _client = null;
2256
2982
  _resilientClient = null;
2257
2983
  }
2258
2984
  }
2259
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2985
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2260
2986
  var init_database = __esm({
2261
2987
  "src/lib/database.ts"() {
2262
2988
  "use strict";
2263
2989
  init_db_retry();
2264
2990
  init_employees();
2991
+ init_database_adapter();
2265
2992
  _client = null;
2266
2993
  _resilientClient = null;
2267
2994
  _walCheckpointTimer = null;
2268
2995
  _daemonClient = null;
2996
+ _adapterClient = null;
2269
2997
  initTurso = initDatabase;
2270
2998
  disposeTurso = disposeDatabase;
2271
2999
  }
2272
3000
  });
2273
3001
 
2274
3002
  // src/lib/license.ts
2275
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
3003
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
2276
3004
  import { randomUUID as randomUUID2 } from "crypto";
2277
- import path8 from "path";
3005
+ import { createRequire as createRequire2 } from "module";
3006
+ import { pathToFileURL as pathToFileURL2 } from "url";
3007
+ import os7 from "os";
3008
+ import path10 from "path";
2278
3009
  import { jwtVerify, importSPKI } from "jose";
2279
3010
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2280
3011
  var init_license = __esm({
2281
3012
  "src/lib/license.ts"() {
2282
3013
  "use strict";
2283
3014
  init_config();
2284
- LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2285
- CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2286
- DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
3015
+ LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
3016
+ CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3017
+ DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
2287
3018
  PLAN_LIMITS = {
2288
3019
  free: { devices: 1, employees: 1, memories: 5e3 },
2289
3020
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2295,12 +3026,12 @@ var init_license = __esm({
2295
3026
  });
2296
3027
 
2297
3028
  // src/lib/plan-limits.ts
2298
- import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
2299
- import path9 from "path";
3029
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
3030
+ import path11 from "path";
2300
3031
  function getLicenseSync() {
2301
3032
  try {
2302
- if (!existsSync8(CACHE_PATH2)) return freeLicense();
2303
- const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
3033
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
3034
+ const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
2304
3035
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2305
3036
  const parts = raw.token.split(".");
2306
3037
  if (parts.length !== 3) return freeLicense();
@@ -2338,8 +3069,8 @@ function assertEmployeeLimitSync(rosterPath) {
2338
3069
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2339
3070
  let count = 0;
2340
3071
  try {
2341
- if (existsSync8(filePath)) {
2342
- const raw = readFileSync9(filePath, "utf8");
3072
+ if (existsSync10(filePath)) {
3073
+ const raw = readFileSync10(filePath, "utf8");
2343
3074
  const employees = JSON.parse(raw);
2344
3075
  count = Array.isArray(employees) ? employees.length : 0;
2345
3076
  }
@@ -2368,29 +3099,30 @@ var init_plan_limits = __esm({
2368
3099
  this.name = "PlanLimitError";
2369
3100
  }
2370
3101
  };
2371
- CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
3102
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
2372
3103
  }
2373
3104
  });
2374
3105
 
2375
3106
  // src/lib/notifications.ts
2376
- import crypto from "crypto";
2377
- import path10 from "path";
2378
- import os6 from "os";
3107
+ import crypto2 from "crypto";
3108
+ import path12 from "path";
3109
+ import os8 from "os";
2379
3110
  import {
2380
- readFileSync as readFileSync10,
3111
+ readFileSync as readFileSync11,
2381
3112
  readdirSync as readdirSync2,
2382
3113
  unlinkSync as unlinkSync4,
2383
- existsSync as existsSync9,
3114
+ existsSync as existsSync11,
2384
3115
  rmdirSync
2385
3116
  } from "fs";
2386
3117
  async function writeNotification(notification) {
2387
3118
  try {
2388
3119
  const client = getClient();
2389
- const id = crypto.randomUUID();
3120
+ const id = crypto2.randomUUID();
2390
3121
  const now = (/* @__PURE__ */ new Date()).toISOString();
3122
+ const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
2391
3123
  await client.execute({
2392
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
2393
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
3124
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
3125
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
2394
3126
  args: [
2395
3127
  id,
2396
3128
  notification.agentId,
@@ -2399,6 +3131,7 @@ async function writeNotification(notification) {
2399
3131
  notification.project,
2400
3132
  notification.summary,
2401
3133
  notification.taskFile ?? null,
3134
+ sessionScope,
2402
3135
  now
2403
3136
  ]
2404
3137
  });
@@ -2407,12 +3140,14 @@ async function writeNotification(notification) {
2407
3140
  `);
2408
3141
  }
2409
3142
  }
2410
- async function markAsReadByTaskFile(taskFile) {
3143
+ async function markAsReadByTaskFile(taskFile, sessionScope) {
2411
3144
  try {
2412
3145
  const client = getClient();
3146
+ const scope = strictSessionScopeFilter(sessionScope);
2413
3147
  await client.execute({
2414
- sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
2415
- args: [taskFile]
3148
+ sql: `UPDATE notifications SET read = 1
3149
+ WHERE task_file = ? AND read = 0${scope.sql}`,
3150
+ args: [taskFile, ...scope.args]
2416
3151
  });
2417
3152
  } catch {
2418
3153
  }
@@ -2421,11 +3156,12 @@ var init_notifications = __esm({
2421
3156
  "src/lib/notifications.ts"() {
2422
3157
  "use strict";
2423
3158
  init_database();
3159
+ init_task_scope();
2424
3160
  }
2425
3161
  });
2426
3162
 
2427
3163
  // src/lib/session-kill-telemetry.ts
2428
- import crypto2 from "crypto";
3164
+ import crypto3 from "crypto";
2429
3165
  async function recordSessionKill(input2) {
2430
3166
  try {
2431
3167
  const client = getClient();
@@ -2435,7 +3171,7 @@ async function recordSessionKill(input2) {
2435
3171
  ticks_idle, estimated_tokens_saved)
2436
3172
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
2437
3173
  args: [
2438
- crypto2.randomUUID(),
3174
+ crypto3.randomUUID(),
2439
3175
  input2.sessionName,
2440
3176
  input2.agentId,
2441
3177
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -2514,12 +3250,12 @@ var init_state_bus = __esm({
2514
3250
  });
2515
3251
 
2516
3252
  // src/lib/tasks-crud.ts
2517
- import crypto3 from "crypto";
2518
- import path11 from "path";
2519
- import os7 from "os";
3253
+ import crypto4 from "crypto";
3254
+ import path13 from "path";
3255
+ import os9 from "os";
2520
3256
  import { execSync as execSync5 } from "child_process";
2521
3257
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2522
- import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
3258
+ import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
2523
3259
  async function writeCheckpoint(input2) {
2524
3260
  const client = getClient();
2525
3261
  const row = await resolveTask(client, input2.taskId);
@@ -2635,7 +3371,7 @@ async function resolveTask(client, identifier, scopeSession) {
2635
3371
  }
2636
3372
  async function createTaskCore(input2) {
2637
3373
  const client = getClient();
2638
- const id = crypto3.randomUUID();
3374
+ const id = crypto4.randomUUID();
2639
3375
  const now = (/* @__PURE__ */ new Date()).toISOString();
2640
3376
  const slug = slugify(input2.title);
2641
3377
  let earlySessionScope = null;
@@ -2694,8 +3430,8 @@ ${laneWarning}` : laneWarning;
2694
3430
  }
2695
3431
  if (input2.baseDir) {
2696
3432
  try {
2697
- await mkdir3(path11.join(input2.baseDir, "exe", "output"), { recursive: true });
2698
- await mkdir3(path11.join(input2.baseDir, "exe", "research"), { recursive: true });
3433
+ await mkdir3(path13.join(input2.baseDir, "exe", "output"), { recursive: true });
3434
+ await mkdir3(path13.join(input2.baseDir, "exe", "research"), { recursive: true });
2699
3435
  await ensureArchitectureDoc(input2.baseDir, input2.projectName);
2700
3436
  await ensureGitignoreExe(input2.baseDir);
2701
3437
  } catch {
@@ -2731,13 +3467,19 @@ ${laneWarning}` : laneWarning;
2731
3467
  });
2732
3468
  if (input2.baseDir) {
2733
3469
  try {
2734
- const EXE_OS_DIR = path11.join(os7.homedir(), ".exe-os");
2735
- const mdPath = path11.join(EXE_OS_DIR, taskFile);
2736
- const mdDir = path11.dirname(mdPath);
2737
- if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3470
+ const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
3471
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
3472
+ const mdDir = path13.dirname(mdPath);
3473
+ if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
2738
3474
  const reviewer = input2.reviewer ?? input2.assignedBy;
2739
3475
  const mdContent = `# ${input2.title}
2740
3476
 
3477
+ ## MANDATORY: When done
3478
+
3479
+ You MUST call update_task with status "done" and a result summary when finished.
3480
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3481
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
3482
+
2741
3483
  **ID:** ${id}
2742
3484
  **Status:** ${initialStatus}
2743
3485
  **Priority:** ${input2.priority}
@@ -2751,12 +3493,6 @@ ${laneWarning}` : laneWarning;
2751
3493
  ## Context
2752
3494
 
2753
3495
  ${input2.context}
2754
-
2755
- ## MANDATORY: When done
2756
-
2757
- You MUST call update_task with status "done" and a result summary when finished.
2758
- If you skip this, your reviewer will not know you're done and your work won't be reviewed.
2759
- Do NOT let a failed commit or any error prevent you from calling update_task(done).
2760
3496
  `;
2761
3497
  await writeFile3(mdPath, mdContent, "utf-8");
2762
3498
  } catch (err) {
@@ -3005,7 +3741,7 @@ ${input2.result}` : `\u26A0\uFE0F ${warning}`;
3005
3741
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3006
3742
  } catch {
3007
3743
  }
3008
- if (input2.status === "done" || input2.status === "cancelled") {
3744
+ if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
3009
3745
  try {
3010
3746
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
3011
3747
  clearQueueForAgent2(String(row.assigned_to));
@@ -3034,9 +3770,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3034
3770
  return { taskFile, assignedTo, assignedBy, taskSlug };
3035
3771
  }
3036
3772
  async function ensureArchitectureDoc(baseDir, projectName) {
3037
- const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
3773
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
3038
3774
  try {
3039
- if (existsSync10(archPath)) return;
3775
+ if (existsSync12(archPath)) return;
3040
3776
  const template = [
3041
3777
  `# ${projectName} \u2014 System Architecture`,
3042
3778
  "",
@@ -3069,10 +3805,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3069
3805
  }
3070
3806
  }
3071
3807
  async function ensureGitignoreExe(baseDir) {
3072
- const gitignorePath = path11.join(baseDir, ".gitignore");
3808
+ const gitignorePath = path13.join(baseDir, ".gitignore");
3073
3809
  try {
3074
- if (existsSync10(gitignorePath)) {
3075
- const content = readFileSync11(gitignorePath, "utf-8");
3810
+ if (existsSync12(gitignorePath)) {
3811
+ const content = readFileSync12(gitignorePath, "utf-8");
3076
3812
  if (/^\/?exe\/?$/m.test(content)) return;
3077
3813
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3078
3814
  } else {
@@ -3103,58 +3839,42 @@ var init_tasks_crud = __esm({
3103
3839
  });
3104
3840
 
3105
3841
  // src/lib/tasks-review.ts
3106
- import path12 from "path";
3107
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
3842
+ import path14 from "path";
3843
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
3108
3844
  async function countPendingReviews(sessionScope) {
3109
3845
  const client = getClient();
3110
- if (sessionScope) {
3111
- const result2 = await client.execute({
3112
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
3113
- args: [sessionScope]
3114
- });
3115
- return Number(result2.rows[0]?.cnt) || 0;
3116
- }
3846
+ const scope = strictSessionScopeFilter(
3847
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3848
+ );
3117
3849
  const result = await client.execute({
3118
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
3119
- args: []
3850
+ sql: `SELECT COUNT(*) as cnt FROM tasks
3851
+ WHERE status = 'needs_review'${scope.sql}`,
3852
+ args: [...scope.args]
3120
3853
  });
3121
3854
  return Number(result.rows[0]?.cnt) || 0;
3122
3855
  }
3123
3856
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
3124
3857
  const client = getClient();
3125
- if (sessionScope) {
3126
- const result2 = await client.execute({
3127
- sql: `SELECT COUNT(*) as cnt FROM tasks
3128
- WHERE status = 'needs_review' AND updated_at > ?
3129
- AND session_scope = ?`,
3130
- args: [sinceIso, sessionScope]
3131
- });
3132
- return Number(result2.rows[0]?.cnt) || 0;
3133
- }
3858
+ const scope = strictSessionScopeFilter(
3859
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3860
+ );
3134
3861
  const result = await client.execute({
3135
3862
  sql: `SELECT COUNT(*) as cnt FROM tasks
3136
- WHERE status = 'needs_review' AND updated_at > ?`,
3137
- args: [sinceIso]
3863
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
3864
+ args: [sinceIso, ...scope.args]
3138
3865
  });
3139
3866
  return Number(result.rows[0]?.cnt) || 0;
3140
3867
  }
3141
3868
  async function listPendingReviews(limit, sessionScope) {
3142
3869
  const client = getClient();
3143
- if (sessionScope) {
3144
- const result2 = await client.execute({
3145
- sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
3146
- WHERE status = 'needs_review'
3147
- AND session_scope = ?
3148
- ORDER BY updated_at ASC LIMIT ?`,
3149
- args: [sessionScope, limit]
3150
- });
3151
- return result2.rows;
3152
- }
3870
+ const scope = strictSessionScopeFilter(
3871
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3872
+ );
3153
3873
  const result = await client.execute({
3154
3874
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
3155
- WHERE status = 'needs_review'
3875
+ WHERE status = 'needs_review'${scope.sql}
3156
3876
  ORDER BY updated_at ASC LIMIT ?`,
3157
- args: [limit]
3877
+ args: [...scope.args, limit]
3158
3878
  });
3159
3879
  return result.rows;
3160
3880
  }
@@ -3166,7 +3886,7 @@ async function cleanupOrphanedReviews() {
3166
3886
  WHERE status IN ('open', 'needs_review', 'in_progress')
3167
3887
  AND assigned_by = 'system'
3168
3888
  AND title LIKE 'Review:%'
3169
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
3889
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
3170
3890
  args: [now]
3171
3891
  });
3172
3892
  const r1b = await client.execute({
@@ -3285,11 +4005,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3285
4005
  );
3286
4006
  }
3287
4007
  try {
3288
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3289
- if (existsSync11(cacheDir)) {
4008
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4009
+ if (existsSync13(cacheDir)) {
3290
4010
  for (const f of readdirSync3(cacheDir)) {
3291
4011
  if (f.startsWith("review-notified-")) {
3292
- unlinkSync5(path12.join(cacheDir, f));
4012
+ unlinkSync5(path14.join(cacheDir, f));
3293
4013
  }
3294
4014
  }
3295
4015
  }
@@ -3306,11 +4026,12 @@ var init_tasks_review = __esm({
3306
4026
  init_tmux_routing();
3307
4027
  init_session_key();
3308
4028
  init_state_bus();
4029
+ init_task_scope();
3309
4030
  }
3310
4031
  });
3311
4032
 
3312
4033
  // src/lib/tasks-chain.ts
3313
- import path13 from "path";
4034
+ import path15 from "path";
3314
4035
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3315
4036
  async function cascadeUnblock(taskId, baseDir, now) {
3316
4037
  const client = getClient();
@@ -3327,7 +4048,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3327
4048
  });
3328
4049
  for (const ur of unblockedRows.rows) {
3329
4050
  try {
3330
- const ubFile = path13.join(baseDir, String(ur.task_file));
4051
+ const ubFile = path15.join(baseDir, String(ur.task_file));
3331
4052
  let ubContent = await readFile3(ubFile, "utf-8");
3332
4053
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3333
4054
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3362,7 +4083,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
3362
4083
  const scScope = sessionScopeFilter();
3363
4084
  const remaining = await client.execute({
3364
4085
  sql: `SELECT COUNT(*) as cnt FROM tasks
3365
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
4086
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
3366
4087
  args: [parentTaskId, ...scScope.args]
3367
4088
  });
3368
4089
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -3396,7 +4117,7 @@ var init_tasks_chain = __esm({
3396
4117
 
3397
4118
  // src/lib/project-name.ts
3398
4119
  import { execSync as execSync6 } from "child_process";
3399
- import path14 from "path";
4120
+ import path16 from "path";
3400
4121
  function getProjectName(cwd) {
3401
4122
  const dir = cwd ?? process.cwd();
3402
4123
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3409,7 +4130,7 @@ function getProjectName(cwd) {
3409
4130
  timeout: 2e3,
3410
4131
  stdio: ["pipe", "pipe", "pipe"]
3411
4132
  }).trim();
3412
- repoRoot = path14.dirname(gitCommonDir);
4133
+ repoRoot = path16.dirname(gitCommonDir);
3413
4134
  } catch {
3414
4135
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3415
4136
  cwd: dir,
@@ -3418,11 +4139,11 @@ function getProjectName(cwd) {
3418
4139
  stdio: ["pipe", "pipe", "pipe"]
3419
4140
  }).trim();
3420
4141
  }
3421
- _cached2 = path14.basename(repoRoot);
4142
+ _cached2 = path16.basename(repoRoot);
3422
4143
  _cachedCwd = dir;
3423
4144
  return _cached2;
3424
4145
  } catch {
3425
- _cached2 = path14.basename(dir);
4146
+ _cached2 = path16.basename(dir);
3426
4147
  _cachedCwd = dir;
3427
4148
  return _cached2;
3428
4149
  }
@@ -3565,10 +4286,10 @@ var init_tasks_notify = __esm({
3565
4286
  });
3566
4287
 
3567
4288
  // src/lib/behaviors.ts
3568
- import crypto4 from "crypto";
4289
+ import crypto5 from "crypto";
3569
4290
  async function storeBehavior(opts) {
3570
4291
  const client = getClient();
3571
- const id = crypto4.randomUUID();
4292
+ const id = crypto5.randomUUID();
3572
4293
  const now = (/* @__PURE__ */ new Date()).toISOString();
3573
4294
  await client.execute({
3574
4295
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -3597,7 +4318,7 @@ __export(skill_learning_exports, {
3597
4318
  storeTrajectory: () => storeTrajectory,
3598
4319
  sweepTrajectories: () => sweepTrajectories
3599
4320
  });
3600
- import crypto5 from "crypto";
4321
+ import crypto6 from "crypto";
3601
4322
  async function extractTrajectory(taskId, agentId) {
3602
4323
  const client = getClient();
3603
4324
  const result = await client.execute({
@@ -3626,11 +4347,11 @@ async function extractTrajectory(taskId, agentId) {
3626
4347
  return signature;
3627
4348
  }
3628
4349
  function hashSignature(signature) {
3629
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4350
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
3630
4351
  }
3631
4352
  async function storeTrajectory(opts) {
3632
4353
  const client = getClient();
3633
- const id = crypto5.randomUUID();
4354
+ const id = crypto6.randomUUID();
3634
4355
  const now = (/* @__PURE__ */ new Date()).toISOString();
3635
4356
  const signatureHash = hashSignature(opts.signature);
3636
4357
  await client.execute({
@@ -3895,8 +4616,8 @@ __export(tasks_exports, {
3895
4616
  updateTaskStatus: () => updateTaskStatus,
3896
4617
  writeCheckpoint: () => writeCheckpoint
3897
4618
  });
3898
- import path15 from "path";
3899
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
4619
+ import path17 from "path";
4620
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
3900
4621
  async function createTask(input2) {
3901
4622
  const result = await createTaskCore(input2);
3902
4623
  if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -3915,12 +4636,12 @@ async function updateTask(input2) {
3915
4636
  const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
3916
4637
  try {
3917
4638
  const agent = String(row.assigned_to);
3918
- const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
3919
- const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
4639
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
4640
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
3920
4641
  if (input2.status === "in_progress") {
3921
4642
  mkdirSync6(cacheDir, { recursive: true });
3922
- writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
3923
- } else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
4643
+ writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4644
+ } else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled" || input2.status === "closed") {
3924
4645
  try {
3925
4646
  unlinkSync6(cachePath);
3926
4647
  } catch {
@@ -3928,10 +4649,10 @@ async function updateTask(input2) {
3928
4649
  }
3929
4650
  } catch {
3930
4651
  }
3931
- if (input2.status === "done") {
4652
+ if (input2.status === "done" || input2.status === "closed") {
3932
4653
  await cleanupReviewFile(row, taskFile, input2.baseDir);
3933
4654
  }
3934
- if (input2.status === "done" || input2.status === "cancelled") {
4655
+ if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
3935
4656
  try {
3936
4657
  const client = getClient();
3937
4658
  const taskTitle = String(row.title);
@@ -3947,7 +4668,7 @@ async function updateTask(input2) {
3947
4668
  if (!isCoordinatorName(assignedAgent)) {
3948
4669
  try {
3949
4670
  const draftClient = getClient();
3950
- if (input2.status === "done") {
4671
+ if (input2.status === "done" || input2.status === "closed") {
3951
4672
  await draftClient.execute({
3952
4673
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
3953
4674
  args: [assignedAgent]
@@ -3964,7 +4685,7 @@ async function updateTask(input2) {
3964
4685
  try {
3965
4686
  const client = getClient();
3966
4687
  const cascaded = await client.execute({
3967
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
4688
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
3968
4689
  WHERE parent_task_id = ? AND status = 'needs_review'`,
3969
4690
  args: [now, taskId]
3970
4691
  });
@@ -3977,14 +4698,14 @@ async function updateTask(input2) {
3977
4698
  } catch {
3978
4699
  }
3979
4700
  }
3980
- const isTerminal = input2.status === "done" || input2.status === "needs_review";
4701
+ const isTerminal = input2.status === "done" || input2.status === "needs_review" || input2.status === "closed";
3981
4702
  if (isTerminal) {
3982
4703
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
3983
4704
  if (!isCoordinator) {
3984
4705
  notifyTaskDone();
3985
4706
  }
3986
4707
  await markTaskNotificationsRead(taskFile);
3987
- if (input2.status === "done") {
4708
+ if (input2.status === "done" || input2.status === "closed") {
3988
4709
  try {
3989
4710
  await cascadeUnblock(taskId, input2.baseDir, now);
3990
4711
  } catch {
@@ -4004,7 +4725,7 @@ async function updateTask(input2) {
4004
4725
  }
4005
4726
  }
4006
4727
  }
4007
- if (input2.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4728
+ if ((input2.status === "done" || input2.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4008
4729
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
4009
4730
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
4010
4731
  taskId,
@@ -4376,6 +5097,7 @@ __export(tmux_routing_exports, {
4376
5097
  isEmployeeAlive: () => isEmployeeAlive,
4377
5098
  isExeSession: () => isExeSession,
4378
5099
  isSessionBusy: () => isSessionBusy,
5100
+ notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
4379
5101
  notifyParentExe: () => notifyParentExe,
4380
5102
  parseParentExe: () => parseParentExe,
4381
5103
  registerParentExe: () => registerParentExe,
@@ -4386,13 +5108,13 @@ __export(tmux_routing_exports, {
4386
5108
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4387
5109
  });
4388
5110
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4389
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync4 } from "fs";
4390
- import path16 from "path";
4391
- import os8 from "os";
5111
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
5112
+ import path18 from "path";
5113
+ import os10 from "os";
4392
5114
  import { fileURLToPath as fileURLToPath2 } from "url";
4393
5115
  import { unlinkSync as unlinkSync7 } from "fs";
4394
5116
  function spawnLockPath(sessionName) {
4395
- return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5117
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4396
5118
  }
4397
5119
  function isProcessAlive(pid) {
4398
5120
  try {
@@ -4403,13 +5125,13 @@ function isProcessAlive(pid) {
4403
5125
  }
4404
5126
  }
4405
5127
  function acquireSpawnLock2(sessionName) {
4406
- if (!existsSync12(SPAWN_LOCK_DIR)) {
5128
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
4407
5129
  mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
4408
5130
  }
4409
5131
  const lockFile = spawnLockPath(sessionName);
4410
- if (existsSync12(lockFile)) {
5132
+ if (existsSync14(lockFile)) {
4411
5133
  try {
4412
- const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
5134
+ const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
4413
5135
  const age = Date.now() - lock.timestamp;
4414
5136
  if (isProcessAlive(lock.pid) && age < 6e4) {
4415
5137
  return false;
@@ -4417,7 +5139,7 @@ function acquireSpawnLock2(sessionName) {
4417
5139
  } catch {
4418
5140
  }
4419
5141
  }
4420
- writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
5142
+ writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4421
5143
  return true;
4422
5144
  }
4423
5145
  function releaseSpawnLock2(sessionName) {
@@ -4429,13 +5151,13 @@ function releaseSpawnLock2(sessionName) {
4429
5151
  function resolveBehaviorsExporterScript() {
4430
5152
  try {
4431
5153
  const thisFile = fileURLToPath2(import.meta.url);
4432
- const scriptPath = path16.join(
4433
- path16.dirname(thisFile),
5154
+ const scriptPath = path18.join(
5155
+ path18.dirname(thisFile),
4434
5156
  "..",
4435
5157
  "bin",
4436
5158
  "exe-export-behaviors.js"
4437
5159
  );
4438
- return existsSync12(scriptPath) ? scriptPath : null;
5160
+ return existsSync14(scriptPath) ? scriptPath : null;
4439
5161
  } catch {
4440
5162
  return null;
4441
5163
  }
@@ -4501,12 +5223,12 @@ function extractRootExe(name) {
4501
5223
  return parts.length > 0 ? parts[parts.length - 1] : null;
4502
5224
  }
4503
5225
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4504
- if (!existsSync12(SESSION_CACHE)) {
5226
+ if (!existsSync14(SESSION_CACHE)) {
4505
5227
  mkdirSync7(SESSION_CACHE, { recursive: true });
4506
5228
  }
4507
5229
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4508
- const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4509
- writeFileSync8(filePath, JSON.stringify({
5230
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5231
+ writeFileSync9(filePath, JSON.stringify({
4510
5232
  parentExe: rootExe,
4511
5233
  dispatchedBy: dispatchedBy || rootExe,
4512
5234
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4514,7 +5236,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4514
5236
  }
4515
5237
  function getParentExe(sessionKey) {
4516
5238
  try {
4517
- const data = JSON.parse(readFileSync12(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5239
+ const data = JSON.parse(readFileSync13(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4518
5240
  return data.parentExe || null;
4519
5241
  } catch {
4520
5242
  return null;
@@ -4522,8 +5244,8 @@ function getParentExe(sessionKey) {
4522
5244
  }
4523
5245
  function getDispatchedBy(sessionKey) {
4524
5246
  try {
4525
- const data = JSON.parse(readFileSync12(
4526
- path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5247
+ const data = JSON.parse(readFileSync13(
5248
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4527
5249
  "utf8"
4528
5250
  ));
4529
5251
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4593,8 +5315,8 @@ async function verifyPaneAtCapacity(sessionName) {
4593
5315
  }
4594
5316
  function readDebounceState() {
4595
5317
  try {
4596
- if (!existsSync12(DEBOUNCE_FILE)) return {};
4597
- const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
5318
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
5319
+ const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
4598
5320
  const state = {};
4599
5321
  for (const [key, val] of Object.entries(raw)) {
4600
5322
  if (typeof val === "number") {
@@ -4610,8 +5332,8 @@ function readDebounceState() {
4610
5332
  }
4611
5333
  function writeDebounceState(state) {
4612
5334
  try {
4613
- if (!existsSync12(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
4614
- writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
5335
+ if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
5336
+ writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
4615
5337
  } catch {
4616
5338
  }
4617
5339
  }
@@ -4709,8 +5431,8 @@ function sendIntercom(targetSession) {
4709
5431
  try {
4710
5432
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4711
5433
  const agent = baseAgentName(rawAgent);
4712
- const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
4713
- if (existsSync12(markerPath)) {
5434
+ const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
5435
+ if (existsSync14(markerPath)) {
4714
5436
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
4715
5437
  return "debounced";
4716
5438
  }
@@ -4719,8 +5441,8 @@ function sendIntercom(targetSession) {
4719
5441
  try {
4720
5442
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4721
5443
  const agent = baseAgentName(rawAgent);
4722
- const taskDir = path16.join(process.cwd(), "exe", agent);
4723
- if (existsSync12(taskDir)) {
5444
+ const taskDir = path18.join(process.cwd(), "exe", agent);
5445
+ if (existsSync14(taskDir)) {
4724
5446
  const files = readdirSync4(taskDir).filter(
4725
5447
  (f) => f.endsWith(".md") && f !== "DONE.txt"
4726
5448
  );
@@ -4780,6 +5502,21 @@ function notifyParentExe(sessionKey) {
4780
5502
  }
4781
5503
  return true;
4782
5504
  }
5505
+ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
5506
+ const transport = getTransport();
5507
+ try {
5508
+ const sessions = transport.listSessions();
5509
+ if (!sessions.includes(coordinatorSession)) return false;
5510
+ execSync7(
5511
+ `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
5512
+ { timeout: 3e3 }
5513
+ );
5514
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
5515
+ return true;
5516
+ } catch {
5517
+ return false;
5518
+ }
5519
+ }
4783
5520
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
4784
5521
  if (isCoordinatorName(employeeName)) {
4785
5522
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
@@ -4853,26 +5590,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4853
5590
  const transport = getTransport();
4854
5591
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4855
5592
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4856
- const logDir = path16.join(os8.homedir(), ".exe-os", "session-logs");
4857
- const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4858
- if (!existsSync12(logDir)) {
5593
+ const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
5594
+ const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5595
+ if (!existsSync14(logDir)) {
4859
5596
  mkdirSync7(logDir, { recursive: true });
4860
5597
  }
4861
5598
  transport.kill(sessionName);
4862
5599
  let cleanupSuffix = "";
4863
5600
  try {
4864
5601
  const thisFile = fileURLToPath2(import.meta.url);
4865
- const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4866
- if (existsSync12(cleanupScript)) {
5602
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5603
+ if (existsSync14(cleanupScript)) {
4867
5604
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4868
5605
  }
4869
5606
  } catch {
4870
5607
  }
4871
5608
  try {
4872
- const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
5609
+ const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
4873
5610
  let claudeJson = {};
4874
5611
  try {
4875
- claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
5612
+ claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
4876
5613
  } catch {
4877
5614
  }
4878
5615
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4880,17 +5617,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4880
5617
  const trustDir = opts?.cwd ?? projectDir;
4881
5618
  if (!projects[trustDir]) projects[trustDir] = {};
4882
5619
  projects[trustDir].hasTrustDialogAccepted = true;
4883
- writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5620
+ writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4884
5621
  } catch {
4885
5622
  }
4886
5623
  try {
4887
- const settingsDir = path16.join(os8.homedir(), ".claude", "projects");
5624
+ const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
4888
5625
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4889
- const projSettingsDir = path16.join(settingsDir, normalizedKey);
4890
- const settingsPath = path16.join(projSettingsDir, "settings.json");
5626
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
5627
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
4891
5628
  let settings = {};
4892
5629
  try {
4893
- settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
5630
+ settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
4894
5631
  } catch {
4895
5632
  }
4896
5633
  const perms = settings.permissions ?? {};
@@ -4919,7 +5656,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4919
5656
  perms.allow = allow;
4920
5657
  settings.permissions = perms;
4921
5658
  mkdirSync7(projSettingsDir, { recursive: true });
4922
- writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5659
+ writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4923
5660
  }
4924
5661
  } catch {
4925
5662
  }
@@ -4934,8 +5671,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4934
5671
  let behaviorsFlag = "";
4935
5672
  let legacyFallbackWarned = false;
4936
5673
  if (!useExeAgent && !useBinSymlink) {
4937
- const identityPath = path16.join(
4938
- os8.homedir(),
5674
+ const identityPath = path18.join(
5675
+ os10.homedir(),
4939
5676
  ".exe-os",
4940
5677
  "identity",
4941
5678
  `${employeeName}.md`
@@ -4944,13 +5681,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4944
5681
  const hasAgentFlag = claudeSupportsAgentFlag();
4945
5682
  if (hasAgentFlag) {
4946
5683
  identityFlag = ` --agent ${employeeName}`;
4947
- } else if (existsSync12(identityPath)) {
5684
+ } else if (existsSync14(identityPath)) {
4948
5685
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
4949
5686
  legacyFallbackWarned = true;
4950
5687
  }
4951
5688
  const behaviorsFile = exportBehaviorsSync(
4952
5689
  employeeName,
4953
- path16.basename(spawnCwd),
5690
+ path18.basename(spawnCwd),
4954
5691
  sessionName
4955
5692
  );
4956
5693
  if (behaviorsFile) {
@@ -4965,16 +5702,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4965
5702
  }
4966
5703
  let sessionContextFlag = "";
4967
5704
  try {
4968
- const ctxDir = path16.join(os8.homedir(), ".exe-os", "session-cache");
5705
+ const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
4969
5706
  mkdirSync7(ctxDir, { recursive: true });
4970
- const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
5707
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
4971
5708
  const ctxContent = [
4972
5709
  `## Session Context`,
4973
5710
  `You are running in tmux session: ${sessionName}.`,
4974
5711
  `Your parent coordinator session is ${exeSession}.`,
4975
5712
  `Your employees (if any) use the -${exeSession} suffix.`
4976
5713
  ].join("\n");
4977
- writeFileSync8(ctxFile, ctxContent);
5714
+ writeFileSync9(ctxFile, ctxContent);
4978
5715
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
4979
5716
  } catch {
4980
5717
  }
@@ -5051,8 +5788,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5051
5788
  transport.pipeLog(sessionName, logFile);
5052
5789
  try {
5053
5790
  const mySession = getMySession();
5054
- const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5055
- writeFileSync8(dispatchInfo, JSON.stringify({
5791
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5792
+ writeFileSync9(dispatchInfo, JSON.stringify({
5056
5793
  dispatchedBy: mySession,
5057
5794
  rootExe: exeSession,
5058
5795
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -5126,15 +5863,15 @@ var init_tmux_routing = __esm({
5126
5863
  init_intercom_queue();
5127
5864
  init_plan_limits();
5128
5865
  init_employees();
5129
- SPAWN_LOCK_DIR = path16.join(os8.homedir(), ".exe-os", "spawn-locks");
5130
- SESSION_CACHE = path16.join(os8.homedir(), ".exe-os", "session-cache");
5866
+ SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
5867
+ SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
5131
5868
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5132
5869
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5133
5870
  VERIFY_PANE_LINES = 200;
5134
5871
  INTERCOM_DEBOUNCE_MS = 3e4;
5135
5872
  CODEX_DEBOUNCE_MS = 12e4;
5136
- INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
5137
- DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
5873
+ INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
5874
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
5138
5875
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5139
5876
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5140
5877
  }
@@ -5157,6 +5894,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
5157
5894
  args: [scope]
5158
5895
  };
5159
5896
  }
5897
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
5898
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
5899
+ if (!scope) return { sql: "", args: [] };
5900
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
5901
+ return {
5902
+ sql: ` AND ${col} = ?`,
5903
+ args: [scope]
5904
+ };
5905
+ }
5160
5906
  var init_task_scope = __esm({
5161
5907
  "src/lib/task-scope.ts"() {
5162
5908
  "use strict";
@@ -5175,14 +5921,14 @@ var init_memory = __esm({
5175
5921
 
5176
5922
  // src/lib/keychain.ts
5177
5923
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
5178
- import { existsSync as existsSync13 } from "fs";
5179
- import path17 from "path";
5180
- import os9 from "os";
5924
+ import { existsSync as existsSync15 } from "fs";
5925
+ import path19 from "path";
5926
+ import os11 from "os";
5181
5927
  function getKeyDir() {
5182
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os9.homedir(), ".exe-os");
5928
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os11.homedir(), ".exe-os");
5183
5929
  }
5184
5930
  function getKeyPath() {
5185
- return path17.join(getKeyDir(), "master.key");
5931
+ return path19.join(getKeyDir(), "master.key");
5186
5932
  }
5187
5933
  async function tryKeytar() {
5188
5934
  try {
@@ -5203,9 +5949,9 @@ async function getMasterKey() {
5203
5949
  }
5204
5950
  }
5205
5951
  const keyPath = getKeyPath();
5206
- if (!existsSync13(keyPath)) {
5952
+ if (!existsSync15(keyPath)) {
5207
5953
  process.stderr.write(
5208
- `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5954
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5209
5955
  `
5210
5956
  );
5211
5957
  return null;
@@ -5235,6 +5981,7 @@ var shard_manager_exports = {};
5235
5981
  __export(shard_manager_exports, {
5236
5982
  disposeShards: () => disposeShards,
5237
5983
  ensureShardSchema: () => ensureShardSchema,
5984
+ getOpenShardCount: () => getOpenShardCount,
5238
5985
  getReadyShardClient: () => getReadyShardClient,
5239
5986
  getShardClient: () => getShardClient,
5240
5987
  getShardsDir: () => getShardsDir,
@@ -5243,15 +5990,18 @@ __export(shard_manager_exports, {
5243
5990
  listShards: () => listShards,
5244
5991
  shardExists: () => shardExists
5245
5992
  });
5246
- import path18 from "path";
5247
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
5993
+ import path20 from "path";
5994
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
5248
5995
  import { createClient as createClient2 } from "@libsql/client";
5249
5996
  function initShardManager(encryptionKey) {
5250
5997
  _encryptionKey = encryptionKey;
5251
- if (!existsSync14(SHARDS_DIR)) {
5998
+ if (!existsSync16(SHARDS_DIR)) {
5252
5999
  mkdirSync8(SHARDS_DIR, { recursive: true });
5253
6000
  }
5254
6001
  _shardingEnabled = true;
6002
+ if (_evictionTimer) clearInterval(_evictionTimer);
6003
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
6004
+ _evictionTimer.unref();
5255
6005
  }
5256
6006
  function isShardingEnabled() {
5257
6007
  return _shardingEnabled;
@@ -5268,21 +6018,28 @@ function getShardClient(projectName) {
5268
6018
  throw new Error(`Invalid project name for shard: "${projectName}"`);
5269
6019
  }
5270
6020
  const cached = _shards.get(safeName);
5271
- if (cached) return cached;
5272
- const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
6021
+ if (cached) {
6022
+ _shardLastAccess.set(safeName, Date.now());
6023
+ return cached;
6024
+ }
6025
+ while (_shards.size >= MAX_OPEN_SHARDS) {
6026
+ evictLRU();
6027
+ }
6028
+ const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
5273
6029
  const client = createClient2({
5274
6030
  url: `file:${dbPath}`,
5275
6031
  encryptionKey: _encryptionKey
5276
6032
  });
5277
6033
  _shards.set(safeName, client);
6034
+ _shardLastAccess.set(safeName, Date.now());
5278
6035
  return client;
5279
6036
  }
5280
6037
  function shardExists(projectName) {
5281
6038
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
5282
- return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
6039
+ return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
5283
6040
  }
5284
6041
  function listShards() {
5285
- if (!existsSync14(SHARDS_DIR)) return [];
6042
+ if (!existsSync16(SHARDS_DIR)) return [];
5286
6043
  return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
5287
6044
  }
5288
6045
  async function ensureShardSchema(client) {
@@ -5334,6 +6091,8 @@ async function ensureShardSchema(client) {
5334
6091
  for (const col of [
5335
6092
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
5336
6093
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
6094
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
6095
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
5337
6096
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
5338
6097
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
5339
6098
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -5356,7 +6115,23 @@ async function ensureShardSchema(client) {
5356
6115
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
5357
6116
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
5358
6117
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
5359
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
6118
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
6119
+ // Metadata enrichment columns (must match database.ts)
6120
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
6121
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
6122
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
6123
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
6124
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
6125
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
6126
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
6127
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
6128
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
6129
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
6130
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
6131
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
6132
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
6133
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
6134
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
5360
6135
  ]) {
5361
6136
  try {
5362
6137
  await client.execute(col);
@@ -5455,21 +6230,69 @@ async function getReadyShardClient(projectName) {
5455
6230
  await ensureShardSchema(client);
5456
6231
  return client;
5457
6232
  }
6233
+ function evictLRU() {
6234
+ let oldest = null;
6235
+ let oldestTime = Infinity;
6236
+ for (const [name, time] of _shardLastAccess) {
6237
+ if (time < oldestTime) {
6238
+ oldestTime = time;
6239
+ oldest = name;
6240
+ }
6241
+ }
6242
+ if (oldest) {
6243
+ const client = _shards.get(oldest);
6244
+ if (client) {
6245
+ client.close();
6246
+ }
6247
+ _shards.delete(oldest);
6248
+ _shardLastAccess.delete(oldest);
6249
+ }
6250
+ }
6251
+ function evictIdleShards() {
6252
+ const now = Date.now();
6253
+ const toEvict = [];
6254
+ for (const [name, lastAccess] of _shardLastAccess) {
6255
+ if (now - lastAccess > SHARD_IDLE_MS) {
6256
+ toEvict.push(name);
6257
+ }
6258
+ }
6259
+ for (const name of toEvict) {
6260
+ const client = _shards.get(name);
6261
+ if (client) {
6262
+ client.close();
6263
+ }
6264
+ _shards.delete(name);
6265
+ _shardLastAccess.delete(name);
6266
+ }
6267
+ }
6268
+ function getOpenShardCount() {
6269
+ return _shards.size;
6270
+ }
5458
6271
  function disposeShards() {
6272
+ if (_evictionTimer) {
6273
+ clearInterval(_evictionTimer);
6274
+ _evictionTimer = null;
6275
+ }
5459
6276
  for (const [, client] of _shards) {
5460
6277
  client.close();
5461
6278
  }
5462
6279
  _shards.clear();
6280
+ _shardLastAccess.clear();
5463
6281
  _shardingEnabled = false;
5464
6282
  _encryptionKey = null;
5465
6283
  }
5466
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
6284
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
5467
6285
  var init_shard_manager = __esm({
5468
6286
  "src/lib/shard-manager.ts"() {
5469
6287
  "use strict";
5470
6288
  init_config();
5471
- SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
6289
+ SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
6290
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
6291
+ MAX_OPEN_SHARDS = 10;
6292
+ EVICTION_INTERVAL_MS = 60 * 1e3;
5472
6293
  _shards = /* @__PURE__ */ new Map();
6294
+ _shardLastAccess = /* @__PURE__ */ new Map();
6295
+ _evictionTimer = null;
5473
6296
  _encryptionKey = null;
5474
6297
  _shardingEnabled = false;
5475
6298
  }
@@ -6233,13 +7056,13 @@ var init_store = __esm({
6233
7056
  });
6234
7057
 
6235
7058
  // src/adapters/claude/hooks/pre-compact.ts
6236
- import crypto6 from "crypto";
7059
+ import crypto7 from "crypto";
6237
7060
 
6238
7061
  // src/lib/active-agent.ts
6239
7062
  init_config();
6240
7063
  init_session_key();
6241
7064
  init_employees();
6242
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
7065
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
6243
7066
  import { execSync as execSync3 } from "child_process";
6244
7067
  import path3 from "path";
6245
7068
  var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
@@ -6406,7 +7229,7 @@ ${taskLines}`);
6406
7229
  recoveryLines.push(`Files: ${lastCheckpoint.files_touched.join(", ")}`);
6407
7230
  }
6408
7231
  await writeMemory2({
6409
- id: crypto6.randomUUID(),
7232
+ id: crypto7.randomUUID(),
6410
7233
  agent_id: agent.agentId,
6411
7234
  agent_role: agent.agentRole,
6412
7235
  session_id: payload.session_id,