@askexenow/exe-os 0.8.0 → 0.8.1

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 (90) hide show
  1. package/README.md +178 -79
  2. package/dist/bin/backfill-responses.js +160 -8
  3. package/dist/bin/backfill-vectors.js +130 -1
  4. package/dist/bin/cleanup-stale-review-tasks.js +130 -1
  5. package/dist/bin/cli.js +10111 -7540
  6. package/dist/bin/exe-agent.js +159 -1
  7. package/dist/bin/exe-assign.js +235 -16
  8. package/dist/bin/exe-boot.js +344 -472
  9. package/dist/bin/exe-call.js +145 -1
  10. package/dist/bin/exe-cloud.js +11 -0
  11. package/dist/bin/exe-dispatch.js +37 -24
  12. package/dist/bin/exe-doctor.js +130 -1
  13. package/dist/bin/exe-export-behaviors.js +150 -7
  14. package/dist/bin/exe-forget.js +822 -665
  15. package/dist/bin/exe-gateway.js +470 -62
  16. package/dist/bin/exe-heartbeat.js +133 -2
  17. package/dist/bin/exe-kill.js +150 -7
  18. package/dist/bin/exe-launch-agent.js +150 -7
  19. package/dist/bin/exe-new-employee.js +756 -224
  20. package/dist/bin/exe-pending-messages.js +132 -2
  21. package/dist/bin/exe-pending-notifications.js +130 -1
  22. package/dist/bin/exe-pending-reviews.js +132 -2
  23. package/dist/bin/exe-review.js +160 -8
  24. package/dist/bin/exe-search.js +2473 -2008
  25. package/dist/bin/exe-session-cleanup.js +238 -51
  26. package/dist/bin/exe-settings.js +11 -0
  27. package/dist/bin/exe-status.js +130 -1
  28. package/dist/bin/exe-team.js +130 -1
  29. package/dist/bin/git-sweep.js +272 -16
  30. package/dist/bin/graph-backfill.js +150 -7
  31. package/dist/bin/graph-export.js +150 -7
  32. package/dist/bin/install.js +5 -0
  33. package/dist/bin/scan-tasks.js +238 -19
  34. package/dist/bin/setup.js +1776 -10
  35. package/dist/bin/shard-migrate.js +150 -7
  36. package/dist/bin/update.js +9 -6
  37. package/dist/bin/wiki-sync.js +150 -7
  38. package/dist/gateway/index.js +470 -62
  39. package/dist/hooks/bug-report-worker.js +195 -35
  40. package/dist/hooks/commit-complete.js +272 -16
  41. package/dist/hooks/error-recall.js +2313 -1847
  42. package/dist/hooks/exe-heartbeat-hook.js +5 -0
  43. package/dist/hooks/ingest-worker.js +330 -58
  44. package/dist/hooks/ingest.js +11 -0
  45. package/dist/hooks/instructions-loaded.js +199 -10
  46. package/dist/hooks/notification.js +199 -10
  47. package/dist/hooks/post-compact.js +199 -10
  48. package/dist/hooks/pre-compact.js +199 -10
  49. package/dist/hooks/pre-tool-use.js +199 -10
  50. package/dist/hooks/prompt-ingest-worker.js +179 -14
  51. package/dist/hooks/prompt-submit.js +781 -285
  52. package/dist/hooks/response-ingest-worker.js +1900 -1405
  53. package/dist/hooks/session-end.js +456 -12
  54. package/dist/hooks/session-start.js +2188 -1724
  55. package/dist/hooks/stop.js +200 -10
  56. package/dist/hooks/subagent-stop.js +199 -10
  57. package/dist/hooks/summary-worker.js +604 -334
  58. package/dist/index.js +554 -61
  59. package/dist/lib/cloud-sync.js +5 -0
  60. package/dist/lib/config.js +13 -0
  61. package/dist/lib/consolidation.js +5 -0
  62. package/dist/lib/database.js +104 -0
  63. package/dist/lib/device-registry.js +109 -0
  64. package/dist/lib/embedder.js +13 -0
  65. package/dist/lib/employee-templates.js +53 -26
  66. package/dist/lib/employees.js +5 -0
  67. package/dist/lib/exe-daemon-client.js +5 -0
  68. package/dist/lib/exe-daemon.js +493 -79
  69. package/dist/lib/file-grep.js +20 -4
  70. package/dist/lib/hybrid-search.js +1435 -190
  71. package/dist/lib/identity-templates.js +126 -5
  72. package/dist/lib/identity.js +5 -0
  73. package/dist/lib/license.js +5 -0
  74. package/dist/lib/messaging.js +37 -24
  75. package/dist/lib/schedules.js +130 -1
  76. package/dist/lib/skill-learning.js +11 -0
  77. package/dist/lib/status-brief.js +5 -0
  78. package/dist/lib/store.js +199 -10
  79. package/dist/lib/task-router.js +72 -6
  80. package/dist/lib/tasks.js +179 -50
  81. package/dist/lib/tmux-routing.js +179 -46
  82. package/dist/mcp/server.js +2129 -1855
  83. package/dist/mcp/tools/create-task.js +86 -36
  84. package/dist/mcp/tools/deactivate-behavior.js +5 -0
  85. package/dist/mcp/tools/list-tasks.js +39 -11
  86. package/dist/mcp/tools/send-message.js +37 -24
  87. package/dist/mcp/tools/update-task.js +153 -38
  88. package/dist/runtime/index.js +451 -59
  89. package/dist/tui/App.js +454 -59
  90. package/package.json +1 -1
@@ -81,6 +81,11 @@ var DEFAULT_CONFIG = {
81
81
  idleKillTicksRequired: 3,
82
82
  idleKillIntercomAckWindowMs: 1e4,
83
83
  maxAutoInstances: 10
84
+ },
85
+ autoUpdate: {
86
+ checkOnBoot: true,
87
+ autoInstall: false,
88
+ checkIntervalMs: 24 * 60 * 60 * 1e3
84
89
  }
85
90
  };
86
91
  function migrateLegacyConfig(raw) {
@@ -138,6 +143,11 @@ function normalizeSessionLifecycle(raw) {
138
143
  const userSL = raw.sessionLifecycle ?? {};
139
144
  raw.sessionLifecycle = { ...defaultSL, ...userSL };
140
145
  }
146
+ function normalizeAutoUpdate(raw) {
147
+ const defaultAU = DEFAULT_CONFIG.autoUpdate;
148
+ const userAU = raw.autoUpdate ?? {};
149
+ raw.autoUpdate = { ...defaultAU, ...userAU };
150
+ }
141
151
  function loadConfigSync() {
142
152
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
143
153
  const configPath = path.join(dir, "config.json");
@@ -151,6 +161,7 @@ function loadConfigSync() {
151
161
  const { config: migratedCfg } = migrateConfig(parsed);
152
162
  normalizeScalingRoadmap(migratedCfg);
153
163
  normalizeSessionLifecycle(migratedCfg);
164
+ normalizeAutoUpdate(migratedCfg);
154
165
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
155
166
  } catch {
156
167
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -80,6 +80,11 @@ function normalizeSessionLifecycle(raw) {
80
80
  const userSL = raw.sessionLifecycle ?? {};
81
81
  raw.sessionLifecycle = { ...defaultSL, ...userSL };
82
82
  }
83
+ function normalizeAutoUpdate(raw) {
84
+ const defaultAU = DEFAULT_CONFIG.autoUpdate;
85
+ const userAU = raw.autoUpdate ?? {};
86
+ raw.autoUpdate = { ...defaultAU, ...userAU };
87
+ }
83
88
  async function loadConfig() {
84
89
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
85
90
  await mkdir(dir, { recursive: true });
@@ -102,6 +107,7 @@ async function loadConfig() {
102
107
  }
103
108
  normalizeScalingRoadmap(migratedCfg);
104
109
  normalizeSessionLifecycle(migratedCfg);
110
+ normalizeAutoUpdate(migratedCfg);
105
111
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
106
112
  if (config.dbPath.startsWith("~")) {
107
113
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -177,6 +183,11 @@ var init_config = __esm({
177
183
  idleKillTicksRequired: 3,
178
184
  idleKillIntercomAckWindowMs: 1e4,
179
185
  maxAutoInstances: 10
186
+ },
187
+ autoUpdate: {
188
+ checkOnBoot: true,
189
+ autoInstall: false,
190
+ checkIntervalMs: 24 * 60 * 60 * 1e3
180
191
  }
181
192
  };
182
193
  CONFIG_MIGRATIONS = [
@@ -413,6 +424,27 @@ async function ensureSchema() {
413
424
  });
414
425
  } catch {
415
426
  }
427
+ try {
428
+ await client.execute({
429
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
430
+ args: []
431
+ });
432
+ } catch {
433
+ }
434
+ try {
435
+ await client.execute({
436
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
437
+ args: []
438
+ });
439
+ } catch {
440
+ }
441
+ try {
442
+ await client.execute({
443
+ sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
444
+ args: []
445
+ });
446
+ } catch {
447
+ }
416
448
  try {
417
449
  await client.execute({
418
450
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -823,6 +855,15 @@ async function ensureSchema() {
823
855
  } catch {
824
856
  }
825
857
  }
858
+ for (const col of [
859
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
860
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
861
+ ]) {
862
+ try {
863
+ await client.execute(col);
864
+ } catch {
865
+ }
866
+ }
826
867
  await client.executeMultiple(`
827
868
  CREATE INDEX IF NOT EXISTS idx_memories_workspace
828
869
  ON memories(workspace_id);
@@ -887,6 +928,34 @@ async function ensureSchema() {
887
928
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
888
929
  ON conversations(channel_id);
889
930
  `);
931
+ try {
932
+ await client.execute({
933
+ sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
934
+ args: []
935
+ });
936
+ } catch {
937
+ }
938
+ try {
939
+ await client.execute({
940
+ sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
941
+ args: []
942
+ });
943
+ } catch {
944
+ }
945
+ try {
946
+ await client.execute({
947
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
948
+ args: []
949
+ });
950
+ } catch {
951
+ }
952
+ try {
953
+ await client.execute({
954
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
955
+ args: []
956
+ });
957
+ } catch {
958
+ }
890
959
  await client.executeMultiple(`
891
960
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
892
961
  content_text,
@@ -913,6 +982,52 @@ async function ensureSchema() {
913
982
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
914
983
  END;
915
984
  `);
985
+ try {
986
+ await client.execute({
987
+ sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
988
+ args: []
989
+ });
990
+ } catch {
991
+ }
992
+ try {
993
+ await client.execute(
994
+ `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
995
+ );
996
+ } catch {
997
+ }
998
+ try {
999
+ await client.execute({
1000
+ sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
1001
+ args: []
1002
+ });
1003
+ await client.execute({
1004
+ sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
1005
+ args: []
1006
+ });
1007
+ } catch {
1008
+ }
1009
+ try {
1010
+ await client.execute({
1011
+ sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
1012
+ args: []
1013
+ });
1014
+ } catch {
1015
+ }
1016
+ try {
1017
+ await client.execute(
1018
+ `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
1019
+ );
1020
+ } catch {
1021
+ }
1022
+ for (const col of [
1023
+ "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
1024
+ "ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
1025
+ ]) {
1026
+ try {
1027
+ await client.execute(col);
1028
+ } catch {
1029
+ }
1030
+ }
916
1031
  }
917
1032
  async function disposeDatabase() {
918
1033
  if (_client) {
@@ -1106,13 +1221,27 @@ async function ensureShardSchema(client) {
1106
1221
  "ALTER TABLE memories ADD COLUMN document_id TEXT",
1107
1222
  "ALTER TABLE memories ADD COLUMN user_id TEXT",
1108
1223
  "ALTER TABLE memories ADD COLUMN char_offset INTEGER",
1109
- "ALTER TABLE memories ADD COLUMN page_number INTEGER"
1224
+ "ALTER TABLE memories ADD COLUMN page_number INTEGER",
1225
+ // Source provenance columns (must match database.ts)
1226
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
1227
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
1228
+ "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
1229
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
1110
1230
  ]) {
1111
1231
  try {
1112
1232
  await client.execute(col);
1113
1233
  } catch {
1114
1234
  }
1115
1235
  }
1236
+ for (const idx of [
1237
+ "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
1238
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
1239
+ ]) {
1240
+ try {
1241
+ await client.execute(idx);
1242
+ } catch {
1243
+ }
1244
+ }
1116
1245
  try {
1117
1246
  await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
1118
1247
  } catch {
@@ -1221,8 +1350,11 @@ var store_exports = {};
1221
1350
  __export(store_exports, {
1222
1351
  attachDocumentMetadata: () => attachDocumentMetadata,
1223
1352
  buildWikiScopeFilter: () => buildWikiScopeFilter,
1353
+ classifyTier: () => classifyTier,
1224
1354
  disposeStore: () => disposeStore,
1225
1355
  flushBatch: () => flushBatch,
1356
+ flushTier3: () => flushTier3,
1357
+ getMemoryCardinality: () => getMemoryCardinality,
1226
1358
  initStore: () => initStore,
1227
1359
  reserveVersions: () => reserveVersions,
1228
1360
  searchMemories: () => searchMemories,
@@ -1268,6 +1400,11 @@ async function initStore(options) {
1268
1400
  const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1269
1401
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1270
1402
  }
1403
+ function classifyTier(record) {
1404
+ if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
1405
+ if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
1406
+ return 3;
1407
+ }
1271
1408
  async function writeMemory(record) {
1272
1409
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
1273
1410
  throw new Error(
@@ -1295,7 +1432,11 @@ async function writeMemory(record) {
1295
1432
  document_id: record.document_id ?? null,
1296
1433
  user_id: record.user_id ?? null,
1297
1434
  char_offset: record.char_offset ?? null,
1298
- page_number: record.page_number ?? null
1435
+ page_number: record.page_number ?? null,
1436
+ source_path: record.source_path ?? null,
1437
+ source_type: record.source_type ?? null,
1438
+ tier: record.tier ?? classifyTier(record),
1439
+ supersedes_id: record.supersedes_id ?? null
1299
1440
  };
1300
1441
  _pendingRecords.push(dbRow);
1301
1442
  if (_flushTimer === null) {
@@ -1327,20 +1468,26 @@ async function flushBatch() {
1327
1468
  const userId = row.user_id ?? null;
1328
1469
  const charOffset = row.char_offset ?? null;
1329
1470
  const pageNumber = row.page_number ?? null;
1471
+ const sourcePath = row.source_path ?? null;
1472
+ const sourceType = row.source_type ?? null;
1473
+ const tier = row.tier ?? 3;
1474
+ const supersedesId = row.supersedes_id ?? null;
1330
1475
  return {
1331
1476
  sql: hasVector ? `INSERT OR IGNORE INTO memories
1332
1477
  (id, agent_id, agent_role, session_id, timestamp,
1333
1478
  tool_name, project_name,
1334
1479
  has_error, raw_text, vector, version, task_id, importance, status,
1335
1480
  confidence, last_accessed,
1336
- workspace_id, document_id, user_id, char_offset, page_number)
1337
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1481
+ workspace_id, document_id, user_id, char_offset, page_number,
1482
+ source_path, source_type, tier, supersedes_id)
1483
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1338
1484
  (id, agent_id, agent_role, session_id, timestamp,
1339
1485
  tool_name, project_name,
1340
1486
  has_error, raw_text, vector, version, task_id, importance, status,
1341
1487
  confidence, last_accessed,
1342
- workspace_id, document_id, user_id, char_offset, page_number)
1343
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1488
+ workspace_id, document_id, user_id, char_offset, page_number,
1489
+ source_path, source_type, tier, supersedes_id)
1490
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1344
1491
  args: hasVector ? [
1345
1492
  row.id,
1346
1493
  row.agent_id,
@@ -1362,7 +1509,11 @@ async function flushBatch() {
1362
1509
  documentId,
1363
1510
  userId,
1364
1511
  charOffset,
1365
- pageNumber
1512
+ pageNumber,
1513
+ sourcePath,
1514
+ sourceType,
1515
+ tier,
1516
+ supersedesId
1366
1517
  ] : [
1367
1518
  row.id,
1368
1519
  row.agent_id,
@@ -1383,7 +1534,11 @@ async function flushBatch() {
1383
1534
  documentId,
1384
1535
  userId,
1385
1536
  charOffset,
1386
- pageNumber
1537
+ pageNumber,
1538
+ sourcePath,
1539
+ sourceType,
1540
+ tier,
1541
+ supersedesId
1387
1542
  ]
1388
1543
  };
1389
1544
  };
@@ -1457,7 +1612,8 @@ async function searchMemories(queryVector, agentId, options) {
1457
1612
  has_error, raw_text, vector, importance, status,
1458
1613
  confidence, last_accessed,
1459
1614
  workspace_id, document_id, user_id,
1460
- char_offset, page_number
1615
+ char_offset, page_number,
1616
+ source_path, source_type
1461
1617
  FROM memories
1462
1618
  WHERE agent_id = ?
1463
1619
  AND vector IS NOT NULL${statusFilter}
@@ -1506,7 +1662,9 @@ async function searchMemories(queryVector, agentId, options) {
1506
1662
  document_id: row.document_id ?? null,
1507
1663
  user_id: row.user_id ?? null,
1508
1664
  char_offset: row.char_offset ?? null,
1509
- page_number: row.page_number ?? null
1665
+ page_number: row.page_number ?? null,
1666
+ source_path: row.source_path ?? null,
1667
+ source_type: row.source_type ?? null
1510
1668
  }));
1511
1669
  }
1512
1670
  async function attachDocumentMetadata(records) {
@@ -1544,6 +1702,25 @@ async function attachDocumentMetadata(records) {
1544
1702
  }
1545
1703
  return records;
1546
1704
  }
1705
+ async function flushTier3(agentId, options) {
1706
+ const client = getClient();
1707
+ const maxAge = options?.maxAgeHours ?? 72;
1708
+ const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
1709
+ if (options?.dryRun) {
1710
+ const result2 = await client.execute({
1711
+ sql: `SELECT COUNT(*) as cnt FROM memories
1712
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
1713
+ args: [agentId, cutoff]
1714
+ });
1715
+ return { archived: Number(result2.rows[0]?.cnt ?? 0) };
1716
+ }
1717
+ const result = await client.execute({
1718
+ sql: `UPDATE memories SET status = 'archived'
1719
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
1720
+ args: [agentId, cutoff]
1721
+ });
1722
+ return { archived: result.rowsAffected };
1723
+ }
1547
1724
  async function disposeStore() {
1548
1725
  if (_flushTimer !== null) {
1549
1726
  clearInterval(_flushTimer);
@@ -1574,6 +1751,18 @@ function reserveVersions(count) {
1574
1751
  }
1575
1752
  return reserved;
1576
1753
  }
1754
+ async function getMemoryCardinality(agentId) {
1755
+ try {
1756
+ const client = getClient();
1757
+ const result = await client.execute({
1758
+ sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
1759
+ args: [agentId]
1760
+ });
1761
+ return Number(result.rows[0]?.cnt) || 0;
1762
+ } catch {
1763
+ return 0;
1764
+ }
1765
+ }
1577
1766
  var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1578
1767
  var init_store = __esm({
1579
1768
  "src/lib/store.ts"() {