@askexenow/exe-os 0.8.37 → 0.8.39

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 (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -1,5 +1,12 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/lib/cloud-sync.ts
2
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync } from "fs";
9
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
3
10
  import path5 from "path";
4
11
  import { homedir } from "os";
5
12
 
@@ -73,7 +80,7 @@ import { execSync } from "child_process";
73
80
  import path2 from "path";
74
81
 
75
82
  // src/lib/config.ts
76
- import { readFile, writeFile, mkdir } from "fs/promises";
83
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
77
84
  import { readFileSync, existsSync, renameSync } from "fs";
78
85
  import path from "path";
79
86
  import os from "os";
@@ -180,15 +187,20 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
180
187
  await mkdir2(path2.dirname(employeesPath), { recursive: true });
181
188
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
182
189
  }
190
+ function findExeBin() {
191
+ try {
192
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
183
197
  function registerBinSymlinks(name) {
184
198
  const created = [];
185
199
  const skipped = [];
186
200
  const errors = [];
187
- let exeBinPath;
188
- try {
189
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
190
- } catch {
191
- errors.push("Could not find 'exe' in PATH");
201
+ const exeBinPath = findExeBin();
202
+ if (!exeBinPath) {
203
+ errors.push("Could not find 'exe-os' in PATH");
192
204
  return { created, skipped, errors };
193
205
  }
194
206
  const binDir = path2.dirname(exeBinPath);
@@ -225,6 +237,15 @@ var LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
225
237
  var CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
226
238
  var DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
227
239
  var API_BASE = "https://askexe.com/cloud";
240
+ var RETRY_DELAY_MS = 500;
241
+ async function fetchRetry(url, init) {
242
+ try {
243
+ return await fetch(url, init);
244
+ } catch {
245
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
246
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
247
+ }
248
+ }
228
249
  var LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
229
250
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
230
251
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -316,7 +337,7 @@ function cacheResponse(token) {
316
337
  async function validateLicense(apiKey, deviceId) {
317
338
  const did = deviceId ?? loadDeviceId();
318
339
  try {
319
- const res = await fetch(`${API_BASE}/auth/activate`, {
340
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
320
341
  method: "POST",
321
342
  headers: { "Content-Type": "application/json" },
322
343
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -351,14 +372,24 @@ async function validateLicense(apiKey, deviceId) {
351
372
  } catch {
352
373
  const cached = await getCachedLicense();
353
374
  if (cached) return cached;
354
- return FREE_LICENSE;
375
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
376
+ }
377
+ }
378
+ var CACHE_MAX_AGE_MS = 36e5;
379
+ function getCacheAgeMs() {
380
+ try {
381
+ const { statSync } = __require("fs");
382
+ const s = statSync(CACHE_PATH);
383
+ return Date.now() - s.mtimeMs;
384
+ } catch {
385
+ return Infinity;
355
386
  }
356
387
  }
357
388
  async function checkLicense() {
358
389
  const key = loadLicense();
359
390
  if (!key) return FREE_LICENSE;
360
391
  const cached = await getCachedLicense();
361
- if (cached) return cached;
392
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
362
393
  const deviceId = loadDeviceId();
363
394
  return validateLicense(key, deviceId);
364
395
  }
@@ -401,16 +432,50 @@ function logError(msg) {
401
432
  }
402
433
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
403
434
  var FETCH_TIMEOUT_MS = 3e4;
435
+ var PUSH_BATCH_SIZE = 5e3;
436
+ var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
437
+ var LOCK_STALE_MS = 3e4;
438
+ async function withRosterLock(fn) {
439
+ if (existsSync5(ROSTER_LOCK_PATH)) {
440
+ try {
441
+ const ts = parseInt(readFileSync5(ROSTER_LOCK_PATH, "utf-8"), 10);
442
+ if (Date.now() - ts < LOCK_STALE_MS) {
443
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
444
+ }
445
+ } catch (err) {
446
+ if (err instanceof Error && err.message.includes("already in progress")) throw err;
447
+ }
448
+ }
449
+ writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
450
+ try {
451
+ return await fn();
452
+ } finally {
453
+ try {
454
+ unlinkSync(ROSTER_LOCK_PATH);
455
+ } catch {
456
+ }
457
+ }
458
+ }
404
459
  async function fetchWithRetry(url, init) {
405
- const attempt = async () => {
406
- const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
407
- return fetch(url, { ...init, signal });
408
- };
409
- const resp = await attempt();
410
- if (resp.status >= 500) {
411
- return attempt();
460
+ const MAX_RETRIES = 3;
461
+ const BASE_DELAY_MS = 200;
462
+ let lastError;
463
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
464
+ try {
465
+ const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
466
+ const resp = await fetch(url, { ...init, signal });
467
+ if (resp.status >= 500 && attempt < MAX_RETRIES) {
468
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
469
+ continue;
470
+ }
471
+ return resp;
472
+ } catch (err) {
473
+ lastError = err;
474
+ if (attempt === MAX_RETRIES) throw err;
475
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
476
+ }
412
477
  }
413
- return resp;
478
+ throw lastError;
414
479
  }
415
480
  function assertSecureEndpoint(endpoint) {
416
481
  if (endpoint.startsWith("https://")) return;
@@ -438,10 +503,15 @@ async function cloudPush(records, maxVersion, config) {
438
503
  headers: {
439
504
  Authorization: `Bearer ${config.apiKey}`,
440
505
  "Content-Type": "application/json",
441
- "X-Device-Id": loadDeviceId()
506
+ "X-Device-Id": loadDeviceId(),
507
+ "X-Expected-Version": String(maxVersion)
442
508
  },
443
509
  body: JSON.stringify({ version: maxVersion, blob })
444
510
  });
511
+ if (resp.status === 409) {
512
+ logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
513
+ return false;
514
+ }
445
515
  return resp.ok;
446
516
  } catch (err) {
447
517
  logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
@@ -528,18 +598,21 @@ async function cloudSync(config) {
528
598
  "SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
529
599
  );
530
600
  const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
531
- const recordsResult = await client.execute({
532
- sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
533
- tool_name, project_name, has_error, raw_text, version,
534
- author_device_id, scope
535
- FROM memories
536
- WHERE version > ?
537
- AND (scope IS NULL OR scope != 'personal')
538
- ORDER BY version ASC`,
539
- args: [lastPushVersion]
540
- });
541
601
  let pushed = 0;
542
- if (recordsResult.rows.length > 0) {
602
+ let batchCursor = lastPushVersion;
603
+ while (true) {
604
+ const recordsResult = await client.execute({
605
+ sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
606
+ tool_name, project_name, has_error, raw_text, version,
607
+ author_device_id, scope
608
+ FROM memories
609
+ WHERE version > ?
610
+ AND (scope IS NULL OR scope != 'personal')
611
+ ORDER BY version ASC
612
+ LIMIT ?`,
613
+ args: [batchCursor, PUSH_BATCH_SIZE]
614
+ });
615
+ if (recordsResult.rows.length === 0) break;
543
616
  const records = recordsResult.rows.map((row) => ({
544
617
  id: row.id,
545
618
  agent_id: row.agent_id,
@@ -556,13 +629,14 @@ async function cloudSync(config) {
556
629
  }));
557
630
  const maxVersion = Number(records[records.length - 1].version);
558
631
  const pushOk = await cloudPush(records, maxVersion, config);
559
- if (pushOk) {
560
- await client.execute({
561
- sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
562
- args: [String(maxVersion)]
563
- });
564
- pushed = records.length;
565
- }
632
+ if (!pushOk) break;
633
+ await client.execute({
634
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
635
+ args: [String(maxVersion)]
636
+ });
637
+ pushed += records.length;
638
+ batchCursor = maxVersion;
639
+ if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
566
640
  }
567
641
  try {
568
642
  await cloudPushRoster(config);
@@ -644,6 +718,28 @@ async function cloudSync(config) {
644
718
  documents: documentsResult
645
719
  };
646
720
  }
721
+ var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
722
+ function recordRosterDeletion(name) {
723
+ let deletions = [];
724
+ try {
725
+ if (existsSync5(ROSTER_DELETIONS_PATH)) {
726
+ deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
727
+ }
728
+ } catch {
729
+ }
730
+ if (!deletions.includes(name)) deletions.push(name);
731
+ writeFileSync2(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
732
+ }
733
+ function consumeRosterDeletions() {
734
+ try {
735
+ if (!existsSync5(ROSTER_DELETIONS_PATH)) return [];
736
+ const deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
737
+ writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
738
+ return deletions;
739
+ } catch {
740
+ return [];
741
+ }
742
+ }
647
743
  function buildRosterBlob(paths) {
648
744
  const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
649
745
  const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
@@ -671,9 +767,10 @@ function buildRosterBlob(paths) {
671
767
  } catch {
672
768
  }
673
769
  }
674
- const content = JSON.stringify({ roster, identities, config });
770
+ const deletedNames = consumeRosterDeletions();
771
+ const content = JSON.stringify({ roster, identities, config, deletedNames });
675
772
  const hash = Buffer.from(content).length;
676
- return { roster, identities, config, version: hash };
773
+ return { roster, identities, config, deletedNames, version: hash };
677
774
  }
678
775
  async function cloudPushRoster(config) {
679
776
  assertSecureEndpoint(config.endpoint);
@@ -756,38 +853,50 @@ function mergeConfig(remoteConfig, configPath) {
756
853
  writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
757
854
  }
758
855
  async function mergeRosterFromRemote(remote, paths) {
759
- const rosterPath = paths?.rosterPath ?? void 0;
760
- const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
761
- const localEmployees = await loadEmployees(rosterPath);
762
- const localNames = new Set(localEmployees.map((e) => e.name));
763
- let added = 0;
764
- for (const remoteEmp of remote.roster) {
765
- if (localNames.has(remoteEmp.name)) continue;
766
- localEmployees.push(remoteEmp);
767
- localNames.add(remoteEmp.name);
768
- added++;
769
- if (remote.identities[`${remoteEmp.name}.md`]) {
770
- if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
771
- const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
772
- if (!existsSync5(idPath)) {
773
- writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
856
+ return withRosterLock(async () => {
857
+ const rosterPath = paths?.rosterPath ?? void 0;
858
+ const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
859
+ const localEmployees = await loadEmployees(rosterPath);
860
+ const localNames = new Set(localEmployees.map((e) => e.name));
861
+ let added = 0;
862
+ for (const remoteEmp of remote.roster) {
863
+ if (localNames.has(remoteEmp.name)) continue;
864
+ localEmployees.push(remoteEmp);
865
+ localNames.add(remoteEmp.name);
866
+ added++;
867
+ if (remote.identities[`${remoteEmp.name}.md`]) {
868
+ if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
869
+ const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
870
+ if (!existsSync5(idPath)) {
871
+ writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
872
+ }
873
+ }
874
+ try {
875
+ registerBinSymlinks(remoteEmp.name);
876
+ } catch {
774
877
  }
775
878
  }
776
- try {
777
- registerBinSymlinks(remoteEmp.name);
778
- } catch {
879
+ let removed = 0;
880
+ if (remote.deletedNames && remote.deletedNames.length > 0) {
881
+ const toRemove = new Set(remote.deletedNames);
882
+ const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
883
+ removed = localEmployees.length - filtered.length;
884
+ if (removed > 0) {
885
+ localEmployees.length = 0;
886
+ localEmployees.push(...filtered);
887
+ }
779
888
  }
780
- }
781
- if (added > 0) {
782
- await saveEmployees(localEmployees, rosterPath);
783
- }
784
- if (remote.config && Object.keys(remote.config).length > 0) {
785
- try {
786
- mergeConfig(remote.config, paths?.configPath);
787
- } catch {
889
+ if (added > 0 || removed > 0) {
890
+ await saveEmployees(localEmployees, rosterPath);
788
891
  }
789
- }
790
- return { added };
892
+ if (remote.config && Object.keys(remote.config).length > 0) {
893
+ try {
894
+ mergeConfig(remote.config, paths?.configPath);
895
+ } catch {
896
+ }
897
+ }
898
+ return { added };
899
+ });
791
900
  }
792
901
  async function cloudPushBlob(route, data, metaKey, config) {
793
902
  if (data.length === 0) return { ok: true };
@@ -855,7 +964,7 @@ async function cloudPullBlob(route, config) {
855
964
  }
856
965
  async function cloudPushBehaviors(config) {
857
966
  const client = getClient();
858
- const result = await client.execute("SELECT * FROM behaviors");
967
+ const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
859
968
  const rows = result.rows;
860
969
  const { ok } = await cloudPushBlob(
861
970
  "/sync/push-behaviors",
@@ -903,13 +1012,13 @@ async function cloudPullBehaviors(config) {
903
1012
  async function cloudPushGraphRAG(config) {
904
1013
  const client = getClient();
905
1014
  const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
906
- client.execute("SELECT * FROM entities"),
907
- client.execute("SELECT * FROM relationships"),
908
- client.execute("SELECT * FROM entity_aliases"),
909
- client.execute("SELECT * FROM entity_memories"),
910
- client.execute("SELECT * FROM relationship_memories"),
911
- client.execute("SELECT * FROM hyperedges"),
912
- client.execute("SELECT * FROM hyperedge_nodes")
1015
+ client.execute("SELECT * FROM entities LIMIT 50000"),
1016
+ client.execute("SELECT * FROM relationships LIMIT 50000"),
1017
+ client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
1018
+ client.execute("SELECT * FROM entity_memories LIMIT 50000"),
1019
+ client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
1020
+ client.execute("SELECT * FROM hyperedges LIMIT 50000"),
1021
+ client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
913
1022
  ]);
914
1023
  const blob = {
915
1024
  entities: entities.rows,
@@ -1011,7 +1120,7 @@ async function cloudPullGraphRAG(config) {
1011
1120
  }
1012
1121
  async function cloudPushTasks(config) {
1013
1122
  const client = getClient();
1014
- const result = await client.execute("SELECT * FROM tasks");
1123
+ const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
1015
1124
  const rows = result.rows;
1016
1125
  const { ok } = await cloudPushBlob(
1017
1126
  "/sync/push-tasks",
@@ -1057,7 +1166,7 @@ async function cloudPullTasks(config) {
1057
1166
  }
1058
1167
  async function cloudPushConversations(config) {
1059
1168
  const client = getClient();
1060
- const result = await client.execute("SELECT * FROM conversations");
1169
+ const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
1061
1170
  const rows = result.rows;
1062
1171
  const { ok } = await cloudPushBlob(
1063
1172
  "/sync/push-conversations",
@@ -1107,8 +1216,8 @@ async function cloudPullConversations(config) {
1107
1216
  async function cloudPushDocuments(config) {
1108
1217
  const client = getClient();
1109
1218
  const [workspaces, documents] = await Promise.all([
1110
- client.execute("SELECT * FROM workspaces"),
1111
- client.execute("SELECT * FROM documents")
1219
+ client.execute("SELECT * FROM workspaces LIMIT 1000"),
1220
+ client.execute("SELECT * FROM documents LIMIT 10000")
1112
1221
  ]);
1113
1222
  const blob = {
1114
1223
  workspaces: workspaces.rows,
@@ -1162,6 +1271,7 @@ async function cloudPullDocuments(config) {
1162
1271
  return { pulled };
1163
1272
  }
1164
1273
  export {
1274
+ assertSecureEndpoint,
1165
1275
  buildRosterBlob,
1166
1276
  cloudPull,
1167
1277
  cloudPullBehaviors,
@@ -1181,5 +1291,6 @@ export {
1181
1291
  cloudPushTasks,
1182
1292
  cloudSync,
1183
1293
  mergeConfig,
1184
- mergeRosterFromRemote
1294
+ mergeRosterFromRemote,
1295
+ recordRosterDeletion
1185
1296
  };
@@ -1,5 +1,5 @@
1
1
  // src/lib/config.ts
2
- import { readFile, writeFile, mkdir } from "fs/promises";
2
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
3
3
  import { readFileSync, existsSync, renameSync } from "fs";
4
4
  import path from "path";
5
5
  import os from "os";
@@ -204,6 +204,9 @@ async function saveConfig(config) {
204
204
  await mkdir(dir, { recursive: true });
205
205
  const configPath = path.join(dir, "config.json");
206
206
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
207
+ if (config.cloud?.apiKey) {
208
+ await chmod(configPath, 384);
209
+ }
207
210
  }
208
211
  async function loadConfigFrom(configPath) {
209
212
  const raw = await readFile(configPath, "utf-8");
@@ -4,15 +4,15 @@ var __esm = (fn, res) => function __init() {
4
4
  };
5
5
 
6
6
  // src/lib/config.ts
7
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
7
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
8
8
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
9
  import path2 from "path";
10
- import os from "os";
10
+ import os2 from "os";
11
11
  function resolveDataDir() {
12
12
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
13
13
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
- const newDir = path2.join(os.homedir(), ".exe-os");
15
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
14
+ const newDir = path2.join(os2.homedir(), ".exe-os");
15
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
16
16
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
17
  try {
18
18
  renameSync(legacyDir, newDir);
@@ -110,6 +110,7 @@ import { createClient } from "@libsql/client";
110
110
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
111
111
  import { existsSync } from "fs";
112
112
  import path from "path";
113
+ import os from "os";
113
114
  import crypto from "crypto";
114
115
 
115
116
  // src/lib/store.ts
@@ -88,6 +88,7 @@ async function ensureSchema() {
88
88
  const client = getRawClient();
89
89
  await client.execute("PRAGMA journal_mode = WAL");
90
90
  await client.execute("PRAGMA busy_timeout = 30000");
91
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
91
92
  try {
92
93
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
93
94
  } catch {
@@ -110,6 +110,7 @@ async function ensureSchema() {
110
110
  const client = getRawClient();
111
111
  await client.execute("PRAGMA journal_mode = WAL");
112
112
  await client.execute("PRAGMA busy_timeout = 30000");
113
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
113
114
  try {
114
115
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
115
116
  } catch {
@@ -924,7 +925,7 @@ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync as
924
925
  import path2 from "path";
925
926
 
926
927
  // src/lib/config.ts
927
- import { readFile, writeFile, mkdir } from "fs/promises";
928
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
928
929
  import { readFileSync, existsSync, renameSync } from "fs";
929
930
  import path from "path";
930
931
  import os from "os";
@@ -24,7 +24,7 @@ __export(config_exports, {
24
24
  migrateConfig: () => migrateConfig,
25
25
  saveConfig: () => saveConfig
26
26
  });
27
- import { readFile, writeFile, mkdir } from "fs/promises";
27
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
28
28
  import { readFileSync, existsSync, renameSync } from "fs";
29
29
  import path from "path";
30
30
  import os from "os";
@@ -150,6 +150,9 @@ async function saveConfig(config) {
150
150
  await mkdir(dir, { recursive: true });
151
151
  const configPath = path.join(dir, "config.json");
152
152
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
+ if (config.cloud?.apiKey) {
154
+ await chmod(configPath, 384);
155
+ }
153
156
  }
154
157
  async function loadConfigFrom(configPath) {
155
158
  const raw = await readFile(configPath, "utf-8");
@@ -274,8 +277,13 @@ var _buffer = "";
274
277
  var _requestCount = 0;
275
278
  var HEALTH_CHECK_INTERVAL = 100;
276
279
  var _pending = /* @__PURE__ */ new Map();
280
+ var MAX_BUFFER = 1e7;
277
281
  function handleData(chunk) {
278
282
  _buffer += chunk.toString();
283
+ if (_buffer.length > MAX_BUFFER) {
284
+ _buffer = "";
285
+ return;
286
+ }
279
287
  let newlineIdx;
280
288
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
281
289
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -467,6 +467,10 @@ function buildCustomEmployeePrompt(name, role) {
467
467
  function getTemplate(name) {
468
468
  return TEMPLATES[name];
469
469
  }
470
+ function getTemplateByRole(role) {
471
+ const lower = role.toLowerCase();
472
+ return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
473
+ }
470
474
  function personalizePrompt(prompt, templateName, actualName) {
471
475
  if (templateName === actualName) return prompt;
472
476
  const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -597,6 +601,7 @@ export {
597
601
  buildCustomEmployeePrompt,
598
602
  getSessionPrompt,
599
603
  getTemplate,
604
+ getTemplateByRole,
600
605
  personalizePrompt,
601
606
  renderClientCOOTemplate
602
607
  };
@@ -5,7 +5,7 @@ import { execSync } from "child_process";
5
5
  import path2 from "path";
6
6
 
7
7
  // src/lib/config.ts
8
- import { readFile, writeFile, mkdir } from "fs/promises";
8
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
9
9
  import { readFileSync, existsSync, renameSync } from "fs";
10
10
  import path from "path";
11
11
  import os from "os";
@@ -165,15 +165,20 @@ function addEmployee(employees, employee) {
165
165
  }
166
166
  return [...employees, normalized];
167
167
  }
168
+ function findExeBin() {
169
+ try {
170
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
168
175
  function registerBinSymlinks(name) {
169
176
  const created = [];
170
177
  const skipped = [];
171
178
  const errors = [];
172
- let exeBinPath;
173
- try {
174
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
175
- } catch {
176
- errors.push("Could not find 'exe' in PATH");
179
+ const exeBinPath = findExeBin();
180
+ if (!exeBinPath) {
181
+ errors.push("Could not find 'exe-os' in PATH");
177
182
  return { created, skipped, errors };
178
183
  }
179
184
  const binDir = path2.dirname(exeBinPath);
@@ -7,7 +7,7 @@ import path2 from "path";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  // src/lib/config.ts
10
- import { readFile, writeFile, mkdir } from "fs/promises";
10
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
11
11
  import { readFileSync, existsSync, renameSync } from "fs";
12
12
  import path from "path";
13
13
  import os from "os";
@@ -110,8 +110,13 @@ var _buffer = "";
110
110
  var _requestCount = 0;
111
111
  var HEALTH_CHECK_INTERVAL = 100;
112
112
  var _pending = /* @__PURE__ */ new Map();
113
+ var MAX_BUFFER = 1e7;
113
114
  function handleData(chunk) {
114
115
  _buffer += chunk.toString();
116
+ if (_buffer.length > MAX_BUFFER) {
117
+ _buffer = "";
118
+ return;
119
+ }
115
120
  let newlineIdx;
116
121
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
117
122
  const line = _buffer.slice(0, newlineIdx).trim();