@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
package/dist/lib/db.js CHANGED
@@ -8,9 +8,34 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/secure-files.ts
12
+ import { chmodSync, existsSync, mkdirSync } from "fs";
13
+ import { chmod, mkdir } from "fs/promises";
14
+ function ensurePrivateDirSync(dirPath) {
15
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
16
+ try {
17
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
18
+ } catch {
19
+ }
20
+ }
21
+ function enforcePrivateFileSync(filePath) {
22
+ try {
23
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
24
+ } catch {
25
+ }
26
+ }
27
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
28
+ var init_secure_files = __esm({
29
+ "src/lib/secure-files.ts"() {
30
+ "use strict";
31
+ PRIVATE_DIR_MODE = 448;
32
+ PRIVATE_FILE_MODE = 384;
33
+ }
34
+ });
35
+
11
36
  // src/lib/config.ts
12
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
13
- import { readFileSync, existsSync, renameSync } from "fs";
37
+ import { readFile, writeFile } from "fs/promises";
38
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
14
39
  import path from "path";
15
40
  import os from "os";
16
41
  function resolveDataDir() {
@@ -18,7 +43,7 @@ function resolveDataDir() {
18
43
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
19
44
  const newDir = path.join(os.homedir(), ".exe-os");
20
45
  const legacyDir = path.join(os.homedir(), ".exe-mem");
21
- if (!existsSync(newDir) && existsSync(legacyDir)) {
46
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
22
47
  try {
23
48
  renameSync(legacyDir, newDir);
24
49
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -33,6 +58,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
33
58
  var init_config = __esm({
34
59
  "src/lib/config.ts"() {
35
60
  "use strict";
61
+ init_secure_files();
36
62
  EXE_AI_DIR = resolveDataDir();
37
63
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
38
64
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -99,13 +125,50 @@ var init_config = __esm({
99
125
  }
100
126
  });
101
127
 
128
+ // src/lib/daemon-auth.ts
129
+ import crypto from "crypto";
130
+ import path4 from "path";
131
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
132
+ function normalizeToken(token) {
133
+ if (!token) return null;
134
+ const trimmed = token.trim();
135
+ return trimmed.length > 0 ? trimmed : null;
136
+ }
137
+ function readDaemonToken() {
138
+ try {
139
+ if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
140
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+ function ensureDaemonToken(seed) {
146
+ const existing = readDaemonToken();
147
+ if (existing) return existing;
148
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
149
+ ensurePrivateDirSync(EXE_AI_DIR);
150
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
151
+ `, "utf8");
152
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
153
+ return token;
154
+ }
155
+ var DAEMON_TOKEN_PATH;
156
+ var init_daemon_auth = __esm({
157
+ "src/lib/daemon-auth.ts"() {
158
+ "use strict";
159
+ init_config();
160
+ init_secure_files();
161
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
162
+ }
163
+ });
164
+
102
165
  // src/lib/exe-daemon-client.ts
103
166
  import net from "net";
104
167
  import os4 from "os";
105
168
  import { spawn } from "child_process";
106
169
  import { randomUUID } from "crypto";
107
- import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
108
- import path4 from "path";
170
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
171
+ import path5 from "path";
109
172
  import { fileURLToPath } from "url";
110
173
  function handleData(chunk) {
111
174
  _buffer += chunk.toString();
@@ -133,9 +196,9 @@ function handleData(chunk) {
133
196
  }
134
197
  }
135
198
  function cleanupStaleFiles() {
136
- if (existsSync3(PID_PATH)) {
199
+ if (existsSync5(PID_PATH)) {
137
200
  try {
138
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
201
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
139
202
  if (pid > 0) {
140
203
  try {
141
204
  process.kill(pid, 0);
@@ -156,11 +219,11 @@ function cleanupStaleFiles() {
156
219
  }
157
220
  }
158
221
  function findPackageRoot() {
159
- let dir = path4.dirname(fileURLToPath(import.meta.url));
160
- const { root } = path4.parse(dir);
222
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
223
+ const { root } = path5.parse(dir);
161
224
  while (dir !== root) {
162
- if (existsSync3(path4.join(dir, "package.json"))) return dir;
163
- dir = path4.dirname(dir);
225
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
226
+ dir = path5.dirname(dir);
164
227
  }
165
228
  return null;
166
229
  }
@@ -186,16 +249,17 @@ function spawnDaemon() {
186
249
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
187
250
  return;
188
251
  }
189
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
190
- if (!existsSync3(daemonPath)) {
252
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
253
+ if (!existsSync5(daemonPath)) {
191
254
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
192
255
  `);
193
256
  return;
194
257
  }
195
258
  const resolvedPath = daemonPath;
259
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
196
260
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
197
261
  `);
198
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
262
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
199
263
  let stderrFd = "ignore";
200
264
  try {
201
265
  stderrFd = openSync(logPath, "a");
@@ -213,7 +277,8 @@ function spawnDaemon() {
213
277
  TMUX_PANE: void 0,
214
278
  // Prevents resolveExeSession() from scoping to one session
215
279
  EXE_DAEMON_SOCK: SOCKET_PATH,
216
- EXE_DAEMON_PID: PID_PATH
280
+ EXE_DAEMON_PID: PID_PATH,
281
+ [DAEMON_TOKEN_ENV]: daemonToken
217
282
  }
218
283
  });
219
284
  child.unref();
@@ -320,13 +385,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
320
385
  return;
321
386
  }
322
387
  const id = randomUUID();
388
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
323
389
  const timer = setTimeout(() => {
324
390
  _pending.delete(id);
325
391
  resolve({ error: "Request timeout" });
326
392
  }, timeoutMs);
327
393
  _pending.set(id, { resolve, timer });
328
394
  try {
329
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
395
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
330
396
  } catch {
331
397
  clearTimeout(timer);
332
398
  _pending.delete(id);
@@ -337,17 +403,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
337
403
  function isClientConnected() {
338
404
  return _connected;
339
405
  }
340
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
406
+ 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;
341
407
  var init_exe_daemon_client = __esm({
342
408
  "src/lib/exe-daemon-client.ts"() {
343
409
  "use strict";
344
410
  init_config();
345
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
346
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
347
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
411
+ init_daemon_auth();
412
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
413
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
414
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
348
415
  SPAWN_LOCK_STALE_MS = 3e4;
349
416
  CONNECT_TIMEOUT_MS = 15e3;
350
417
  REQUEST_TIMEOUT_MS = 3e4;
418
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
351
419
  _socket = null;
352
420
  _connected = false;
353
421
  _buffer = "";
@@ -610,7 +678,7 @@ function wrapWithRetry(client) {
610
678
  // src/lib/employees.ts
611
679
  init_config();
612
680
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
613
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
681
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
614
682
  import { execSync } from "child_process";
615
683
  import path2 from "path";
616
684
  import os2 from "os";
@@ -630,7 +698,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
630
698
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
631
699
  }
632
700
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
633
- if (!existsSync2(employeesPath)) return [];
701
+ if (!existsSync3(employeesPath)) return [];
634
702
  try {
635
703
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
636
704
  } catch {
@@ -1581,6 +1649,7 @@ async function ensureSchema() {
1581
1649
  project TEXT NOT NULL,
1582
1650
  summary TEXT NOT NULL,
1583
1651
  task_file TEXT,
1652
+ session_scope TEXT,
1584
1653
  read INTEGER NOT NULL DEFAULT 0,
1585
1654
  created_at TEXT NOT NULL
1586
1655
  );
@@ -1589,7 +1658,7 @@ async function ensureSchema() {
1589
1658
  ON notifications(read);
1590
1659
 
1591
1660
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1592
- ON notifications(agent_id);
1661
+ ON notifications(agent_id, session_scope);
1593
1662
 
1594
1663
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1595
1664
  ON notifications(task_file);
@@ -1627,6 +1696,7 @@ async function ensureSchema() {
1627
1696
  target_agent TEXT NOT NULL,
1628
1697
  target_project TEXT,
1629
1698
  target_device TEXT NOT NULL DEFAULT 'local',
1699
+ session_scope TEXT,
1630
1700
  content TEXT NOT NULL,
1631
1701
  priority TEXT DEFAULT 'normal',
1632
1702
  status TEXT DEFAULT 'pending',
@@ -1640,10 +1710,31 @@ async function ensureSchema() {
1640
1710
  );
1641
1711
 
1642
1712
  CREATE INDEX IF NOT EXISTS idx_messages_target
1643
- ON messages(target_agent, status);
1713
+ ON messages(target_agent, session_scope, status);
1644
1714
 
1645
1715
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1646
- ON messages(target_agent, from_agent, server_seq);
1716
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1717
+ `);
1718
+ try {
1719
+ await client.execute({
1720
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1721
+ args: []
1722
+ });
1723
+ } catch {
1724
+ }
1725
+ try {
1726
+ await client.execute({
1727
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1728
+ args: []
1729
+ });
1730
+ } catch {
1731
+ }
1732
+ await client.executeMultiple(`
1733
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1734
+ ON notifications(agent_id, session_scope, read, created_at);
1735
+
1736
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1737
+ ON messages(target_agent, session_scope, status, created_at);
1647
1738
  `);
1648
1739
  try {
1649
1740
  await client.execute({
@@ -2227,6 +2318,13 @@ async function ensureSchema() {
2227
2318
  } catch {
2228
2319
  }
2229
2320
  }
2321
+ try {
2322
+ await client.execute({
2323
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2324
+ args: []
2325
+ });
2326
+ } catch {
2327
+ }
2230
2328
  }
2231
2329
  var disposeTurso = disposeDatabase;
2232
2330
  async function disposeDatabase() {
@@ -8,9 +8,34 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/lib/secure-files.ts
12
+ import { chmodSync, existsSync, mkdirSync } from "fs";
13
+ import { chmod, mkdir } from "fs/promises";
14
+ function ensurePrivateDirSync(dirPath) {
15
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
16
+ try {
17
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
18
+ } catch {
19
+ }
20
+ }
21
+ function enforcePrivateFileSync(filePath) {
22
+ try {
23
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
24
+ } catch {
25
+ }
26
+ }
27
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
28
+ var init_secure_files = __esm({
29
+ "src/lib/secure-files.ts"() {
30
+ "use strict";
31
+ PRIVATE_DIR_MODE = 448;
32
+ PRIVATE_FILE_MODE = 384;
33
+ }
34
+ });
35
+
11
36
  // src/lib/config.ts
12
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
13
- import { readFileSync, existsSync, renameSync } from "fs";
37
+ import { readFile, writeFile } from "fs/promises";
38
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
14
39
  import path from "path";
15
40
  import os from "os";
16
41
  function resolveDataDir() {
@@ -18,7 +43,7 @@ function resolveDataDir() {
18
43
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
19
44
  const newDir = path.join(os.homedir(), ".exe-os");
20
45
  const legacyDir = path.join(os.homedir(), ".exe-mem");
21
- if (!existsSync(newDir) && existsSync(legacyDir)) {
46
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
22
47
  try {
23
48
  renameSync(legacyDir, newDir);
24
49
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -33,6 +58,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
33
58
  var init_config = __esm({
34
59
  "src/lib/config.ts"() {
35
60
  "use strict";
61
+ init_secure_files();
36
62
  EXE_AI_DIR = resolveDataDir();
37
63
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
38
64
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -156,7 +182,7 @@ var init_db_retry = __esm({
156
182
 
157
183
  // src/lib/employees.ts
158
184
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
159
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
185
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
160
186
  import { execSync } from "child_process";
161
187
  import path2 from "path";
162
188
  import os2 from "os";
@@ -173,7 +199,7 @@ function getCoordinatorName(employees = loadEmployeesSync()) {
173
199
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
174
200
  }
175
201
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
176
- if (!existsSync2(employeesPath)) return [];
202
+ if (!existsSync3(employeesPath)) return [];
177
203
  try {
178
204
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
179
205
  } catch {
@@ -776,13 +802,50 @@ var init_database_adapter = __esm({
776
802
  }
777
803
  });
778
804
 
805
+ // src/lib/daemon-auth.ts
806
+ import crypto from "crypto";
807
+ import path4 from "path";
808
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
809
+ function normalizeToken(token) {
810
+ if (!token) return null;
811
+ const trimmed = token.trim();
812
+ return trimmed.length > 0 ? trimmed : null;
813
+ }
814
+ function readDaemonToken() {
815
+ try {
816
+ if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
817
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
818
+ } catch {
819
+ return null;
820
+ }
821
+ }
822
+ function ensureDaemonToken(seed) {
823
+ const existing = readDaemonToken();
824
+ if (existing) return existing;
825
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
826
+ ensurePrivateDirSync(EXE_AI_DIR);
827
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
828
+ `, "utf8");
829
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
830
+ return token;
831
+ }
832
+ var DAEMON_TOKEN_PATH;
833
+ var init_daemon_auth = __esm({
834
+ "src/lib/daemon-auth.ts"() {
835
+ "use strict";
836
+ init_config();
837
+ init_secure_files();
838
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
839
+ }
840
+ });
841
+
779
842
  // src/lib/exe-daemon-client.ts
780
843
  import net from "net";
781
844
  import os4 from "os";
782
845
  import { spawn } from "child_process";
783
846
  import { randomUUID } from "crypto";
784
- import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
785
- import path4 from "path";
847
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
848
+ import path5 from "path";
786
849
  import { fileURLToPath } from "url";
787
850
  function handleData(chunk) {
788
851
  _buffer += chunk.toString();
@@ -810,9 +873,9 @@ function handleData(chunk) {
810
873
  }
811
874
  }
812
875
  function cleanupStaleFiles() {
813
- if (existsSync3(PID_PATH)) {
876
+ if (existsSync5(PID_PATH)) {
814
877
  try {
815
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
878
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
816
879
  if (pid > 0) {
817
880
  try {
818
881
  process.kill(pid, 0);
@@ -833,11 +896,11 @@ function cleanupStaleFiles() {
833
896
  }
834
897
  }
835
898
  function findPackageRoot() {
836
- let dir = path4.dirname(fileURLToPath(import.meta.url));
837
- const { root } = path4.parse(dir);
899
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
900
+ const { root } = path5.parse(dir);
838
901
  while (dir !== root) {
839
- if (existsSync3(path4.join(dir, "package.json"))) return dir;
840
- dir = path4.dirname(dir);
902
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
903
+ dir = path5.dirname(dir);
841
904
  }
842
905
  return null;
843
906
  }
@@ -863,16 +926,17 @@ function spawnDaemon() {
863
926
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
864
927
  return;
865
928
  }
866
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
867
- if (!existsSync3(daemonPath)) {
929
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
930
+ if (!existsSync5(daemonPath)) {
868
931
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
869
932
  `);
870
933
  return;
871
934
  }
872
935
  const resolvedPath = daemonPath;
936
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
873
937
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
874
938
  `);
875
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
939
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
876
940
  let stderrFd = "ignore";
877
941
  try {
878
942
  stderrFd = openSync(logPath, "a");
@@ -890,7 +954,8 @@ function spawnDaemon() {
890
954
  TMUX_PANE: void 0,
891
955
  // Prevents resolveExeSession() from scoping to one session
892
956
  EXE_DAEMON_SOCK: SOCKET_PATH,
893
- EXE_DAEMON_PID: PID_PATH
957
+ EXE_DAEMON_PID: PID_PATH,
958
+ [DAEMON_TOKEN_ENV]: daemonToken
894
959
  }
895
960
  });
896
961
  child.unref();
@@ -997,13 +1062,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
997
1062
  return;
998
1063
  }
999
1064
  const id = randomUUID();
1065
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1000
1066
  const timer = setTimeout(() => {
1001
1067
  _pending.delete(id);
1002
1068
  resolve({ error: "Request timeout" });
1003
1069
  }, timeoutMs);
1004
1070
  _pending.set(id, { resolve, timer });
1005
1071
  try {
1006
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1072
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1007
1073
  } catch {
1008
1074
  clearTimeout(timer);
1009
1075
  _pending.delete(id);
@@ -1014,17 +1080,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1014
1080
  function isClientConnected() {
1015
1081
  return _connected;
1016
1082
  }
1017
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1083
+ 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;
1018
1084
  var init_exe_daemon_client = __esm({
1019
1085
  "src/lib/exe-daemon-client.ts"() {
1020
1086
  "use strict";
1021
1087
  init_config();
1022
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
1023
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
1024
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
1088
+ init_daemon_auth();
1089
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1090
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1091
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1025
1092
  SPAWN_LOCK_STALE_MS = 3e4;
1026
1093
  CONNECT_TIMEOUT_MS = 15e3;
1027
1094
  REQUEST_TIMEOUT_MS = 3e4;
1095
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1028
1096
  _socket = null;
1029
1097
  _connected = false;
1030
1098
  _buffer = "";
@@ -1603,6 +1671,7 @@ async function ensureSchema() {
1603
1671
  project TEXT NOT NULL,
1604
1672
  summary TEXT NOT NULL,
1605
1673
  task_file TEXT,
1674
+ session_scope TEXT,
1606
1675
  read INTEGER NOT NULL DEFAULT 0,
1607
1676
  created_at TEXT NOT NULL
1608
1677
  );
@@ -1611,7 +1680,7 @@ async function ensureSchema() {
1611
1680
  ON notifications(read);
1612
1681
 
1613
1682
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1614
- ON notifications(agent_id);
1683
+ ON notifications(agent_id, session_scope);
1615
1684
 
1616
1685
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1617
1686
  ON notifications(task_file);
@@ -1649,6 +1718,7 @@ async function ensureSchema() {
1649
1718
  target_agent TEXT NOT NULL,
1650
1719
  target_project TEXT,
1651
1720
  target_device TEXT NOT NULL DEFAULT 'local',
1721
+ session_scope TEXT,
1652
1722
  content TEXT NOT NULL,
1653
1723
  priority TEXT DEFAULT 'normal',
1654
1724
  status TEXT DEFAULT 'pending',
@@ -1662,10 +1732,31 @@ async function ensureSchema() {
1662
1732
  );
1663
1733
 
1664
1734
  CREATE INDEX IF NOT EXISTS idx_messages_target
1665
- ON messages(target_agent, status);
1735
+ ON messages(target_agent, session_scope, status);
1666
1736
 
1667
1737
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1668
- ON messages(target_agent, from_agent, server_seq);
1738
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1739
+ `);
1740
+ try {
1741
+ await client.execute({
1742
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1743
+ args: []
1744
+ });
1745
+ } catch {
1746
+ }
1747
+ try {
1748
+ await client.execute({
1749
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1750
+ args: []
1751
+ });
1752
+ } catch {
1753
+ }
1754
+ await client.executeMultiple(`
1755
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1756
+ ON notifications(agent_id, session_scope, read, created_at);
1757
+
1758
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1759
+ ON messages(target_agent, session_scope, status, created_at);
1669
1760
  `);
1670
1761
  try {
1671
1762
  await client.execute({
@@ -2249,6 +2340,13 @@ async function ensureSchema() {
2249
2340
  } catch {
2250
2341
  }
2251
2342
  }
2343
+ try {
2344
+ await client.execute({
2345
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2346
+ args: []
2347
+ });
2348
+ } catch {
2349
+ }
2252
2350
  }
2253
2351
  async function disposeDatabase() {
2254
2352
  if (_walCheckpointTimer) {
@@ -2288,15 +2386,15 @@ var init_database = __esm({
2288
2386
 
2289
2387
  // src/lib/device-registry.ts
2290
2388
  init_config();
2291
- import crypto from "crypto";
2389
+ import crypto2 from "crypto";
2292
2390
  import os5 from "os";
2293
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync4 } from "fs";
2294
- import path5 from "path";
2295
- var DEVICE_JSON_PATH = path5.join(EXE_AI_DIR, "device.json");
2391
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
2392
+ import path6 from "path";
2393
+ var DEVICE_JSON_PATH = path6.join(EXE_AI_DIR, "device.json");
2296
2394
  function getDeviceInfo() {
2297
- if (existsSync4(DEVICE_JSON_PATH)) {
2395
+ if (existsSync6(DEVICE_JSON_PATH)) {
2298
2396
  try {
2299
- const raw = readFileSync4(DEVICE_JSON_PATH, "utf8");
2397
+ const raw = readFileSync5(DEVICE_JSON_PATH, "utf8");
2300
2398
  const data = JSON.parse(raw);
2301
2399
  if (data.deviceId && data.friendlyName && data.hostname) {
2302
2400
  return data;
@@ -2306,18 +2404,18 @@ function getDeviceInfo() {
2306
2404
  }
2307
2405
  const hostname = os5.hostname();
2308
2406
  const info = {
2309
- deviceId: crypto.randomUUID(),
2407
+ deviceId: crypto2.randomUUID(),
2310
2408
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
2311
2409
  hostname
2312
2410
  };
2313
- mkdirSync(path5.dirname(DEVICE_JSON_PATH), { recursive: true });
2314
- writeFileSync2(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
2411
+ mkdirSync2(path6.dirname(DEVICE_JSON_PATH), { recursive: true });
2412
+ writeFileSync3(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
2315
2413
  return info;
2316
2414
  }
2317
2415
  function setFriendlyName(name) {
2318
2416
  const info = getDeviceInfo();
2319
2417
  info.friendlyName = name;
2320
- writeFileSync2(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
2418
+ writeFileSync3(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
2321
2419
  }
2322
2420
  async function resolveTargetDevice(targetAgent, targetProject) {
2323
2421
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));