@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
package/dist/index.js CHANGED
@@ -461,6 +461,7 @@ async function ensureSchema() {
461
461
  const client = getRawClient();
462
462
  await client.execute("PRAGMA journal_mode = WAL");
463
463
  await client.execute("PRAGMA busy_timeout = 30000");
464
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
464
465
  try {
465
466
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
466
467
  } catch {
@@ -1284,7 +1285,7 @@ __export(config_exports, {
1284
1285
  migrateConfig: () => migrateConfig,
1285
1286
  saveConfig: () => saveConfig
1286
1287
  });
1287
- import { readFile, writeFile, mkdir } from "fs/promises";
1288
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
1288
1289
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
1289
1290
  import path4 from "path";
1290
1291
  import os4 from "os";
@@ -1410,6 +1411,9 @@ async function saveConfig(config2) {
1410
1411
  await mkdir(dir, { recursive: true });
1411
1412
  const configPath = path4.join(dir, "config.json");
1412
1413
  await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
1414
+ if (config2.cloud?.apiKey) {
1415
+ await chmod(configPath, 384);
1416
+ }
1413
1417
  }
1414
1418
  async function loadConfigFrom(configPath) {
1415
1419
  const raw = await readFile(configPath, "utf-8");
@@ -3958,12 +3962,13 @@ var init_memory = __esm({
3958
3962
  });
3959
3963
 
3960
3964
  // src/lib/keychain.ts
3961
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod } from "fs/promises";
3965
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
3962
3966
  import { existsSync as existsSync11 } from "fs";
3963
3967
  import path15 from "path";
3968
+ import os7 from "os";
3964
3969
  import crypto6 from "crypto";
3965
3970
  function getKeyDir() {
3966
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(process.env.HOME ?? "/tmp", ".exe-os");
3971
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os7.homedir(), ".exe-os");
3967
3972
  }
3968
3973
  function getKeyPath() {
3969
3974
  return path15.join(getKeyDir(), "master.key");
@@ -4020,7 +4025,7 @@ __export(shard_manager_exports, {
4020
4025
  shardExists: () => shardExists
4021
4026
  });
4022
4027
  import path16 from "path";
4023
- import { existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
4028
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
4024
4029
  import { createClient as createClient2 } from "@libsql/client";
4025
4030
  function initShardManager(encryptionKey) {
4026
4031
  _encryptionKey = encryptionKey;
@@ -4059,7 +4064,6 @@ function shardExists(projectName) {
4059
4064
  }
4060
4065
  function listShards() {
4061
4066
  if (!existsSync12(SHARDS_DIR)) return [];
4062
- const { readdirSync: readdirSync3 } = __require("fs");
4063
4067
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4064
4068
  }
4065
4069
  async function ensureShardSchema(client) {
@@ -4265,6 +4269,28 @@ __export(store_exports, {
4265
4269
  vectorToBlob: () => vectorToBlob,
4266
4270
  writeMemory: () => writeMemory
4267
4271
  });
4272
+ function isBusyError2(err) {
4273
+ if (err instanceof Error) {
4274
+ const msg = err.message.toLowerCase();
4275
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
4276
+ }
4277
+ return false;
4278
+ }
4279
+ async function retryOnBusy2(fn, label) {
4280
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
4281
+ try {
4282
+ return await fn();
4283
+ } catch (err) {
4284
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
4285
+ process.stderr.write(
4286
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
4287
+ `
4288
+ );
4289
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
4290
+ }
4291
+ }
4292
+ throw new Error("unreachable");
4293
+ }
4268
4294
  async function initStore(options) {
4269
4295
  if (_flushTimer !== null) {
4270
4296
  clearInterval(_flushTimer);
@@ -4293,14 +4319,17 @@ async function initStore(options) {
4293
4319
  dbPath,
4294
4320
  encryptionKey: hexKey
4295
4321
  });
4296
- await ensureSchema();
4322
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
4297
4323
  try {
4298
4324
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4299
4325
  initShardManager2(hexKey);
4300
4326
  } catch {
4301
4327
  }
4302
4328
  const client = getClient();
4303
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
4329
+ const vResult = await retryOnBusy2(
4330
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
4331
+ "version-query"
4332
+ );
4304
4333
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4305
4334
  }
4306
4335
  function classifyTier(record) {
@@ -4343,6 +4372,12 @@ async function writeMemory(record) {
4343
4372
  supersedes_id: record.supersedes_id ?? null
4344
4373
  };
4345
4374
  _pendingRecords.push(dbRow);
4375
+ const MAX_PENDING = 1e3;
4376
+ if (_pendingRecords.length > MAX_PENDING) {
4377
+ const dropped = _pendingRecords.length - MAX_PENDING;
4378
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
4379
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
4380
+ }
4346
4381
  if (_flushTimer === null) {
4347
4382
  _flushTimer = setInterval(() => {
4348
4383
  void flushBatch();
@@ -4674,7 +4709,7 @@ async function getMemoryCardinality(agentId) {
4674
4709
  return 0;
4675
4710
  }
4676
4711
  }
4677
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4712
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4678
4713
  var init_store = __esm({
4679
4714
  "src/lib/store.ts"() {
4680
4715
  "use strict";
@@ -4682,6 +4717,8 @@ var init_store = __esm({
4682
4717
  init_database();
4683
4718
  init_keychain();
4684
4719
  init_config();
4720
+ INIT_MAX_RETRIES = 3;
4721
+ INIT_RETRY_DELAY_MS = 1e3;
4685
4722
  _pendingRecords = [];
4686
4723
  _batchSize = 20;
4687
4724
  _flushIntervalMs = 1e4;
@@ -5074,7 +5111,8 @@ async function gqlRequest(query, variables) {
5074
5111
  "Content-Type": "application/json",
5075
5112
  Authorization: `Bearer ${config.apiToken}`
5076
5113
  },
5077
- body: JSON.stringify({ query, variables })
5114
+ body: JSON.stringify({ query, variables }),
5115
+ signal: AbortSignal.timeout(3e4)
5078
5116
  });
5079
5117
  if (!res.ok) {
5080
5118
  throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -5302,6 +5340,10 @@ import path17 from "path";
5302
5340
  import { fileURLToPath as fileURLToPath2 } from "url";
5303
5341
  function handleData(chunk) {
5304
5342
  _buffer += chunk.toString();
5343
+ if (_buffer.length > MAX_BUFFER) {
5344
+ _buffer = "";
5345
+ return;
5346
+ }
5305
5347
  let newlineIdx;
5306
5348
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
5307
5349
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -5609,7 +5651,7 @@ function disconnectClient() {
5609
5651
  entry.resolve({ error: "Client disconnected" });
5610
5652
  }
5611
5653
  }
5612
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
5654
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
5613
5655
  var init_exe_daemon_client = __esm({
5614
5656
  "src/lib/exe-daemon-client.ts"() {
5615
5657
  "use strict";
@@ -5626,6 +5668,7 @@ var init_exe_daemon_client = __esm({
5626
5668
  _requestCount = 0;
5627
5669
  HEALTH_CHECK_INTERVAL = 100;
5628
5670
  _pending = /* @__PURE__ */ new Map();
5671
+ MAX_BUFFER = 1e7;
5629
5672
  }
5630
5673
  });
5631
5674
 
@@ -5715,12 +5758,30 @@ async function wikiFetch(config2, path20, method = "GET", body) {
5715
5758
  const controller = new AbortController();
5716
5759
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
5717
5760
  try {
5718
- const response = await fetch(url, {
5719
- method,
5720
- headers,
5721
- body: body ? JSON.stringify(body) : void 0,
5722
- signal: controller.signal
5723
- });
5761
+ let response;
5762
+ try {
5763
+ response = await fetch(url, {
5764
+ method,
5765
+ headers,
5766
+ body: body ? JSON.stringify(body) : void 0,
5767
+ signal: controller.signal
5768
+ });
5769
+ } catch {
5770
+ clearTimeout(timeout);
5771
+ const retryController = new AbortController();
5772
+ const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
5773
+ try {
5774
+ await new Promise((r) => setTimeout(r, 500));
5775
+ response = await fetch(url, {
5776
+ method,
5777
+ headers,
5778
+ body: body ? JSON.stringify(body) : void 0,
5779
+ signal: retryController.signal
5780
+ });
5781
+ } finally {
5782
+ clearTimeout(retryTimeout);
5783
+ }
5784
+ }
5724
5785
  if (!response.ok) {
5725
5786
  throw new Error(`Wiki API ${method} ${path20}: ${response.status} ${response.statusText}`);
5726
5787
  }
@@ -9809,7 +9870,8 @@ var OllamaProvider = class {
9809
9870
  const res = await fetch(`${this.host}/api/chat`, {
9810
9871
  method: "POST",
9811
9872
  headers: { "Content-Type": "application/json" },
9812
- body: JSON.stringify(body)
9873
+ body: JSON.stringify(body),
9874
+ signal: AbortSignal.timeout(3e4)
9813
9875
  });
9814
9876
  if (!res.ok) {
9815
9877
  throw new Error(`Ollama API error: ${res.status} ${await res.text()}`);
@@ -10237,7 +10299,7 @@ var SignalAdapter = class {
10237
10299
  if (/^https?:\/\//i.test(trimmed)) {
10238
10300
  return trimmed.replace(/\/+$/, "");
10239
10301
  }
10240
- return `http://${trimmed}`.replace(/\/+$/, "");
10302
+ return `https://${trimmed}`.replace(/\/+$/, "");
10241
10303
  }
10242
10304
  async connect(config2) {
10243
10305
  this.baseUrl = this.normalizeBaseUrl(
@@ -10659,9 +10721,15 @@ var WebChatAdapter = class {
10659
10721
  res.end(JSON.stringify({ error: "Not found" }));
10660
10722
  }
10661
10723
  async handleChatRequest(req, res) {
10724
+ const MAX_BODY_SIZE = 1048576;
10662
10725
  let body = "";
10663
10726
  for await (const chunk of req) {
10664
10727
  body += chunk;
10728
+ if (body.length > MAX_BODY_SIZE) {
10729
+ res.writeHead(413, { "Content-Type": "application/json" });
10730
+ res.end(JSON.stringify({ error: "Request body too large" }));
10731
+ return;
10732
+ }
10665
10733
  }
10666
10734
  let parsed;
10667
10735
  try {
@@ -11141,11 +11209,12 @@ var SlackAdapter = class {
11141
11209
  // src/gateway/adapters/imessage.ts
11142
11210
  import { execFile } from "child_process";
11143
11211
  import { promisify } from "util";
11212
+ import os8 from "os";
11144
11213
  import path18 from "path";
11145
11214
  var execFileAsync = promisify(execFile);
11146
11215
  var POLL_INTERVAL_MS = 5e3;
11147
11216
  var MESSAGES_DB_PATH = path18.join(
11148
- process.env.HOME ?? "/Users",
11217
+ process.env.HOME ?? os8.homedir(),
11149
11218
  "Library/Messages/chat.db"
11150
11219
  );
11151
11220
  var IMessageAdapter = class {
@@ -11991,8 +12060,9 @@ async function ensureCRMContact(info) {
11991
12060
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
11992
12061
  import { randomUUID as randomUUID14 } from "crypto";
11993
12062
  import path19 from "path";
11994
- import os7 from "os";
11995
- var TRIGGERS_PATH = path19.join(os7.homedir(), ".exe-os", "triggers.json");
12063
+ import os9 from "os";
12064
+ var TRIGGERS_PATH = path19.join(os9.homedir(), ".exe-os", "triggers.json");
12065
+ var GRAPH_API_VERSION = "v21.0";
11996
12066
  function substituteTemplate(template, record) {
11997
12067
  return template.replace(
11998
12068
  /\{\{(\w+(?:\.\w+)*)\}\}/g,
@@ -12070,7 +12140,7 @@ async function executeSendWhatsapp(params) {
12070
12140
  const message = params.message ?? params.text;
12071
12141
  if (!to || !message)
12072
12142
  throw new Error("send_whatsapp requires 'to' and 'message' params");
12073
- const url = `https://graph.facebook.com/v21.0/${account.phoneNumberId}/messages`;
12143
+ const url = `https://graph.facebook.com/${GRAPH_API_VERSION}/${account.phoneNumberId}/messages`;
12074
12144
  const res = await fetch(url, {
12075
12145
  method: "POST",
12076
12146
  headers: {
@@ -12082,7 +12152,8 @@ async function executeSendWhatsapp(params) {
12082
12152
  to,
12083
12153
  type: "text",
12084
12154
  text: { body: message }
12085
- })
12155
+ }),
12156
+ signal: AbortSignal.timeout(3e4)
12086
12157
  });
12087
12158
  if (!res.ok) {
12088
12159
  const errBody = await res.text();