@hasna/mementos 0.14.18 → 0.14.20
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/dist/cli/index.js +73 -41
- package/dist/index.js +476 -30
- package/dist/mcp/index.js +64 -31
- package/dist/sdk/index.js +397 -0
- package/dist/server/index.js +652 -164
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -9765,6 +9765,7 @@ CREATE TABLE IF NOT EXISTS machines (
|
|
|
9765
9765
|
name TEXT NOT NULL UNIQUE,
|
|
9766
9766
|
hostname TEXT NOT NULL,
|
|
9767
9767
|
platform TEXT NOT NULL DEFAULT 'unknown',
|
|
9768
|
+
is_primary INTEGER NOT NULL DEFAULT 0,
|
|
9768
9769
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
9769
9770
|
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
9770
9771
|
);
|
|
@@ -10144,33 +10145,44 @@ CREATE INDEX IF NOT EXISTS idx_memories_sequence_group ON memories(sequence_grou
|
|
|
10144
10145
|
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
10145
10146
|
`,
|
|
10146
10147
|
`
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10148
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
10149
|
+
id TEXT PRIMARY KEY,
|
|
10150
|
+
subject TEXT NOT NULL,
|
|
10151
|
+
description TEXT DEFAULT '',
|
|
10152
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'in_progress', 'completed', 'failed', 'cancelled')),
|
|
10153
|
+
priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('critical', 'high', 'medium', 'low')),
|
|
10154
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
10155
|
+
assigned_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
10156
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
10157
|
+
session_id TEXT,
|
|
10158
|
+
parent_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
10159
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
10160
|
+
progress REAL NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 1),
|
|
10161
|
+
due_at TEXT,
|
|
10162
|
+
started_at TEXT,
|
|
10163
|
+
completed_at TEXT,
|
|
10164
|
+
failed_at TEXT,
|
|
10165
|
+
error TEXT,
|
|
10166
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
10167
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
10168
|
+
);
|
|
10169
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
10170
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
|
|
10171
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(assigned_agent_id);
|
|
10172
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
|
|
10173
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id);
|
|
10174
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id);
|
|
10175
|
+
|
|
10176
|
+
CREATE TABLE IF NOT EXISTS task_comments (
|
|
10177
|
+
id TEXT PRIMARY KEY,
|
|
10178
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
10179
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
10180
|
+
body TEXT NOT NULL,
|
|
10181
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
10182
|
+
);
|
|
10183
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_task ON task_comments(task_id);
|
|
10184
|
+
CREATE INDEX IF NOT EXISTS idx_task_comments_agent ON task_comments(agent_id);
|
|
10185
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (34);
|
|
10174
10186
|
`
|
|
10175
10187
|
];
|
|
10176
10188
|
});
|
|
@@ -10892,7 +10904,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
10892
10904
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?, ?, ?, ?, ?)`, [
|
|
10893
10905
|
id,
|
|
10894
10906
|
input.key,
|
|
10895
|
-
|
|
10907
|
+
safeValue,
|
|
10896
10908
|
input.category || "knowledge",
|
|
10897
10909
|
input.scope || "private",
|
|
10898
10910
|
input.summary || null,
|
|
@@ -11688,8 +11700,12 @@ function parseMachine(row) {
|
|
|
11688
11700
|
is_primary: Boolean(row.is_primary)
|
|
11689
11701
|
};
|
|
11690
11702
|
}
|
|
11703
|
+
function normalizeHostname(host) {
|
|
11704
|
+
return host.replace(/\.(local|lan|home|internal)$/i, "");
|
|
11705
|
+
}
|
|
11691
11706
|
function registerMachine(name, db = getDatabase()) {
|
|
11692
|
-
const
|
|
11707
|
+
const rawHost = hostname();
|
|
11708
|
+
const host = normalizeHostname(rawHost);
|
|
11693
11709
|
const plat = platform2();
|
|
11694
11710
|
const machineName = name?.trim() || host;
|
|
11695
11711
|
const existing = parseMachine(db.query("SELECT * FROM machines WHERE hostname = ?").get(host));
|
|
@@ -11765,7 +11781,7 @@ function touchMachine(id, db = getDatabase()) {
|
|
|
11765
11781
|
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now(), id]);
|
|
11766
11782
|
}
|
|
11767
11783
|
function getCurrentMachineId(db = getDatabase()) {
|
|
11768
|
-
const host = hostname();
|
|
11784
|
+
const host = normalizeHostname(hostname());
|
|
11769
11785
|
const m = db.query("SELECT id FROM machines WHERE hostname = ?").get(host);
|
|
11770
11786
|
if (m) {
|
|
11771
11787
|
touchMachine(m.id, db);
|
|
@@ -12306,6 +12322,10 @@ function buildFilterConditions(filter) {
|
|
|
12306
12322
|
params.push(tag);
|
|
12307
12323
|
}
|
|
12308
12324
|
}
|
|
12325
|
+
if (filter.namespace) {
|
|
12326
|
+
conditions.push("m.namespace = ?");
|
|
12327
|
+
params.push(filter.namespace);
|
|
12328
|
+
}
|
|
12309
12329
|
return { conditions, params };
|
|
12310
12330
|
}
|
|
12311
12331
|
function searchWithFts5(d, query, queryLower, filter, graphBoostedIds) {
|
|
@@ -14161,9 +14181,423 @@ function clearActiveModel() {
|
|
|
14161
14181
|
delete config.activeModel;
|
|
14162
14182
|
writeConfig(config);
|
|
14163
14183
|
}
|
|
14184
|
+
// src/db/tasks.ts
|
|
14185
|
+
init_database();
|
|
14186
|
+
function parseTask(row) {
|
|
14187
|
+
return {
|
|
14188
|
+
id: row.id,
|
|
14189
|
+
subject: row.subject,
|
|
14190
|
+
description: row.description ?? "",
|
|
14191
|
+
status: row.status,
|
|
14192
|
+
priority: row.priority,
|
|
14193
|
+
tags: JSON.parse(row.tags ?? "[]"),
|
|
14194
|
+
assigned_agent_id: row.assigned_agent_id ?? null,
|
|
14195
|
+
project_id: row.project_id ?? null,
|
|
14196
|
+
session_id: row.session_id ?? null,
|
|
14197
|
+
parent_task_id: row.parent_task_id ?? null,
|
|
14198
|
+
metadata: JSON.parse(row.metadata ?? "{}"),
|
|
14199
|
+
progress: row.progress,
|
|
14200
|
+
due_at: row.due_at ?? null,
|
|
14201
|
+
started_at: row.started_at ?? null,
|
|
14202
|
+
completed_at: row.completed_at ?? null,
|
|
14203
|
+
failed_at: row.failed_at ?? null,
|
|
14204
|
+
error: row.error ?? null,
|
|
14205
|
+
created_at: row.created_at,
|
|
14206
|
+
updated_at: row.updated_at
|
|
14207
|
+
};
|
|
14208
|
+
}
|
|
14209
|
+
function createTask(db, input) {
|
|
14210
|
+
const id = uuid();
|
|
14211
|
+
const ts = now();
|
|
14212
|
+
db.run(`INSERT INTO tasks (id, subject, description, status, priority, tags, assigned_agent_id, project_id, session_id, parent_task_id, metadata, due_at, created_at, updated_at)
|
|
14213
|
+
VALUES (?, ?, ?, 'pending', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
14214
|
+
id,
|
|
14215
|
+
input.subject,
|
|
14216
|
+
input.description ?? "",
|
|
14217
|
+
input.priority ?? "medium",
|
|
14218
|
+
JSON.stringify(input.tags ?? []),
|
|
14219
|
+
input.assigned_agent_id ?? null,
|
|
14220
|
+
input.project_id ?? null,
|
|
14221
|
+
input.session_id ?? null,
|
|
14222
|
+
input.parent_task_id ?? null,
|
|
14223
|
+
JSON.stringify(input.metadata ?? {}),
|
|
14224
|
+
input.due_at ?? null,
|
|
14225
|
+
ts,
|
|
14226
|
+
ts
|
|
14227
|
+
]);
|
|
14228
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14229
|
+
if (!row) {
|
|
14230
|
+
throw new Error(`Failed to load task after create: ${id}`);
|
|
14231
|
+
}
|
|
14232
|
+
return parseTask(row);
|
|
14233
|
+
}
|
|
14234
|
+
function getTask(db, id) {
|
|
14235
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14236
|
+
return row ? parseTask(row) : null;
|
|
14237
|
+
}
|
|
14238
|
+
function listTasks(db, filter) {
|
|
14239
|
+
let sql = "SELECT * FROM tasks WHERE 1=1";
|
|
14240
|
+
let countSql = "SELECT COUNT(*) as c FROM tasks WHERE 1=1";
|
|
14241
|
+
const params = [];
|
|
14242
|
+
const countParams = [];
|
|
14243
|
+
if (filter?.status) {
|
|
14244
|
+
sql += " AND status = ?";
|
|
14245
|
+
countSql += " AND status = ?";
|
|
14246
|
+
params.push(filter.status);
|
|
14247
|
+
countParams.push(filter.status);
|
|
14248
|
+
}
|
|
14249
|
+
if (filter?.priority) {
|
|
14250
|
+
sql += " AND priority = ?";
|
|
14251
|
+
countSql += " AND priority = ?";
|
|
14252
|
+
params.push(filter.priority);
|
|
14253
|
+
countParams.push(filter.priority);
|
|
14254
|
+
}
|
|
14255
|
+
if (filter?.assigned_agent_id) {
|
|
14256
|
+
sql += " AND assigned_agent_id = ?";
|
|
14257
|
+
countSql += " AND assigned_agent_id = ?";
|
|
14258
|
+
params.push(filter.assigned_agent_id);
|
|
14259
|
+
countParams.push(filter.assigned_agent_id);
|
|
14260
|
+
}
|
|
14261
|
+
if (filter?.project_id) {
|
|
14262
|
+
sql += " AND project_id = ?";
|
|
14263
|
+
countSql += " AND project_id = ?";
|
|
14264
|
+
params.push(filter.project_id);
|
|
14265
|
+
countParams.push(filter.project_id);
|
|
14266
|
+
}
|
|
14267
|
+
if (filter?.session_id) {
|
|
14268
|
+
sql += " AND session_id = ?";
|
|
14269
|
+
countSql += " AND session_id = ?";
|
|
14270
|
+
params.push(filter.session_id);
|
|
14271
|
+
countParams.push(filter.session_id);
|
|
14272
|
+
}
|
|
14273
|
+
if (filter?.parent_task_id !== undefined) {
|
|
14274
|
+
if (filter.parent_task_id === null) {
|
|
14275
|
+
sql += " AND parent_task_id IS NULL";
|
|
14276
|
+
countSql += " AND parent_task_id IS NULL";
|
|
14277
|
+
} else {
|
|
14278
|
+
sql += " AND parent_task_id = ?";
|
|
14279
|
+
countSql += " AND parent_task_id = ?";
|
|
14280
|
+
params.push(filter.parent_task_id);
|
|
14281
|
+
countParams.push(filter.parent_task_id);
|
|
14282
|
+
}
|
|
14283
|
+
}
|
|
14284
|
+
if (filter?.tags?.length) {
|
|
14285
|
+
const placeholders = filter.tags.map(() => "?").join(", ");
|
|
14286
|
+
sql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14287
|
+
countSql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14288
|
+
params.push(...filter.tags);
|
|
14289
|
+
countParams.push(...filter.tags);
|
|
14290
|
+
}
|
|
14291
|
+
sql += " ORDER BY CASE priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 WHEN 'low' THEN 4 END, created_at ASC";
|
|
14292
|
+
if (filter?.limit !== undefined) {
|
|
14293
|
+
sql += " LIMIT ?";
|
|
14294
|
+
params.push(filter.limit);
|
|
14295
|
+
}
|
|
14296
|
+
if (filter?.offset !== undefined) {
|
|
14297
|
+
sql += " OFFSET ?";
|
|
14298
|
+
params.push(filter.offset);
|
|
14299
|
+
}
|
|
14300
|
+
const rows = db.query(sql).all(...params);
|
|
14301
|
+
const countRow = db.query(countSql).get(...countParams);
|
|
14302
|
+
return { tasks: rows.map(parseTask), count: countRow.c };
|
|
14303
|
+
}
|
|
14304
|
+
function updateTask(db, id, input) {
|
|
14305
|
+
const existing = getTask(db, id);
|
|
14306
|
+
if (!existing)
|
|
14307
|
+
return null;
|
|
14308
|
+
const updates = [];
|
|
14309
|
+
const params = [];
|
|
14310
|
+
const ts = now();
|
|
14311
|
+
if (input.subject !== undefined) {
|
|
14312
|
+
updates.push("subject = ?");
|
|
14313
|
+
params.push(input.subject);
|
|
14314
|
+
}
|
|
14315
|
+
if (input.description !== undefined) {
|
|
14316
|
+
updates.push("description = ?");
|
|
14317
|
+
params.push(input.description);
|
|
14318
|
+
}
|
|
14319
|
+
if (input.status !== undefined) {
|
|
14320
|
+
updates.push("status = ?");
|
|
14321
|
+
params.push(input.status);
|
|
14322
|
+
if (input.status === "in_progress" && !existing.started_at) {
|
|
14323
|
+
updates.push("started_at = ?");
|
|
14324
|
+
params.push(ts);
|
|
14325
|
+
}
|
|
14326
|
+
if (input.status === "completed") {
|
|
14327
|
+
updates.push("completed_at = ?");
|
|
14328
|
+
params.push(ts);
|
|
14329
|
+
updates.push("progress = ?");
|
|
14330
|
+
params.push(1);
|
|
14331
|
+
}
|
|
14332
|
+
if (input.status === "failed") {
|
|
14333
|
+
updates.push("failed_at = ?");
|
|
14334
|
+
params.push(ts);
|
|
14335
|
+
}
|
|
14336
|
+
}
|
|
14337
|
+
if (input.priority !== undefined) {
|
|
14338
|
+
updates.push("priority = ?");
|
|
14339
|
+
params.push(input.priority);
|
|
14340
|
+
}
|
|
14341
|
+
if (input.tags !== undefined) {
|
|
14342
|
+
updates.push("tags = ?");
|
|
14343
|
+
params.push(JSON.stringify(input.tags));
|
|
14344
|
+
}
|
|
14345
|
+
if (input.assigned_agent_id !== undefined) {
|
|
14346
|
+
updates.push("assigned_agent_id = ?");
|
|
14347
|
+
params.push(input.assigned_agent_id);
|
|
14348
|
+
}
|
|
14349
|
+
if (input.metadata !== undefined) {
|
|
14350
|
+
updates.push("metadata = ?");
|
|
14351
|
+
params.push(JSON.stringify(input.metadata));
|
|
14352
|
+
}
|
|
14353
|
+
if (input.progress !== undefined) {
|
|
14354
|
+
updates.push("progress = ?");
|
|
14355
|
+
params.push(input.progress);
|
|
14356
|
+
}
|
|
14357
|
+
if (input.due_at !== undefined) {
|
|
14358
|
+
updates.push("due_at = ?");
|
|
14359
|
+
params.push(input.due_at);
|
|
14360
|
+
}
|
|
14361
|
+
if (input.error !== undefined) {
|
|
14362
|
+
updates.push("error = ?");
|
|
14363
|
+
params.push(input.error);
|
|
14364
|
+
}
|
|
14365
|
+
if (updates.length === 0)
|
|
14366
|
+
return existing;
|
|
14367
|
+
updates.push("updated_at = ?");
|
|
14368
|
+
params.push(ts);
|
|
14369
|
+
params.push(id);
|
|
14370
|
+
db.run(`UPDATE tasks SET ${updates.join(", ")} WHERE id = ?`, ...params);
|
|
14371
|
+
return getTask(db, id);
|
|
14372
|
+
}
|
|
14373
|
+
function deleteTask(db, id) {
|
|
14374
|
+
const result = db.run("DELETE FROM tasks WHERE id = ?", id);
|
|
14375
|
+
return result.changes > 0;
|
|
14376
|
+
}
|
|
14377
|
+
function addTaskComment(db, taskId, body, agentId) {
|
|
14378
|
+
const id = uuid();
|
|
14379
|
+
db.run("INSERT INTO task_comments (id, task_id, agent_id, body) VALUES (?, ?, ?, ?)", [id, taskId, agentId ?? null, body]);
|
|
14380
|
+
const row = db.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
14381
|
+
return parseTaskComment(row);
|
|
14382
|
+
}
|
|
14383
|
+
function parseTaskComment(row) {
|
|
14384
|
+
return {
|
|
14385
|
+
id: row.id,
|
|
14386
|
+
task_id: row.task_id,
|
|
14387
|
+
agent_id: row.agent_id ?? null,
|
|
14388
|
+
body: row.body,
|
|
14389
|
+
created_at: row.created_at
|
|
14390
|
+
};
|
|
14391
|
+
}
|
|
14392
|
+
function listTaskComments(db, taskId) {
|
|
14393
|
+
const rows = db.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC").all(taskId);
|
|
14394
|
+
return { comments: rows.map(parseTaskComment), count: rows.length };
|
|
14395
|
+
}
|
|
14396
|
+
function deleteTaskComment(db, id) {
|
|
14397
|
+
const result = db.run("DELETE FROM task_comments WHERE id = ?", id);
|
|
14398
|
+
return result.changes > 0;
|
|
14399
|
+
}
|
|
14400
|
+
function getTaskStats(db, filter) {
|
|
14401
|
+
let where = "WHERE 1=1";
|
|
14402
|
+
const params = [];
|
|
14403
|
+
if (filter?.project_id) {
|
|
14404
|
+
where += " AND project_id = ?";
|
|
14405
|
+
params.push(filter.project_id);
|
|
14406
|
+
}
|
|
14407
|
+
if (filter?.agent_id) {
|
|
14408
|
+
where += " AND assigned_agent_id = ?";
|
|
14409
|
+
params.push(filter.agent_id);
|
|
14410
|
+
}
|
|
14411
|
+
const total = db.query(`SELECT COUNT(*) as c FROM tasks ${where}`).get(...params).c;
|
|
14412
|
+
const byStatus = {};
|
|
14413
|
+
const statusRows = db.query(`SELECT status, COUNT(*) as c FROM tasks ${where} GROUP BY status`).all(...params);
|
|
14414
|
+
for (const row of statusRows)
|
|
14415
|
+
byStatus[row.status] = row.c;
|
|
14416
|
+
const byPriority = {};
|
|
14417
|
+
const priorityRows = db.query(`SELECT priority, COUNT(*) as c FROM tasks ${where} GROUP BY priority`).all(...params);
|
|
14418
|
+
for (const row of priorityRows)
|
|
14419
|
+
byPriority[row.priority] = row.c;
|
|
14420
|
+
const overdue = db.query(`SELECT COUNT(*) as c FROM tasks ${where} AND status != 'completed' AND status != 'cancelled' AND due_at IS NOT NULL AND due_at < datetime('now')`).get(...params).c;
|
|
14421
|
+
return {
|
|
14422
|
+
total,
|
|
14423
|
+
by_status: {
|
|
14424
|
+
pending: byStatus["pending"] ?? 0,
|
|
14425
|
+
in_progress: byStatus["in_progress"] ?? 0,
|
|
14426
|
+
completed: byStatus["completed"] ?? 0,
|
|
14427
|
+
failed: byStatus["failed"] ?? 0,
|
|
14428
|
+
cancelled: byStatus["cancelled"] ?? 0
|
|
14429
|
+
},
|
|
14430
|
+
by_priority: {
|
|
14431
|
+
critical: byPriority["critical"] ?? 0,
|
|
14432
|
+
high: byPriority["high"] ?? 0,
|
|
14433
|
+
medium: byPriority["medium"] ?? 0,
|
|
14434
|
+
low: byPriority["low"] ?? 0
|
|
14435
|
+
},
|
|
14436
|
+
overdue
|
|
14437
|
+
};
|
|
14438
|
+
}
|
|
14439
|
+
// src/lib/task-runner.ts
|
|
14440
|
+
init_database();
|
|
14441
|
+
var _handlers = new Map;
|
|
14442
|
+
var _defaultHandler = null;
|
|
14443
|
+
function registerTaskHandler(type, handler) {
|
|
14444
|
+
_handlers.set(type, handler);
|
|
14445
|
+
}
|
|
14446
|
+
function setDefaultTaskHandler(handler) {
|
|
14447
|
+
_defaultHandler = handler;
|
|
14448
|
+
}
|
|
14449
|
+
function resolveHandler(task) {
|
|
14450
|
+
for (const tag of task.tags) {
|
|
14451
|
+
const h = _handlers.get(tag);
|
|
14452
|
+
if (h)
|
|
14453
|
+
return h;
|
|
14454
|
+
}
|
|
14455
|
+
const metaType = task.metadata?.["type"];
|
|
14456
|
+
if (metaType) {
|
|
14457
|
+
const h = _handlers.get(metaType);
|
|
14458
|
+
if (h)
|
|
14459
|
+
return h;
|
|
14460
|
+
}
|
|
14461
|
+
return _defaultHandler;
|
|
14462
|
+
}
|
|
14463
|
+
var _workerStarted = false;
|
|
14464
|
+
var _processing = false;
|
|
14465
|
+
var _totalProcessed = 0;
|
|
14466
|
+
var _pollIntervalMs = 60000;
|
|
14467
|
+
function startTaskRunner(intervalMs) {
|
|
14468
|
+
if (_workerStarted)
|
|
14469
|
+
return;
|
|
14470
|
+
_workerStarted = true;
|
|
14471
|
+
if (intervalMs)
|
|
14472
|
+
_pollIntervalMs = intervalMs;
|
|
14473
|
+
console.log(`[task-runner] Started, polling every ${_pollIntervalMs / 1000}s`);
|
|
14474
|
+
_tick();
|
|
14475
|
+
setInterval(() => {
|
|
14476
|
+
_tick();
|
|
14477
|
+
}, _pollIntervalMs);
|
|
14478
|
+
}
|
|
14479
|
+
async function _tick() {
|
|
14480
|
+
if (_processing)
|
|
14481
|
+
return;
|
|
14482
|
+
let task = null;
|
|
14483
|
+
try {
|
|
14484
|
+
const db = getDatabase();
|
|
14485
|
+
const result = listTasks(db, { status: "pending", limit: 1 });
|
|
14486
|
+
if (result.tasks.length === 0)
|
|
14487
|
+
return;
|
|
14488
|
+
task = result.tasks[0] ?? null;
|
|
14489
|
+
} catch (e) {
|
|
14490
|
+
console.error("[task-runner] Failed to list pending tasks:", e);
|
|
14491
|
+
return;
|
|
14492
|
+
}
|
|
14493
|
+
if (!task)
|
|
14494
|
+
return;
|
|
14495
|
+
_processing = true;
|
|
14496
|
+
try {
|
|
14497
|
+
const db = getDatabase();
|
|
14498
|
+
updateTask(db, task.id, { status: "in_progress" });
|
|
14499
|
+
task = getTask(db, task.id);
|
|
14500
|
+
} catch (e) {
|
|
14501
|
+
console.error("[task-runner] Failed to claim task:", e);
|
|
14502
|
+
_processing = false;
|
|
14503
|
+
return;
|
|
14504
|
+
}
|
|
14505
|
+
const handler = resolveHandler(task);
|
|
14506
|
+
if (!handler) {
|
|
14507
|
+
console.warn(`[task-runner] No handler for task: ${task.subject} (tags: ${JSON.stringify(task.tags)}, meta.type: ${task.metadata?.["type"]})`);
|
|
14508
|
+
try {
|
|
14509
|
+
const db = getDatabase();
|
|
14510
|
+
updateTask(db, task.id, {
|
|
14511
|
+
status: "failed",
|
|
14512
|
+
error: "No handler registered for this task type"
|
|
14513
|
+
});
|
|
14514
|
+
} catch {}
|
|
14515
|
+
_processing = false;
|
|
14516
|
+
_totalProcessed++;
|
|
14517
|
+
return;
|
|
14518
|
+
}
|
|
14519
|
+
const taskId = task.id;
|
|
14520
|
+
let progress = 0;
|
|
14521
|
+
const ctx = {
|
|
14522
|
+
task,
|
|
14523
|
+
updateProgress: (p) => {
|
|
14524
|
+
progress = Math.min(1, Math.max(0, p));
|
|
14525
|
+
try {
|
|
14526
|
+
const db = getDatabase();
|
|
14527
|
+
updateTask(db, taskId, { progress });
|
|
14528
|
+
} catch {}
|
|
14529
|
+
},
|
|
14530
|
+
addComment: (body, agentId) => {
|
|
14531
|
+
try {
|
|
14532
|
+
const db = getDatabase();
|
|
14533
|
+
addTaskComment(db, taskId, body, agentId);
|
|
14534
|
+
} catch {}
|
|
14535
|
+
}
|
|
14536
|
+
};
|
|
14537
|
+
try {
|
|
14538
|
+
await handler(ctx);
|
|
14539
|
+
const db = getDatabase();
|
|
14540
|
+
updateTask(db, taskId, { status: "completed", progress: 1 });
|
|
14541
|
+
console.log(`[task-runner] Completed: ${task.subject}`);
|
|
14542
|
+
} catch (e) {
|
|
14543
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
14544
|
+
try {
|
|
14545
|
+
const db = getDatabase();
|
|
14546
|
+
updateTask(db, taskId, { status: "failed", error: errorMsg });
|
|
14547
|
+
} catch {}
|
|
14548
|
+
console.error(`[task-runner] Failed: ${task.subject} \u2014 ${errorMsg}`);
|
|
14549
|
+
} finally {
|
|
14550
|
+
_processing = false;
|
|
14551
|
+
_totalProcessed++;
|
|
14552
|
+
}
|
|
14553
|
+
}
|
|
14554
|
+
function getTaskRunnerStats() {
|
|
14555
|
+
try {
|
|
14556
|
+
const db = getDatabase();
|
|
14557
|
+
const rows = db.query("SELECT status, COUNT(*) as c FROM tasks GROUP BY status").all();
|
|
14558
|
+
const stats = {
|
|
14559
|
+
pending: 0,
|
|
14560
|
+
inProgress: 0,
|
|
14561
|
+
completed: 0,
|
|
14562
|
+
failed: 0,
|
|
14563
|
+
cancelled: 0,
|
|
14564
|
+
totalProcessed: _totalProcessed
|
|
14565
|
+
};
|
|
14566
|
+
for (const row of rows) {
|
|
14567
|
+
switch (row.status) {
|
|
14568
|
+
case "pending":
|
|
14569
|
+
stats.pending = row.c;
|
|
14570
|
+
break;
|
|
14571
|
+
case "in_progress":
|
|
14572
|
+
stats.inProgress = row.c;
|
|
14573
|
+
break;
|
|
14574
|
+
case "completed":
|
|
14575
|
+
stats.completed = row.c;
|
|
14576
|
+
break;
|
|
14577
|
+
case "failed":
|
|
14578
|
+
stats.failed = row.c;
|
|
14579
|
+
break;
|
|
14580
|
+
case "cancelled":
|
|
14581
|
+
stats.cancelled = row.c;
|
|
14582
|
+
break;
|
|
14583
|
+
}
|
|
14584
|
+
}
|
|
14585
|
+
return stats;
|
|
14586
|
+
} catch {
|
|
14587
|
+
return {
|
|
14588
|
+
pending: 0,
|
|
14589
|
+
inProgress: 0,
|
|
14590
|
+
completed: 0,
|
|
14591
|
+
failed: 0,
|
|
14592
|
+
cancelled: 0,
|
|
14593
|
+
totalProcessed: _totalProcessed
|
|
14594
|
+
};
|
|
14595
|
+
}
|
|
14596
|
+
}
|
|
14164
14597
|
export {
|
|
14165
14598
|
withMemoryLock,
|
|
14166
14599
|
uuid,
|
|
14600
|
+
updateTask,
|
|
14167
14601
|
updateMemory,
|
|
14168
14602
|
updateEntity,
|
|
14169
14603
|
updateAgent,
|
|
@@ -14173,9 +14607,11 @@ export {
|
|
|
14173
14607
|
touchMachine,
|
|
14174
14608
|
touchAgent,
|
|
14175
14609
|
syncMemories,
|
|
14610
|
+
startTaskRunner,
|
|
14176
14611
|
shortUuid,
|
|
14177
14612
|
setPrimaryMachine,
|
|
14178
14613
|
setFocus,
|
|
14614
|
+
setDefaultTaskHandler,
|
|
14179
14615
|
setActiveProfile,
|
|
14180
14616
|
setActiveModel,
|
|
14181
14617
|
searchMemories,
|
|
@@ -14188,6 +14624,7 @@ export {
|
|
|
14188
14624
|
releaseMemoryWriteLock,
|
|
14189
14625
|
releaseLock,
|
|
14190
14626
|
releaseAllAgentLocks,
|
|
14627
|
+
registerTaskHandler,
|
|
14191
14628
|
registerProject,
|
|
14192
14629
|
registerMachine,
|
|
14193
14630
|
registerAgent,
|
|
@@ -14200,6 +14637,8 @@ export {
|
|
|
14200
14637
|
mergeEntities,
|
|
14201
14638
|
memoryLockId,
|
|
14202
14639
|
loadConfig,
|
|
14640
|
+
listTasks,
|
|
14641
|
+
listTaskComments,
|
|
14203
14642
|
listRelations,
|
|
14204
14643
|
listProjects,
|
|
14205
14644
|
listProfiles,
|
|
@@ -14211,6 +14650,9 @@ export {
|
|
|
14211
14650
|
listAgentLocks,
|
|
14212
14651
|
linkEntityToMemory,
|
|
14213
14652
|
incrementRecallCount,
|
|
14653
|
+
getTaskStats,
|
|
14654
|
+
getTaskRunnerStats,
|
|
14655
|
+
getTask,
|
|
14214
14656
|
getRelation,
|
|
14215
14657
|
getRelatedEntities,
|
|
14216
14658
|
getProject,
|
|
@@ -14243,6 +14685,8 @@ export {
|
|
|
14243
14685
|
findPath,
|
|
14244
14686
|
enforceQuotas,
|
|
14245
14687
|
deprioritizeStale,
|
|
14688
|
+
deleteTaskComment,
|
|
14689
|
+
deleteTask,
|
|
14246
14690
|
deleteRelation,
|
|
14247
14691
|
deleteProfile,
|
|
14248
14692
|
deleteMemory,
|
|
@@ -14250,6 +14694,7 @@ export {
|
|
|
14250
14694
|
deleteEntity,
|
|
14251
14695
|
defaultSyncAgents,
|
|
14252
14696
|
dedup,
|
|
14697
|
+
createTask,
|
|
14253
14698
|
createRelation,
|
|
14254
14699
|
createMemory,
|
|
14255
14700
|
createEntity,
|
|
@@ -14267,6 +14712,7 @@ export {
|
|
|
14267
14712
|
archiveUnused,
|
|
14268
14713
|
archiveStale,
|
|
14269
14714
|
agentHoldsLock,
|
|
14715
|
+
addTaskComment,
|
|
14270
14716
|
acquireMemoryWriteLock,
|
|
14271
14717
|
acquireLock,
|
|
14272
14718
|
VersionConflictError,
|