@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());
@@ -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"() {
@@ -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"() {