@hasna/mementos 0.14.18 → 0.14.19
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 +473 -30
- package/dist/mcp/index.js +64 -31
- package/dist/sdk/index.js +397 -0
- package/dist/server/index.js +649 -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,420 @@ 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
|
+
return parseTask(row);
|
|
14230
|
+
}
|
|
14231
|
+
function getTask(db, id) {
|
|
14232
|
+
const row = db.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
14233
|
+
return row ? parseTask(row) : null;
|
|
14234
|
+
}
|
|
14235
|
+
function listTasks(db, filter) {
|
|
14236
|
+
let sql = "SELECT * FROM tasks WHERE 1=1";
|
|
14237
|
+
let countSql = "SELECT COUNT(*) as c FROM tasks WHERE 1=1";
|
|
14238
|
+
const params = [];
|
|
14239
|
+
const countParams = [];
|
|
14240
|
+
if (filter?.status) {
|
|
14241
|
+
sql += " AND status = ?";
|
|
14242
|
+
countSql += " AND status = ?";
|
|
14243
|
+
params.push(filter.status);
|
|
14244
|
+
countParams.push(filter.status);
|
|
14245
|
+
}
|
|
14246
|
+
if (filter?.priority) {
|
|
14247
|
+
sql += " AND priority = ?";
|
|
14248
|
+
countSql += " AND priority = ?";
|
|
14249
|
+
params.push(filter.priority);
|
|
14250
|
+
countParams.push(filter.priority);
|
|
14251
|
+
}
|
|
14252
|
+
if (filter?.assigned_agent_id) {
|
|
14253
|
+
sql += " AND assigned_agent_id = ?";
|
|
14254
|
+
countSql += " AND assigned_agent_id = ?";
|
|
14255
|
+
params.push(filter.assigned_agent_id);
|
|
14256
|
+
countParams.push(filter.assigned_agent_id);
|
|
14257
|
+
}
|
|
14258
|
+
if (filter?.project_id) {
|
|
14259
|
+
sql += " AND project_id = ?";
|
|
14260
|
+
countSql += " AND project_id = ?";
|
|
14261
|
+
params.push(filter.project_id);
|
|
14262
|
+
countParams.push(filter.project_id);
|
|
14263
|
+
}
|
|
14264
|
+
if (filter?.session_id) {
|
|
14265
|
+
sql += " AND session_id = ?";
|
|
14266
|
+
countSql += " AND session_id = ?";
|
|
14267
|
+
params.push(filter.session_id);
|
|
14268
|
+
countParams.push(filter.session_id);
|
|
14269
|
+
}
|
|
14270
|
+
if (filter?.parent_task_id !== undefined) {
|
|
14271
|
+
if (filter.parent_task_id === null) {
|
|
14272
|
+
sql += " AND parent_task_id IS NULL";
|
|
14273
|
+
countSql += " AND parent_task_id IS NULL";
|
|
14274
|
+
} else {
|
|
14275
|
+
sql += " AND parent_task_id = ?";
|
|
14276
|
+
countSql += " AND parent_task_id = ?";
|
|
14277
|
+
params.push(filter.parent_task_id);
|
|
14278
|
+
countParams.push(filter.parent_task_id);
|
|
14279
|
+
}
|
|
14280
|
+
}
|
|
14281
|
+
if (filter?.tags?.length) {
|
|
14282
|
+
const placeholders = filter.tags.map(() => "?").join(", ");
|
|
14283
|
+
sql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14284
|
+
countSql += ` AND EXISTS (SELECT 1 FROM json_each(tags) WHERE value IN (${placeholders}))`;
|
|
14285
|
+
params.push(...filter.tags);
|
|
14286
|
+
countParams.push(...filter.tags);
|
|
14287
|
+
}
|
|
14288
|
+
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";
|
|
14289
|
+
if (filter?.limit !== undefined) {
|
|
14290
|
+
sql += " LIMIT ?";
|
|
14291
|
+
params.push(filter.limit);
|
|
14292
|
+
}
|
|
14293
|
+
if (filter?.offset !== undefined) {
|
|
14294
|
+
sql += " OFFSET ?";
|
|
14295
|
+
params.push(filter.offset);
|
|
14296
|
+
}
|
|
14297
|
+
const rows = db.query(sql).all(...params);
|
|
14298
|
+
const countRow = db.query(countSql).get(...countParams);
|
|
14299
|
+
return { tasks: rows.map(parseTask), count: countRow.c };
|
|
14300
|
+
}
|
|
14301
|
+
function updateTask(db, id, input) {
|
|
14302
|
+
const existing = getTask(db, id);
|
|
14303
|
+
if (!existing)
|
|
14304
|
+
return null;
|
|
14305
|
+
const updates = [];
|
|
14306
|
+
const params = [];
|
|
14307
|
+
const ts = now();
|
|
14308
|
+
if (input.subject !== undefined) {
|
|
14309
|
+
updates.push("subject = ?");
|
|
14310
|
+
params.push(input.subject);
|
|
14311
|
+
}
|
|
14312
|
+
if (input.description !== undefined) {
|
|
14313
|
+
updates.push("description = ?");
|
|
14314
|
+
params.push(input.description);
|
|
14315
|
+
}
|
|
14316
|
+
if (input.status !== undefined) {
|
|
14317
|
+
updates.push("status = ?");
|
|
14318
|
+
params.push(input.status);
|
|
14319
|
+
if (input.status === "in_progress" && !existing.started_at) {
|
|
14320
|
+
updates.push("started_at = ?");
|
|
14321
|
+
params.push(ts);
|
|
14322
|
+
}
|
|
14323
|
+
if (input.status === "completed") {
|
|
14324
|
+
updates.push("completed_at = ?");
|
|
14325
|
+
params.push(ts);
|
|
14326
|
+
updates.push("progress = ?");
|
|
14327
|
+
params.push(1);
|
|
14328
|
+
}
|
|
14329
|
+
if (input.status === "failed") {
|
|
14330
|
+
updates.push("failed_at = ?");
|
|
14331
|
+
params.push(ts);
|
|
14332
|
+
}
|
|
14333
|
+
}
|
|
14334
|
+
if (input.priority !== undefined) {
|
|
14335
|
+
updates.push("priority = ?");
|
|
14336
|
+
params.push(input.priority);
|
|
14337
|
+
}
|
|
14338
|
+
if (input.tags !== undefined) {
|
|
14339
|
+
updates.push("tags = ?");
|
|
14340
|
+
params.push(JSON.stringify(input.tags));
|
|
14341
|
+
}
|
|
14342
|
+
if (input.assigned_agent_id !== undefined) {
|
|
14343
|
+
updates.push("assigned_agent_id = ?");
|
|
14344
|
+
params.push(input.assigned_agent_id);
|
|
14345
|
+
}
|
|
14346
|
+
if (input.metadata !== undefined) {
|
|
14347
|
+
updates.push("metadata = ?");
|
|
14348
|
+
params.push(JSON.stringify(input.metadata));
|
|
14349
|
+
}
|
|
14350
|
+
if (input.progress !== undefined) {
|
|
14351
|
+
updates.push("progress = ?");
|
|
14352
|
+
params.push(input.progress);
|
|
14353
|
+
}
|
|
14354
|
+
if (input.due_at !== undefined) {
|
|
14355
|
+
updates.push("due_at = ?");
|
|
14356
|
+
params.push(input.due_at);
|
|
14357
|
+
}
|
|
14358
|
+
if (input.error !== undefined) {
|
|
14359
|
+
updates.push("error = ?");
|
|
14360
|
+
params.push(input.error);
|
|
14361
|
+
}
|
|
14362
|
+
if (updates.length === 0)
|
|
14363
|
+
return existing;
|
|
14364
|
+
updates.push("updated_at = ?");
|
|
14365
|
+
params.push(ts);
|
|
14366
|
+
params.push(id);
|
|
14367
|
+
db.run(`UPDATE tasks SET ${updates.join(", ")} WHERE id = ?`, ...params);
|
|
14368
|
+
return getTask(db, id);
|
|
14369
|
+
}
|
|
14370
|
+
function deleteTask(db, id) {
|
|
14371
|
+
const result = db.run("DELETE FROM tasks WHERE id = ?", id);
|
|
14372
|
+
return result.changes > 0;
|
|
14373
|
+
}
|
|
14374
|
+
function addTaskComment(db, taskId, body, agentId) {
|
|
14375
|
+
const id = uuid();
|
|
14376
|
+
db.run("INSERT INTO task_comments (id, task_id, agent_id, body) VALUES (?, ?, ?, ?)", [id, taskId, agentId ?? null, body]);
|
|
14377
|
+
const row = db.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
14378
|
+
return parseTaskComment(row);
|
|
14379
|
+
}
|
|
14380
|
+
function parseTaskComment(row) {
|
|
14381
|
+
return {
|
|
14382
|
+
id: row.id,
|
|
14383
|
+
task_id: row.task_id,
|
|
14384
|
+
agent_id: row.agent_id ?? null,
|
|
14385
|
+
body: row.body,
|
|
14386
|
+
created_at: row.created_at
|
|
14387
|
+
};
|
|
14388
|
+
}
|
|
14389
|
+
function listTaskComments(db, taskId) {
|
|
14390
|
+
const rows = db.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at ASC").all(taskId);
|
|
14391
|
+
return { comments: rows.map(parseTaskComment), count: rows.length };
|
|
14392
|
+
}
|
|
14393
|
+
function deleteTaskComment(db, id) {
|
|
14394
|
+
const result = db.run("DELETE FROM task_comments WHERE id = ?", id);
|
|
14395
|
+
return result.changes > 0;
|
|
14396
|
+
}
|
|
14397
|
+
function getTaskStats(db, filter) {
|
|
14398
|
+
let where = "WHERE 1=1";
|
|
14399
|
+
const params = [];
|
|
14400
|
+
if (filter?.project_id) {
|
|
14401
|
+
where += " AND project_id = ?";
|
|
14402
|
+
params.push(filter.project_id);
|
|
14403
|
+
}
|
|
14404
|
+
if (filter?.agent_id) {
|
|
14405
|
+
where += " AND assigned_agent_id = ?";
|
|
14406
|
+
params.push(filter.agent_id);
|
|
14407
|
+
}
|
|
14408
|
+
const total = db.query(`SELECT COUNT(*) as c FROM tasks ${where}`).get(...params).c;
|
|
14409
|
+
const byStatus = {};
|
|
14410
|
+
const statusRows = db.query(`SELECT status, COUNT(*) as c FROM tasks ${where} GROUP BY status`).all(...params);
|
|
14411
|
+
for (const row of statusRows)
|
|
14412
|
+
byStatus[row.status] = row.c;
|
|
14413
|
+
const byPriority = {};
|
|
14414
|
+
const priorityRows = db.query(`SELECT priority, COUNT(*) as c FROM tasks ${where} GROUP BY priority`).all(...params);
|
|
14415
|
+
for (const row of priorityRows)
|
|
14416
|
+
byPriority[row.priority] = row.c;
|
|
14417
|
+
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;
|
|
14418
|
+
return {
|
|
14419
|
+
total,
|
|
14420
|
+
by_status: {
|
|
14421
|
+
pending: byStatus["pending"] ?? 0,
|
|
14422
|
+
in_progress: byStatus["in_progress"] ?? 0,
|
|
14423
|
+
completed: byStatus["completed"] ?? 0,
|
|
14424
|
+
failed: byStatus["failed"] ?? 0,
|
|
14425
|
+
cancelled: byStatus["cancelled"] ?? 0
|
|
14426
|
+
},
|
|
14427
|
+
by_priority: {
|
|
14428
|
+
critical: byPriority["critical"] ?? 0,
|
|
14429
|
+
high: byPriority["high"] ?? 0,
|
|
14430
|
+
medium: byPriority["medium"] ?? 0,
|
|
14431
|
+
low: byPriority["low"] ?? 0
|
|
14432
|
+
},
|
|
14433
|
+
overdue
|
|
14434
|
+
};
|
|
14435
|
+
}
|
|
14436
|
+
// src/lib/task-runner.ts
|
|
14437
|
+
init_database();
|
|
14438
|
+
var _handlers = new Map;
|
|
14439
|
+
var _defaultHandler = null;
|
|
14440
|
+
function registerTaskHandler(type, handler) {
|
|
14441
|
+
_handlers.set(type, handler);
|
|
14442
|
+
}
|
|
14443
|
+
function setDefaultTaskHandler(handler) {
|
|
14444
|
+
_defaultHandler = handler;
|
|
14445
|
+
}
|
|
14446
|
+
function resolveHandler(task) {
|
|
14447
|
+
for (const tag of task.tags) {
|
|
14448
|
+
const h = _handlers.get(tag);
|
|
14449
|
+
if (h)
|
|
14450
|
+
return h;
|
|
14451
|
+
}
|
|
14452
|
+
const metaType = task.metadata?.["type"];
|
|
14453
|
+
if (metaType) {
|
|
14454
|
+
const h = _handlers.get(metaType);
|
|
14455
|
+
if (h)
|
|
14456
|
+
return h;
|
|
14457
|
+
}
|
|
14458
|
+
return _defaultHandler;
|
|
14459
|
+
}
|
|
14460
|
+
var _workerStarted = false;
|
|
14461
|
+
var _processing = false;
|
|
14462
|
+
var _totalProcessed = 0;
|
|
14463
|
+
var _pollIntervalMs = 60000;
|
|
14464
|
+
function startTaskRunner(intervalMs) {
|
|
14465
|
+
if (_workerStarted)
|
|
14466
|
+
return;
|
|
14467
|
+
_workerStarted = true;
|
|
14468
|
+
if (intervalMs)
|
|
14469
|
+
_pollIntervalMs = intervalMs;
|
|
14470
|
+
console.log(`[task-runner] Started, polling every ${_pollIntervalMs / 1000}s`);
|
|
14471
|
+
_tick();
|
|
14472
|
+
setInterval(() => {
|
|
14473
|
+
_tick();
|
|
14474
|
+
}, _pollIntervalMs);
|
|
14475
|
+
}
|
|
14476
|
+
async function _tick() {
|
|
14477
|
+
if (_processing)
|
|
14478
|
+
return;
|
|
14479
|
+
let task = null;
|
|
14480
|
+
try {
|
|
14481
|
+
const db = getDatabase();
|
|
14482
|
+
const result = listTasks(db, { status: "pending", limit: 1 });
|
|
14483
|
+
if (result.tasks.length === 0)
|
|
14484
|
+
return;
|
|
14485
|
+
task = result.tasks[0] ?? null;
|
|
14486
|
+
} catch (e) {
|
|
14487
|
+
console.error("[task-runner] Failed to list pending tasks:", e);
|
|
14488
|
+
return;
|
|
14489
|
+
}
|
|
14490
|
+
if (!task)
|
|
14491
|
+
return;
|
|
14492
|
+
_processing = true;
|
|
14493
|
+
try {
|
|
14494
|
+
const db = getDatabase();
|
|
14495
|
+
updateTask(db, task.id, { status: "in_progress" });
|
|
14496
|
+
task = getTask(db, task.id);
|
|
14497
|
+
} catch (e) {
|
|
14498
|
+
console.error("[task-runner] Failed to claim task:", e);
|
|
14499
|
+
_processing = false;
|
|
14500
|
+
return;
|
|
14501
|
+
}
|
|
14502
|
+
const handler = resolveHandler(task);
|
|
14503
|
+
if (!handler) {
|
|
14504
|
+
console.warn(`[task-runner] No handler for task: ${task.subject} (tags: ${JSON.stringify(task.tags)}, meta.type: ${task.metadata?.["type"]})`);
|
|
14505
|
+
try {
|
|
14506
|
+
const db = getDatabase();
|
|
14507
|
+
updateTask(db, task.id, {
|
|
14508
|
+
status: "failed",
|
|
14509
|
+
error: "No handler registered for this task type"
|
|
14510
|
+
});
|
|
14511
|
+
} catch {}
|
|
14512
|
+
_processing = false;
|
|
14513
|
+
_totalProcessed++;
|
|
14514
|
+
return;
|
|
14515
|
+
}
|
|
14516
|
+
const taskId = task.id;
|
|
14517
|
+
let progress = 0;
|
|
14518
|
+
const ctx = {
|
|
14519
|
+
task,
|
|
14520
|
+
updateProgress: (p) => {
|
|
14521
|
+
progress = Math.min(1, Math.max(0, p));
|
|
14522
|
+
try {
|
|
14523
|
+
const db = getDatabase();
|
|
14524
|
+
updateTask(db, taskId, { progress });
|
|
14525
|
+
} catch {}
|
|
14526
|
+
},
|
|
14527
|
+
addComment: (body, agentId) => {
|
|
14528
|
+
try {
|
|
14529
|
+
const db = getDatabase();
|
|
14530
|
+
addTaskComment(db, taskId, body, agentId);
|
|
14531
|
+
} catch {}
|
|
14532
|
+
}
|
|
14533
|
+
};
|
|
14534
|
+
try {
|
|
14535
|
+
await handler(ctx);
|
|
14536
|
+
const db = getDatabase();
|
|
14537
|
+
updateTask(db, taskId, { status: "completed", progress: 1 });
|
|
14538
|
+
console.log(`[task-runner] Completed: ${task.subject}`);
|
|
14539
|
+
} catch (e) {
|
|
14540
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
14541
|
+
try {
|
|
14542
|
+
const db = getDatabase();
|
|
14543
|
+
updateTask(db, taskId, { status: "failed", error: errorMsg });
|
|
14544
|
+
} catch {}
|
|
14545
|
+
console.error(`[task-runner] Failed: ${task.subject} \u2014 ${errorMsg}`);
|
|
14546
|
+
} finally {
|
|
14547
|
+
_processing = false;
|
|
14548
|
+
_totalProcessed++;
|
|
14549
|
+
}
|
|
14550
|
+
}
|
|
14551
|
+
function getTaskRunnerStats() {
|
|
14552
|
+
try {
|
|
14553
|
+
const db = getDatabase();
|
|
14554
|
+
const rows = db.query("SELECT status, COUNT(*) as c FROM tasks GROUP BY status").all();
|
|
14555
|
+
const stats = {
|
|
14556
|
+
pending: 0,
|
|
14557
|
+
inProgress: 0,
|
|
14558
|
+
completed: 0,
|
|
14559
|
+
failed: 0,
|
|
14560
|
+
cancelled: 0,
|
|
14561
|
+
totalProcessed: _totalProcessed
|
|
14562
|
+
};
|
|
14563
|
+
for (const row of rows) {
|
|
14564
|
+
switch (row.status) {
|
|
14565
|
+
case "pending":
|
|
14566
|
+
stats.pending = row.c;
|
|
14567
|
+
break;
|
|
14568
|
+
case "in_progress":
|
|
14569
|
+
stats.inProgress = row.c;
|
|
14570
|
+
break;
|
|
14571
|
+
case "completed":
|
|
14572
|
+
stats.completed = row.c;
|
|
14573
|
+
break;
|
|
14574
|
+
case "failed":
|
|
14575
|
+
stats.failed = row.c;
|
|
14576
|
+
break;
|
|
14577
|
+
case "cancelled":
|
|
14578
|
+
stats.cancelled = row.c;
|
|
14579
|
+
break;
|
|
14580
|
+
}
|
|
14581
|
+
}
|
|
14582
|
+
return stats;
|
|
14583
|
+
} catch {
|
|
14584
|
+
return {
|
|
14585
|
+
pending: 0,
|
|
14586
|
+
inProgress: 0,
|
|
14587
|
+
completed: 0,
|
|
14588
|
+
failed: 0,
|
|
14589
|
+
cancelled: 0,
|
|
14590
|
+
totalProcessed: _totalProcessed
|
|
14591
|
+
};
|
|
14592
|
+
}
|
|
14593
|
+
}
|
|
14164
14594
|
export {
|
|
14165
14595
|
withMemoryLock,
|
|
14166
14596
|
uuid,
|
|
14597
|
+
updateTask,
|
|
14167
14598
|
updateMemory,
|
|
14168
14599
|
updateEntity,
|
|
14169
14600
|
updateAgent,
|
|
@@ -14173,9 +14604,11 @@ export {
|
|
|
14173
14604
|
touchMachine,
|
|
14174
14605
|
touchAgent,
|
|
14175
14606
|
syncMemories,
|
|
14607
|
+
startTaskRunner,
|
|
14176
14608
|
shortUuid,
|
|
14177
14609
|
setPrimaryMachine,
|
|
14178
14610
|
setFocus,
|
|
14611
|
+
setDefaultTaskHandler,
|
|
14179
14612
|
setActiveProfile,
|
|
14180
14613
|
setActiveModel,
|
|
14181
14614
|
searchMemories,
|
|
@@ -14188,6 +14621,7 @@ export {
|
|
|
14188
14621
|
releaseMemoryWriteLock,
|
|
14189
14622
|
releaseLock,
|
|
14190
14623
|
releaseAllAgentLocks,
|
|
14624
|
+
registerTaskHandler,
|
|
14191
14625
|
registerProject,
|
|
14192
14626
|
registerMachine,
|
|
14193
14627
|
registerAgent,
|
|
@@ -14200,6 +14634,8 @@ export {
|
|
|
14200
14634
|
mergeEntities,
|
|
14201
14635
|
memoryLockId,
|
|
14202
14636
|
loadConfig,
|
|
14637
|
+
listTasks,
|
|
14638
|
+
listTaskComments,
|
|
14203
14639
|
listRelations,
|
|
14204
14640
|
listProjects,
|
|
14205
14641
|
listProfiles,
|
|
@@ -14211,6 +14647,9 @@ export {
|
|
|
14211
14647
|
listAgentLocks,
|
|
14212
14648
|
linkEntityToMemory,
|
|
14213
14649
|
incrementRecallCount,
|
|
14650
|
+
getTaskStats,
|
|
14651
|
+
getTaskRunnerStats,
|
|
14652
|
+
getTask,
|
|
14214
14653
|
getRelation,
|
|
14215
14654
|
getRelatedEntities,
|
|
14216
14655
|
getProject,
|
|
@@ -14243,6 +14682,8 @@ export {
|
|
|
14243
14682
|
findPath,
|
|
14244
14683
|
enforceQuotas,
|
|
14245
14684
|
deprioritizeStale,
|
|
14685
|
+
deleteTaskComment,
|
|
14686
|
+
deleteTask,
|
|
14246
14687
|
deleteRelation,
|
|
14247
14688
|
deleteProfile,
|
|
14248
14689
|
deleteMemory,
|
|
@@ -14250,6 +14691,7 @@ export {
|
|
|
14250
14691
|
deleteEntity,
|
|
14251
14692
|
defaultSyncAgents,
|
|
14252
14693
|
dedup,
|
|
14694
|
+
createTask,
|
|
14253
14695
|
createRelation,
|
|
14254
14696
|
createMemory,
|
|
14255
14697
|
createEntity,
|
|
@@ -14267,6 +14709,7 @@ export {
|
|
|
14267
14709
|
archiveUnused,
|
|
14268
14710
|
archiveStale,
|
|
14269
14711
|
agentHoldsLock,
|
|
14712
|
+
addTaskComment,
|
|
14270
14713
|
acquireMemoryWriteLock,
|
|
14271
14714
|
acquireLock,
|
|
14272
14715
|
VersionConflictError,
|