@hasna/todos 0.11.81 → 0.11.83

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.
@@ -1 +1 @@
1
- {"version":3,"file":"task-project-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-project-tools.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAmJjD,UAAU,kBAAkB;IAC1B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,kBAAkB,QA43FlF"}
1
+ {"version":3,"file":"task-project-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-project-tools.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAmJjD,UAAU,kBAAkB;IAC1B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,kBAAkB,QAo4FlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-workflow-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-workflow-tools.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAKjD,UAAU,mBAAmB;IAC3B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,mBAAmB,QA+VpF"}
1
+ {"version":3,"file":"task-workflow-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-workflow-tools.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAKjD,UAAU,mBAAmB;IAC3B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,mBAAmB,QAkZpF"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.11.81",
3
+ "packageVersion": "0.11.83",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "bc87cdfeb8453260c82e7a6aecf33d8d52ef9892",
6
- "generatedAt": "2026-07-08T11:32:21.756Z"
5
+ "gitCommit": "69a46e4c566599f0e473686aa6c49aa6df6be20d",
6
+ "generatedAt": "2026-07-08T14:22:35.687Z"
7
7
  }
@@ -943,7 +943,18 @@ function createPostgresTodosStorageAdapter(options) {
943
943
  claimNext: (agentId, filters) => claimNextTask(agentId, filters, store),
944
944
  getNext: (_agentId, filters) => getNextTask(filters, store),
945
945
  getActiveWork: (filters) => getActiveWork(filters, store),
946
- getChangedSince: (since, filters) => getChangedSince(since, filters, store)
946
+ getChangedSince: (since, filters) => getChangedSince(since, filters, store),
947
+ lock: (id, agentId) => lockTask(id, agentId, store),
948
+ unlock: (id, agentId) => unlockTask(id, agentId, store)
949
+ },
950
+ dependencies: {
951
+ add: (taskId, dependsOn, context) => addDependency(taskId, dependsOn, store, context),
952
+ remove: (taskId, dependsOn) => removeDependency(taskId, dependsOn, store),
953
+ list: (taskId) => listDependencies(taskId, store)
954
+ },
955
+ verifications: {
956
+ add: (input, context) => addVerification(input, store, context),
957
+ list: (taskId) => listVerifications(taskId, store)
947
958
  },
948
959
  projects: {
949
960
  create: (input, context) => createProject(input, store, context),
@@ -989,6 +1000,7 @@ function createPostgresTodosStorageAdapter(options) {
989
1000
  audit: {
990
1001
  logTaskChange: (taskId, action, field, oldValue, newValue, agentId, context) => logTaskChange(taskId, action, field, oldValue, newValue, agentId, store, context),
991
1002
  addComment: (input, context) => addComment(input, store, context),
1003
+ getComments: async (taskId) => (await store.list("comments")).filter((comment) => comment.task_id === taskId).sort((a, b) => a.created_at.localeCompare(b.created_at)),
992
1004
  getTaskHistory: async (taskId) => (await store.list("audit_history")).filter((entry2) => entry2.task_id === taskId).sort((a, b) => a.created_at.localeCompare(b.created_at)),
993
1005
  getRecentActivity: async (limit = 20) => (await store.list("audit_history")).sort((a, b) => b.created_at.localeCompare(a.created_at)).slice(0, limit)
994
1006
  },
@@ -1397,6 +1409,103 @@ async function patchTask(task, patch, store) {
1397
1409
  await store.upsert("tasks", updated);
1398
1410
  return updated;
1399
1411
  }
1412
+ function cloudLockExpired(lockedAt) {
1413
+ if (!lockedAt)
1414
+ return true;
1415
+ return new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000 < Date.now();
1416
+ }
1417
+ function cloudLockExpiresAt(lockedAt) {
1418
+ return new Date(new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000).toISOString();
1419
+ }
1420
+ async function lockTask(id, agentId, store) {
1421
+ const task = await requireRecord("tasks", id, store);
1422
+ if (task.status === "completed" || task.status === "cancelled") {
1423
+ return { success: false, error: `Task is ${task.status} and cannot be locked` };
1424
+ }
1425
+ if (task.locked_by && task.locked_by !== agentId && !cloudLockExpired(task.locked_at)) {
1426
+ return { success: false, locked_by: task.locked_by, locked_at: task.locked_at ?? undefined, error: `Task is locked by ${task.locked_by}` };
1427
+ }
1428
+ const timestamp = new Date().toISOString();
1429
+ await patchTask(task, { locked_by: agentId, locked_at: timestamp }, store);
1430
+ return { success: true, locked_by: agentId, locked_at: timestamp, expires_at: cloudLockExpiresAt(timestamp) };
1431
+ }
1432
+ async function unlockTask(id, agentId, store) {
1433
+ const task = await requireRecord("tasks", id, store);
1434
+ if (agentId && task.locked_by && task.locked_by !== agentId) {
1435
+ throw new Error(`Task ${id} is locked by ${task.locked_by}, not ${agentId}`);
1436
+ }
1437
+ await patchTask(task, { locked_by: null, locked_at: null }, store);
1438
+ return true;
1439
+ }
1440
+ function dependencyId(taskId, dependsOn) {
1441
+ return `${taskId}::${dependsOn}`;
1442
+ }
1443
+ async function addDependency(taskId, dependsOn, store, context) {
1444
+ if (taskId === dependsOn)
1445
+ throw new Error("A task cannot depend on itself");
1446
+ if (!await store.get("tasks", taskId))
1447
+ throw new Error(`Task not found: ${taskId}`);
1448
+ if (!await store.get("tasks", dependsOn))
1449
+ throw new Error(`Task not found: ${dependsOn}`);
1450
+ const edges = await store.list("dependencies");
1451
+ const adjacency = new Map;
1452
+ for (const edge of edges) {
1453
+ if (!adjacency.has(edge.task_id))
1454
+ adjacency.set(edge.task_id, []);
1455
+ adjacency.get(edge.task_id).push(edge.depends_on);
1456
+ }
1457
+ const queue = [dependsOn];
1458
+ const seen = new Set;
1459
+ while (queue.length) {
1460
+ const node = queue.shift();
1461
+ if (node === taskId)
1462
+ throw new Error(`Adding dependency ${taskId} -> ${dependsOn} would create a cycle`);
1463
+ if (seen.has(node))
1464
+ continue;
1465
+ seen.add(node);
1466
+ for (const next of adjacency.get(node) ?? [])
1467
+ queue.push(next);
1468
+ }
1469
+ const timestamp = new Date().toISOString();
1470
+ const record = { id: dependencyId(taskId, dependsOn), task_id: taskId, depends_on: dependsOn, created_at: timestamp, updated_at: timestamp };
1471
+ await store.upsert("dependencies", record, context);
1472
+ return { task_id: taskId, depends_on: dependsOn };
1473
+ }
1474
+ async function removeDependency(taskId, dependsOn, store) {
1475
+ const existing = await store.get("dependencies", dependencyId(taskId, dependsOn));
1476
+ if (!existing)
1477
+ return false;
1478
+ await store.delete("dependencies", dependencyId(taskId, dependsOn));
1479
+ return true;
1480
+ }
1481
+ async function listDependencies(taskId, store) {
1482
+ const edges = await store.list("dependencies");
1483
+ return {
1484
+ dependencies: edges.filter((edge) => edge.task_id === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on })),
1485
+ blocked_by: edges.filter((edge) => edge.depends_on === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on }))
1486
+ };
1487
+ }
1488
+ async function addVerification(input, store, context) {
1489
+ if (!await store.get("tasks", input.task_id))
1490
+ throw new Error(`Task not found: ${input.task_id}`);
1491
+ const timestamp = new Date().toISOString();
1492
+ const verification = {
1493
+ id: randomUUID(),
1494
+ task_id: input.task_id,
1495
+ command: input.command,
1496
+ status: input.status ?? "unknown",
1497
+ output_summary: input.output_summary ?? null,
1498
+ artifact_path: input.artifact_path ?? null,
1499
+ agent_id: input.agent_id ?? context?.agentId ?? null,
1500
+ run_at: timestamp,
1501
+ created_at: timestamp
1502
+ };
1503
+ await store.upsert("verifications", { ...verification, updated_at: timestamp }, context);
1504
+ return verification;
1505
+ }
1506
+ async function listVerifications(taskId, store) {
1507
+ return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
1508
+ }
1400
1509
  function toFilterArray(value) {
1401
1510
  return Array.isArray(value) ? value : [value];
1402
1511
  }
@@ -1770,7 +1879,7 @@ function compareClock(left, right) {
1770
1879
  function numberValue2(value) {
1771
1880
  return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
1772
1881
  }
1773
- var TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
1882
+ var CLOUD_LOCK_EXPIRY_MINUTES = 30, TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
1774
1883
  var init_postgres_adapter = () => {};
1775
1884
 
1776
1885
  // src/server/cloud.ts
@@ -8183,7 +8292,7 @@ function tasksFromTemplate(templateId, projectId, variables, taskListId, db, _vi
8183
8292
  const taskId = positionToId.get(tt.position);
8184
8293
  const depId = positionToId.get(depPos);
8185
8294
  if (taskId && depId) {
8186
- addDependency(taskId, depId, d);
8295
+ addDependency2(taskId, depId, d);
8187
8296
  }
8188
8297
  }
8189
8298
  }
@@ -8237,7 +8346,7 @@ var init_templates = __esm(() => {
8237
8346
  });
8238
8347
 
8239
8348
  // src/db/task-graph.ts
8240
- function addDependency(taskId, dependsOn, db) {
8349
+ function addDependency2(taskId, dependsOn, db) {
8241
8350
  const d = db || getDatabase();
8242
8351
  if (!getTask(taskId, d))
8243
8352
  throw new TaskNotFoundError(taskId);
@@ -8248,7 +8357,7 @@ function addDependency(taskId, dependsOn, db) {
8248
8357
  }
8249
8358
  d.run("INSERT OR IGNORE INTO task_dependencies (task_id, depends_on) VALUES (?, ?)", [taskId, dependsOn]);
8250
8359
  }
8251
- function removeDependency(taskId, dependsOn, db) {
8360
+ function removeDependency2(taskId, dependsOn, db) {
8252
8361
  const d = db || getDatabase();
8253
8362
  const result = d.run("DELETE FROM task_dependencies WHERE task_id = ? AND depends_on = ?", [taskId, dependsOn]);
8254
8363
  return result.changes > 0;
@@ -8375,11 +8484,11 @@ var init_task_graph = __esm(() => {
8375
8484
  // src/db/task-lifecycle.ts
8376
8485
  var exports_task_lifecycle = {};
8377
8486
  __export(exports_task_lifecycle, {
8378
- unlockTask: () => unlockTask,
8487
+ unlockTask: () => unlockTask2,
8379
8488
  stealTask: () => stealTask,
8380
8489
  startTask: () => startTask2,
8381
8490
  spawnNextRecurrence: () => spawnNextRecurrence,
8382
- lockTask: () => lockTask,
8491
+ lockTask: () => lockTask2,
8383
8492
  getTasksChangedSince: () => getTasksChangedSince,
8384
8493
  getTaskLockStatus: () => getTaskLockStatus,
8385
8494
  getStaleTasks: () => getStaleTasks,
@@ -8577,7 +8686,7 @@ function completeTask2(id, agentId, db, options) {
8577
8686
  }
8578
8687
  return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: finalVersion, updated_at: timestamp, metadata: meta };
8579
8688
  }
8580
- function lockTask(id, agentId, db) {
8689
+ function lockTask2(id, agentId, db) {
8581
8690
  const d = db || getDatabase();
8582
8691
  const task = getTask(id, d);
8583
8692
  if (!task)
@@ -8624,7 +8733,7 @@ function lockTask(id, agentId, db) {
8624
8733
  logTaskChange2(id, "lock", "locked_by", task.locked_by, agentId, agentId, d);
8625
8734
  return { success: true, locked_by: agentId, locked_at: timestamp, expires_at: lockExpiresAt(timestamp) };
8626
8735
  }
8627
- function unlockTask(id, agentId, db) {
8736
+ function unlockTask2(id, agentId, db) {
8628
8737
  const d = db || getDatabase();
8629
8738
  const task = getTask(id, d);
8630
8739
  if (!task)
@@ -9606,7 +9715,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
9606
9715
  }, d);
9607
9716
  if (options?.depends_on_prev && created.length > 0) {
9608
9717
  const prev = created[created.length - 1];
9609
- addDependency(task.id, prev.id, d);
9718
+ addDependency2(task.id, prev.id, d);
9610
9719
  }
9611
9720
  created.push(task);
9612
9721
  }
@@ -9736,7 +9845,7 @@ function bulkCreateTasks(inputs, db) {
9736
9845
  for (const depTempId of input.depends_on_temp_ids) {
9737
9846
  const depRealId = tempIdToRealId.get(depTempId);
9738
9847
  if (depRealId) {
9739
- addDependency(taskId, depRealId, d);
9848
+ addDependency2(taskId, depRealId, d);
9740
9849
  }
9741
9850
  }
9742
9851
  }
@@ -12290,7 +12399,7 @@ __export(exports_tasks, {
12290
12399
  updateTaskBoard: () => updateTaskBoard,
12291
12400
  updateTask: () => updateTask2,
12292
12401
  unwatchTask: () => unwatchTask,
12293
- unlockTask: () => unlockTask,
12402
+ unlockTask: () => unlockTask2,
12294
12403
  unarchiveTask: () => unarchiveTask,
12295
12404
  stopFocusSession: () => stopFocusSession,
12296
12405
  stealTask: () => stealTask,
@@ -12302,7 +12411,7 @@ __export(exports_tasks, {
12302
12411
  resumeFocusSession: () => resumeFocusSession,
12303
12412
  replaceTaskTags: () => replaceTaskTags,
12304
12413
  renderTaskBoard: () => renderTaskBoard,
12305
- removeDependency: () => removeDependency,
12414
+ removeDependency: () => removeDependency2,
12306
12415
  redistributeStaleTasks: () => redistributeStaleTasks,
12307
12416
  pauseFocusSession: () => pauseFocusSession,
12308
12417
  notifyWatchers: () => notifyWatchers,
@@ -12311,7 +12420,7 @@ __export(exports_tasks, {
12311
12420
  moveBoardCard: () => moveBoardCard,
12312
12421
  logTime: () => logTime,
12313
12422
  logCost: () => logCost,
12314
- lockTask: () => lockTask,
12423
+ lockTask: () => lockTask2,
12315
12424
  listTasks: () => listTasks2,
12316
12425
  listTaskBoards: () => listTaskBoards,
12317
12426
  listFocusSessions: () => listFocusSessions,
@@ -12366,7 +12475,7 @@ __export(exports_tasks, {
12366
12475
  buildTaskBoardSnapshot: () => buildTaskBoardSnapshot,
12367
12476
  archiveTasks: () => archiveTasks,
12368
12477
  archiveCompletedTasks: () => archiveCompletedTasks,
12369
- addDependency: () => addDependency
12478
+ addDependency: () => addDependency2
12370
12479
  });
12371
12480
  var init_tasks = __esm(() => {
12372
12481
  init_task_crud();
@@ -14825,6 +14934,7 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
14825
14934
  audit: {
14826
14935
  logTaskChange: (taskId, action, field, oldValue, newValue, agentId) => logTaskChange2(taskId, action, field, oldValue, newValue, agentId, database()),
14827
14936
  addComment: (input) => addComment2(input, database()),
14937
+ getComments: (taskId) => listComments(taskId, database()),
14828
14938
  getTaskHistory: (taskId) => getTaskHistory(taskId, database()),
14829
14939
  getRecentActivity: (limit) => getRecentActivity(limit, database())
14830
14940
  },
@@ -15566,6 +15676,7 @@ async function handleV1Request(req, url) {
15566
15676
  const resource = segments[1];
15567
15677
  const id = segments[2];
15568
15678
  const action = segments[3];
15679
+ const subId = segments[4];
15569
15680
  try {
15570
15681
  if (resource === "tasks") {
15571
15682
  if (id === "exists" && !action) {
@@ -15616,6 +15727,114 @@ async function handleV1Request(req, url) {
15616
15727
  return error(405, `method ${method} not allowed on /v1/tasks`);
15617
15728
  }
15618
15729
  if (action) {
15730
+ if (action === "comments") {
15731
+ if (method === "GET") {
15732
+ const comments = await store.audit.getComments(id);
15733
+ return json2({ comments, count: comments.length });
15734
+ }
15735
+ if (method === "POST") {
15736
+ const body2 = await readJson(req) ?? {};
15737
+ if (typeof body2.content !== "string" || !body2.content.trim()) {
15738
+ return error(400, "content is required");
15739
+ }
15740
+ const target = await store.tasks.get(id);
15741
+ if (!target)
15742
+ return error(404, "task not found");
15743
+ const comment = await store.audit.addComment({
15744
+ task_id: id,
15745
+ content: body2.content,
15746
+ agent_id: body2.agent_id ?? principal.agent ?? undefined,
15747
+ session_id: body2.session_id,
15748
+ type: body2.type,
15749
+ progress_pct: body2.progress_pct
15750
+ }, contextFromPrincipal(principal, body2));
15751
+ return json2({ comment }, 201);
15752
+ }
15753
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
15754
+ }
15755
+ if (action === "lock" || action === "unlock") {
15756
+ if (method !== "POST")
15757
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
15758
+ if (typeof store.tasks.lock !== "function" || typeof store.tasks.unlock !== "function") {
15759
+ return error(501, "task locking is not supported by this storage backend");
15760
+ }
15761
+ const body2 = await readJson(req) ?? {};
15762
+ if (!await store.tasks.get(id))
15763
+ return error(404, "task not found");
15764
+ if (action === "lock") {
15765
+ const agentId2 = body2.agent_id || principal.agent || "todos-serve";
15766
+ return json2({ result: await store.tasks.lock(id, agentId2) });
15767
+ }
15768
+ const released = await store.tasks.unlock(id, body2.agent_id || principal.agent || undefined);
15769
+ return json2({ success: released });
15770
+ }
15771
+ if (action === "dependencies") {
15772
+ if (!store.dependencies)
15773
+ return error(501, "dependencies are not supported by this storage backend");
15774
+ if (method === "GET") {
15775
+ if (!await store.tasks.get(id))
15776
+ return error(404, "task not found");
15777
+ const edges = await store.dependencies.list(id);
15778
+ return json2(edges);
15779
+ }
15780
+ if (method === "POST") {
15781
+ const body2 = await readJson(req) ?? {};
15782
+ if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
15783
+ return error(400, "depends_on is required");
15784
+ }
15785
+ try {
15786
+ const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
15787
+ return json2({ dependency }, 201);
15788
+ } catch (e) {
15789
+ const msg = e.message || "";
15790
+ if (msg.includes("not found"))
15791
+ return error(404, msg);
15792
+ if (msg.includes("cycle") || msg.includes("itself"))
15793
+ return error(409, msg);
15794
+ throw e;
15795
+ }
15796
+ }
15797
+ if (method === "DELETE") {
15798
+ if (!subId)
15799
+ return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
15800
+ const removed = await store.dependencies.remove(id, subId);
15801
+ return json2({ removed });
15802
+ }
15803
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
15804
+ }
15805
+ if (action === "verifications") {
15806
+ if (!store.verifications)
15807
+ return error(501, "verifications are not supported by this storage backend");
15808
+ if (method === "GET") {
15809
+ if (!await store.tasks.get(id))
15810
+ return error(404, "task not found");
15811
+ const verifications = await store.verifications.list(id);
15812
+ return json2({ verifications, count: verifications.length });
15813
+ }
15814
+ if (method === "POST") {
15815
+ const body2 = await readJson(req) ?? {};
15816
+ if (typeof body2.command !== "string" || !body2.command.trim()) {
15817
+ return error(400, "command is required");
15818
+ }
15819
+ try {
15820
+ const verification = await store.verifications.add({
15821
+ task_id: id,
15822
+ command: body2.command,
15823
+ status: body2.status,
15824
+ output_summary: body2.output_summary,
15825
+ artifact_path: body2.artifact_path,
15826
+ agent_id: body2.agent_id
15827
+ }, contextFromPrincipal(principal, body2));
15828
+ return json2({ verification }, 201);
15829
+ } catch (e) {
15830
+ const msg = e.message || "";
15831
+ if (msg.includes("not found"))
15832
+ return error(404, msg);
15833
+ throw e;
15834
+ }
15835
+ }
15836
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/verifications`);
15837
+ }
15619
15838
  const body = await readJson(req) ?? {};
15620
15839
  const agentId = body.agent_id || principal.agent || "todos-serve";
15621
15840
  if (action === "start" && method === "POST") {
@@ -15720,10 +15939,13 @@ async function handleV1Request(req, url) {
15720
15939
  }
15721
15940
  if (!id && method === "POST") {
15722
15941
  const body = await readJson(req);
15723
- if (!body || typeof body.name !== "string")
15942
+ if (!body || typeof body.name !== "string" || !body.name.trim())
15724
15943
  return error(400, "name is required");
15725
- const agent = await store.agents.register(body, contextFromPrincipal(principal));
15726
- return json2({ agent }, 201);
15944
+ const result = await store.agents.register(body, contextFromPrincipal(principal));
15945
+ if (result && typeof result === "object" && "conflict" in result) {
15946
+ return error(409, result.message ?? "agent name conflict", { conflict: true });
15947
+ }
15948
+ return json2({ agent: result }, 201);
15727
15949
  }
15728
15950
  if (id && method === "GET") {
15729
15951
  const agent = await store.agents.get(id);
@@ -43550,6 +43772,23 @@ async function cloudGetStats(client) {
43550
43772
  const raw = await client.transport.get("/stats");
43551
43773
  return raw ?? {};
43552
43774
  }
43775
+ async function cloudListAgents(client) {
43776
+ const res = await client.list("agents");
43777
+ const envelope = res.raw;
43778
+ return Array.isArray(envelope?.agents) ? envelope.agents : res.items;
43779
+ }
43780
+ async function cloudListProjects(client) {
43781
+ const res = await client.list("projects");
43782
+ const envelope = res.raw;
43783
+ return Array.isArray(envelope?.projects) ? envelope.projects : res.items;
43784
+ }
43785
+ async function cloudAddComment(client, taskId, input) {
43786
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(taskId)}/comments`, input);
43787
+ if (raw && typeof raw === "object" && "comment" in raw) {
43788
+ return raw.comment;
43789
+ }
43790
+ return raw;
43791
+ }
43553
43792
  async function cloudCountTasks(client, filter = {}) {
43554
43793
  const { limit: _drop, offset: _o, ...rest } = filter;
43555
43794
  const res = await client.list("tasks", { query: { ...toListQuery(rest), limit: 1 } });
@@ -43559,6 +43798,13 @@ async function cloudCountTasks(client, filter = {}) {
43559
43798
  const tasks = await cloudListTasks(client, rest);
43560
43799
  return tasks.length;
43561
43800
  }
43801
+ async function cloudRegisterAgent(client, input) {
43802
+ const raw = await client.transport.post("/agents", input);
43803
+ if (raw && typeof raw === "object" && "agent" in raw) {
43804
+ return raw.agent;
43805
+ }
43806
+ return raw;
43807
+ }
43562
43808
  var _cache;
43563
43809
  var init_cloud_router = __esm(() => {
43564
43810
  init_storage();
@@ -46874,7 +47120,7 @@ ${line}`,
46874
47120
  const taskId = byTitle.get(dependency.task_title.toLowerCase());
46875
47121
  const dependsOnId = byTitle.get(dependency.depends_on_title.toLowerCase());
46876
47122
  if (taskId && dependsOnId)
46877
- addDependency(taskId, dependsOnId, d);
47123
+ addDependency2(taskId, dependsOnId, d);
46878
47124
  }
46879
47125
  }
46880
47126
  return {
@@ -49745,7 +49991,7 @@ function registerTaskProjectTools(server, ctx) {
49745
49991
  }, async ({ task_id, agent_id }) => {
49746
49992
  try {
49747
49993
  const resolvedId = resolveId(task_id);
49748
- const result = lockTask(resolvedId, agent_id);
49994
+ const result = lockTask2(resolvedId, agent_id);
49749
49995
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], isError: !result.success };
49750
49996
  } catch (e) {
49751
49997
  return { content: [{ type: "text", text: formatError2(e) }], isError: true };
@@ -49759,7 +50005,7 @@ function registerTaskProjectTools(server, ctx) {
49759
50005
  }, async ({ task_id, agent_id }) => {
49760
50006
  try {
49761
50007
  const resolvedId = resolveId(task_id);
49762
- const success = unlockTask(resolvedId, agent_id);
50008
+ const success = unlockTask2(resolvedId, agent_id);
49763
50009
  return { content: [{ type: "text", text: JSON.stringify({ success }, null, 2) }] };
49764
50010
  } catch (e) {
49765
50011
  return { content: [{ type: "text", text: formatError2(e) }], isError: true };
@@ -49896,7 +50142,7 @@ function registerTaskProjectTools(server, ctx) {
49896
50142
  try {
49897
50143
  const resolvedId = resolveId(task_id);
49898
50144
  const resolvedDep = resolveId(depends_on);
49899
- addDependency(resolvedId, resolvedDep);
50145
+ addDependency2(resolvedId, resolvedDep);
49900
50146
  return { content: [{ type: "text", text: `${resolvedId.slice(0, 8)} now depends on ${resolvedDep.slice(0, 8)}` }] };
49901
50147
  } catch (e) {
49902
50148
  return { content: [{ type: "text", text: formatError2(e) }], isError: true };
@@ -49909,7 +50155,7 @@ function registerTaskProjectTools(server, ctx) {
49909
50155
  depends_on: exports_external.string().describe("Task ID to remove dependency on")
49910
50156
  }, async ({ task_id, depends_on }) => {
49911
50157
  try {
49912
- removeDependency(resolveId(task_id), resolveId(depends_on));
50158
+ removeDependency2(resolveId(task_id), resolveId(depends_on));
49913
50159
  return { content: [{ type: "text", text: "Dependency removed." }] };
49914
50160
  } catch (e) {
49915
50161
  return { content: [{ type: "text", text: formatError2(e) }], isError: true };
@@ -50896,6 +51142,11 @@ Tasks:` : null,
50896
51142
  author: exports_external.string().optional().describe("Author agent ID or name")
50897
51143
  }, async ({ task_id, body, author }) => {
50898
51144
  try {
51145
+ const cloud = getTodosCloudClient();
51146
+ if (cloud) {
51147
+ await cloudAddComment(cloud, task_id, { content: body, agent_id: author });
51148
+ return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
51149
+ }
50899
51150
  const resolvedId = resolveId(task_id);
50900
51151
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
50901
51152
  const comment = addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
@@ -51806,6 +52057,56 @@ function registerTaskWorkflowTools(server, ctx) {
51806
52057
  max_description_chars: exports_external.number().optional().describe("Max task description chars in compact mode (default: 180)")
51807
52058
  }, async ({ agent_id, project_id, task_list_id, explain_blocked, detail, max_description_chars }) => {
51808
52059
  try {
52060
+ const cloud = getTodosCloudClient();
52061
+ if (cloud) {
52062
+ const baseFilter = {};
52063
+ if (project_id)
52064
+ baseFilter.project_id = project_id;
52065
+ if (task_list_id)
52066
+ baseFilter.task_list_id = task_list_id;
52067
+ const [stats, pending, in_progress, completed, activeTasks, nextTasks] = await Promise.all([
52068
+ cloudGetStats(cloud),
52069
+ cloudCountTasks(cloud, { ...baseFilter, status: "pending" }),
52070
+ cloudCountTasks(cloud, { ...baseFilter, status: "in_progress" }),
52071
+ cloudCountTasks(cloud, { ...baseFilter, status: "completed" }),
52072
+ cloudListTasks(cloud, { ...baseFilter, status: "in_progress", limit: 5 }),
52073
+ cloudListTasks(cloud, { ...baseFilter, status: "pending", limit: 1 })
52074
+ ]);
52075
+ const cloudStatus = {
52076
+ source: "cloud",
52077
+ total: stats.tasks ?? pending + in_progress + completed,
52078
+ pending,
52079
+ in_progress,
52080
+ completed,
52081
+ active_work: activeTasks,
52082
+ next_task: nextTasks[0] ?? null,
52083
+ stale_count: 0,
52084
+ overdue_recurring: 0
52085
+ };
52086
+ const next_task2 = nextTasks[0] ?? null;
52087
+ const payload2 = {
52088
+ status: cloudStatus,
52089
+ next_task: next_task2,
52090
+ overdue_count: 0,
52091
+ latest_handoff: null,
52092
+ as_of: new Date().toISOString()
52093
+ };
52094
+ if (detail === "full") {
52095
+ return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
52096
+ }
52097
+ return {
52098
+ content: [{
52099
+ type: "text",
52100
+ text: compactJson({
52101
+ status: compactStatus(cloudStatus),
52102
+ next_task: next_task2 ? compactTask(next_task2, max_description_chars || 180) : null,
52103
+ overdue_count: 0,
52104
+ latest_handoff: null,
52105
+ as_of: payload2.as_of
52106
+ })
52107
+ }]
52108
+ };
52109
+ }
51809
52110
  const { getStatus: getStatus2, getNextTask: getNextTask3, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
51810
52111
  const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
51811
52112
  const filters = {};
@@ -52241,6 +52542,27 @@ ${lines.join(`
52241
52542
  if (shouldRegisterTool("get_health")) {
52242
52543
  server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
52243
52544
  try {
52545
+ const cloud = getTodosCloudClient();
52546
+ if (cloud) {
52547
+ const [stats, pending2, inProgress2, completed2, cancelled2, projects2, agents2] = await Promise.all([
52548
+ cloudGetStats(cloud),
52549
+ cloudCountTasks(cloud, { status: "pending" }),
52550
+ cloudCountTasks(cloud, { status: "in_progress" }),
52551
+ cloudCountTasks(cloud, { status: "completed" }),
52552
+ cloudCountTasks(cloud, { status: "cancelled" }),
52553
+ cloudListProjects(cloud),
52554
+ cloudListAgents(cloud)
52555
+ ]);
52556
+ const projectCount = stats.projects ?? projects2.length;
52557
+ const lines2 = [
52558
+ `=== System Health (cloud) ===`,
52559
+ `Tasks: ${pending2} pending | ${inProgress2} in progress | ${completed2} completed | ${cancelled2} cancelled`,
52560
+ `Projects: ${projectCount} total`,
52561
+ `Agents: ${agents2.length} registered`
52562
+ ];
52563
+ return { content: [{ type: "text", text: lines2.join(`
52564
+ `) }] };
52565
+ }
52244
52566
  const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
52245
52567
  const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
52246
52568
  const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
@@ -52279,6 +52601,7 @@ ${lines.join(`
52279
52601
  }
52280
52602
  var init_task_auto_tools = __esm(() => {
52281
52603
  init_zod();
52604
+ init_cloud_router();
52282
52605
  });
52283
52606
 
52284
52607
  // src/lib/context-packs.ts
@@ -53601,6 +53924,11 @@ No blocked tasks.`,
53601
53924
  author: exports_external.string().optional().describe("Author agent ID or name")
53602
53925
  }, async ({ task_id, body, author }) => {
53603
53926
  try {
53927
+ const cloud = getTodosCloudClient();
53928
+ if (cloud) {
53929
+ await cloudAddComment(cloud, task_id, { content: body, agent_id: author });
53930
+ return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
53931
+ }
53604
53932
  const { addComment: addComment3 } = (init_comments(), __toCommonJS(exports_comments));
53605
53933
  const resolvedId = resolveId(task_id);
53606
53934
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
@@ -88161,6 +88489,33 @@ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError
88161
88489
  force: exports_external.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
88162
88490
  }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
88163
88491
  try {
88492
+ const cloud = getTodosCloudClient();
88493
+ if (cloud) {
88494
+ const agent2 = await cloudRegisterAgent(cloud, {
88495
+ name,
88496
+ description,
88497
+ role,
88498
+ title,
88499
+ level,
88500
+ permissions,
88501
+ capabilities,
88502
+ session_id,
88503
+ working_dir,
88504
+ force
88505
+ });
88506
+ return {
88507
+ content: [{
88508
+ type: "text",
88509
+ text: `Agent registered:
88510
+ ID: ${agent2.id}
88511
+ Name: ${agent2.name}${agent2.description ? `
88512
+ Description: ${agent2.description}` : ""}
88513
+ Session: ${agent2.session_id ?? "unbound"}
88514
+ Created: ${agent2.created_at}
88515
+ Last seen: ${agent2.last_seen_at}`
88516
+ }]
88517
+ };
88518
+ }
88164
88519
  const pool = getAgentPoolForProject(working_dir);
88165
88520
  const result = registerAgent2({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
88166
88521
  if (isAgentConflict(result)) {
@@ -88254,7 +88609,8 @@ No names available. Wait for an active agent to go stale (30min timeout).`
88254
88609
  include_archived: exports_external.boolean().optional().describe("Include archived agents in the list (default: false)")
88255
88610
  }, async ({ include_archived }) => {
88256
88611
  try {
88257
- const agents = listAgents({ include_archived: include_archived ?? false });
88612
+ const cloud = getTodosCloudClient();
88613
+ const agents = cloud ? await cloudListAgents(cloud) : listAgents({ include_archived: include_archived ?? false });
88258
88614
  if (agents.length === 0) {
88259
88615
  return { content: [{ type: "text", text: "No agents registered." }] };
88260
88616
  }
@@ -88474,6 +88830,7 @@ var init_agents2 = __esm(() => {
88474
88830
  init_agents();
88475
88831
  init_config2();
88476
88832
  init_database();
88833
+ init_cloud_router();
88477
88834
  });
88478
88835
 
88479
88836
  // src/db/builtin-templates.ts
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAuB,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AA4B1F;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAgB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAYpE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAuPtF"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAuB,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AA4B1F;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAgB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAYpE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA6XtF"}