@askexenow/exe-os 0.9.8 → 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 +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -3,9 +3,47 @@ var __esm = (fn, res) => function __init() {
3
3
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
4
  };
5
5
 
6
+ // src/lib/secure-files.ts
7
+ import { chmodSync, existsSync, mkdirSync } from "fs";
8
+ import { chmod, mkdir } from "fs/promises";
9
+ async function ensurePrivateDir(dirPath) {
10
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
11
+ try {
12
+ await chmod(dirPath, PRIVATE_DIR_MODE);
13
+ } catch {
14
+ }
15
+ }
16
+ function ensurePrivateDirSync(dirPath) {
17
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
18
+ try {
19
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
20
+ } catch {
21
+ }
22
+ }
23
+ async function enforcePrivateFile(filePath) {
24
+ try {
25
+ await chmod(filePath, PRIVATE_FILE_MODE);
26
+ } catch {
27
+ }
28
+ }
29
+ function enforcePrivateFileSync(filePath) {
30
+ try {
31
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
32
+ } catch {
33
+ }
34
+ }
35
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
36
+ var init_secure_files = __esm({
37
+ "src/lib/secure-files.ts"() {
38
+ "use strict";
39
+ PRIVATE_DIR_MODE = 448;
40
+ PRIVATE_FILE_MODE = 384;
41
+ }
42
+ });
43
+
6
44
  // src/lib/config.ts
7
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
8
- import { readFileSync, existsSync, renameSync } from "fs";
45
+ import { readFile, writeFile } from "fs/promises";
46
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
47
  import path from "path";
10
48
  import os from "os";
11
49
  function resolveDataDir() {
@@ -13,7 +51,7 @@ function resolveDataDir() {
13
51
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
52
  const newDir = path.join(os.homedir(), ".exe-os");
15
53
  const legacyDir = path.join(os.homedir(), ".exe-mem");
16
- if (!existsSync(newDir) && existsSync(legacyDir)) {
54
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
55
  try {
18
56
  renameSync(legacyDir, newDir);
19
57
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -76,9 +114,9 @@ function normalizeAutoUpdate(raw) {
76
114
  }
77
115
  async function loadConfig() {
78
116
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
79
- await mkdir(dir, { recursive: true });
117
+ await ensurePrivateDir(dir);
80
118
  const configPath = path.join(dir, "config.json");
81
- if (!existsSync(configPath)) {
119
+ if (!existsSync2(configPath)) {
82
120
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
83
121
  }
84
122
  const raw = await readFile(configPath, "utf-8");
@@ -91,6 +129,7 @@ async function loadConfig() {
91
129
  `);
92
130
  try {
93
131
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
132
+ await enforcePrivateFile(configPath);
94
133
  } catch {
95
134
  }
96
135
  }
@@ -108,17 +147,16 @@ async function loadConfig() {
108
147
  }
109
148
  async function saveConfig(config) {
110
149
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
111
- await mkdir(dir, { recursive: true });
150
+ await ensurePrivateDir(dir);
112
151
  const configPath = path.join(dir, "config.json");
113
152
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
114
- if (config.cloud?.apiKey) {
115
- await chmod(configPath, 384);
116
- }
153
+ await enforcePrivateFile(configPath);
117
154
  }
118
155
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
119
156
  var init_config = __esm({
120
157
  "src/lib/config.ts"() {
121
158
  "use strict";
159
+ init_secure_files();
122
160
  EXE_AI_DIR = resolveDataDir();
123
161
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
124
162
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -204,12 +242,12 @@ var init_db_retry = __esm({
204
242
 
205
243
  // src/lib/employees.ts
206
244
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
207
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
245
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
208
246
  import { execSync } from "child_process";
209
247
  import path2 from "path";
210
248
  import os2 from "os";
211
249
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
212
- if (!existsSync2(employeesPath)) return [];
250
+ if (!existsSync3(employeesPath)) return [];
213
251
  try {
214
252
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
215
253
  } catch {
@@ -260,7 +298,7 @@ var init_database = __esm({
260
298
 
261
299
  // src/lib/crdt-sync.ts
262
300
  import * as Y from "yjs";
263
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
301
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
264
302
  import path5 from "path";
265
303
  import { homedir } from "os";
266
304
  var DEFAULT_STATE_PATH;
@@ -299,10 +337,10 @@ var init_runtime_table = __esm({
299
337
  });
300
338
 
301
339
  // src/lib/agent-config.ts
302
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
340
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
303
341
  import path7 from "path";
304
342
  function loadAgentConfig() {
305
- if (!existsSync6(AGENT_CONFIG_PATH)) return {};
343
+ if (!existsSync7(AGENT_CONFIG_PATH)) return {};
306
344
  try {
307
345
  return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
308
346
  } catch {
@@ -311,8 +349,9 @@ function loadAgentConfig() {
311
349
  }
312
350
  function saveAgentConfig(config) {
313
351
  const dir = path7.dirname(AGENT_CONFIG_PATH);
314
- if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
352
+ ensurePrivateDirSync(dir);
315
353
  writeFileSync5(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
354
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
316
355
  }
317
356
  function setAgentRuntime(agentId, runtime, model) {
318
357
  const knownModels = KNOWN_RUNTIMES[runtime];
@@ -344,6 +383,7 @@ var init_agent_config = __esm({
344
383
  "use strict";
345
384
  init_config();
346
385
  init_runtime_table();
386
+ init_secure_files();
347
387
  AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
348
388
  KNOWN_RUNTIMES = {
349
389
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
@@ -384,7 +424,7 @@ function isMainModule(importMetaUrl) {
384
424
 
385
425
  // src/lib/cloud-sync.ts
386
426
  init_database();
387
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync3, openSync, closeSync } from "fs";
427
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync3, openSync, closeSync } from "fs";
388
428
  import crypto2 from "crypto";
389
429
  import path6 from "path";
390
430
  import { homedir as homedir2 } from "os";
@@ -397,8 +437,11 @@ import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
397
437
 
398
438
  // src/lib/license.ts
399
439
  init_config();
400
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync } from "fs";
440
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
401
441
  import { randomUUID } from "crypto";
442
+ import { createRequire as createRequire2 } from "module";
443
+ import { pathToFileURL as pathToFileURL2 } from "url";
444
+ import os4 from "os";
402
445
  import path4 from "path";
403
446
  import { jwtVerify, importSPKI } from "jose";
404
447
  var LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
@@ -409,6 +452,7 @@ var DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
409
452
  init_config();
410
453
  init_crdt_sync();
411
454
  init_employees();
455
+ init_secure_files();
412
456
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
413
457
  var ROSTER_LOCK_PATH = path6.join(EXE_AI_DIR, "roster-merge.lock");
414
458
  function assertSecureEndpoint(endpoint) {
@@ -70,9 +70,34 @@ var init_db_retry = __esm({
70
70
  }
71
71
  });
72
72
 
73
+ // src/lib/secure-files.ts
74
+ import { chmodSync, existsSync, mkdirSync } from "fs";
75
+ import { chmod, mkdir } from "fs/promises";
76
+ async function ensurePrivateDir(dirPath) {
77
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
78
+ try {
79
+ await chmod(dirPath, PRIVATE_DIR_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ async function enforcePrivateFile(filePath) {
84
+ try {
85
+ await chmod(filePath, PRIVATE_FILE_MODE);
86
+ } catch {
87
+ }
88
+ }
89
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
90
+ var init_secure_files = __esm({
91
+ "src/lib/secure-files.ts"() {
92
+ "use strict";
93
+ PRIVATE_DIR_MODE = 448;
94
+ PRIVATE_FILE_MODE = 384;
95
+ }
96
+ });
97
+
73
98
  // src/lib/config.ts
74
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
75
- import { readFileSync, existsSync, renameSync } from "fs";
99
+ import { readFile, writeFile } from "fs/promises";
100
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
76
101
  import path from "path";
77
102
  import os from "os";
78
103
  function resolveDataDir() {
@@ -80,7 +105,7 @@ function resolveDataDir() {
80
105
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
81
106
  const newDir = path.join(os.homedir(), ".exe-os");
82
107
  const legacyDir = path.join(os.homedir(), ".exe-mem");
83
- if (!existsSync(newDir) && existsSync(legacyDir)) {
108
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
84
109
  try {
85
110
  renameSync(legacyDir, newDir);
86
111
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -143,9 +168,9 @@ function normalizeAutoUpdate(raw) {
143
168
  }
144
169
  async function loadConfig() {
145
170
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
146
- await mkdir(dir, { recursive: true });
171
+ await ensurePrivateDir(dir);
147
172
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
173
+ if (!existsSync2(configPath)) {
149
174
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
175
  }
151
176
  const raw = await readFile(configPath, "utf-8");
@@ -158,6 +183,7 @@ async function loadConfig() {
158
183
  `);
159
184
  try {
160
185
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
186
+ await enforcePrivateFile(configPath);
161
187
  } catch {
162
188
  }
163
189
  }
@@ -177,6 +203,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
177
203
  var init_config = __esm({
178
204
  "src/lib/config.ts"() {
179
205
  "use strict";
206
+ init_secure_files();
180
207
  EXE_AI_DIR = resolveDataDir();
181
208
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
182
209
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -255,7 +282,7 @@ var init_config = __esm({
255
282
 
256
283
  // src/lib/employees.ts
257
284
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
258
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
285
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
259
286
  import { execSync } from "child_process";
260
287
  import path2 from "path";
261
288
  import os2 from "os";
@@ -272,7 +299,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
272
299
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
273
300
  }
274
301
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
275
- if (!existsSync2(employeesPath)) return [];
302
+ if (!existsSync3(employeesPath)) return [];
276
303
  try {
277
304
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
278
305
  } catch {
@@ -1228,6 +1255,7 @@ async function ensureSchema() {
1228
1255
  project TEXT NOT NULL,
1229
1256
  summary TEXT NOT NULL,
1230
1257
  task_file TEXT,
1258
+ session_scope TEXT,
1231
1259
  read INTEGER NOT NULL DEFAULT 0,
1232
1260
  created_at TEXT NOT NULL
1233
1261
  );
@@ -1236,7 +1264,7 @@ async function ensureSchema() {
1236
1264
  ON notifications(read);
1237
1265
 
1238
1266
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1239
- ON notifications(agent_id);
1267
+ ON notifications(agent_id, session_scope);
1240
1268
 
1241
1269
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1242
1270
  ON notifications(task_file);
@@ -1274,6 +1302,7 @@ async function ensureSchema() {
1274
1302
  target_agent TEXT NOT NULL,
1275
1303
  target_project TEXT,
1276
1304
  target_device TEXT NOT NULL DEFAULT 'local',
1305
+ session_scope TEXT,
1277
1306
  content TEXT NOT NULL,
1278
1307
  priority TEXT DEFAULT 'normal',
1279
1308
  status TEXT DEFAULT 'pending',
@@ -1287,10 +1316,31 @@ async function ensureSchema() {
1287
1316
  );
1288
1317
 
1289
1318
  CREATE INDEX IF NOT EXISTS idx_messages_target
1290
- ON messages(target_agent, status);
1319
+ ON messages(target_agent, session_scope, status);
1291
1320
 
1292
1321
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1293
- ON messages(target_agent, from_agent, server_seq);
1322
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1323
+ `);
1324
+ try {
1325
+ await client.execute({
1326
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1327
+ args: []
1328
+ });
1329
+ } catch {
1330
+ }
1331
+ try {
1332
+ await client.execute({
1333
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1334
+ args: []
1335
+ });
1336
+ } catch {
1337
+ }
1338
+ await client.executeMultiple(`
1339
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1340
+ ON notifications(agent_id, session_scope, read, created_at);
1341
+
1342
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1343
+ ON messages(target_agent, session_scope, status, created_at);
1294
1344
  `);
1295
1345
  try {
1296
1346
  await client.execute({
@@ -1874,6 +1924,13 @@ async function ensureSchema() {
1874
1924
  } catch {
1875
1925
  }
1876
1926
  }
1927
+ try {
1928
+ await client.execute({
1929
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
1930
+ args: []
1931
+ });
1932
+ } catch {
1933
+ }
1877
1934
  }
1878
1935
  async function disposeDatabase() {
1879
1936
  if (_walCheckpointTimer) {
@@ -1916,6 +1973,7 @@ var shard_manager_exports = {};
1916
1973
  __export(shard_manager_exports, {
1917
1974
  disposeShards: () => disposeShards,
1918
1975
  ensureShardSchema: () => ensureShardSchema,
1976
+ getOpenShardCount: () => getOpenShardCount,
1919
1977
  getReadyShardClient: () => getReadyShardClient,
1920
1978
  getShardClient: () => getShardClient,
1921
1979
  getShardsDir: () => getShardsDir,
@@ -1925,14 +1983,17 @@ __export(shard_manager_exports, {
1925
1983
  shardExists: () => shardExists
1926
1984
  });
1927
1985
  import path5 from "path";
1928
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1986
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1929
1987
  import { createClient as createClient2 } from "@libsql/client";
1930
1988
  function initShardManager(encryptionKey) {
1931
1989
  _encryptionKey = encryptionKey;
1932
- if (!existsSync4(SHARDS_DIR)) {
1933
- mkdirSync(SHARDS_DIR, { recursive: true });
1990
+ if (!existsSync5(SHARDS_DIR)) {
1991
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1934
1992
  }
1935
1993
  _shardingEnabled = true;
1994
+ if (_evictionTimer) clearInterval(_evictionTimer);
1995
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
1996
+ _evictionTimer.unref();
1936
1997
  }
1937
1998
  function isShardingEnabled() {
1938
1999
  return _shardingEnabled;
@@ -1949,21 +2010,28 @@ function getShardClient(projectName) {
1949
2010
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1950
2011
  }
1951
2012
  const cached = _shards.get(safeName);
1952
- if (cached) return cached;
2013
+ if (cached) {
2014
+ _shardLastAccess.set(safeName, Date.now());
2015
+ return cached;
2016
+ }
2017
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2018
+ evictLRU();
2019
+ }
1953
2020
  const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1954
2021
  const client = createClient2({
1955
2022
  url: `file:${dbPath}`,
1956
2023
  encryptionKey: _encryptionKey
1957
2024
  });
1958
2025
  _shards.set(safeName, client);
2026
+ _shardLastAccess.set(safeName, Date.now());
1959
2027
  return client;
1960
2028
  }
1961
2029
  function shardExists(projectName) {
1962
2030
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1963
- return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
2031
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1964
2032
  }
1965
2033
  function listShards() {
1966
- if (!existsSync4(SHARDS_DIR)) return [];
2034
+ if (!existsSync5(SHARDS_DIR)) return [];
1967
2035
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1968
2036
  }
1969
2037
  async function ensureShardSchema(client) {
@@ -2015,6 +2083,8 @@ async function ensureShardSchema(client) {
2015
2083
  for (const col of [
2016
2084
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
2017
2085
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2086
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2087
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
2018
2088
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
2019
2089
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
2020
2090
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -2152,21 +2222,69 @@ async function getReadyShardClient(projectName) {
2152
2222
  await ensureShardSchema(client);
2153
2223
  return client;
2154
2224
  }
2225
+ function evictLRU() {
2226
+ let oldest = null;
2227
+ let oldestTime = Infinity;
2228
+ for (const [name, time] of _shardLastAccess) {
2229
+ if (time < oldestTime) {
2230
+ oldestTime = time;
2231
+ oldest = name;
2232
+ }
2233
+ }
2234
+ if (oldest) {
2235
+ const client = _shards.get(oldest);
2236
+ if (client) {
2237
+ client.close();
2238
+ }
2239
+ _shards.delete(oldest);
2240
+ _shardLastAccess.delete(oldest);
2241
+ }
2242
+ }
2243
+ function evictIdleShards() {
2244
+ const now = Date.now();
2245
+ const toEvict = [];
2246
+ for (const [name, lastAccess] of _shardLastAccess) {
2247
+ if (now - lastAccess > SHARD_IDLE_MS) {
2248
+ toEvict.push(name);
2249
+ }
2250
+ }
2251
+ for (const name of toEvict) {
2252
+ const client = _shards.get(name);
2253
+ if (client) {
2254
+ client.close();
2255
+ }
2256
+ _shards.delete(name);
2257
+ _shardLastAccess.delete(name);
2258
+ }
2259
+ }
2260
+ function getOpenShardCount() {
2261
+ return _shards.size;
2262
+ }
2155
2263
  function disposeShards() {
2264
+ if (_evictionTimer) {
2265
+ clearInterval(_evictionTimer);
2266
+ _evictionTimer = null;
2267
+ }
2156
2268
  for (const [, client] of _shards) {
2157
2269
  client.close();
2158
2270
  }
2159
2271
  _shards.clear();
2272
+ _shardLastAccess.clear();
2160
2273
  _shardingEnabled = false;
2161
2274
  _encryptionKey = null;
2162
2275
  }
2163
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2276
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
2164
2277
  var init_shard_manager = __esm({
2165
2278
  "src/lib/shard-manager.ts"() {
2166
2279
  "use strict";
2167
2280
  init_config();
2168
2281
  SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2282
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2283
+ MAX_OPEN_SHARDS = 10;
2284
+ EVICTION_INTERVAL_MS = 60 * 1e3;
2169
2285
  _shards = /* @__PURE__ */ new Map();
2286
+ _shardLastAccess = /* @__PURE__ */ new Map();
2287
+ _evictionTimer = null;
2170
2288
  _encryptionKey = null;
2171
2289
  _shardingEnabled = false;
2172
2290
  }
@@ -2463,7 +2581,7 @@ __export(active_agent_exports, {
2463
2581
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
2464
2582
  writeActiveAgent: () => writeActiveAgent
2465
2583
  });
2466
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2584
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2467
2585
  import { execSync as execSync3 } from "child_process";
2468
2586
  import path7 from "path";
2469
2587
  function isNameWithOptionalInstance(candidate, baseName) {
@@ -2513,7 +2631,7 @@ function getMarkerPath() {
2513
2631
  }
2514
2632
  function writeActiveAgent(agentId, agentRole) {
2515
2633
  try {
2516
- mkdirSync3(CACHE_DIR, { recursive: true });
2634
+ mkdirSync4(CACHE_DIR, { recursive: true });
2517
2635
  writeFileSync3(
2518
2636
  getMarkerPath(),
2519
2637
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
@@ -2632,9 +2750,9 @@ var init_active_agent = __esm({
2632
2750
  import os6 from "os";
2633
2751
  import path8 from "path";
2634
2752
  import {
2635
- existsSync as existsSync6,
2753
+ existsSync as existsSync7,
2636
2754
  lstatSync,
2637
- mkdirSync as mkdirSync4,
2755
+ mkdirSync as mkdirSync5,
2638
2756
  readlinkSync as readlinkSync2,
2639
2757
  symlinkSync as symlinkSync2
2640
2758
  } from "fs";
@@ -2660,12 +2778,12 @@ var init_mcp_prefix = __esm({
2660
2778
  });
2661
2779
 
2662
2780
  // src/lib/preferences.ts
2663
- import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
2781
+ import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
2664
2782
  import path9 from "path";
2665
2783
  import os7 from "os";
2666
2784
  function loadPreferences(homeDir = os7.homedir()) {
2667
2785
  const configPath = path9.join(homeDir, ".exe-os", "config.json");
2668
- if (!existsSync7(configPath)) return {};
2786
+ if (!existsSync8(configPath)) return {};
2669
2787
  try {
2670
2788
  const config = JSON.parse(readFileSync4(configPath, "utf-8"));
2671
2789
  return config.preferences ?? {};
@@ -2676,12 +2794,13 @@ function loadPreferences(homeDir = os7.homedir()) {
2676
2794
  var init_preferences = __esm({
2677
2795
  "src/lib/preferences.ts"() {
2678
2796
  "use strict";
2797
+ init_secure_files();
2679
2798
  }
2680
2799
  });
2681
2800
 
2682
2801
  // src/adapters/claude/installer.ts
2683
2802
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
2684
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2803
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
2685
2804
  import path10 from "path";
2686
2805
  import os8 from "os";
2687
2806
  import { execSync as execSync4 } from "child_process";
@@ -2692,7 +2811,7 @@ function resolvePackageRoot() {
2692
2811
  const root = path10.parse(dir).root;
2693
2812
  while (dir !== root) {
2694
2813
  const pkgPath = path10.join(dir, "package.json");
2695
- if (existsSync8(pkgPath)) {
2814
+ if (existsSync9(pkgPath)) {
2696
2815
  try {
2697
2816
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2698
2817
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
@@ -2735,7 +2854,7 @@ __export(installer_exports, {
2735
2854
  verifyCodexHooks: () => verifyCodexHooks
2736
2855
  });
2737
2856
  import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
2738
- import { existsSync as existsSync9 } from "fs";
2857
+ import { existsSync as existsSync10 } from "fs";
2739
2858
  import path11 from "path";
2740
2859
  import os9 from "os";
2741
2860
  async function mergeCodexHooks(packageRoot, homeDir = os9.homedir()) {
@@ -2747,7 +2866,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os9.homedir()) {
2747
2866
  await mkdir5(codexDir, { recursive: true });
2748
2867
  await mkdir5(logsDir, { recursive: true });
2749
2868
  let hooksJson = {};
2750
- if (existsSync9(hooksPath)) {
2869
+ if (existsSync10(hooksPath)) {
2751
2870
  try {
2752
2871
  hooksJson = JSON.parse(await readFile5(hooksPath, "utf-8"));
2753
2872
  } catch {
@@ -2858,7 +2977,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os9.homedir()) {
2858
2977
  }
2859
2978
  function verifyCodexHooks(homeDir = os9.homedir()) {
2860
2979
  const hooksPath = path11.join(homeDir, ".codex", "hooks.json");
2861
- if (!existsSync9(hooksPath)) return false;
2980
+ if (!existsSync10(hooksPath)) return false;
2862
2981
  try {
2863
2982
  const hooksJson = JSON.parse(
2864
2983
  __require("fs").readFileSync(hooksPath, "utf-8")
@@ -2885,7 +3004,7 @@ async function installCodexStatusLine(homeDir = os9.homedir()) {
2885
3004
  const configPath = path11.join(codexDir, "config.toml");
2886
3005
  await mkdir5(codexDir, { recursive: true });
2887
3006
  let content = "";
2888
- if (existsSync9(configPath)) {
3007
+ if (existsSync10(configPath)) {
2889
3008
  content = await readFile5(configPath, "utf-8");
2890
3009
  if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
2891
3010
  return "already-configured";
@@ -2936,7 +3055,7 @@ var init_installer2 = __esm({
2936
3055
  import os10 from "os";
2937
3056
  import path12 from "path";
2938
3057
  import {
2939
- existsSync as existsSync10,
3058
+ existsSync as existsSync11,
2940
3059
  readFileSync as readFileSync6,
2941
3060
  writeFileSync as writeFileSync6,
2942
3061
  mkdirSync as mkdirSync7,
@@ -2950,7 +3069,7 @@ init_database();
2950
3069
 
2951
3070
  // src/lib/keychain.ts
2952
3071
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2953
- import { existsSync as existsSync3 } from "fs";
3072
+ import { existsSync as existsSync4 } from "fs";
2954
3073
  import path4 from "path";
2955
3074
  import os4 from "os";
2956
3075
  var SERVICE = "exe-mem";
@@ -2980,7 +3099,7 @@ async function getMasterKey() {
2980
3099
  }
2981
3100
  }
2982
3101
  const keyPath = getKeyPath();
2983
- if (!existsSync3(keyPath)) {
3102
+ if (!existsSync4(keyPath)) {
2984
3103
  process.stderr.write(
2985
3104
  `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2986
3105
  `
@@ -3296,8 +3415,8 @@ function vectorToBlob(vector) {
3296
3415
  import os5 from "os";
3297
3416
  import path6 from "path";
3298
3417
  import {
3299
- existsSync as existsSync5,
3300
- mkdirSync as mkdirSync2,
3418
+ existsSync as existsSync6,
3419
+ mkdirSync as mkdirSync3,
3301
3420
  readdirSync as readdirSync2,
3302
3421
  statSync,
3303
3422
  unlinkSync as unlinkSync2,
@@ -3344,7 +3463,7 @@ var BEHAVIORS_EXPORT_DIR = path6.join(
3344
3463
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
3345
3464
  var EXPORT_BEHAVIOR_LIMIT = 30;
3346
3465
  function sweepStaleBehaviorExports(now = Date.now()) {
3347
- if (!existsSync5(BEHAVIORS_EXPORT_DIR)) return;
3466
+ if (!existsSync6(BEHAVIORS_EXPORT_DIR)) return;
3348
3467
  let entries;
3349
3468
  try {
3350
3469
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -3394,7 +3513,7 @@ function exportFilePath(agentId, projectName, sessionKey) {
3394
3513
  );
3395
3514
  }
3396
3515
  async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
3397
- mkdirSync2(BEHAVIORS_EXPORT_DIR, { recursive: true });
3516
+ mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
3398
3517
  sweepStaleBehaviorExports();
3399
3518
  const behaviors = await listBehaviors(agentId, projectName, EXPORT_BEHAVIOR_LIMIT);
3400
3519
  if (behaviors.length === 0) return null;
@@ -3445,7 +3564,7 @@ function resolveAgent(argv) {
3445
3564
  function loadIdentity(agent) {
3446
3565
  const dir = path12.join(os10.homedir(), ".exe-os", "identity");
3447
3566
  const exact = path12.join(dir, `${agent}.md`);
3448
- if (existsSync10(exact)) {
3567
+ if (existsSync11(exact)) {
3449
3568
  const content = readFileSync6(exact, "utf-8").trim();
3450
3569
  if (content) return content;
3451
3570
  }
@@ -3473,7 +3592,7 @@ function writePromptFile(agent, identity, behaviorsPath) {
3473
3592
  const promptDir = path12.join(os10.homedir(), ".exe-os", "codex-prompt");
3474
3593
  mkdirSync7(promptDir, { recursive: true });
3475
3594
  let prompt = identity;
3476
- if (behaviorsPath && existsSync10(behaviorsPath)) {
3595
+ if (behaviorsPath && existsSync11(behaviorsPath)) {
3477
3596
  const behaviors = readFileSync6(behaviorsPath, "utf-8").trim();
3478
3597
  if (behaviors) {
3479
3598
  prompt += "\n\n" + behaviors;
@@ -3600,7 +3719,7 @@ async function main() {
3600
3719
  const { execSync: es } = await import("child_process");
3601
3720
  const worktreeDir = path12.join(process.cwd(), ".worktrees", worktreeName);
3602
3721
  const branchName = `${worktreeName}/codex-${Date.now()}`;
3603
- if (existsSync10(worktreeDir)) {
3722
+ if (existsSync11(worktreeDir)) {
3604
3723
  worktreePath = worktreeDir;
3605
3724
  process.stderr.write(`[exe-start-codex] Reusing worktree at ${worktreeDir}
3606
3725
  `);