@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
@@ -24,7 +24,7 @@ async function completeReminder(idOrText) {
24
24
  });
25
25
  if (result.rows.length === 0) {
26
26
  result = await client.execute({
27
- sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%'`,
27
+ sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
28
28
  args: [idOrText]
29
29
  });
30
30
  }
@@ -59,7 +59,7 @@ __export(config_exports, {
59
59
  migrateConfig: () => migrateConfig,
60
60
  saveConfig: () => saveConfig
61
61
  });
62
- import { readFile, writeFile, mkdir } from "fs/promises";
62
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
63
63
  import { readFileSync, existsSync, renameSync } from "fs";
64
64
  import path from "path";
65
65
  import os from "os";
@@ -185,6 +185,9 @@ async function saveConfig(config) {
185
185
  await mkdir(dir, { recursive: true });
186
186
  const configPath = path.join(dir, "config.json");
187
187
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
188
+ if (config.cloud?.apiKey) {
189
+ await chmod(configPath, 384);
190
+ }
188
191
  }
189
192
  async function loadConfigFrom(configPath) {
190
193
  const raw = await readFile(configPath, "utf-8");
@@ -602,15 +605,20 @@ function addEmployee(employees, employee) {
602
605
  }
603
606
  return [...employees, normalized];
604
607
  }
608
+ function findExeBin() {
609
+ try {
610
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
611
+ } catch {
612
+ return null;
613
+ }
614
+ }
605
615
  function registerBinSymlinks(name) {
606
616
  const created = [];
607
617
  const skipped = [];
608
618
  const errors = [];
609
- let exeBinPath;
610
- try {
611
- exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
612
- } catch {
613
- errors.push("Could not find 'exe' in PATH");
619
+ const exeBinPath = findExeBin();
620
+ if (!exeBinPath) {
621
+ errors.push("Could not find 'exe-os' in PATH");
614
622
  return { created, skipped, errors };
615
623
  }
616
624
  const binDir = path4.dirname(exeBinPath);
@@ -30,7 +30,7 @@ import { execSync as execSync2 } from "child_process";
30
30
  import path2 from "path";
31
31
 
32
32
  // src/lib/config.ts
33
- import { readFile, writeFile, mkdir } from "fs/promises";
33
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
34
34
  import { readFileSync, existsSync, renameSync } from "fs";
35
35
  import path from "path";
36
36
  import os from "os";
@@ -216,7 +216,7 @@ function registerDeactivateBehavior(server) {
216
216
  },
217
217
  async ({ behavior_id }) => {
218
218
  const caller = getActiveAgent();
219
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
219
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
220
220
  if (!allowed) {
221
221
  return {
222
222
  content: [{
@@ -17,7 +17,7 @@ function getClient() {
17
17
  // src/lib/reminders.ts
18
18
  async function listReminders(includeCompleted = false) {
19
19
  const client = getClient();
20
- const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST`;
20
+ const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST LIMIT 500` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST LIMIT 500`;
21
21
  const result = await client.execute(sql);
22
22
  return result.rows.map((row) => ({
23
23
  id: String(row.id),
@@ -28,7 +28,7 @@ var init_database = __esm({
28
28
  });
29
29
 
30
30
  // src/lib/config.ts
31
- import { readFile, writeFile, mkdir } from "fs/promises";
31
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
32
32
  import { readFileSync, existsSync, renameSync } from "fs";
33
33
  import path from "path";
34
34
  import os from "os";
@@ -455,36 +455,44 @@ function registerListTasks(server) {
455
455
  }
456
456
  },
457
457
  async ({ assigned_to, status, project_name, priority }) => {
458
- const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
459
- const tasks = await listTasks({
460
- assignedTo: assigned_to,
461
- status,
462
- projectName: resolvedProject,
463
- priority
464
- });
465
- if (tasks.length === 0) {
458
+ try {
459
+ const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
460
+ const tasks = await listTasks({
461
+ assignedTo: assigned_to,
462
+ status,
463
+ projectName: resolvedProject,
464
+ priority
465
+ });
466
+ if (tasks.length === 0) {
467
+ return {
468
+ content: [{ type: "text", text: "No tasks found." }]
469
+ };
470
+ }
471
+ const lines = tasks.map((t) => {
472
+ const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
473
+ let budgetNote = "";
474
+ if (t.budgetTokens !== null) {
475
+ const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
476
+ budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
477
+ }
478
+ return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
479
+ });
480
+ return {
481
+ content: [
482
+ {
483
+ type: "text",
484
+ text: `${tasks.length} task(s):
485
+ ${lines.join("\n")}`
486
+ }
487
+ ]
488
+ };
489
+ } catch (err) {
490
+ const msg = err instanceof Error ? err.message : String(err);
466
491
  return {
467
- content: [{ type: "text", text: "No tasks found." }]
492
+ content: [{ type: "text", text: `Failed to list tasks: ${msg}` }],
493
+ isError: true
468
494
  };
469
495
  }
470
- const lines = tasks.map((t) => {
471
- const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
472
- let budgetNote = "";
473
- if (t.budgetTokens !== null) {
474
- const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
475
- budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
476
- }
477
- return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
478
- });
479
- return {
480
- content: [
481
- {
482
- type: "text",
483
- text: `${tasks.length} task(s):
484
- ${lines.join("\n")}`
485
- }
486
- ]
487
- };
488
496
  }
489
497
  );
490
498
  }
@@ -342,7 +342,7 @@ var init_intercom_queue = __esm({
342
342
  });
343
343
 
344
344
  // src/lib/config.ts
345
- import { readFile, writeFile, mkdir } from "fs/promises";
345
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
346
346
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
347
347
  import path3 from "path";
348
348
  import os3 from "os";
@@ -44,7 +44,7 @@ var init_database = __esm({
44
44
  });
45
45
 
46
46
  // src/lib/config.ts
47
- import { readFile, writeFile, mkdir } from "fs/promises";
47
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
48
48
  import { readFileSync, existsSync, renameSync } from "fs";
49
49
  import path from "path";
50
50
  import os from "os";
@@ -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 {
@@ -1269,7 +1270,7 @@ var init_database = __esm({
1269
1270
  });
1270
1271
 
1271
1272
  // src/lib/config.ts
1272
- import { readFile, writeFile, mkdir } from "fs/promises";
1273
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
1273
1274
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
1274
1275
  import path4 from "path";
1275
1276
  import os4 from "os";
@@ -3904,12 +3905,13 @@ var init_memory = __esm({
3904
3905
  });
3905
3906
 
3906
3907
  // src/lib/keychain.ts
3907
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod } from "fs/promises";
3908
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
3908
3909
  import { existsSync as existsSync11 } from "fs";
3909
3910
  import path15 from "path";
3911
+ import os7 from "os";
3910
3912
  import crypto6 from "crypto";
3911
3913
  function getKeyDir() {
3912
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(process.env.HOME ?? "/tmp", ".exe-os");
3914
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os7.homedir(), ".exe-os");
3913
3915
  }
3914
3916
  function getKeyPath() {
3915
3917
  return path15.join(getKeyDir(), "master.key");
@@ -3966,7 +3968,7 @@ __export(shard_manager_exports, {
3966
3968
  shardExists: () => shardExists
3967
3969
  });
3968
3970
  import path16 from "path";
3969
- import { existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
3971
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
3970
3972
  import { createClient as createClient2 } from "@libsql/client";
3971
3973
  function initShardManager(encryptionKey) {
3972
3974
  _encryptionKey = encryptionKey;
@@ -4005,7 +4007,6 @@ function shardExists(projectName) {
4005
4007
  }
4006
4008
  function listShards() {
4007
4009
  if (!existsSync12(SHARDS_DIR)) return [];
4008
- const { readdirSync: readdirSync3 } = __require("fs");
4009
4010
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4010
4011
  }
4011
4012
  async function ensureShardSchema(client) {
@@ -4211,6 +4212,28 @@ __export(store_exports, {
4211
4212
  vectorToBlob: () => vectorToBlob,
4212
4213
  writeMemory: () => writeMemory
4213
4214
  });
4215
+ function isBusyError2(err) {
4216
+ if (err instanceof Error) {
4217
+ const msg = err.message.toLowerCase();
4218
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
4219
+ }
4220
+ return false;
4221
+ }
4222
+ async function retryOnBusy2(fn, label) {
4223
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
4224
+ try {
4225
+ return await fn();
4226
+ } catch (err) {
4227
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
4228
+ process.stderr.write(
4229
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
4230
+ `
4231
+ );
4232
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
4233
+ }
4234
+ }
4235
+ throw new Error("unreachable");
4236
+ }
4214
4237
  async function initStore(options) {
4215
4238
  if (_flushTimer !== null) {
4216
4239
  clearInterval(_flushTimer);
@@ -4239,14 +4262,17 @@ async function initStore(options) {
4239
4262
  dbPath,
4240
4263
  encryptionKey: hexKey
4241
4264
  });
4242
- await ensureSchema();
4265
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
4243
4266
  try {
4244
4267
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4245
4268
  initShardManager2(hexKey);
4246
4269
  } catch {
4247
4270
  }
4248
4271
  const client = getClient();
4249
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
4272
+ const vResult = await retryOnBusy2(
4273
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
4274
+ "version-query"
4275
+ );
4250
4276
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4251
4277
  }
4252
4278
  function classifyTier(record) {
@@ -4289,6 +4315,12 @@ async function writeMemory(record) {
4289
4315
  supersedes_id: record.supersedes_id ?? null
4290
4316
  };
4291
4317
  _pendingRecords.push(dbRow);
4318
+ const MAX_PENDING = 1e3;
4319
+ if (_pendingRecords.length > MAX_PENDING) {
4320
+ const dropped = _pendingRecords.length - MAX_PENDING;
4321
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
4322
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
4323
+ }
4292
4324
  if (_flushTimer === null) {
4293
4325
  _flushTimer = setInterval(() => {
4294
4326
  void flushBatch();
@@ -4620,7 +4652,7 @@ async function getMemoryCardinality(agentId) {
4620
4652
  return 0;
4621
4653
  }
4622
4654
  }
4623
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4655
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4624
4656
  var init_store = __esm({
4625
4657
  "src/lib/store.ts"() {
4626
4658
  "use strict";
@@ -4628,6 +4660,8 @@ var init_store = __esm({
4628
4660
  init_database();
4629
4661
  init_keychain();
4630
4662
  init_config();
4663
+ INIT_MAX_RETRIES = 3;
4664
+ INIT_RETRY_DELAY_MS = 1e3;
4631
4665
  _pendingRecords = [];
4632
4666
  _batchSize = 20;
4633
4667
  _flushIntervalMs = 1e4;