@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.
- package/README.md +178 -79
- package/dist/bin/backfill-responses.js +160 -8
- package/dist/bin/backfill-vectors.js +130 -1
- package/dist/bin/cleanup-stale-review-tasks.js +130 -1
- package/dist/bin/cli.js +10111 -7540
- package/dist/bin/exe-agent.js +159 -1
- package/dist/bin/exe-assign.js +235 -16
- package/dist/bin/exe-boot.js +344 -472
- package/dist/bin/exe-call.js +145 -1
- package/dist/bin/exe-cloud.js +11 -0
- package/dist/bin/exe-dispatch.js +37 -24
- package/dist/bin/exe-doctor.js +130 -1
- package/dist/bin/exe-export-behaviors.js +150 -7
- package/dist/bin/exe-forget.js +822 -665
- package/dist/bin/exe-gateway.js +470 -62
- package/dist/bin/exe-heartbeat.js +133 -2
- package/dist/bin/exe-kill.js +150 -7
- package/dist/bin/exe-launch-agent.js +150 -7
- package/dist/bin/exe-new-employee.js +756 -224
- package/dist/bin/exe-pending-messages.js +132 -2
- package/dist/bin/exe-pending-notifications.js +130 -1
- package/dist/bin/exe-pending-reviews.js +132 -2
- package/dist/bin/exe-review.js +160 -8
- package/dist/bin/exe-search.js +2473 -2008
- package/dist/bin/exe-session-cleanup.js +238 -51
- package/dist/bin/exe-settings.js +11 -0
- package/dist/bin/exe-status.js +130 -1
- package/dist/bin/exe-team.js +130 -1
- package/dist/bin/git-sweep.js +272 -16
- package/dist/bin/graph-backfill.js +150 -7
- package/dist/bin/graph-export.js +150 -7
- package/dist/bin/install.js +5 -0
- package/dist/bin/scan-tasks.js +238 -19
- package/dist/bin/setup.js +1776 -10
- package/dist/bin/shard-migrate.js +150 -7
- package/dist/bin/update.js +9 -6
- package/dist/bin/wiki-sync.js +150 -7
- package/dist/gateway/index.js +470 -62
- package/dist/hooks/bug-report-worker.js +195 -35
- package/dist/hooks/commit-complete.js +272 -16
- package/dist/hooks/error-recall.js +2313 -1847
- package/dist/hooks/exe-heartbeat-hook.js +5 -0
- package/dist/hooks/ingest-worker.js +330 -58
- package/dist/hooks/ingest.js +11 -0
- package/dist/hooks/instructions-loaded.js +199 -10
- package/dist/hooks/notification.js +199 -10
- package/dist/hooks/post-compact.js +199 -10
- package/dist/hooks/pre-compact.js +199 -10
- package/dist/hooks/pre-tool-use.js +199 -10
- package/dist/hooks/prompt-ingest-worker.js +179 -14
- package/dist/hooks/prompt-submit.js +781 -285
- package/dist/hooks/response-ingest-worker.js +1900 -1405
- package/dist/hooks/session-end.js +456 -12
- package/dist/hooks/session-start.js +2188 -1724
- package/dist/hooks/stop.js +200 -10
- package/dist/hooks/subagent-stop.js +199 -10
- package/dist/hooks/summary-worker.js +604 -334
- package/dist/index.js +554 -61
- package/dist/lib/cloud-sync.js +5 -0
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +5 -0
- package/dist/lib/database.js +104 -0
- package/dist/lib/device-registry.js +109 -0
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employee-templates.js +53 -26
- package/dist/lib/employees.js +5 -0
- package/dist/lib/exe-daemon-client.js +5 -0
- package/dist/lib/exe-daemon.js +493 -79
- package/dist/lib/file-grep.js +20 -4
- package/dist/lib/hybrid-search.js +1435 -190
- package/dist/lib/identity-templates.js +126 -5
- package/dist/lib/identity.js +5 -0
- package/dist/lib/license.js +5 -0
- package/dist/lib/messaging.js +37 -24
- package/dist/lib/schedules.js +130 -1
- package/dist/lib/skill-learning.js +11 -0
- package/dist/lib/status-brief.js +5 -0
- package/dist/lib/store.js +199 -10
- package/dist/lib/task-router.js +72 -6
- package/dist/lib/tasks.js +179 -50
- package/dist/lib/tmux-routing.js +179 -46
- package/dist/mcp/server.js +2129 -1855
- package/dist/mcp/tools/create-task.js +86 -36
- package/dist/mcp/tools/deactivate-behavior.js +5 -0
- package/dist/mcp/tools/list-tasks.js +39 -11
- package/dist/mcp/tools/send-message.js +37 -24
- package/dist/mcp/tools/update-task.js +153 -38
- package/dist/runtime/index.js +451 -59
- package/dist/tui/App.js +454 -59
- package/package.json +1 -1
package/dist/lib/store.js
CHANGED
|
@@ -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 mkdir2(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: path2.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 = [
|
|
@@ -310,13 +321,27 @@ async function ensureShardSchema(client) {
|
|
|
310
321
|
"ALTER TABLE memories ADD COLUMN document_id TEXT",
|
|
311
322
|
"ALTER TABLE memories ADD COLUMN user_id TEXT",
|
|
312
323
|
"ALTER TABLE memories ADD COLUMN char_offset INTEGER",
|
|
313
|
-
"ALTER TABLE memories ADD COLUMN page_number INTEGER"
|
|
324
|
+
"ALTER TABLE memories ADD COLUMN page_number INTEGER",
|
|
325
|
+
// Source provenance columns (must match database.ts)
|
|
326
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
327
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
|
|
328
|
+
"ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
|
|
329
|
+
"ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
|
|
314
330
|
]) {
|
|
315
331
|
try {
|
|
316
332
|
await client.execute(col);
|
|
317
333
|
} catch {
|
|
318
334
|
}
|
|
319
335
|
}
|
|
336
|
+
for (const idx of [
|
|
337
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
338
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
339
|
+
]) {
|
|
340
|
+
try {
|
|
341
|
+
await client.execute(idx);
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
320
345
|
try {
|
|
321
346
|
await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
|
|
322
347
|
} catch {
|
|
@@ -633,6 +658,27 @@ async function ensureSchema() {
|
|
|
633
658
|
});
|
|
634
659
|
} catch {
|
|
635
660
|
}
|
|
661
|
+
try {
|
|
662
|
+
await client.execute({
|
|
663
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
|
|
664
|
+
args: []
|
|
665
|
+
});
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
await client.execute({
|
|
670
|
+
sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
|
|
671
|
+
args: []
|
|
672
|
+
});
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
await client.execute({
|
|
677
|
+
sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
|
|
678
|
+
args: []
|
|
679
|
+
});
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
636
682
|
try {
|
|
637
683
|
await client.execute({
|
|
638
684
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -1043,6 +1089,15 @@ async function ensureSchema() {
|
|
|
1043
1089
|
} catch {
|
|
1044
1090
|
}
|
|
1045
1091
|
}
|
|
1092
|
+
for (const col of [
|
|
1093
|
+
"ALTER TABLE memories ADD COLUMN source_path TEXT",
|
|
1094
|
+
"ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
|
|
1095
|
+
]) {
|
|
1096
|
+
try {
|
|
1097
|
+
await client.execute(col);
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1046
1101
|
await client.executeMultiple(`
|
|
1047
1102
|
CREATE INDEX IF NOT EXISTS idx_memories_workspace
|
|
1048
1103
|
ON memories(workspace_id);
|
|
@@ -1107,6 +1162,34 @@ async function ensureSchema() {
|
|
|
1107
1162
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1108
1163
|
ON conversations(channel_id);
|
|
1109
1164
|
`);
|
|
1165
|
+
try {
|
|
1166
|
+
await client.execute({
|
|
1167
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
1168
|
+
args: []
|
|
1169
|
+
});
|
|
1170
|
+
} catch {
|
|
1171
|
+
}
|
|
1172
|
+
try {
|
|
1173
|
+
await client.execute({
|
|
1174
|
+
sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
|
|
1175
|
+
args: []
|
|
1176
|
+
});
|
|
1177
|
+
} catch {
|
|
1178
|
+
}
|
|
1179
|
+
try {
|
|
1180
|
+
await client.execute({
|
|
1181
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
|
|
1182
|
+
args: []
|
|
1183
|
+
});
|
|
1184
|
+
} catch {
|
|
1185
|
+
}
|
|
1186
|
+
try {
|
|
1187
|
+
await client.execute({
|
|
1188
|
+
sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
|
|
1189
|
+
args: []
|
|
1190
|
+
});
|
|
1191
|
+
} catch {
|
|
1192
|
+
}
|
|
1110
1193
|
await client.executeMultiple(`
|
|
1111
1194
|
CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
|
|
1112
1195
|
content_text,
|
|
@@ -1133,6 +1216,52 @@ async function ensureSchema() {
|
|
|
1133
1216
|
VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
|
|
1134
1217
|
END;
|
|
1135
1218
|
`);
|
|
1219
|
+
try {
|
|
1220
|
+
await client.execute({
|
|
1221
|
+
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
1222
|
+
args: []
|
|
1223
|
+
});
|
|
1224
|
+
} catch {
|
|
1225
|
+
}
|
|
1226
|
+
try {
|
|
1227
|
+
await client.execute(
|
|
1228
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
|
|
1229
|
+
);
|
|
1230
|
+
} catch {
|
|
1231
|
+
}
|
|
1232
|
+
try {
|
|
1233
|
+
await client.execute({
|
|
1234
|
+
sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
|
|
1235
|
+
args: []
|
|
1236
|
+
});
|
|
1237
|
+
await client.execute({
|
|
1238
|
+
sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
|
|
1239
|
+
args: []
|
|
1240
|
+
});
|
|
1241
|
+
} catch {
|
|
1242
|
+
}
|
|
1243
|
+
try {
|
|
1244
|
+
await client.execute({
|
|
1245
|
+
sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
|
|
1246
|
+
args: []
|
|
1247
|
+
});
|
|
1248
|
+
} catch {
|
|
1249
|
+
}
|
|
1250
|
+
try {
|
|
1251
|
+
await client.execute(
|
|
1252
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
|
|
1253
|
+
);
|
|
1254
|
+
} catch {
|
|
1255
|
+
}
|
|
1256
|
+
for (const col of [
|
|
1257
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
|
|
1258
|
+
"ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
|
|
1259
|
+
]) {
|
|
1260
|
+
try {
|
|
1261
|
+
await client.execute(col);
|
|
1262
|
+
} catch {
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1136
1265
|
}
|
|
1137
1266
|
var disposeTurso = disposeDatabase;
|
|
1138
1267
|
async function disposeDatabase() {
|
|
@@ -1231,6 +1360,11 @@ async function initStore(options) {
|
|
|
1231
1360
|
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1232
1361
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1233
1362
|
}
|
|
1363
|
+
function classifyTier(record) {
|
|
1364
|
+
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
1365
|
+
if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
|
|
1366
|
+
return 3;
|
|
1367
|
+
}
|
|
1234
1368
|
async function writeMemory(record) {
|
|
1235
1369
|
if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
|
|
1236
1370
|
throw new Error(
|
|
@@ -1258,7 +1392,11 @@ async function writeMemory(record) {
|
|
|
1258
1392
|
document_id: record.document_id ?? null,
|
|
1259
1393
|
user_id: record.user_id ?? null,
|
|
1260
1394
|
char_offset: record.char_offset ?? null,
|
|
1261
|
-
page_number: record.page_number ?? null
|
|
1395
|
+
page_number: record.page_number ?? null,
|
|
1396
|
+
source_path: record.source_path ?? null,
|
|
1397
|
+
source_type: record.source_type ?? null,
|
|
1398
|
+
tier: record.tier ?? classifyTier(record),
|
|
1399
|
+
supersedes_id: record.supersedes_id ?? null
|
|
1262
1400
|
};
|
|
1263
1401
|
_pendingRecords.push(dbRow);
|
|
1264
1402
|
if (_flushTimer === null) {
|
|
@@ -1290,20 +1428,26 @@ async function flushBatch() {
|
|
|
1290
1428
|
const userId = row.user_id ?? null;
|
|
1291
1429
|
const charOffset = row.char_offset ?? null;
|
|
1292
1430
|
const pageNumber = row.page_number ?? null;
|
|
1431
|
+
const sourcePath = row.source_path ?? null;
|
|
1432
|
+
const sourceType = row.source_type ?? null;
|
|
1433
|
+
const tier = row.tier ?? 3;
|
|
1434
|
+
const supersedesId = row.supersedes_id ?? null;
|
|
1293
1435
|
return {
|
|
1294
1436
|
sql: hasVector ? `INSERT OR IGNORE INTO memories
|
|
1295
1437
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1296
1438
|
tool_name, project_name,
|
|
1297
1439
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1298
1440
|
confidence, last_accessed,
|
|
1299
|
-
workspace_id, document_id, user_id, char_offset, page_number
|
|
1300
|
-
|
|
1441
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1442
|
+
source_path, source_type, tier, supersedes_id)
|
|
1443
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
|
|
1301
1444
|
(id, agent_id, agent_role, session_id, timestamp,
|
|
1302
1445
|
tool_name, project_name,
|
|
1303
1446
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
1304
1447
|
confidence, last_accessed,
|
|
1305
|
-
workspace_id, document_id, user_id, char_offset, page_number
|
|
1306
|
-
|
|
1448
|
+
workspace_id, document_id, user_id, char_offset, page_number,
|
|
1449
|
+
source_path, source_type, tier, supersedes_id)
|
|
1450
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1307
1451
|
args: hasVector ? [
|
|
1308
1452
|
row.id,
|
|
1309
1453
|
row.agent_id,
|
|
@@ -1325,7 +1469,11 @@ async function flushBatch() {
|
|
|
1325
1469
|
documentId,
|
|
1326
1470
|
userId,
|
|
1327
1471
|
charOffset,
|
|
1328
|
-
pageNumber
|
|
1472
|
+
pageNumber,
|
|
1473
|
+
sourcePath,
|
|
1474
|
+
sourceType,
|
|
1475
|
+
tier,
|
|
1476
|
+
supersedesId
|
|
1329
1477
|
] : [
|
|
1330
1478
|
row.id,
|
|
1331
1479
|
row.agent_id,
|
|
@@ -1346,7 +1494,11 @@ async function flushBatch() {
|
|
|
1346
1494
|
documentId,
|
|
1347
1495
|
userId,
|
|
1348
1496
|
charOffset,
|
|
1349
|
-
pageNumber
|
|
1497
|
+
pageNumber,
|
|
1498
|
+
sourcePath,
|
|
1499
|
+
sourceType,
|
|
1500
|
+
tier,
|
|
1501
|
+
supersedesId
|
|
1350
1502
|
]
|
|
1351
1503
|
};
|
|
1352
1504
|
};
|
|
@@ -1420,7 +1572,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
1420
1572
|
has_error, raw_text, vector, importance, status,
|
|
1421
1573
|
confidence, last_accessed,
|
|
1422
1574
|
workspace_id, document_id, user_id,
|
|
1423
|
-
char_offset, page_number
|
|
1575
|
+
char_offset, page_number,
|
|
1576
|
+
source_path, source_type
|
|
1424
1577
|
FROM memories
|
|
1425
1578
|
WHERE agent_id = ?
|
|
1426
1579
|
AND vector IS NOT NULL${statusFilter}
|
|
@@ -1469,7 +1622,9 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
1469
1622
|
document_id: row.document_id ?? null,
|
|
1470
1623
|
user_id: row.user_id ?? null,
|
|
1471
1624
|
char_offset: row.char_offset ?? null,
|
|
1472
|
-
page_number: row.page_number ?? null
|
|
1625
|
+
page_number: row.page_number ?? null,
|
|
1626
|
+
source_path: row.source_path ?? null,
|
|
1627
|
+
source_type: row.source_type ?? null
|
|
1473
1628
|
}));
|
|
1474
1629
|
}
|
|
1475
1630
|
async function attachDocumentMetadata(records) {
|
|
@@ -1507,6 +1662,25 @@ async function attachDocumentMetadata(records) {
|
|
|
1507
1662
|
}
|
|
1508
1663
|
return records;
|
|
1509
1664
|
}
|
|
1665
|
+
async function flushTier3(agentId, options) {
|
|
1666
|
+
const client = getClient();
|
|
1667
|
+
const maxAge = options?.maxAgeHours ?? 72;
|
|
1668
|
+
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
1669
|
+
if (options?.dryRun) {
|
|
1670
|
+
const result2 = await client.execute({
|
|
1671
|
+
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
1672
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
1673
|
+
args: [agentId, cutoff]
|
|
1674
|
+
});
|
|
1675
|
+
return { archived: Number(result2.rows[0]?.cnt ?? 0) };
|
|
1676
|
+
}
|
|
1677
|
+
const result = await client.execute({
|
|
1678
|
+
sql: `UPDATE memories SET status = 'archived'
|
|
1679
|
+
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
1680
|
+
args: [agentId, cutoff]
|
|
1681
|
+
});
|
|
1682
|
+
return { archived: result.rowsAffected };
|
|
1683
|
+
}
|
|
1510
1684
|
async function disposeStore() {
|
|
1511
1685
|
if (_flushTimer !== null) {
|
|
1512
1686
|
clearInterval(_flushTimer);
|
|
@@ -1537,11 +1711,26 @@ function reserveVersions(count) {
|
|
|
1537
1711
|
}
|
|
1538
1712
|
return reserved;
|
|
1539
1713
|
}
|
|
1714
|
+
async function getMemoryCardinality(agentId) {
|
|
1715
|
+
try {
|
|
1716
|
+
const client = getClient();
|
|
1717
|
+
const result = await client.execute({
|
|
1718
|
+
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
1719
|
+
args: [agentId]
|
|
1720
|
+
});
|
|
1721
|
+
return Number(result.rows[0]?.cnt) || 0;
|
|
1722
|
+
} catch {
|
|
1723
|
+
return 0;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1540
1726
|
export {
|
|
1541
1727
|
attachDocumentMetadata,
|
|
1542
1728
|
buildWikiScopeFilter,
|
|
1729
|
+
classifyTier,
|
|
1543
1730
|
disposeStore,
|
|
1544
1731
|
flushBatch,
|
|
1732
|
+
flushTier3,
|
|
1733
|
+
getMemoryCardinality,
|
|
1545
1734
|
initStore,
|
|
1546
1735
|
reserveVersions,
|
|
1547
1736
|
searchMemories,
|
package/dist/lib/task-router.js
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
// src/lib/task-router.ts
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
|
+
var DEFAULT_BLOOM_CONFIG = {
|
|
4
|
+
complexityToTier: {
|
|
5
|
+
routine: "junior",
|
|
6
|
+
standard: "standard",
|
|
7
|
+
complex: "senior",
|
|
8
|
+
critical: "specialist"
|
|
9
|
+
},
|
|
10
|
+
tierRules: {
|
|
11
|
+
junior: {
|
|
12
|
+
eligible: ["tom"],
|
|
13
|
+
reviewRequired: false,
|
|
14
|
+
manualOnly: false
|
|
15
|
+
},
|
|
16
|
+
standard: {
|
|
17
|
+
eligible: ["tom"],
|
|
18
|
+
reviewRequired: false,
|
|
19
|
+
manualOnly: false
|
|
20
|
+
},
|
|
21
|
+
senior: {
|
|
22
|
+
eligible: [],
|
|
23
|
+
// any specialist, but review required
|
|
24
|
+
reviewRequired: true,
|
|
25
|
+
manualOnly: false
|
|
26
|
+
},
|
|
27
|
+
specialist: {
|
|
28
|
+
eligible: [],
|
|
29
|
+
reviewRequired: true,
|
|
30
|
+
manualOnly: true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
|
|
35
|
+
const tier = config.complexityToTier[complexity];
|
|
36
|
+
const rule = config.tierRules[tier];
|
|
37
|
+
return {
|
|
38
|
+
tier,
|
|
39
|
+
reviewRequired: rule.reviewRequired,
|
|
40
|
+
manualOnly: rule.manualOnly,
|
|
41
|
+
eligible: rule.eligible
|
|
42
|
+
};
|
|
43
|
+
}
|
|
3
44
|
async function scoreEmployee(taskVector, agentId, searchFn) {
|
|
4
45
|
const results = await searchFn(taskVector, agentId, { limit: 5 });
|
|
5
46
|
if (results.length === 0) {
|
|
@@ -12,13 +53,29 @@ async function scoreEmployee(taskVector, agentId, searchFn) {
|
|
|
12
53
|
}
|
|
13
54
|
return { agentId, score: results.length / 5 };
|
|
14
55
|
}
|
|
15
|
-
async function routeTask(taskDescription, employees, embedFn, searchFn) {
|
|
16
|
-
|
|
56
|
+
async function routeTask(taskDescription, employees, embedFn, searchFn, options) {
|
|
57
|
+
let specialists = employees.filter((e) => e.name !== "exe");
|
|
17
58
|
if (specialists.length === 0) {
|
|
18
59
|
throw new Error(
|
|
19
60
|
"No specialist employees available. Create one with /exe-new-employee."
|
|
20
61
|
);
|
|
21
62
|
}
|
|
63
|
+
let bloomRouting;
|
|
64
|
+
if (options?.complexity) {
|
|
65
|
+
bloomRouting = resolveBloomRouting(options.complexity, options.bloomConfig);
|
|
66
|
+
if (bloomRouting.manualOnly) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Task complexity "${options.complexity}" requires manual assignment (tier: ${bloomRouting.tier}).`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (bloomRouting.eligible.length > 0) {
|
|
72
|
+
const eligible = new Set(bloomRouting.eligible);
|
|
73
|
+
const filtered = specialists.filter((e) => eligible.has(e.name));
|
|
74
|
+
if (filtered.length > 0) {
|
|
75
|
+
specialists = filtered;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
22
79
|
const taskVector = await embedFn(taskDescription);
|
|
23
80
|
const scored = await Promise.all(
|
|
24
81
|
specialists.map(async (emp) => {
|
|
@@ -27,7 +84,7 @@ async function routeTask(taskDescription, employees, embedFn, searchFn) {
|
|
|
27
84
|
})
|
|
28
85
|
);
|
|
29
86
|
scored.sort((a, b) => b.score - a.score);
|
|
30
|
-
return scored[0];
|
|
87
|
+
return { ...scored[0], bloomRouting };
|
|
31
88
|
}
|
|
32
89
|
function createAssignmentMemory(taskDescription, assignedTo, assignedBy) {
|
|
33
90
|
return {
|
|
@@ -44,19 +101,28 @@ function createAssignmentMemory(taskDescription, assignedTo, assignedBy) {
|
|
|
44
101
|
// Will be backfilled
|
|
45
102
|
};
|
|
46
103
|
}
|
|
47
|
-
function formatAssignmentOutput(employee, task, score, mode) {
|
|
104
|
+
function formatAssignmentOutput(employee, task, score, mode, bloomRouting) {
|
|
48
105
|
const modeLabel = mode === "auto" ? "auto-routed" : "direct";
|
|
49
106
|
const percent = Math.round(score * 100);
|
|
50
|
-
|
|
107
|
+
const lines = [
|
|
51
108
|
`Assigned to ${employee.name} (${employee.role})`,
|
|
52
109
|
`Mode: ${modeLabel}`,
|
|
53
110
|
`Relevance: ${percent}%`,
|
|
54
111
|
`Task: ${task}`
|
|
55
|
-
]
|
|
112
|
+
];
|
|
113
|
+
if (bloomRouting) {
|
|
114
|
+
lines.push(`Complexity tier: ${bloomRouting.tier}`);
|
|
115
|
+
if (bloomRouting.reviewRequired) {
|
|
116
|
+
lines.push(`Review: required`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return lines.join("\n");
|
|
56
120
|
}
|
|
57
121
|
export {
|
|
122
|
+
DEFAULT_BLOOM_CONFIG,
|
|
58
123
|
createAssignmentMemory,
|
|
59
124
|
formatAssignmentOutput,
|
|
125
|
+
resolveBloomRouting,
|
|
60
126
|
routeTask,
|
|
61
127
|
scoreEmployee
|
|
62
128
|
};
|