@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
@@ -64,9 +64,47 @@ var init_db_retry = __esm({
64
64
  }
65
65
  });
66
66
 
67
+ // src/lib/secure-files.ts
68
+ import { chmodSync, existsSync, mkdirSync } from "fs";
69
+ import { chmod, mkdir } from "fs/promises";
70
+ async function ensurePrivateDir(dirPath) {
71
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
72
+ try {
73
+ await chmod(dirPath, PRIVATE_DIR_MODE);
74
+ } catch {
75
+ }
76
+ }
77
+ function ensurePrivateDirSync(dirPath) {
78
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
79
+ try {
80
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
81
+ } catch {
82
+ }
83
+ }
84
+ async function enforcePrivateFile(filePath) {
85
+ try {
86
+ await chmod(filePath, PRIVATE_FILE_MODE);
87
+ } catch {
88
+ }
89
+ }
90
+ function enforcePrivateFileSync(filePath) {
91
+ try {
92
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
93
+ } catch {
94
+ }
95
+ }
96
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
97
+ var init_secure_files = __esm({
98
+ "src/lib/secure-files.ts"() {
99
+ "use strict";
100
+ PRIVATE_DIR_MODE = 448;
101
+ PRIVATE_FILE_MODE = 384;
102
+ }
103
+ });
104
+
67
105
  // src/lib/config.ts
68
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
69
- import { readFileSync, existsSync, renameSync } from "fs";
106
+ import { readFile, writeFile } from "fs/promises";
107
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
70
108
  import path from "path";
71
109
  import os from "os";
72
110
  function resolveDataDir() {
@@ -74,7 +112,7 @@ function resolveDataDir() {
74
112
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
75
113
  const newDir = path.join(os.homedir(), ".exe-os");
76
114
  const legacyDir = path.join(os.homedir(), ".exe-mem");
77
- if (!existsSync(newDir) && existsSync(legacyDir)) {
115
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
78
116
  try {
79
117
  renameSync(legacyDir, newDir);
80
118
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -137,9 +175,9 @@ function normalizeAutoUpdate(raw) {
137
175
  }
138
176
  async function loadConfig() {
139
177
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
140
- await mkdir(dir, { recursive: true });
178
+ await ensurePrivateDir(dir);
141
179
  const configPath = path.join(dir, "config.json");
142
- if (!existsSync(configPath)) {
180
+ if (!existsSync2(configPath)) {
143
181
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
144
182
  }
145
183
  const raw = await readFile(configPath, "utf-8");
@@ -152,6 +190,7 @@ async function loadConfig() {
152
190
  `);
153
191
  try {
154
192
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
193
+ await enforcePrivateFile(configPath);
155
194
  } catch {
156
195
  }
157
196
  }
@@ -171,6 +210,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
171
210
  var init_config = __esm({
172
211
  "src/lib/config.ts"() {
173
212
  "use strict";
213
+ init_secure_files();
174
214
  EXE_AI_DIR = resolveDataDir();
175
215
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
176
216
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -249,7 +289,7 @@ var init_config = __esm({
249
289
 
250
290
  // src/lib/employees.ts
251
291
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
252
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
292
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
253
293
  import { execSync } from "child_process";
254
294
  import path2 from "path";
255
295
  import os2 from "os";
@@ -270,7 +310,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
270
310
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
271
311
  }
272
312
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
273
- if (!existsSync2(employeesPath)) return [];
313
+ if (!existsSync3(employeesPath)) return [];
274
314
  try {
275
315
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
276
316
  } catch {
@@ -1215,6 +1255,7 @@ async function ensureSchema() {
1215
1255
  project TEXT NOT NULL,
1216
1256
  summary TEXT NOT NULL,
1217
1257
  task_file TEXT,
1258
+ session_scope TEXT,
1218
1259
  read INTEGER NOT NULL DEFAULT 0,
1219
1260
  created_at TEXT NOT NULL
1220
1261
  );
@@ -1223,7 +1264,7 @@ async function ensureSchema() {
1223
1264
  ON notifications(read);
1224
1265
 
1225
1266
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1226
- ON notifications(agent_id);
1267
+ ON notifications(agent_id, session_scope);
1227
1268
 
1228
1269
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1229
1270
  ON notifications(task_file);
@@ -1261,6 +1302,7 @@ async function ensureSchema() {
1261
1302
  target_agent TEXT NOT NULL,
1262
1303
  target_project TEXT,
1263
1304
  target_device TEXT NOT NULL DEFAULT 'local',
1305
+ session_scope TEXT,
1264
1306
  content TEXT NOT NULL,
1265
1307
  priority TEXT DEFAULT 'normal',
1266
1308
  status TEXT DEFAULT 'pending',
@@ -1274,10 +1316,31 @@ async function ensureSchema() {
1274
1316
  );
1275
1317
 
1276
1318
  CREATE INDEX IF NOT EXISTS idx_messages_target
1277
- ON messages(target_agent, status);
1319
+ ON messages(target_agent, session_scope, status);
1278
1320
 
1279
1321
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1280
- 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);
1281
1344
  `);
1282
1345
  try {
1283
1346
  await client.execute({
@@ -1861,6 +1924,13 @@ async function ensureSchema() {
1861
1924
  } catch {
1862
1925
  }
1863
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
+ }
1864
1934
  }
1865
1935
  var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1866
1936
  var init_database = __esm({
@@ -1883,6 +1953,7 @@ var shard_manager_exports = {};
1883
1953
  __export(shard_manager_exports, {
1884
1954
  disposeShards: () => disposeShards,
1885
1955
  ensureShardSchema: () => ensureShardSchema,
1956
+ getOpenShardCount: () => getOpenShardCount,
1886
1957
  getReadyShardClient: () => getReadyShardClient,
1887
1958
  getShardClient: () => getShardClient,
1888
1959
  getShardsDir: () => getShardsDir,
@@ -1892,14 +1963,17 @@ __export(shard_manager_exports, {
1892
1963
  shardExists: () => shardExists
1893
1964
  });
1894
1965
  import path5 from "path";
1895
- import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1966
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
1896
1967
  import { createClient as createClient2 } from "@libsql/client";
1897
1968
  function initShardManager(encryptionKey) {
1898
1969
  _encryptionKey = encryptionKey;
1899
- if (!existsSync4(SHARDS_DIR)) {
1900
- mkdirSync(SHARDS_DIR, { recursive: true });
1970
+ if (!existsSync5(SHARDS_DIR)) {
1971
+ mkdirSync2(SHARDS_DIR, { recursive: true });
1901
1972
  }
1902
1973
  _shardingEnabled = true;
1974
+ if (_evictionTimer) clearInterval(_evictionTimer);
1975
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
1976
+ _evictionTimer.unref();
1903
1977
  }
1904
1978
  function isShardingEnabled() {
1905
1979
  return _shardingEnabled;
@@ -1916,21 +1990,28 @@ function getShardClient(projectName) {
1916
1990
  throw new Error(`Invalid project name for shard: "${projectName}"`);
1917
1991
  }
1918
1992
  const cached = _shards.get(safeName);
1919
- if (cached) return cached;
1993
+ if (cached) {
1994
+ _shardLastAccess.set(safeName, Date.now());
1995
+ return cached;
1996
+ }
1997
+ while (_shards.size >= MAX_OPEN_SHARDS) {
1998
+ evictLRU();
1999
+ }
1920
2000
  const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
1921
2001
  const client = createClient2({
1922
2002
  url: `file:${dbPath}`,
1923
2003
  encryptionKey: _encryptionKey
1924
2004
  });
1925
2005
  _shards.set(safeName, client);
2006
+ _shardLastAccess.set(safeName, Date.now());
1926
2007
  return client;
1927
2008
  }
1928
2009
  function shardExists(projectName) {
1929
2010
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1930
- return existsSync4(path5.join(SHARDS_DIR, `${safeName}.db`));
2011
+ return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
1931
2012
  }
1932
2013
  function listShards() {
1933
- if (!existsSync4(SHARDS_DIR)) return [];
2014
+ if (!existsSync5(SHARDS_DIR)) return [];
1934
2015
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1935
2016
  }
1936
2017
  async function ensureShardSchema(client) {
@@ -1982,6 +2063,8 @@ async function ensureShardSchema(client) {
1982
2063
  for (const col of [
1983
2064
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
1984
2065
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2066
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2067
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
1985
2068
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
1986
2069
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
1987
2070
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -2119,21 +2202,69 @@ async function getReadyShardClient(projectName) {
2119
2202
  await ensureShardSchema(client);
2120
2203
  return client;
2121
2204
  }
2205
+ function evictLRU() {
2206
+ let oldest = null;
2207
+ let oldestTime = Infinity;
2208
+ for (const [name, time] of _shardLastAccess) {
2209
+ if (time < oldestTime) {
2210
+ oldestTime = time;
2211
+ oldest = name;
2212
+ }
2213
+ }
2214
+ if (oldest) {
2215
+ const client = _shards.get(oldest);
2216
+ if (client) {
2217
+ client.close();
2218
+ }
2219
+ _shards.delete(oldest);
2220
+ _shardLastAccess.delete(oldest);
2221
+ }
2222
+ }
2223
+ function evictIdleShards() {
2224
+ const now = Date.now();
2225
+ const toEvict = [];
2226
+ for (const [name, lastAccess] of _shardLastAccess) {
2227
+ if (now - lastAccess > SHARD_IDLE_MS) {
2228
+ toEvict.push(name);
2229
+ }
2230
+ }
2231
+ for (const name of toEvict) {
2232
+ const client = _shards.get(name);
2233
+ if (client) {
2234
+ client.close();
2235
+ }
2236
+ _shards.delete(name);
2237
+ _shardLastAccess.delete(name);
2238
+ }
2239
+ }
2240
+ function getOpenShardCount() {
2241
+ return _shards.size;
2242
+ }
2122
2243
  function disposeShards() {
2244
+ if (_evictionTimer) {
2245
+ clearInterval(_evictionTimer);
2246
+ _evictionTimer = null;
2247
+ }
2123
2248
  for (const [, client] of _shards) {
2124
2249
  client.close();
2125
2250
  }
2126
2251
  _shards.clear();
2252
+ _shardLastAccess.clear();
2127
2253
  _shardingEnabled = false;
2128
2254
  _encryptionKey = null;
2129
2255
  }
2130
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2256
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
2131
2257
  var init_shard_manager = __esm({
2132
2258
  "src/lib/shard-manager.ts"() {
2133
2259
  "use strict";
2134
2260
  init_config();
2135
2261
  SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
2262
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2263
+ MAX_OPEN_SHARDS = 10;
2264
+ EVICTION_INTERVAL_MS = 60 * 1e3;
2136
2265
  _shards = /* @__PURE__ */ new Map();
2266
+ _shardLastAccess = /* @__PURE__ */ new Map();
2267
+ _evictionTimer = null;
2137
2268
  _encryptionKey = null;
2138
2269
  _shardingEnabled = false;
2139
2270
  }
@@ -2326,13 +2457,50 @@ ${p.content}`).join("\n\n");
2326
2457
  }
2327
2458
  });
2328
2459
 
2460
+ // src/lib/daemon-auth.ts
2461
+ import crypto from "crypto";
2462
+ import path6 from "path";
2463
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2464
+ function normalizeToken(token) {
2465
+ if (!token) return null;
2466
+ const trimmed = token.trim();
2467
+ return trimmed.length > 0 ? trimmed : null;
2468
+ }
2469
+ function readDaemonToken() {
2470
+ try {
2471
+ if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
2472
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
2473
+ } catch {
2474
+ return null;
2475
+ }
2476
+ }
2477
+ function ensureDaemonToken(seed) {
2478
+ const existing = readDaemonToken();
2479
+ if (existing) return existing;
2480
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
2481
+ ensurePrivateDirSync(EXE_AI_DIR);
2482
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
2483
+ `, "utf8");
2484
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
2485
+ return token;
2486
+ }
2487
+ var DAEMON_TOKEN_PATH;
2488
+ var init_daemon_auth = __esm({
2489
+ "src/lib/daemon-auth.ts"() {
2490
+ "use strict";
2491
+ init_config();
2492
+ init_secure_files();
2493
+ DAEMON_TOKEN_PATH = path6.join(EXE_AI_DIR, "exed.token");
2494
+ }
2495
+ });
2496
+
2329
2497
  // src/lib/exe-daemon-client.ts
2330
2498
  import net from "net";
2331
2499
  import os5 from "os";
2332
2500
  import { spawn } from "child_process";
2333
2501
  import { randomUUID as randomUUID2 } from "crypto";
2334
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
2335
- import path6 from "path";
2502
+ import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2503
+ import path7 from "path";
2336
2504
  import { fileURLToPath } from "url";
2337
2505
  function handleData(chunk) {
2338
2506
  _buffer += chunk.toString();
@@ -2360,9 +2528,9 @@ function handleData(chunk) {
2360
2528
  }
2361
2529
  }
2362
2530
  function cleanupStaleFiles() {
2363
- if (existsSync5(PID_PATH)) {
2531
+ if (existsSync7(PID_PATH)) {
2364
2532
  try {
2365
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2533
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
2366
2534
  if (pid > 0) {
2367
2535
  try {
2368
2536
  process.kill(pid, 0);
@@ -2383,11 +2551,11 @@ function cleanupStaleFiles() {
2383
2551
  }
2384
2552
  }
2385
2553
  function findPackageRoot() {
2386
- let dir = path6.dirname(fileURLToPath(import.meta.url));
2387
- const { root } = path6.parse(dir);
2554
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
2555
+ const { root } = path7.parse(dir);
2388
2556
  while (dir !== root) {
2389
- if (existsSync5(path6.join(dir, "package.json"))) return dir;
2390
- dir = path6.dirname(dir);
2557
+ if (existsSync7(path7.join(dir, "package.json"))) return dir;
2558
+ dir = path7.dirname(dir);
2391
2559
  }
2392
2560
  return null;
2393
2561
  }
@@ -2413,16 +2581,17 @@ function spawnDaemon() {
2413
2581
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
2414
2582
  return;
2415
2583
  }
2416
- const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2417
- if (!existsSync5(daemonPath)) {
2584
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2585
+ if (!existsSync7(daemonPath)) {
2418
2586
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2419
2587
  `);
2420
2588
  return;
2421
2589
  }
2422
2590
  const resolvedPath = daemonPath;
2591
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
2423
2592
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2424
2593
  `);
2425
- const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
2594
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
2426
2595
  let stderrFd = "ignore";
2427
2596
  try {
2428
2597
  stderrFd = openSync(logPath, "a");
@@ -2440,7 +2609,8 @@ function spawnDaemon() {
2440
2609
  TMUX_PANE: void 0,
2441
2610
  // Prevents resolveExeSession() from scoping to one session
2442
2611
  EXE_DAEMON_SOCK: SOCKET_PATH,
2443
- EXE_DAEMON_PID: PID_PATH
2612
+ EXE_DAEMON_PID: PID_PATH,
2613
+ [DAEMON_TOKEN_ENV]: daemonToken
2444
2614
  }
2445
2615
  });
2446
2616
  child.unref();
@@ -2550,13 +2720,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
2550
2720
  return;
2551
2721
  }
2552
2722
  const id = randomUUID2();
2723
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
2553
2724
  const timer = setTimeout(() => {
2554
2725
  _pending.delete(id);
2555
2726
  resolve({ error: "Request timeout" });
2556
2727
  }, timeoutMs);
2557
2728
  _pending.set(id, { resolve, timer });
2558
2729
  try {
2559
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
2730
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
2560
2731
  } catch {
2561
2732
  clearTimeout(timer);
2562
2733
  _pending.delete(id);
@@ -2585,9 +2756,9 @@ function killAndRespawnDaemon() {
2585
2756
  }
2586
2757
  try {
2587
2758
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
2588
- if (existsSync5(PID_PATH)) {
2759
+ if (existsSync7(PID_PATH)) {
2589
2760
  try {
2590
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2761
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
2591
2762
  if (pid > 0) {
2592
2763
  try {
2593
2764
  process.kill(pid, "SIGKILL");
@@ -2691,17 +2862,19 @@ async function embedViaClient(text, priority = "high") {
2691
2862
  );
2692
2863
  return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
2693
2864
  }
2694
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2865
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
2695
2866
  var init_exe_daemon_client = __esm({
2696
2867
  "src/lib/exe-daemon-client.ts"() {
2697
2868
  "use strict";
2698
2869
  init_config();
2699
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
2700
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
2701
- SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
2870
+ init_daemon_auth();
2871
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
2872
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
2873
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
2702
2874
  SPAWN_LOCK_STALE_MS = 3e4;
2703
2875
  CONNECT_TIMEOUT_MS = 15e3;
2704
2876
  REQUEST_TIMEOUT_MS = 3e4;
2877
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
2705
2878
  _socket = null;
2706
2879
  _connected = false;
2707
2880
  _buffer = "";
@@ -2717,10 +2890,10 @@ var init_exe_daemon_client = __esm({
2717
2890
  });
2718
2891
 
2719
2892
  // src/bin/backfill-conversations.ts
2720
- import crypto from "crypto";
2893
+ import crypto2 from "crypto";
2721
2894
  import { createReadStream } from "fs";
2722
2895
  import { readdir, stat } from "fs/promises";
2723
- import path7 from "path";
2896
+ import path8 from "path";
2724
2897
  import { createInterface } from "readline";
2725
2898
  import { homedir } from "os";
2726
2899
  import { parseArgs } from "util";
@@ -2736,7 +2909,7 @@ init_database();
2736
2909
 
2737
2910
  // src/lib/keychain.ts
2738
2911
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2739
- import { existsSync as existsSync3 } from "fs";
2912
+ import { existsSync as existsSync4 } from "fs";
2740
2913
  import path4 from "path";
2741
2914
  import os4 from "os";
2742
2915
  var SERVICE = "exe-mem";
@@ -2766,7 +2939,7 @@ async function getMasterKey() {
2766
2939
  }
2767
2940
  }
2768
2941
  const keyPath = getKeyPath();
2769
- if (!existsSync3(keyPath)) {
2942
+ if (!existsSync4(keyPath)) {
2770
2943
  process.stderr.write(
2771
2944
  `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2772
2945
  `
@@ -3215,7 +3388,7 @@ var MIN_MESSAGES = 3;
3215
3388
  var MAX_SUMMARY_LENGTH = 4e3;
3216
3389
  var MAX_WALK_DEPTH = 10;
3217
3390
  async function findJsonlFiles(sinceDate, projectFilter) {
3218
- const projectsDir = path7.join(homedir(), ".claude", "projects");
3391
+ const projectsDir = path8.join(homedir(), ".claude", "projects");
3219
3392
  const files = [];
3220
3393
  async function walk(dir, depth = 0) {
3221
3394
  if (depth > MAX_WALK_DEPTH) return;
@@ -3226,7 +3399,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
3226
3399
  return;
3227
3400
  }
3228
3401
  for (const entry of entries) {
3229
- const full = path7.join(dir, entry.name);
3402
+ const full = path8.join(dir, entry.name);
3230
3403
  if (entry.isDirectory()) {
3231
3404
  if (entry.name === "subagents" || entry.name === "tool-results") continue;
3232
3405
  await walk(full, depth + 1);
@@ -3251,7 +3424,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
3251
3424
  if (!entry.isDirectory()) continue;
3252
3425
  const decoded = decodeProjectDir(entry.name);
3253
3426
  if (decoded.toLowerCase().includes(projectFilter.toLowerCase())) {
3254
- await walk(path7.join(projectsDir, entry.name));
3427
+ await walk(path8.join(projectsDir, entry.name));
3255
3428
  }
3256
3429
  }
3257
3430
  } else {
@@ -3268,14 +3441,14 @@ function decodeProjectDir(dirName) {
3268
3441
  return dirName;
3269
3442
  }
3270
3443
  function projectNameFromPath(filePath) {
3271
- const projectsDir = path7.join(homedir(), ".claude", "projects");
3272
- const relative = path7.relative(projectsDir, filePath);
3273
- const projectDir = relative.split(path7.sep)[0] ?? "unknown";
3444
+ const projectsDir = path8.join(homedir(), ".claude", "projects");
3445
+ const relative = path8.relative(projectsDir, filePath);
3446
+ const projectDir = relative.split(path8.sep)[0] ?? "unknown";
3274
3447
  return decodeProjectDir(projectDir);
3275
3448
  }
3276
3449
  async function parseConversation(filePath) {
3277
3450
  const conv = {
3278
- sessionId: path7.basename(filePath, ".jsonl"),
3451
+ sessionId: path8.basename(filePath, ".jsonl"),
3279
3452
  projectName: projectNameFromPath(filePath),
3280
3453
  cwd: void 0,
3281
3454
  startTime: void 0,
@@ -3339,7 +3512,7 @@ async function parseConversation(filePath) {
3339
3512
  }
3340
3513
  }
3341
3514
  if (conv.cwd) {
3342
- conv.projectName = path7.basename(conv.cwd);
3515
+ conv.projectName = path8.basename(conv.cwd);
3343
3516
  const worktreeMatch = conv.cwd.match(/\.worktrees\/([^/]+)/);
3344
3517
  if (worktreeMatch?.[1]) {
3345
3518
  conv.agentId = worktreeMatch[1];
@@ -3512,7 +3685,7 @@ async function backfillConversations(options) {
3512
3685
  }
3513
3686
  }
3514
3687
  await writeMemory({
3515
- id: crypto.randomUUID(),
3688
+ id: crypto2.randomUUID(),
3516
3689
  agent_id: conv.agentId,
3517
3690
  agent_role: isCoordinatorName(conv.agentId) ? "COO" : "specialist",
3518
3691
  session_id: conv.sessionId,