@hasna/todos 0.9.28 → 0.9.30
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 +274 -131
- package/dist/index.js +55 -12
- package/dist/mcp/index.js +199 -58
- package/dist/server/index.js +65 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -264,6 +264,10 @@ var MIGRATIONS = [
|
|
|
264
264
|
ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
|
|
265
265
|
|
|
266
266
|
INSERT OR IGNORE INTO _migrations (id) VALUES (10);
|
|
267
|
+
`,
|
|
268
|
+
`
|
|
269
|
+
ALTER TABLE agents ADD COLUMN reports_to TEXT;
|
|
270
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (11);
|
|
267
271
|
`
|
|
268
272
|
];
|
|
269
273
|
var _db = null;
|
|
@@ -385,6 +389,7 @@ function ensureSchema(db) {
|
|
|
385
389
|
ensureColumn("tasks", "approved_at", "TEXT");
|
|
386
390
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
387
391
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
392
|
+
ensureColumn("agents", "reports_to", "TEXT");
|
|
388
393
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
389
394
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
390
395
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
@@ -1115,7 +1120,18 @@ function updateTask(id, input, db) {
|
|
|
1115
1120
|
logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
|
|
1116
1121
|
if (input.approved_by !== undefined)
|
|
1117
1122
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
1118
|
-
return
|
|
1123
|
+
return {
|
|
1124
|
+
...task,
|
|
1125
|
+
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
1126
|
+
tags: input.tags ?? task.tags,
|
|
1127
|
+
metadata: input.metadata ?? task.metadata,
|
|
1128
|
+
version: task.version + 1,
|
|
1129
|
+
updated_at: now(),
|
|
1130
|
+
completed_at: input.status === "completed" ? now() : task.completed_at,
|
|
1131
|
+
requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
|
|
1132
|
+
approved_by: input.approved_by ?? task.approved_by,
|
|
1133
|
+
approved_at: input.approved_by ? now() : task.approved_at
|
|
1134
|
+
};
|
|
1119
1135
|
}
|
|
1120
1136
|
function deleteTask(id, db) {
|
|
1121
1137
|
const d = db || getDatabase();
|
|
@@ -1137,6 +1153,9 @@ function getBlockingDeps(id, db) {
|
|
|
1137
1153
|
}
|
|
1138
1154
|
function startTask(id, agentId, db) {
|
|
1139
1155
|
const d = db || getDatabase();
|
|
1156
|
+
const task = getTask(id, d);
|
|
1157
|
+
if (!task)
|
|
1158
|
+
throw new TaskNotFoundError(id);
|
|
1140
1159
|
const blocking = getBlockingDeps(id, d);
|
|
1141
1160
|
if (blocking.length > 0) {
|
|
1142
1161
|
const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
|
|
@@ -1147,15 +1166,12 @@ function startTask(id, agentId, db) {
|
|
|
1147
1166
|
const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
1148
1167
|
WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
|
|
1149
1168
|
if (result.changes === 0) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
throw new TaskNotFoundError(id);
|
|
1153
|
-
if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
|
|
1154
|
-
throw new LockError(id, current.locked_by);
|
|
1169
|
+
if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
|
|
1170
|
+
throw new LockError(id, task.locked_by);
|
|
1155
1171
|
}
|
|
1156
1172
|
}
|
|
1157
1173
|
logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
|
|
1158
|
-
return
|
|
1174
|
+
return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
|
|
1159
1175
|
}
|
|
1160
1176
|
function completeTask(id, agentId, db, evidence) {
|
|
1161
1177
|
const d = db || getDatabase();
|
|
@@ -1167,14 +1183,15 @@ function completeTask(id, agentId, db, evidence) {
|
|
|
1167
1183
|
}
|
|
1168
1184
|
checkCompletionGuard(task, agentId || null, d);
|
|
1169
1185
|
if (evidence) {
|
|
1170
|
-
const
|
|
1171
|
-
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(
|
|
1186
|
+
const meta2 = { ...task.metadata, _evidence: evidence };
|
|
1187
|
+
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
|
|
1172
1188
|
}
|
|
1173
1189
|
const timestamp = now();
|
|
1174
1190
|
d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
|
|
1175
1191
|
WHERE id = ?`, [timestamp, timestamp, id]);
|
|
1176
1192
|
logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
|
|
1177
|
-
|
|
1193
|
+
const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
|
|
1194
|
+
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
1178
1195
|
}
|
|
1179
1196
|
function lockTask(id, agentId, db) {
|
|
1180
1197
|
const d = db || getDatabase();
|
|
@@ -1376,13 +1393,14 @@ function registerAgent(input, db) {
|
|
|
1376
1393
|
}
|
|
1377
1394
|
const id = shortUuid();
|
|
1378
1395
|
const timestamp = now();
|
|
1379
|
-
d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
|
|
1380
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
1396
|
+
d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
|
|
1397
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
1381
1398
|
id,
|
|
1382
1399
|
input.name,
|
|
1383
1400
|
input.description || null,
|
|
1384
1401
|
input.role || "agent",
|
|
1385
1402
|
JSON.stringify(input.permissions || ["*"]),
|
|
1403
|
+
input.reports_to || null,
|
|
1386
1404
|
JSON.stringify(input.metadata || {}),
|
|
1387
1405
|
timestamp,
|
|
1388
1406
|
timestamp
|
|
@@ -1430,6 +1448,10 @@ function updateAgent(id, input, db) {
|
|
|
1430
1448
|
sets.push("permissions = ?");
|
|
1431
1449
|
params.push(JSON.stringify(input.permissions));
|
|
1432
1450
|
}
|
|
1451
|
+
if (input.reports_to !== undefined) {
|
|
1452
|
+
sets.push("reports_to = ?");
|
|
1453
|
+
params.push(input.reports_to);
|
|
1454
|
+
}
|
|
1433
1455
|
if (input.metadata !== undefined) {
|
|
1434
1456
|
sets.push("metadata = ?");
|
|
1435
1457
|
params.push(JSON.stringify(input.metadata));
|
|
@@ -1442,6 +1464,25 @@ function deleteAgent(id, db) {
|
|
|
1442
1464
|
const d = db || getDatabase();
|
|
1443
1465
|
return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
|
|
1444
1466
|
}
|
|
1467
|
+
function getDirectReports(agentId, db) {
|
|
1468
|
+
const d = db || getDatabase();
|
|
1469
|
+
return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
|
|
1470
|
+
}
|
|
1471
|
+
function getOrgChart(db) {
|
|
1472
|
+
const agents = listAgents(db);
|
|
1473
|
+
const byManager = new Map;
|
|
1474
|
+
for (const a of agents) {
|
|
1475
|
+
const key = a.reports_to;
|
|
1476
|
+
if (!byManager.has(key))
|
|
1477
|
+
byManager.set(key, []);
|
|
1478
|
+
byManager.get(key).push(a);
|
|
1479
|
+
}
|
|
1480
|
+
function buildTree(parentId) {
|
|
1481
|
+
const children = byManager.get(parentId) || [];
|
|
1482
|
+
return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
|
|
1483
|
+
}
|
|
1484
|
+
return buildTree(null);
|
|
1485
|
+
}
|
|
1445
1486
|
// src/db/task-lists.ts
|
|
1446
1487
|
function rowToTaskList(row) {
|
|
1447
1488
|
return {
|
|
@@ -2252,6 +2293,8 @@ export {
|
|
|
2252
2293
|
getProjectByPath,
|
|
2253
2294
|
getProject,
|
|
2254
2295
|
getPlan,
|
|
2296
|
+
getOrgChart,
|
|
2297
|
+
getDirectReports,
|
|
2255
2298
|
getDatabase,
|
|
2256
2299
|
getCompletionGuardConfig,
|
|
2257
2300
|
getComment,
|
package/dist/mcp/index.js
CHANGED
|
@@ -187,6 +187,7 @@ function ensureSchema(db) {
|
|
|
187
187
|
ensureColumn("tasks", "approved_at", "TEXT");
|
|
188
188
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
189
189
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
190
|
+
ensureColumn("agents", "reports_to", "TEXT");
|
|
190
191
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
191
192
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
192
193
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
@@ -478,6 +479,10 @@ var init_database = __esm(() => {
|
|
|
478
479
|
ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
|
|
479
480
|
|
|
480
481
|
INSERT OR IGNORE INTO _migrations (id) VALUES (10);
|
|
482
|
+
`,
|
|
483
|
+
`
|
|
484
|
+
ALTER TABLE agents ADD COLUMN reports_to TEXT;
|
|
485
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (11);
|
|
481
486
|
`
|
|
482
487
|
];
|
|
483
488
|
});
|
|
@@ -509,6 +514,132 @@ var init_audit = __esm(() => {
|
|
|
509
514
|
init_database();
|
|
510
515
|
});
|
|
511
516
|
|
|
517
|
+
// src/db/agents.ts
|
|
518
|
+
var exports_agents = {};
|
|
519
|
+
__export(exports_agents, {
|
|
520
|
+
updateAgentActivity: () => updateAgentActivity,
|
|
521
|
+
updateAgent: () => updateAgent,
|
|
522
|
+
registerAgent: () => registerAgent,
|
|
523
|
+
listAgents: () => listAgents,
|
|
524
|
+
getOrgChart: () => getOrgChart,
|
|
525
|
+
getDirectReports: () => getDirectReports,
|
|
526
|
+
getAgentByName: () => getAgentByName,
|
|
527
|
+
getAgent: () => getAgent,
|
|
528
|
+
deleteAgent: () => deleteAgent
|
|
529
|
+
});
|
|
530
|
+
function shortUuid() {
|
|
531
|
+
return crypto.randomUUID().slice(0, 8);
|
|
532
|
+
}
|
|
533
|
+
function rowToAgent(row) {
|
|
534
|
+
return {
|
|
535
|
+
...row,
|
|
536
|
+
permissions: JSON.parse(row.permissions || '["*"]'),
|
|
537
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
function registerAgent(input, db) {
|
|
541
|
+
const d = db || getDatabase();
|
|
542
|
+
const existing = getAgentByName(input.name, d);
|
|
543
|
+
if (existing) {
|
|
544
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
|
|
545
|
+
return getAgent(existing.id, d);
|
|
546
|
+
}
|
|
547
|
+
const id = shortUuid();
|
|
548
|
+
const timestamp = now();
|
|
549
|
+
d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
|
|
550
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
551
|
+
id,
|
|
552
|
+
input.name,
|
|
553
|
+
input.description || null,
|
|
554
|
+
input.role || "agent",
|
|
555
|
+
JSON.stringify(input.permissions || ["*"]),
|
|
556
|
+
input.reports_to || null,
|
|
557
|
+
JSON.stringify(input.metadata || {}),
|
|
558
|
+
timestamp,
|
|
559
|
+
timestamp
|
|
560
|
+
]);
|
|
561
|
+
return getAgent(id, d);
|
|
562
|
+
}
|
|
563
|
+
function getAgent(id, db) {
|
|
564
|
+
const d = db || getDatabase();
|
|
565
|
+
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
566
|
+
return row ? rowToAgent(row) : null;
|
|
567
|
+
}
|
|
568
|
+
function getAgentByName(name, db) {
|
|
569
|
+
const d = db || getDatabase();
|
|
570
|
+
const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
|
|
571
|
+
return row ? rowToAgent(row) : null;
|
|
572
|
+
}
|
|
573
|
+
function listAgents(db) {
|
|
574
|
+
const d = db || getDatabase();
|
|
575
|
+
return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
|
|
576
|
+
}
|
|
577
|
+
function updateAgentActivity(id, db) {
|
|
578
|
+
const d = db || getDatabase();
|
|
579
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), id]);
|
|
580
|
+
}
|
|
581
|
+
function updateAgent(id, input, db) {
|
|
582
|
+
const d = db || getDatabase();
|
|
583
|
+
const agent = getAgent(id, d);
|
|
584
|
+
if (!agent)
|
|
585
|
+
throw new Error(`Agent not found: ${id}`);
|
|
586
|
+
const sets = ["last_seen_at = ?"];
|
|
587
|
+
const params = [now()];
|
|
588
|
+
if (input.name !== undefined) {
|
|
589
|
+
sets.push("name = ?");
|
|
590
|
+
params.push(input.name);
|
|
591
|
+
}
|
|
592
|
+
if (input.description !== undefined) {
|
|
593
|
+
sets.push("description = ?");
|
|
594
|
+
params.push(input.description);
|
|
595
|
+
}
|
|
596
|
+
if (input.role !== undefined) {
|
|
597
|
+
sets.push("role = ?");
|
|
598
|
+
params.push(input.role);
|
|
599
|
+
}
|
|
600
|
+
if (input.permissions !== undefined) {
|
|
601
|
+
sets.push("permissions = ?");
|
|
602
|
+
params.push(JSON.stringify(input.permissions));
|
|
603
|
+
}
|
|
604
|
+
if (input.reports_to !== undefined) {
|
|
605
|
+
sets.push("reports_to = ?");
|
|
606
|
+
params.push(input.reports_to);
|
|
607
|
+
}
|
|
608
|
+
if (input.metadata !== undefined) {
|
|
609
|
+
sets.push("metadata = ?");
|
|
610
|
+
params.push(JSON.stringify(input.metadata));
|
|
611
|
+
}
|
|
612
|
+
params.push(id);
|
|
613
|
+
d.run(`UPDATE agents SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
614
|
+
return getAgent(id, d);
|
|
615
|
+
}
|
|
616
|
+
function deleteAgent(id, db) {
|
|
617
|
+
const d = db || getDatabase();
|
|
618
|
+
return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
|
|
619
|
+
}
|
|
620
|
+
function getDirectReports(agentId, db) {
|
|
621
|
+
const d = db || getDatabase();
|
|
622
|
+
return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
|
|
623
|
+
}
|
|
624
|
+
function getOrgChart(db) {
|
|
625
|
+
const agents = listAgents(db);
|
|
626
|
+
const byManager = new Map;
|
|
627
|
+
for (const a of agents) {
|
|
628
|
+
const key = a.reports_to;
|
|
629
|
+
if (!byManager.has(key))
|
|
630
|
+
byManager.set(key, []);
|
|
631
|
+
byManager.get(key).push(a);
|
|
632
|
+
}
|
|
633
|
+
function buildTree(parentId) {
|
|
634
|
+
const children = byManager.get(parentId) || [];
|
|
635
|
+
return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
|
|
636
|
+
}
|
|
637
|
+
return buildTree(null);
|
|
638
|
+
}
|
|
639
|
+
var init_agents = __esm(() => {
|
|
640
|
+
init_database();
|
|
641
|
+
});
|
|
642
|
+
|
|
512
643
|
// src/db/webhooks.ts
|
|
513
644
|
var exports_webhooks = {};
|
|
514
645
|
__export(exports_webhooks, {
|
|
@@ -5154,7 +5285,18 @@ function updateTask(id, input, db) {
|
|
|
5154
5285
|
logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
|
|
5155
5286
|
if (input.approved_by !== undefined)
|
|
5156
5287
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
5157
|
-
return
|
|
5288
|
+
return {
|
|
5289
|
+
...task,
|
|
5290
|
+
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
5291
|
+
tags: input.tags ?? task.tags,
|
|
5292
|
+
metadata: input.metadata ?? task.metadata,
|
|
5293
|
+
version: task.version + 1,
|
|
5294
|
+
updated_at: now(),
|
|
5295
|
+
completed_at: input.status === "completed" ? now() : task.completed_at,
|
|
5296
|
+
requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
|
|
5297
|
+
approved_by: input.approved_by ?? task.approved_by,
|
|
5298
|
+
approved_at: input.approved_by ? now() : task.approved_at
|
|
5299
|
+
};
|
|
5158
5300
|
}
|
|
5159
5301
|
function deleteTask(id, db) {
|
|
5160
5302
|
const d = db || getDatabase();
|
|
@@ -5176,6 +5318,9 @@ function getBlockingDeps(id, db) {
|
|
|
5176
5318
|
}
|
|
5177
5319
|
function startTask(id, agentId, db) {
|
|
5178
5320
|
const d = db || getDatabase();
|
|
5321
|
+
const task = getTask(id, d);
|
|
5322
|
+
if (!task)
|
|
5323
|
+
throw new TaskNotFoundError(id);
|
|
5179
5324
|
const blocking = getBlockingDeps(id, d);
|
|
5180
5325
|
if (blocking.length > 0) {
|
|
5181
5326
|
const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
|
|
@@ -5186,15 +5331,12 @@ function startTask(id, agentId, db) {
|
|
|
5186
5331
|
const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
5187
5332
|
WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
|
|
5188
5333
|
if (result.changes === 0) {
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
throw new TaskNotFoundError(id);
|
|
5192
|
-
if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
|
|
5193
|
-
throw new LockError(id, current.locked_by);
|
|
5334
|
+
if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
|
|
5335
|
+
throw new LockError(id, task.locked_by);
|
|
5194
5336
|
}
|
|
5195
5337
|
}
|
|
5196
5338
|
logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
|
|
5197
|
-
return
|
|
5339
|
+
return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
|
|
5198
5340
|
}
|
|
5199
5341
|
function completeTask(id, agentId, db, evidence) {
|
|
5200
5342
|
const d = db || getDatabase();
|
|
@@ -5206,14 +5348,15 @@ function completeTask(id, agentId, db, evidence) {
|
|
|
5206
5348
|
}
|
|
5207
5349
|
checkCompletionGuard(task, agentId || null, d);
|
|
5208
5350
|
if (evidence) {
|
|
5209
|
-
const
|
|
5210
|
-
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(
|
|
5351
|
+
const meta2 = { ...task.metadata, _evidence: evidence };
|
|
5352
|
+
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
|
|
5211
5353
|
}
|
|
5212
5354
|
const timestamp = now();
|
|
5213
5355
|
d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
|
|
5214
5356
|
WHERE id = ?`, [timestamp, timestamp, id]);
|
|
5215
5357
|
logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
|
|
5216
|
-
|
|
5358
|
+
const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
|
|
5359
|
+
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
5217
5360
|
}
|
|
5218
5361
|
function lockTask(id, agentId, db) {
|
|
5219
5362
|
const d = db || getDatabase();
|
|
@@ -5387,54 +5530,8 @@ function deletePlan(id, db) {
|
|
|
5387
5530
|
return result.changes > 0;
|
|
5388
5531
|
}
|
|
5389
5532
|
|
|
5390
|
-
// src/
|
|
5391
|
-
|
|
5392
|
-
function shortUuid() {
|
|
5393
|
-
return crypto.randomUUID().slice(0, 8);
|
|
5394
|
-
}
|
|
5395
|
-
function rowToAgent(row) {
|
|
5396
|
-
return {
|
|
5397
|
-
...row,
|
|
5398
|
-
permissions: JSON.parse(row.permissions || '["*"]'),
|
|
5399
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
5400
|
-
};
|
|
5401
|
-
}
|
|
5402
|
-
function registerAgent(input, db) {
|
|
5403
|
-
const d = db || getDatabase();
|
|
5404
|
-
const existing = getAgentByName(input.name, d);
|
|
5405
|
-
if (existing) {
|
|
5406
|
-
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
|
|
5407
|
-
return getAgent(existing.id, d);
|
|
5408
|
-
}
|
|
5409
|
-
const id = shortUuid();
|
|
5410
|
-
const timestamp = now();
|
|
5411
|
-
d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
|
|
5412
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5413
|
-
id,
|
|
5414
|
-
input.name,
|
|
5415
|
-
input.description || null,
|
|
5416
|
-
input.role || "agent",
|
|
5417
|
-
JSON.stringify(input.permissions || ["*"]),
|
|
5418
|
-
JSON.stringify(input.metadata || {}),
|
|
5419
|
-
timestamp,
|
|
5420
|
-
timestamp
|
|
5421
|
-
]);
|
|
5422
|
-
return getAgent(id, d);
|
|
5423
|
-
}
|
|
5424
|
-
function getAgent(id, db) {
|
|
5425
|
-
const d = db || getDatabase();
|
|
5426
|
-
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
5427
|
-
return row ? rowToAgent(row) : null;
|
|
5428
|
-
}
|
|
5429
|
-
function getAgentByName(name, db) {
|
|
5430
|
-
const d = db || getDatabase();
|
|
5431
|
-
const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
|
|
5432
|
-
return row ? rowToAgent(row) : null;
|
|
5433
|
-
}
|
|
5434
|
-
function listAgents(db) {
|
|
5435
|
-
const d = db || getDatabase();
|
|
5436
|
-
return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
|
|
5437
|
-
}
|
|
5533
|
+
// src/mcp/index.ts
|
|
5534
|
+
init_agents();
|
|
5438
5535
|
|
|
5439
5536
|
// src/db/task-lists.ts
|
|
5440
5537
|
init_database();
|
|
@@ -6959,6 +7056,50 @@ In Progress:`);
|
|
|
6959
7056
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
6960
7057
|
}
|
|
6961
7058
|
});
|
|
7059
|
+
server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {}, async () => {
|
|
7060
|
+
try {
|
|
7061
|
+
let render = function(nodes, indent = 0) {
|
|
7062
|
+
return nodes.map((n) => {
|
|
7063
|
+
const prefix = " ".repeat(indent);
|
|
7064
|
+
const role = n.agent.role ? ` (${n.agent.role})` : "";
|
|
7065
|
+
const line = `${prefix}${n.agent.name}${role} [${n.agent.id}]`;
|
|
7066
|
+
const children = n.reports.length > 0 ? `
|
|
7067
|
+
` + render(n.reports, indent + 1) : "";
|
|
7068
|
+
return line + children;
|
|
7069
|
+
}).join(`
|
|
7070
|
+
`);
|
|
7071
|
+
};
|
|
7072
|
+
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
7073
|
+
const tree = getOrgChart2();
|
|
7074
|
+
const text = tree.length > 0 ? render(tree) : "No agents registered.";
|
|
7075
|
+
return { content: [{ type: "text", text }] };
|
|
7076
|
+
} catch (e) {
|
|
7077
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7078
|
+
}
|
|
7079
|
+
});
|
|
7080
|
+
server.tool("set_reports_to", "Set who an agent reports to in the org chart.", {
|
|
7081
|
+
agent_name: exports_external.string(),
|
|
7082
|
+
manager_name: exports_external.string().optional()
|
|
7083
|
+
}, async ({ agent_name, manager_name }) => {
|
|
7084
|
+
try {
|
|
7085
|
+
const agent = getAgentByName(agent_name);
|
|
7086
|
+
if (!agent)
|
|
7087
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
7088
|
+
let managerId = null;
|
|
7089
|
+
if (manager_name) {
|
|
7090
|
+
const manager = getAgentByName(manager_name);
|
|
7091
|
+
if (!manager)
|
|
7092
|
+
return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
|
|
7093
|
+
managerId = manager.id;
|
|
7094
|
+
}
|
|
7095
|
+
const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
7096
|
+
updateAgent2(agent.id, { reports_to: managerId });
|
|
7097
|
+
const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
|
|
7098
|
+
return { content: [{ type: "text", text: result }] };
|
|
7099
|
+
} catch (e) {
|
|
7100
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7101
|
+
}
|
|
7102
|
+
});
|
|
6962
7103
|
server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
|
|
6963
7104
|
const all = [
|
|
6964
7105
|
"create_task",
|
package/dist/server/index.js
CHANGED
|
@@ -249,6 +249,7 @@ function ensureSchema(db) {
|
|
|
249
249
|
ensureColumn("tasks", "approved_at", "TEXT");
|
|
250
250
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
251
251
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
252
|
+
ensureColumn("agents", "reports_to", "TEXT");
|
|
252
253
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
253
254
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
254
255
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
@@ -526,6 +527,10 @@ var init_database = __esm(() => {
|
|
|
526
527
|
ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
|
|
527
528
|
|
|
528
529
|
INSERT OR IGNORE INTO _migrations (id) VALUES (10);
|
|
530
|
+
`,
|
|
531
|
+
`
|
|
532
|
+
ALTER TABLE agents ADD COLUMN reports_to TEXT;
|
|
533
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (11);
|
|
529
534
|
`
|
|
530
535
|
];
|
|
531
536
|
});
|
|
@@ -680,6 +685,8 @@ __export(exports_agents, {
|
|
|
680
685
|
updateAgent: () => updateAgent,
|
|
681
686
|
registerAgent: () => registerAgent,
|
|
682
687
|
listAgents: () => listAgents,
|
|
688
|
+
getOrgChart: () => getOrgChart,
|
|
689
|
+
getDirectReports: () => getDirectReports,
|
|
683
690
|
getAgentByName: () => getAgentByName,
|
|
684
691
|
getAgent: () => getAgent,
|
|
685
692
|
deleteAgent: () => deleteAgent
|
|
@@ -703,13 +710,14 @@ function registerAgent(input, db) {
|
|
|
703
710
|
}
|
|
704
711
|
const id = shortUuid();
|
|
705
712
|
const timestamp = now();
|
|
706
|
-
d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
|
|
707
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
713
|
+
d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
|
|
714
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
708
715
|
id,
|
|
709
716
|
input.name,
|
|
710
717
|
input.description || null,
|
|
711
718
|
input.role || "agent",
|
|
712
719
|
JSON.stringify(input.permissions || ["*"]),
|
|
720
|
+
input.reports_to || null,
|
|
713
721
|
JSON.stringify(input.metadata || {}),
|
|
714
722
|
timestamp,
|
|
715
723
|
timestamp
|
|
@@ -757,6 +765,10 @@ function updateAgent(id, input, db) {
|
|
|
757
765
|
sets.push("permissions = ?");
|
|
758
766
|
params.push(JSON.stringify(input.permissions));
|
|
759
767
|
}
|
|
768
|
+
if (input.reports_to !== undefined) {
|
|
769
|
+
sets.push("reports_to = ?");
|
|
770
|
+
params.push(input.reports_to);
|
|
771
|
+
}
|
|
760
772
|
if (input.metadata !== undefined) {
|
|
761
773
|
sets.push("metadata = ?");
|
|
762
774
|
params.push(JSON.stringify(input.metadata));
|
|
@@ -769,6 +781,25 @@ function deleteAgent(id, db) {
|
|
|
769
781
|
const d = db || getDatabase();
|
|
770
782
|
return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
|
|
771
783
|
}
|
|
784
|
+
function getDirectReports(agentId, db) {
|
|
785
|
+
const d = db || getDatabase();
|
|
786
|
+
return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
|
|
787
|
+
}
|
|
788
|
+
function getOrgChart(db) {
|
|
789
|
+
const agents = listAgents(db);
|
|
790
|
+
const byManager = new Map;
|
|
791
|
+
for (const a of agents) {
|
|
792
|
+
const key = a.reports_to;
|
|
793
|
+
if (!byManager.has(key))
|
|
794
|
+
byManager.set(key, []);
|
|
795
|
+
byManager.get(key).push(a);
|
|
796
|
+
}
|
|
797
|
+
function buildTree(parentId) {
|
|
798
|
+
const children = byManager.get(parentId) || [];
|
|
799
|
+
return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
|
|
800
|
+
}
|
|
801
|
+
return buildTree(null);
|
|
802
|
+
}
|
|
772
803
|
var init_agents = __esm(() => {
|
|
773
804
|
init_database();
|
|
774
805
|
});
|
|
@@ -1233,7 +1264,18 @@ function updateTask(id, input, db) {
|
|
|
1233
1264
|
logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
|
|
1234
1265
|
if (input.approved_by !== undefined)
|
|
1235
1266
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
1236
|
-
return
|
|
1267
|
+
return {
|
|
1268
|
+
...task,
|
|
1269
|
+
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
1270
|
+
tags: input.tags ?? task.tags,
|
|
1271
|
+
metadata: input.metadata ?? task.metadata,
|
|
1272
|
+
version: task.version + 1,
|
|
1273
|
+
updated_at: now(),
|
|
1274
|
+
completed_at: input.status === "completed" ? now() : task.completed_at,
|
|
1275
|
+
requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
|
|
1276
|
+
approved_by: input.approved_by ?? task.approved_by,
|
|
1277
|
+
approved_at: input.approved_by ? now() : task.approved_at
|
|
1278
|
+
};
|
|
1237
1279
|
}
|
|
1238
1280
|
function deleteTask(id, db) {
|
|
1239
1281
|
const d = db || getDatabase();
|
|
@@ -1255,6 +1297,9 @@ function getBlockingDeps(id, db) {
|
|
|
1255
1297
|
}
|
|
1256
1298
|
function startTask(id, agentId, db) {
|
|
1257
1299
|
const d = db || getDatabase();
|
|
1300
|
+
const task = getTask(id, d);
|
|
1301
|
+
if (!task)
|
|
1302
|
+
throw new TaskNotFoundError(id);
|
|
1258
1303
|
const blocking = getBlockingDeps(id, d);
|
|
1259
1304
|
if (blocking.length > 0) {
|
|
1260
1305
|
const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
|
|
@@ -1265,15 +1310,12 @@ function startTask(id, agentId, db) {
|
|
|
1265
1310
|
const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
1266
1311
|
WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
|
|
1267
1312
|
if (result.changes === 0) {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
throw new TaskNotFoundError(id);
|
|
1271
|
-
if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
|
|
1272
|
-
throw new LockError(id, current.locked_by);
|
|
1313
|
+
if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
|
|
1314
|
+
throw new LockError(id, task.locked_by);
|
|
1273
1315
|
}
|
|
1274
1316
|
}
|
|
1275
1317
|
logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
|
|
1276
|
-
return
|
|
1318
|
+
return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
|
|
1277
1319
|
}
|
|
1278
1320
|
function completeTask(id, agentId, db, evidence) {
|
|
1279
1321
|
const d = db || getDatabase();
|
|
@@ -1285,14 +1327,15 @@ function completeTask(id, agentId, db, evidence) {
|
|
|
1285
1327
|
}
|
|
1286
1328
|
checkCompletionGuard(task, agentId || null, d);
|
|
1287
1329
|
if (evidence) {
|
|
1288
|
-
const
|
|
1289
|
-
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(
|
|
1330
|
+
const meta2 = { ...task.metadata, _evidence: evidence };
|
|
1331
|
+
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
|
|
1290
1332
|
}
|
|
1291
1333
|
const timestamp = now();
|
|
1292
1334
|
d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
|
|
1293
1335
|
WHERE id = ?`, [timestamp, timestamp, id]);
|
|
1294
1336
|
logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
|
|
1295
|
-
|
|
1337
|
+
const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
|
|
1338
|
+
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
1296
1339
|
}
|
|
1297
1340
|
function getTaskDependencies(taskId, db) {
|
|
1298
1341
|
const d = db || getDatabase();
|
|
@@ -1739,6 +1782,16 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
1739
1782
|
return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
|
|
1740
1783
|
}
|
|
1741
1784
|
}
|
|
1785
|
+
if (path === "/api/org" && method === "GET") {
|
|
1786
|
+
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
1787
|
+
return json(getOrgChart2(), 200, port);
|
|
1788
|
+
}
|
|
1789
|
+
const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
|
|
1790
|
+
if (teamMatch && method === "GET") {
|
|
1791
|
+
const agentId = decodeURIComponent(teamMatch[1]);
|
|
1792
|
+
const { getDirectReports: getDirectReports2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
1793
|
+
return json(getDirectReports2(agentId), 200, port);
|
|
1794
|
+
}
|
|
1742
1795
|
if (path === "/api/agents" && method === "GET") {
|
|
1743
1796
|
return json(listAgents(), 200, port);
|
|
1744
1797
|
}
|