@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -1,7 +1,18 @@
1
+ var __defProp = Object.defineProperty;
1
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
2
9
  var __esm = (fn, res) => function __init() {
3
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
11
  };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
5
16
 
6
17
  // src/lib/secure-files.ts
7
18
  import { chmodSync, existsSync, mkdirSync } from "fs";
@@ -13,12 +24,25 @@ async function ensurePrivateDir(dirPath) {
13
24
  } catch {
14
25
  }
15
26
  }
27
+ function ensurePrivateDirSync(dirPath) {
28
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
29
+ try {
30
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
31
+ } catch {
32
+ }
33
+ }
16
34
  async function enforcePrivateFile(filePath) {
17
35
  try {
18
36
  await chmod(filePath, PRIVATE_FILE_MODE);
19
37
  } catch {
20
38
  }
21
39
  }
40
+ function enforcePrivateFileSync(filePath) {
41
+ try {
42
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
43
+ } catch {
44
+ }
45
+ }
22
46
  var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
23
47
  var init_secure_files = __esm({
24
48
  "src/lib/secure-files.ts"() {
@@ -29,6 +53,21 @@ var init_secure_files = __esm({
29
53
  });
30
54
 
31
55
  // src/lib/config.ts
56
+ var config_exports = {};
57
+ __export(config_exports, {
58
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
59
+ CONFIG_PATH: () => CONFIG_PATH,
60
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
61
+ DB_PATH: () => DB_PATH,
62
+ EXE_AI_DIR: () => EXE_AI_DIR,
63
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
64
+ MODELS_DIR: () => MODELS_DIR,
65
+ loadConfig: () => loadConfig,
66
+ loadConfigFrom: () => loadConfigFrom,
67
+ loadConfigSync: () => loadConfigSync,
68
+ migrateConfig: () => migrateConfig,
69
+ saveConfig: () => saveConfig
70
+ });
32
71
  import { readFile, writeFile } from "fs/promises";
33
72
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
34
73
  import path from "path";
@@ -132,6 +171,46 @@ async function loadConfig() {
132
171
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
133
172
  }
134
173
  }
174
+ function loadConfigSync() {
175
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
176
+ const configPath = path.join(dir, "config.json");
177
+ if (!existsSync2(configPath)) {
178
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
179
+ }
180
+ try {
181
+ const raw = readFileSync(configPath, "utf-8");
182
+ let parsed = JSON.parse(raw);
183
+ parsed = migrateLegacyConfig(parsed);
184
+ const { config: migratedCfg } = migrateConfig(parsed);
185
+ normalizeScalingRoadmap(migratedCfg);
186
+ normalizeSessionLifecycle(migratedCfg);
187
+ normalizeAutoUpdate(migratedCfg);
188
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
189
+ } catch {
190
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
191
+ }
192
+ }
193
+ async function saveConfig(config) {
194
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
195
+ await ensurePrivateDir(dir);
196
+ const configPath = path.join(dir, "config.json");
197
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
198
+ await enforcePrivateFile(configPath);
199
+ }
200
+ async function loadConfigFrom(configPath) {
201
+ const raw = await readFile(configPath, "utf-8");
202
+ try {
203
+ let parsed = JSON.parse(raw);
204
+ parsed = migrateLegacyConfig(parsed);
205
+ const { config: migratedCfg } = migrateConfig(parsed);
206
+ normalizeScalingRoadmap(migratedCfg);
207
+ normalizeSessionLifecycle(migratedCfg);
208
+ normalizeAutoUpdate(migratedCfg);
209
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
210
+ } catch {
211
+ return { ...DEFAULT_CONFIG };
212
+ }
213
+ }
135
214
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
136
215
  var init_config = __esm({
137
216
  "src/lib/config.ts"() {
@@ -213,8 +292,551 @@ var init_config = __esm({
213
292
  }
214
293
  });
215
294
 
295
+ // src/types/memory.ts
296
+ var EMBEDDING_DIM;
297
+ var init_memory = __esm({
298
+ "src/types/memory.ts"() {
299
+ "use strict";
300
+ EMBEDDING_DIM = 1024;
301
+ }
302
+ });
303
+
304
+ // src/lib/daemon-auth.ts
305
+ import crypto from "crypto";
306
+ import path4 from "path";
307
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
308
+ function normalizeToken(token) {
309
+ if (!token) return null;
310
+ const trimmed = token.trim();
311
+ return trimmed.length > 0 ? trimmed : null;
312
+ }
313
+ function readDaemonToken() {
314
+ try {
315
+ if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
316
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
317
+ } catch {
318
+ return null;
319
+ }
320
+ }
321
+ function ensureDaemonToken(seed) {
322
+ const existing = readDaemonToken();
323
+ if (existing) return existing;
324
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
325
+ ensurePrivateDirSync(EXE_AI_DIR);
326
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
327
+ `, "utf8");
328
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
329
+ return token;
330
+ }
331
+ var DAEMON_TOKEN_PATH;
332
+ var init_daemon_auth = __esm({
333
+ "src/lib/daemon-auth.ts"() {
334
+ "use strict";
335
+ init_config();
336
+ init_secure_files();
337
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
338
+ }
339
+ });
340
+
341
+ // src/lib/exe-daemon-client.ts
342
+ import net from "net";
343
+ import os4 from "os";
344
+ import { spawn } from "child_process";
345
+ import { randomUUID } from "crypto";
346
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
347
+ import path5 from "path";
348
+ import { fileURLToPath } from "url";
349
+ function handleData(chunk) {
350
+ _buffer += chunk.toString();
351
+ if (_buffer.length > MAX_BUFFER) {
352
+ _buffer = "";
353
+ return;
354
+ }
355
+ let newlineIdx;
356
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
357
+ const line = _buffer.slice(0, newlineIdx).trim();
358
+ _buffer = _buffer.slice(newlineIdx + 1);
359
+ if (!line) continue;
360
+ try {
361
+ const response = JSON.parse(line);
362
+ const id = response.id;
363
+ if (!id) continue;
364
+ const entry = _pending.get(id);
365
+ if (entry) {
366
+ clearTimeout(entry.timer);
367
+ _pending.delete(id);
368
+ entry.resolve(response);
369
+ }
370
+ } catch {
371
+ }
372
+ }
373
+ }
374
+ function cleanupStaleFiles() {
375
+ if (existsSync5(PID_PATH)) {
376
+ try {
377
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
378
+ if (pid > 0) {
379
+ try {
380
+ process.kill(pid, 0);
381
+ return;
382
+ } catch {
383
+ }
384
+ }
385
+ } catch {
386
+ }
387
+ try {
388
+ unlinkSync2(PID_PATH);
389
+ } catch {
390
+ }
391
+ try {
392
+ unlinkSync2(SOCKET_PATH);
393
+ } catch {
394
+ }
395
+ }
396
+ }
397
+ function findPackageRoot() {
398
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
399
+ const { root } = path5.parse(dir);
400
+ while (dir !== root) {
401
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
402
+ dir = path5.dirname(dir);
403
+ }
404
+ return null;
405
+ }
406
+ function getAvailableMemoryGB() {
407
+ if (process.platform === "darwin") {
408
+ try {
409
+ const { execSync: execSync2 } = __require("child_process");
410
+ const vmstat = execSync2("vm_stat", { encoding: "utf8" });
411
+ const pageSize = 16384;
412
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
413
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
414
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
415
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
416
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
417
+ const freePages = free ? parseInt(free[1], 10) : 0;
418
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
419
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
420
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
421
+ } catch {
422
+ return os4.freemem() / (1024 * 1024 * 1024);
423
+ }
424
+ }
425
+ return os4.freemem() / (1024 * 1024 * 1024);
426
+ }
427
+ function spawnDaemon() {
428
+ const freeGB = getAvailableMemoryGB();
429
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
430
+ if (totalGB <= 8) {
431
+ process.stderr.write(
432
+ `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
433
+ `
434
+ );
435
+ return;
436
+ }
437
+ if (totalGB <= 16 && freeGB < 2) {
438
+ process.stderr.write(
439
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
440
+ `
441
+ );
442
+ return;
443
+ }
444
+ const pkgRoot = findPackageRoot();
445
+ if (!pkgRoot) {
446
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
447
+ return;
448
+ }
449
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
450
+ if (!existsSync5(daemonPath)) {
451
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
452
+ `);
453
+ return;
454
+ }
455
+ const resolvedPath = daemonPath;
456
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
457
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
458
+ `);
459
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
460
+ let stderrFd = "ignore";
461
+ try {
462
+ stderrFd = openSync(logPath, "a");
463
+ } catch {
464
+ }
465
+ const heapCapMB = totalGB <= 8 ? 256 : 512;
466
+ const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
467
+ const child = spawn(process.execPath, nodeArgs, {
468
+ detached: true,
469
+ stdio: ["ignore", "ignore", stderrFd],
470
+ env: {
471
+ ...process.env,
472
+ TMUX: void 0,
473
+ // Daemon is global — must not inherit session scope
474
+ TMUX_PANE: void 0,
475
+ // Prevents resolveExeSession() from scoping to one session
476
+ EXE_DAEMON_SOCK: SOCKET_PATH,
477
+ EXE_DAEMON_PID: PID_PATH,
478
+ [DAEMON_TOKEN_ENV]: daemonToken
479
+ }
480
+ });
481
+ child.unref();
482
+ if (typeof stderrFd === "number") {
483
+ try {
484
+ closeSync(stderrFd);
485
+ } catch {
486
+ }
487
+ }
488
+ }
489
+ function acquireSpawnLock() {
490
+ try {
491
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
492
+ closeSync(fd);
493
+ return true;
494
+ } catch {
495
+ try {
496
+ const stat = statSync(SPAWN_LOCK_PATH);
497
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
498
+ try {
499
+ unlinkSync2(SPAWN_LOCK_PATH);
500
+ } catch {
501
+ }
502
+ try {
503
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
504
+ closeSync(fd);
505
+ return true;
506
+ } catch {
507
+ }
508
+ }
509
+ } catch {
510
+ }
511
+ return false;
512
+ }
513
+ }
514
+ function releaseSpawnLock() {
515
+ try {
516
+ unlinkSync2(SPAWN_LOCK_PATH);
517
+ } catch {
518
+ }
519
+ }
520
+ function connectToSocket() {
521
+ return new Promise((resolve) => {
522
+ if (_socket && _connected) {
523
+ resolve(true);
524
+ return;
525
+ }
526
+ const socket = net.createConnection({ path: SOCKET_PATH });
527
+ const connectTimeout = setTimeout(() => {
528
+ socket.destroy();
529
+ resolve(false);
530
+ }, 2e3);
531
+ socket.on("connect", () => {
532
+ clearTimeout(connectTimeout);
533
+ _socket = socket;
534
+ _connected = true;
535
+ _buffer = "";
536
+ socket.on("data", handleData);
537
+ socket.on("close", () => {
538
+ _connected = false;
539
+ _socket = null;
540
+ for (const [id, entry] of _pending) {
541
+ clearTimeout(entry.timer);
542
+ _pending.delete(id);
543
+ entry.resolve({ error: "Connection closed" });
544
+ }
545
+ });
546
+ socket.on("error", () => {
547
+ _connected = false;
548
+ _socket = null;
549
+ });
550
+ resolve(true);
551
+ });
552
+ socket.on("error", () => {
553
+ clearTimeout(connectTimeout);
554
+ resolve(false);
555
+ });
556
+ });
557
+ }
558
+ async function connectEmbedDaemon() {
559
+ if (_socket && _connected) return true;
560
+ if (await connectToSocket()) return true;
561
+ if (acquireSpawnLock()) {
562
+ try {
563
+ cleanupStaleFiles();
564
+ spawnDaemon();
565
+ } finally {
566
+ releaseSpawnLock();
567
+ }
568
+ }
569
+ const start = Date.now();
570
+ let delay = 100;
571
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
572
+ await new Promise((r) => setTimeout(r, delay));
573
+ if (await connectToSocket()) return true;
574
+ delay = Math.min(delay * 2, 3e3);
575
+ }
576
+ return false;
577
+ }
578
+ function sendRequest(texts, priority) {
579
+ return sendDaemonRequest({ texts, priority });
580
+ }
581
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
582
+ return new Promise((resolve) => {
583
+ if (!_socket || !_connected) {
584
+ resolve({ error: "Not connected" });
585
+ return;
586
+ }
587
+ const id = randomUUID();
588
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
589
+ const timer = setTimeout(() => {
590
+ _pending.delete(id);
591
+ resolve({ error: "Request timeout" });
592
+ }, timeoutMs);
593
+ _pending.set(id, { resolve, timer });
594
+ try {
595
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
596
+ } catch {
597
+ clearTimeout(timer);
598
+ _pending.delete(id);
599
+ resolve({ error: "Write failed" });
600
+ }
601
+ });
602
+ }
603
+ async function pingDaemon() {
604
+ if (!_socket || !_connected) return null;
605
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
606
+ if (response.health) {
607
+ return response.health;
608
+ }
609
+ return null;
610
+ }
611
+ function killAndRespawnDaemon() {
612
+ if (!acquireSpawnLock()) {
613
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
614
+ if (_socket) {
615
+ _socket.destroy();
616
+ _socket = null;
617
+ }
618
+ _connected = false;
619
+ _buffer = "";
620
+ return;
621
+ }
622
+ try {
623
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
624
+ if (existsSync5(PID_PATH)) {
625
+ try {
626
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
627
+ if (pid > 0) {
628
+ try {
629
+ process.kill(pid, "SIGKILL");
630
+ } catch {
631
+ }
632
+ }
633
+ } catch {
634
+ }
635
+ }
636
+ if (_socket) {
637
+ _socket.destroy();
638
+ _socket = null;
639
+ }
640
+ _connected = false;
641
+ _buffer = "";
642
+ try {
643
+ unlinkSync2(PID_PATH);
644
+ } catch {
645
+ }
646
+ try {
647
+ unlinkSync2(SOCKET_PATH);
648
+ } catch {
649
+ }
650
+ spawnDaemon();
651
+ } finally {
652
+ releaseSpawnLock();
653
+ }
654
+ }
655
+ function isDaemonTooYoung() {
656
+ try {
657
+ const stat = statSync(PID_PATH);
658
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
663
+ async function retryThenRestart(doRequest, label) {
664
+ const result = await doRequest();
665
+ if (!result.error) {
666
+ _consecutiveFailures = 0;
667
+ return result;
668
+ }
669
+ _consecutiveFailures++;
670
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
671
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
672
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
673
+ `);
674
+ await new Promise((r) => setTimeout(r, delayMs));
675
+ if (!_connected) {
676
+ if (!await connectToSocket()) continue;
677
+ }
678
+ const retry = await doRequest();
679
+ if (!retry.error) {
680
+ _consecutiveFailures = 0;
681
+ return retry;
682
+ }
683
+ _consecutiveFailures++;
684
+ }
685
+ if (isDaemonTooYoung()) {
686
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
687
+ `);
688
+ return { error: result.error };
689
+ }
690
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
691
+ `);
692
+ killAndRespawnDaemon();
693
+ const start = Date.now();
694
+ let delay = 200;
695
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
696
+ await new Promise((r) => setTimeout(r, delay));
697
+ if (await connectToSocket()) break;
698
+ delay = Math.min(delay * 2, 3e3);
699
+ }
700
+ if (!_connected) return { error: "Daemon restart failed" };
701
+ const final = await doRequest();
702
+ if (!final.error) _consecutiveFailures = 0;
703
+ return final;
704
+ }
705
+ async function embedViaClient(text, priority = "high") {
706
+ if (!_connected && !await connectEmbedDaemon()) return null;
707
+ _requestCount++;
708
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
709
+ const health = await pingDaemon();
710
+ if (!health && !isDaemonTooYoung()) {
711
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
712
+ `);
713
+ killAndRespawnDaemon();
714
+ const start = Date.now();
715
+ let d = 200;
716
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
717
+ await new Promise((r) => setTimeout(r, d));
718
+ if (await connectToSocket()) break;
719
+ d = Math.min(d * 2, 3e3);
720
+ }
721
+ if (!_connected) return null;
722
+ }
723
+ }
724
+ const result = await retryThenRestart(
725
+ () => sendRequest([text], priority),
726
+ "Embed"
727
+ );
728
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
729
+ }
730
+ function disconnectClient() {
731
+ if (_socket) {
732
+ _socket.destroy();
733
+ _socket = null;
734
+ }
735
+ _connected = false;
736
+ _buffer = "";
737
+ for (const [id, entry] of _pending) {
738
+ clearTimeout(entry.timer);
739
+ _pending.delete(id);
740
+ entry.resolve({ error: "Client disconnected" });
741
+ }
742
+ }
743
+ 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;
744
+ var init_exe_daemon_client = __esm({
745
+ "src/lib/exe-daemon-client.ts"() {
746
+ "use strict";
747
+ init_config();
748
+ init_daemon_auth();
749
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
750
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
751
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
752
+ SPAWN_LOCK_STALE_MS = 3e4;
753
+ CONNECT_TIMEOUT_MS = 15e3;
754
+ REQUEST_TIMEOUT_MS = 3e4;
755
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
756
+ _socket = null;
757
+ _connected = false;
758
+ _buffer = "";
759
+ _requestCount = 0;
760
+ _consecutiveFailures = 0;
761
+ HEALTH_CHECK_INTERVAL = 100;
762
+ MAX_RETRIES_BEFORE_RESTART = 3;
763
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
764
+ MIN_DAEMON_AGE_MS = 3e4;
765
+ _pending = /* @__PURE__ */ new Map();
766
+ MAX_BUFFER = 1e7;
767
+ }
768
+ });
769
+
770
+ // src/lib/embedder.ts
771
+ var embedder_exports = {};
772
+ __export(embedder_exports, {
773
+ disposeEmbedder: () => disposeEmbedder,
774
+ embed: () => embed,
775
+ embedDirect: () => embedDirect,
776
+ getEmbedder: () => getEmbedder
777
+ });
778
+ async function getEmbedder() {
779
+ const ok = await connectEmbedDaemon();
780
+ if (!ok) {
781
+ throw new Error(
782
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
783
+ );
784
+ }
785
+ }
786
+ async function embed(text) {
787
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
788
+ const vector = await embedViaClient(text, priority);
789
+ if (!vector) {
790
+ throw new Error(
791
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
792
+ );
793
+ }
794
+ if (vector.length !== EMBEDDING_DIM) {
795
+ throw new Error(
796
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
797
+ );
798
+ }
799
+ return vector;
800
+ }
801
+ async function disposeEmbedder() {
802
+ disconnectClient();
803
+ }
804
+ async function embedDirect(text) {
805
+ const llamaCpp = await import("node-llama-cpp");
806
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
807
+ const { existsSync: existsSync6 } = await import("fs");
808
+ const path6 = await import("path");
809
+ const modelPath = path6.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
810
+ if (!existsSync6(modelPath)) {
811
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
812
+ }
813
+ const llama = await llamaCpp.getLlama();
814
+ const model = await llama.loadModel({ modelPath });
815
+ const context = await model.createEmbeddingContext();
816
+ try {
817
+ const embedding = await context.getEmbeddingFor(text);
818
+ const vector = Array.from(embedding.vector);
819
+ if (vector.length !== EMBEDDING_DIM) {
820
+ throw new Error(
821
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
822
+ );
823
+ }
824
+ return vector;
825
+ } finally {
826
+ await context.dispose();
827
+ await model.dispose();
828
+ }
829
+ }
830
+ var init_embedder = __esm({
831
+ "src/lib/embedder.ts"() {
832
+ "use strict";
833
+ init_memory();
834
+ init_exe_daemon_client();
835
+ }
836
+ });
837
+
216
838
  // src/lib/skill-learning.ts
217
- import crypto2 from "crypto";
839
+ import crypto3 from "crypto";
218
840
 
219
841
  // src/lib/database.ts
220
842
  import { createClient } from "@libsql/client";
@@ -245,6 +867,7 @@ var BOOLEAN_COLUMN_NAMES = new Set(
245
867
  );
246
868
 
247
869
  // src/lib/database.ts
870
+ init_memory();
248
871
  var _resilientClient = null;
249
872
  var _daemonClient = null;
250
873
  var _adapterClient = null;
@@ -252,7 +875,7 @@ function getClient() {
252
875
  if (!_adapterClient) {
253
876
  throw new Error("Database client not initialized. Call initDatabase() first.");
254
877
  }
255
- if (process.env.DATABASE_URL) {
878
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
256
879
  return _adapterClient;
257
880
  }
258
881
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -265,15 +888,22 @@ function getClient() {
265
888
  }
266
889
 
267
890
  // src/lib/behaviors.ts
268
- import crypto from "crypto";
891
+ import crypto2 from "crypto";
269
892
  async function storeBehavior(opts) {
270
893
  const client = getClient();
271
- const id = crypto.randomUUID();
894
+ const id = crypto2.randomUUID();
272
895
  const now = (/* @__PURE__ */ new Date()).toISOString();
896
+ let vector = null;
897
+ try {
898
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
899
+ const vec = await embed2(opts.content);
900
+ vector = new Float32Array(vec);
901
+ } catch {
902
+ }
273
903
  await client.execute({
274
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
275
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
276
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
904
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
905
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
906
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
277
907
  });
278
908
  return id;
279
909
  }
@@ -309,11 +939,11 @@ async function extractTrajectory(taskId, agentId) {
309
939
  return signature;
310
940
  }
311
941
  function hashSignature(signature) {
312
- return crypto2.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
942
+ return crypto3.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
313
943
  }
314
944
  async function storeTrajectory(opts) {
315
945
  const client = getClient();
316
- const id = crypto2.randomUUID();
946
+ const id = crypto3.randomUUID();
317
947
  const now = (/* @__PURE__ */ new Date()).toISOString();
318
948
  const signatureHash = hashSignature(opts.signature);
319
949
  await client.execute({