@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
@@ -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());
@@ -124,6 +130,7 @@ function loadConfigSync() {
124
130
  const { config: migratedCfg } = migrateConfig(parsed);
125
131
  normalizeScalingRoadmap(migratedCfg);
126
132
  normalizeSessionLifecycle(migratedCfg);
133
+ normalizeAutoUpdate(migratedCfg);
127
134
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
128
135
  } catch {
129
136
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -195,6 +202,11 @@ var init_config = __esm({
195
202
  idleKillTicksRequired: 3,
196
203
  idleKillIntercomAckWindowMs: 1e4,
197
204
  maxAutoInstances: 10
205
+ },
206
+ autoUpdate: {
207
+ checkOnBoot: true,
208
+ autoInstall: false,
209
+ checkIntervalMs: 24 * 60 * 60 * 1e3
198
210
  }
199
211
  };
200
212
  CONFIG_MIGRATIONS = [
@@ -440,6 +452,27 @@ async function ensureSchema() {
440
452
  });
441
453
  } catch {
442
454
  }
455
+ try {
456
+ await client.execute({
457
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
458
+ args: []
459
+ });
460
+ } catch {
461
+ }
462
+ try {
463
+ await client.execute({
464
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
465
+ args: []
466
+ });
467
+ } catch {
468
+ }
469
+ try {
470
+ await client.execute({
471
+ sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
472
+ args: []
473
+ });
474
+ } catch {
475
+ }
443
476
  try {
444
477
  await client.execute({
445
478
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -850,6 +883,15 @@ async function ensureSchema() {
850
883
  } catch {
851
884
  }
852
885
  }
886
+ for (const col of [
887
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
888
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
889
+ ]) {
890
+ try {
891
+ await client.execute(col);
892
+ } catch {
893
+ }
894
+ }
853
895
  await client.executeMultiple(`
854
896
  CREATE INDEX IF NOT EXISTS idx_memories_workspace
855
897
  ON memories(workspace_id);
@@ -914,6 +956,34 @@ async function ensureSchema() {
914
956
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
915
957
  ON conversations(channel_id);
916
958
  `);
959
+ try {
960
+ await client.execute({
961
+ sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
962
+ args: []
963
+ });
964
+ } catch {
965
+ }
966
+ try {
967
+ await client.execute({
968
+ sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
969
+ args: []
970
+ });
971
+ } catch {
972
+ }
973
+ try {
974
+ await client.execute({
975
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
976
+ args: []
977
+ });
978
+ } catch {
979
+ }
980
+ try {
981
+ await client.execute({
982
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
983
+ args: []
984
+ });
985
+ } catch {
986
+ }
917
987
  await client.executeMultiple(`
918
988
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
919
989
  content_text,
@@ -940,6 +1010,52 @@ async function ensureSchema() {
940
1010
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
941
1011
  END;
942
1012
  `);
1013
+ try {
1014
+ await client.execute({
1015
+ sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
1016
+ args: []
1017
+ });
1018
+ } catch {
1019
+ }
1020
+ try {
1021
+ await client.execute(
1022
+ `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
1023
+ );
1024
+ } catch {
1025
+ }
1026
+ try {
1027
+ await client.execute({
1028
+ sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
1029
+ args: []
1030
+ });
1031
+ await client.execute({
1032
+ sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
1033
+ args: []
1034
+ });
1035
+ } catch {
1036
+ }
1037
+ try {
1038
+ await client.execute({
1039
+ sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
1040
+ args: []
1041
+ });
1042
+ } catch {
1043
+ }
1044
+ try {
1045
+ await client.execute(
1046
+ `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
1047
+ );
1048
+ } catch {
1049
+ }
1050
+ for (const col of [
1051
+ "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
1052
+ "ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
1053
+ ]) {
1054
+ try {
1055
+ await client.execute(col);
1056
+ } catch {
1057
+ }
1058
+ }
943
1059
  }
944
1060
  async function disposeDatabase() {
945
1061
  if (_client) {
@@ -1124,13 +1240,27 @@ async function ensureShardSchema(client) {
1124
1240
  "ALTER TABLE memories ADD COLUMN document_id TEXT",
1125
1241
  "ALTER TABLE memories ADD COLUMN user_id TEXT",
1126
1242
  "ALTER TABLE memories ADD COLUMN char_offset INTEGER",
1127
- "ALTER TABLE memories ADD COLUMN page_number INTEGER"
1243
+ "ALTER TABLE memories ADD COLUMN page_number INTEGER",
1244
+ // Source provenance columns (must match database.ts)
1245
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
1246
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
1247
+ "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
1248
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
1128
1249
  ]) {
1129
1250
  try {
1130
1251
  await client.execute(col);
1131
1252
  } catch {
1132
1253
  }
1133
1254
  }
1255
+ for (const idx of [
1256
+ "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
1257
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
1258
+ ]) {
1259
+ try {
1260
+ await client.execute(idx);
1261
+ } catch {
1262
+ }
1263
+ }
1134
1264
  try {
1135
1265
  await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
1136
1266
  } catch {
@@ -1239,8 +1369,11 @@ var store_exports = {};
1239
1369
  __export(store_exports, {
1240
1370
  attachDocumentMetadata: () => attachDocumentMetadata,
1241
1371
  buildWikiScopeFilter: () => buildWikiScopeFilter,
1372
+ classifyTier: () => classifyTier,
1242
1373
  disposeStore: () => disposeStore,
1243
1374
  flushBatch: () => flushBatch,
1375
+ flushTier3: () => flushTier3,
1376
+ getMemoryCardinality: () => getMemoryCardinality,
1244
1377
  initStore: () => initStore,
1245
1378
  reserveVersions: () => reserveVersions,
1246
1379
  searchMemories: () => searchMemories,
@@ -1286,6 +1419,11 @@ async function initStore(options) {
1286
1419
  const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1287
1420
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1288
1421
  }
1422
+ function classifyTier(record) {
1423
+ if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
1424
+ if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
1425
+ return 3;
1426
+ }
1289
1427
  async function writeMemory(record) {
1290
1428
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
1291
1429
  throw new Error(
@@ -1313,7 +1451,11 @@ async function writeMemory(record) {
1313
1451
  document_id: record.document_id ?? null,
1314
1452
  user_id: record.user_id ?? null,
1315
1453
  char_offset: record.char_offset ?? null,
1316
- page_number: record.page_number ?? null
1454
+ page_number: record.page_number ?? null,
1455
+ source_path: record.source_path ?? null,
1456
+ source_type: record.source_type ?? null,
1457
+ tier: record.tier ?? classifyTier(record),
1458
+ supersedes_id: record.supersedes_id ?? null
1317
1459
  };
1318
1460
  _pendingRecords.push(dbRow);
1319
1461
  if (_flushTimer === null) {
@@ -1345,20 +1487,26 @@ async function flushBatch() {
1345
1487
  const userId = row.user_id ?? null;
1346
1488
  const charOffset = row.char_offset ?? null;
1347
1489
  const pageNumber = row.page_number ?? null;
1490
+ const sourcePath = row.source_path ?? null;
1491
+ const sourceType = row.source_type ?? null;
1492
+ const tier = row.tier ?? 3;
1493
+ const supersedesId = row.supersedes_id ?? null;
1348
1494
  return {
1349
1495
  sql: hasVector ? `INSERT OR IGNORE INTO memories
1350
1496
  (id, agent_id, agent_role, session_id, timestamp,
1351
1497
  tool_name, project_name,
1352
1498
  has_error, raw_text, vector, version, task_id, importance, status,
1353
1499
  confidence, last_accessed,
1354
- workspace_id, document_id, user_id, char_offset, page_number)
1355
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1500
+ workspace_id, document_id, user_id, char_offset, page_number,
1501
+ source_path, source_type, tier, supersedes_id)
1502
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1356
1503
  (id, agent_id, agent_role, session_id, timestamp,
1357
1504
  tool_name, project_name,
1358
1505
  has_error, raw_text, vector, version, task_id, importance, status,
1359
1506
  confidence, last_accessed,
1360
- workspace_id, document_id, user_id, char_offset, page_number)
1361
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1507
+ workspace_id, document_id, user_id, char_offset, page_number,
1508
+ source_path, source_type, tier, supersedes_id)
1509
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1362
1510
  args: hasVector ? [
1363
1511
  row.id,
1364
1512
  row.agent_id,
@@ -1380,7 +1528,11 @@ async function flushBatch() {
1380
1528
  documentId,
1381
1529
  userId,
1382
1530
  charOffset,
1383
- pageNumber
1531
+ pageNumber,
1532
+ sourcePath,
1533
+ sourceType,
1534
+ tier,
1535
+ supersedesId
1384
1536
  ] : [
1385
1537
  row.id,
1386
1538
  row.agent_id,
@@ -1401,7 +1553,11 @@ async function flushBatch() {
1401
1553
  documentId,
1402
1554
  userId,
1403
1555
  charOffset,
1404
- pageNumber
1556
+ pageNumber,
1557
+ sourcePath,
1558
+ sourceType,
1559
+ tier,
1560
+ supersedesId
1405
1561
  ]
1406
1562
  };
1407
1563
  };
@@ -1475,7 +1631,8 @@ async function searchMemories(queryVector, agentId, options) {
1475
1631
  has_error, raw_text, vector, importance, status,
1476
1632
  confidence, last_accessed,
1477
1633
  workspace_id, document_id, user_id,
1478
- char_offset, page_number
1634
+ char_offset, page_number,
1635
+ source_path, source_type
1479
1636
  FROM memories
1480
1637
  WHERE agent_id = ?
1481
1638
  AND vector IS NOT NULL${statusFilter}
@@ -1524,7 +1681,9 @@ async function searchMemories(queryVector, agentId, options) {
1524
1681
  document_id: row.document_id ?? null,
1525
1682
  user_id: row.user_id ?? null,
1526
1683
  char_offset: row.char_offset ?? null,
1527
- page_number: row.page_number ?? null
1684
+ page_number: row.page_number ?? null,
1685
+ source_path: row.source_path ?? null,
1686
+ source_type: row.source_type ?? null
1528
1687
  }));
1529
1688
  }
1530
1689
  async function attachDocumentMetadata(records) {
@@ -1562,6 +1721,25 @@ async function attachDocumentMetadata(records) {
1562
1721
  }
1563
1722
  return records;
1564
1723
  }
1724
+ async function flushTier3(agentId, options) {
1725
+ const client = getClient();
1726
+ const maxAge = options?.maxAgeHours ?? 72;
1727
+ const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
1728
+ if (options?.dryRun) {
1729
+ const result2 = await client.execute({
1730
+ sql: `SELECT COUNT(*) as cnt FROM memories
1731
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
1732
+ args: [agentId, cutoff]
1733
+ });
1734
+ return { archived: Number(result2.rows[0]?.cnt ?? 0) };
1735
+ }
1736
+ const result = await client.execute({
1737
+ sql: `UPDATE memories SET status = 'archived'
1738
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
1739
+ args: [agentId, cutoff]
1740
+ });
1741
+ return { archived: result.rowsAffected };
1742
+ }
1565
1743
  async function disposeStore() {
1566
1744
  if (_flushTimer !== null) {
1567
1745
  clearInterval(_flushTimer);
@@ -1592,6 +1770,18 @@ function reserveVersions(count) {
1592
1770
  }
1593
1771
  return reserved;
1594
1772
  }
1773
+ async function getMemoryCardinality(agentId) {
1774
+ try {
1775
+ const client = getClient();
1776
+ const result = await client.execute({
1777
+ sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
1778
+ args: [agentId]
1779
+ });
1780
+ return Number(result.rows[0]?.cnt) || 0;
1781
+ } catch {
1782
+ return 0;
1783
+ }
1784
+ }
1595
1785
  var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1596
1786
  var init_store = __esm({
1597
1787
  "src/lib/store.ts"() {
@@ -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 = [
@@ -422,6 +433,27 @@ async function ensureSchema() {
422
433
  });
423
434
  } catch {
424
435
  }
436
+ try {
437
+ await client.execute({
438
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
439
+ args: []
440
+ });
441
+ } catch {
442
+ }
443
+ try {
444
+ await client.execute({
445
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
446
+ args: []
447
+ });
448
+ } catch {
449
+ }
450
+ try {
451
+ await client.execute({
452
+ sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
453
+ args: []
454
+ });
455
+ } catch {
456
+ }
425
457
  try {
426
458
  await client.execute({
427
459
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -832,6 +864,15 @@ async function ensureSchema() {
832
864
  } catch {
833
865
  }
834
866
  }
867
+ for (const col of [
868
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
869
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
870
+ ]) {
871
+ try {
872
+ await client.execute(col);
873
+ } catch {
874
+ }
875
+ }
835
876
  await client.executeMultiple(`
836
877
  CREATE INDEX IF NOT EXISTS idx_memories_workspace
837
878
  ON memories(workspace_id);
@@ -896,6 +937,34 @@ async function ensureSchema() {
896
937
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
897
938
  ON conversations(channel_id);
898
939
  `);
940
+ try {
941
+ await client.execute({
942
+ sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
943
+ args: []
944
+ });
945
+ } catch {
946
+ }
947
+ try {
948
+ await client.execute({
949
+ sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
950
+ args: []
951
+ });
952
+ } catch {
953
+ }
954
+ try {
955
+ await client.execute({
956
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
957
+ args: []
958
+ });
959
+ } catch {
960
+ }
961
+ try {
962
+ await client.execute({
963
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
964
+ args: []
965
+ });
966
+ } catch {
967
+ }
899
968
  await client.executeMultiple(`
900
969
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
901
970
  content_text,
@@ -922,6 +991,52 @@ async function ensureSchema() {
922
991
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
923
992
  END;
924
993
  `);
994
+ try {
995
+ await client.execute({
996
+ sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
997
+ args: []
998
+ });
999
+ } catch {
1000
+ }
1001
+ try {
1002
+ await client.execute(
1003
+ `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
1004
+ );
1005
+ } catch {
1006
+ }
1007
+ try {
1008
+ await client.execute({
1009
+ sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
1010
+ args: []
1011
+ });
1012
+ await client.execute({
1013
+ sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
1014
+ args: []
1015
+ });
1016
+ } catch {
1017
+ }
1018
+ try {
1019
+ await client.execute({
1020
+ sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
1021
+ args: []
1022
+ });
1023
+ } catch {
1024
+ }
1025
+ try {
1026
+ await client.execute(
1027
+ `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
1028
+ );
1029
+ } catch {
1030
+ }
1031
+ for (const col of [
1032
+ "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
1033
+ "ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
1034
+ ]) {
1035
+ try {
1036
+ await client.execute(col);
1037
+ } catch {
1038
+ }
1039
+ }
925
1040
  }
926
1041
  async function disposeDatabase() {
927
1042
  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"() {