@hasna/todos 0.13.10 → 0.13.12

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.
Files changed (42) hide show
  1. package/dist/cli/assignee-guard.d.ts +8 -1
  2. package/dist/cli/assignee-guard.d.ts.map +1 -1
  3. package/dist/cli/cloud-router.d.ts.map +1 -1
  4. package/dist/cli/commands/query-commands.d.ts.map +1 -1
  5. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  6. package/dist/cli/index.js +153 -102
  7. package/dist/contracts.js +92 -67
  8. package/dist/db/agent-metrics.d.ts.map +1 -1
  9. package/dist/db/audit.d.ts.map +1 -1
  10. package/dist/db/budgets.d.ts.map +1 -1
  11. package/dist/db/database.d.ts +44 -0
  12. package/dist/db/database.d.ts.map +1 -1
  13. package/dist/db/handoffs.d.ts.map +1 -1
  14. package/dist/db/task-crud.d.ts +1 -0
  15. package/dist/db/task-crud.d.ts.map +1 -1
  16. package/dist/db/task-files.d.ts.map +1 -1
  17. package/dist/db/task-lifecycle.d.ts.map +1 -1
  18. package/dist/db/task-relations.d.ts.map +1 -1
  19. package/dist/db/task-status.d.ts.map +1 -1
  20. package/dist/db/webhooks.d.ts.map +1 -1
  21. package/dist/index.js +119 -85
  22. package/dist/lib/assignee-validation.d.ts +22 -0
  23. package/dist/lib/assignee-validation.d.ts.map +1 -1
  24. package/dist/lib/capacity-forecasts.d.ts.map +1 -1
  25. package/dist/lib/completion-guard.d.ts.map +1 -1
  26. package/dist/lib/local-notifications.d.ts.map +1 -1
  27. package/dist/lib/local-reports.d.ts.map +1 -1
  28. package/dist/lib/notification-reminders.d.ts.map +1 -1
  29. package/dist/lib/search.d.ts.map +1 -1
  30. package/dist/lib/usage-ledger.d.ts.map +1 -1
  31. package/dist/mcp/index.js +138 -93
  32. package/dist/mcp/tools/agents.d.ts.map +1 -1
  33. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  34. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  35. package/dist/mcp/tools/task-resources.d.ts.map +1 -1
  36. package/dist/mcp.js +1 -1
  37. package/dist/registry.js +92 -67
  38. package/dist/release-provenance.json +5 -5
  39. package/dist/server/index.js +138 -93
  40. package/dist/server/routes.d.ts.map +1 -1
  41. package/dist/storage.js +57 -42
  42. package/package.json +1 -1
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.13.10",
73
+ version: "0.13.12",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -9312,14 +9312,17 @@ var exports_database = {};
9312
9312
  __export(exports_database, {
9313
9313
  uuid: () => uuid,
9314
9314
  resolvePartialId: () => resolvePartialId,
9315
+ resolveAssignedToAliases: () => resolveAssignedToAliases,
9315
9316
  resetDatabase: () => resetDatabase,
9316
9317
  now: () => now,
9318
+ lowerInClause: () => lowerInClause,
9317
9319
  lockExpiryCutoff: () => lockExpiryCutoff,
9318
9320
  isLockExpired: () => isLockExpired,
9319
9321
  getDatabasePath: () => getDatabasePath,
9320
9322
  getDatabase: () => getDatabase,
9321
9323
  closeDatabase: () => closeDatabase,
9322
9324
  clearExpiredLocks: () => clearExpiredLocks,
9325
+ assignedToAliasSet: () => assignedToAliasSet,
9323
9326
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
9324
9327
  });
9325
9328
  import { Database } from "bun:sqlite";
@@ -9548,6 +9551,33 @@ function resolvePartialId(db, table, partialId) {
9548
9551
  }
9549
9552
  return null;
9550
9553
  }
9554
+ function resolveAssignedToAliases(db, ref) {
9555
+ const aliases = new Set([ref]);
9556
+ let agentId;
9557
+ try {
9558
+ agentId = resolvePartialId(db, "agents", ref);
9559
+ } catch (err) {
9560
+ if (!(err instanceof IdentityAliasAmbiguousError))
9561
+ throw err;
9562
+ agentId = null;
9563
+ }
9564
+ if (agentId) {
9565
+ aliases.add(agentId);
9566
+ const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
9567
+ if (row?.name)
9568
+ aliases.add(row.name);
9569
+ }
9570
+ return [...aliases];
9571
+ }
9572
+ function assignedToAliasSet(db, ref) {
9573
+ return new Set(resolveAssignedToAliases(db, ref).map((a) => a.toLowerCase()));
9574
+ }
9575
+ function lowerInClause(column, values, params) {
9576
+ if (values.length === 0)
9577
+ return "1=0";
9578
+ params.push(...values.map((v) => v.toLowerCase()));
9579
+ return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
9580
+ }
9551
9581
  var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
9552
9582
  var init_database = __esm(() => {
9553
9583
  init_schema();
@@ -10117,17 +10147,19 @@ function checkCompletionGuard(task, agentId, db, configOverride) {
10117
10147
  throw new CompletionGuardError(`Too fast: task was started ${Math.floor(elapsedSeconds)}s ago. Minimum work duration is ${config.min_work_seconds}s. Wait ${remaining}s.`, remaining);
10118
10148
  }
10119
10149
  }
10150
+ const assignedAliasParams = [];
10151
+ const assignedInClause = agent ? lowerInClause("assigned_to", resolveAssignedToAliases(db, agent), assignedAliasParams) : "";
10120
10152
  if (agent && config.max_completions_per_window && config.window_minutes) {
10121
10153
  const windowStart = new Date(Date.now() - config.window_minutes * 60 * 1000).toISOString();
10122
10154
  const result = db.query(`SELECT COUNT(*) as count FROM tasks
10123
- WHERE completed_at > ? AND (assigned_to = ? OR agent_id = ?)`).get(windowStart, agent, agent);
10155
+ WHERE completed_at > ? AND (${assignedInClause} OR agent_id = ?)`).get(windowStart, ...assignedAliasParams, agent);
10124
10156
  if (result.count >= config.max_completions_per_window) {
10125
10157
  throw new CompletionGuardError(`Rate limit: ${result.count} tasks completed in the last ${config.window_minutes} minutes (max ${config.max_completions_per_window}). Slow down.`);
10126
10158
  }
10127
10159
  }
10128
10160
  if (agent && config.cooldown_seconds) {
10129
10161
  const result = db.query(`SELECT MAX(completed_at) as last_completed FROM tasks
10130
- WHERE completed_at IS NOT NULL AND (assigned_to = ? OR agent_id = ?) AND id != ?`).get(agent, agent, task.id);
10162
+ WHERE completed_at IS NOT NULL AND (${assignedInClause} OR agent_id = ?) AND id != ?`).get(...assignedAliasParams, agent, task.id);
10131
10163
  if (result.last_completed) {
10132
10164
  const elapsedSeconds = (Date.now() - new Date(result.last_completed).getTime()) / 1000;
10133
10165
  if (elapsedSeconds < config.cooldown_seconds) {
@@ -10141,6 +10173,7 @@ var init_completion_guard = __esm(() => {
10141
10173
  init_types();
10142
10174
  init_config2();
10143
10175
  init_projects();
10176
+ init_database();
10144
10177
  });
10145
10178
 
10146
10179
  // src/lib/event-emission-safety.ts
@@ -12318,13 +12351,17 @@ function logDelivery(d, webhookId, event, payload, statusCode, response, attempt
12318
12351
  const id = uuid();
12319
12352
  d.run(`INSERT INTO webhook_deliveries (id, webhook_id, event, payload, status_code, response, attempt, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, webhookId, event, payload, statusCode, response, attempt, now()]);
12320
12353
  }
12321
- function matchesScope(wh, payload) {
12354
+ function matchesScope(wh, payload, db) {
12322
12355
  if (wh.project_id && payload.project_id !== wh.project_id)
12323
12356
  return false;
12324
12357
  if (wh.task_list_id && payload.task_list_id !== wh.task_list_id)
12325
12358
  return false;
12326
- if (wh.agent_id && payload.agent_id !== wh.agent_id && payload.assigned_to !== wh.agent_id)
12327
- return false;
12359
+ if (wh.agent_id && payload.agent_id !== wh.agent_id) {
12360
+ const aliasSet = assignedToAliasSet(db, wh.agent_id);
12361
+ const assignedTo = typeof payload.assigned_to === "string" ? payload.assigned_to.toLowerCase() : "";
12362
+ if (!aliasSet.has(assignedTo))
12363
+ return false;
12364
+ }
12328
12365
  if (wh.task_id && payload.id !== wh.task_id)
12329
12366
  return false;
12330
12367
  return true;
@@ -12391,7 +12428,7 @@ async function dispatchWebhook2(event, payload, db) {
12391
12428
  const webhooks = listWebhooks(d).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
12392
12429
  const payloadObj = typeof payload === "object" && payload !== null ? payload : {};
12393
12430
  for (const wh of webhooks) {
12394
- if (!matchesScope(wh, payloadObj))
12431
+ if (!matchesScope(wh, payloadObj, d))
12395
12432
  continue;
12396
12433
  const body = JSON.stringify({ event, payload, timestamp: now() });
12397
12434
  deliverWebhook(wh, event, body, 1, d).catch((err) => {
@@ -13463,14 +13500,16 @@ function getNextTask2(agentId, filters, db) {
13463
13500
  conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
13464
13501
  const where = conditions.join(" AND ");
13465
13502
  let recentProjectIds = [];
13503
+ const assignedAliasParams = [];
13504
+ const assignedInClause = agentId ? lowerInClause("assigned_to", resolveAssignedToAliases(d, agentId), assignedAliasParams) : "";
13466
13505
  if (agentId) {
13467
- const recentRows = d.query(`SELECT DISTINCT project_id FROM tasks WHERE assigned_to = ? AND status = 'completed' AND project_id IS NOT NULL ORDER BY completed_at DESC LIMIT 3`).all(agentId);
13506
+ const recentRows = d.query(`SELECT DISTINCT project_id FROM tasks WHERE ${assignedInClause} AND status = 'completed' AND project_id IS NOT NULL ORDER BY completed_at DESC LIMIT 3`).all(...assignedAliasParams);
13468
13507
  recentProjectIds = recentRows.map((r) => r.project_id);
13469
13508
  }
13470
13509
  let sql = `SELECT * FROM tasks WHERE ${where} ORDER BY `;
13471
13510
  if (agentId) {
13472
- sql += `CASE WHEN assigned_to = ? THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
13473
- params.push(agentId);
13511
+ sql += `CASE WHEN ${assignedInClause} THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
13512
+ params.push(...assignedAliasParams);
13474
13513
  }
13475
13514
  if (recentProjectIds.length > 0) {
13476
13515
  const placeholders = recentProjectIds.map(() => "?").join(",");
@@ -13864,30 +13903,6 @@ function getTaskWithRelations(id, db) {
13864
13903
  checklist
13865
13904
  };
13866
13905
  }
13867
- function resolveAssignedToAliases(db, ref) {
13868
- const aliases = new Set([ref]);
13869
- let agentId;
13870
- try {
13871
- agentId = resolvePartialId(db, "agents", ref);
13872
- } catch (err) {
13873
- if (!(err instanceof IdentityAliasAmbiguousError))
13874
- throw err;
13875
- agentId = null;
13876
- }
13877
- if (agentId) {
13878
- aliases.add(agentId);
13879
- const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
13880
- if (row?.name)
13881
- aliases.add(row.name);
13882
- }
13883
- return [...aliases];
13884
- }
13885
- function lowerInClause(column, values, params) {
13886
- if (values.length === 0)
13887
- return "1=0";
13888
- params.push(...values.map((v) => v.toLowerCase()));
13889
- return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
13890
- }
13891
13906
  function listTasks2(filter = {}, db) {
13892
13907
  const d = db || getDatabase();
13893
13908
  const { clearExpiredLocks: clearExpiredLocks2 } = (init_database(), __toCommonJS(exports_database));
@@ -14360,6 +14375,7 @@ function deleteTask(id, db) {
14360
14375
  var init_task_crud = __esm(() => {
14361
14376
  init_types();
14362
14377
  init_database();
14378
+ init_database();
14363
14379
  init_completion_guard();
14364
14380
  init_event_emission_safety();
14365
14381
  init_event_hooks();
@@ -14522,8 +14538,8 @@ function getTaskStats(filters, db) {
14522
14538
  params.push(filters.task_list_id);
14523
14539
  }
14524
14540
  if (filters?.agent_id) {
14525
- conditions.push("(agent_id = ? OR assigned_to = ?)");
14526
- params.push(filters.agent_id, filters.agent_id);
14541
+ params.push(filters.agent_id);
14542
+ conditions.push(`(agent_id = ? OR ${lowerInClause("assigned_to", resolveAssignedToAliases(d, filters.agent_id), params)})`);
14527
14543
  }
14528
14544
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
14529
14545
  const totalRow = d.query(`SELECT COUNT(*) as count FROM tasks ${where}`).get(...params);
@@ -14706,8 +14722,7 @@ function getEscalatedTasks(opts = {}, db, at = new Date) {
14706
14722
  params.push(opts.project_id);
14707
14723
  }
14708
14724
  if (opts.agent_id) {
14709
- conditions.push("assigned_to = ?");
14710
- params.push(opts.agent_id);
14725
+ conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
14711
14726
  }
14712
14727
  const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC, created_at ASC`).all(...params);
14713
14728
  return rows.map(rowToTask).map((task) => {
@@ -14755,8 +14770,7 @@ function notifyUpcomingDeadlines(opts = {}, db) {
14755
14770
  params.push(opts.project_id);
14756
14771
  }
14757
14772
  if (opts.agent_id) {
14758
- conditions.push("assigned_to = ?");
14759
- params.push(opts.agent_id);
14773
+ conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
14760
14774
  }
14761
14775
  const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
14762
14776
  return rows.map(rowToTask);
@@ -15014,13 +15028,14 @@ function getTimeReport(opts, db) {
15014
15028
  params.push(opts.plan_id);
15015
15029
  }
15016
15030
  if (opts?.agent_id) {
15031
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.agent_id), params);
15017
15032
  conditions.push(`(
15018
- t.assigned_to = ?
15033
+ ${assignedInClause}
15019
15034
  OR t.agent_id = ?
15020
15035
  OR EXISTS (SELECT 1 FROM task_time_logs ttl WHERE ttl.task_id = t.id AND ttl.agent_id = ?)
15021
15036
  OR EXISTS (SELECT 1 FROM focus_sessions fs WHERE fs.task_id = t.id AND fs.agent_id = ?)
15022
15037
  )`);
15023
- params.push(opts.agent_id, opts.agent_id, opts.agent_id, opts.agent_id);
15038
+ params.push(opts.agent_id, opts.agent_id, opts.agent_id);
15024
15039
  }
15025
15040
  if (opts?.since) {
15026
15041
  conditions.push("(t.completed_at >= ? OR t.updated_at >= ?)");
@@ -16016,7 +16031,7 @@ function listActiveFiles(db) {
16016
16031
  a.name AS agent_name
16017
16032
  FROM task_files tf
16018
16033
  JOIN tasks t ON tf.task_id = t.id
16019
- LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
16034
+ LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name))))
16020
16035
  WHERE t.status = 'in_progress'
16021
16036
  AND tf.status != 'removed'
16022
16037
  ORDER BY tf.updated_at DESC
@@ -19048,8 +19063,9 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
19048
19063
  }
19049
19064
  }
19050
19065
  function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
19066
+ const aliasSet = assignedToAliasSet(getDatabase(), agentId);
19051
19067
  const pending = listTasks2({ status: "pending" });
19052
- const queue = pending.filter((t) => t.assigned_to === agentId || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
19068
+ const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
19053
19069
  const order = { critical: 0, high: 1, medium: 2, low: 3 };
19054
19070
  queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
19055
19071
  return json2(queue.map((t) => taskToSummary2(t)));
@@ -19441,8 +19457,7 @@ function searchTasks(options, projectId, taskListId, db) {
19441
19457
  }
19442
19458
  }
19443
19459
  if (opts.assigned_to) {
19444
- sql += " AND t.assigned_to = ?";
19445
- params.push(opts.assigned_to);
19460
+ sql += ` AND ${lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.assigned_to), params)}`;
19446
19461
  }
19447
19462
  if (opts.agent_id) {
19448
19463
  sql += " AND t.agent_id = ?";
@@ -44798,10 +44813,11 @@ function validateAssignee(input, ctx) {
44798
44813
  if (ctx.allowSeat) {
44799
44814
  return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
44800
44815
  }
44816
+ const hint = ctx.seatHint?.(raw);
44801
44817
  return {
44802
44818
  ok: false,
44803
44819
  reason: "seat",
44804
- message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or pass --assign-seat if filing at the seat is what you mean.`
44820
+ message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or repeat the command with --assign-seat added to confirm the seat is deliberate` + (hint ? `: ${hint}` : " (see --help for this command's exact form).")
44805
44821
  };
44806
44822
  }
44807
44823
  if (byId) {
@@ -60746,11 +60762,12 @@ function runAlerts(input, db, checkedAt, quieted) {
60746
60762
  if (input.include_runs === false)
60747
60763
  return [];
60748
60764
  const since = Date.parse(input.run_since || new Date(Date.parse(checkedAt) - 24 * 60 * 60000).toISOString());
60765
+ const runAlertAliases = input.agent_id ? assignedToAliasSet(db, input.agent_id) : null;
60749
60766
  return listTaskRuns(undefined, db).filter((run) => run.completed_at && Date.parse(run.completed_at) >= since).map((run) => {
60750
60767
  const task = getTask(run.task_id, db);
60751
60768
  if (input.project_id && task?.project_id !== input.project_id)
60752
60769
  return null;
60753
- if (input.agent_id && run.agent_id !== input.agent_id && task?.assigned_to !== input.agent_id)
60770
+ if (input.agent_id && run.agent_id !== input.agent_id && !(runAlertAliases && runAlertAliases.has((task?.assigned_to ?? "").toLowerCase())))
60754
60771
  return null;
60755
60772
  const failed = ["failed", "cancelled"].includes(run.status);
60756
60773
  return {
@@ -60795,6 +60812,7 @@ async function checkLocalNotifications(input = {}, db) {
60795
60812
  const warnings = [];
60796
60813
  if (Number.isNaN(checkedMs))
60797
60814
  warnings.push("Invalid check timestamp; due-soon window may be incomplete.");
60815
+ const notifyAgentAliases = input.agent_id ? assignedToAliasSet(d, input.agent_id) : null;
60798
60816
  const alerts = [];
60799
60817
  for (const escalation of getEscalatedTasks({ project_id: input.project_id, agent_id: input.agent_id }, d, new Date(checkedAt))) {
60800
60818
  for (const reason of escalation.reasons) {
@@ -60806,7 +60824,7 @@ async function checkLocalNotifications(input = {}, db) {
60806
60824
  for (const task of listTasks2({ project_id: input.project_id, include_archived: false, limit: 5000 }, d)) {
60807
60825
  if (!unfinished(task) || !task.due_at)
60808
60826
  continue;
60809
- if (input.agent_id && task.assigned_to !== input.agent_id && task.agent_id !== input.agent_id)
60827
+ if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
60810
60828
  continue;
60811
60829
  if (task.due_at > checkedAt && task.due_at <= dueSoonUntil) {
60812
60830
  alerts.push(taskAlert(task, "task_due_soon", "task.due_soon", "warning", `Due soon: ${task.title}`, task.due_at, quieted));
@@ -60814,7 +60832,7 @@ async function checkLocalNotifications(input = {}, db) {
60814
60832
  }
60815
60833
  }
60816
60834
  for (const task of getStaleTasks(staleMinutes, { project_id: input.project_id }, d)) {
60817
- if (input.agent_id && task.assigned_to !== input.agent_id && task.agent_id !== input.agent_id)
60835
+ if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
60818
60836
  continue;
60819
60837
  alerts.push(taskAlert(task, "task_stale", "task.stale", "warning", `Stale task: ${task.title}`, checkedAt, quieted, { stale_minutes: staleMinutes }));
60820
60838
  }
@@ -62834,10 +62852,12 @@ function matchesProfile(profile, query) {
62834
62852
  return false;
62835
62853
  return true;
62836
62854
  }
62837
- function taskMatchesAgent(task, agentId) {
62855
+ function taskMatchesAgent(task, agentId, aliasSet) {
62838
62856
  if (!agentId)
62839
62857
  return true;
62840
- return task.assigned_to === agentId || task.agent_id === agentId;
62858
+ if (task.agent_id === agentId)
62859
+ return true;
62860
+ return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
62841
62861
  }
62842
62862
  function estimateRemaining(task) {
62843
62863
  if (task.status === "completed" || task.status === "cancelled")
@@ -62907,7 +62927,8 @@ function removeCapacityProfile(idOrAgent, projectId) {
62907
62927
  }
62908
62928
  function getPlanningForecast(input = {}, db) {
62909
62929
  const startDate = input.start_date ? dateOnly(input.start_date) : todayIso();
62910
- const tasks = listTasks2({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id));
62930
+ const aliasSet = input.agent_id ? assignedToAliasSet(db || getDatabase(), input.agent_id) : undefined;
62931
+ const tasks = listTasks2({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id, aliasSet));
62911
62932
  const reportByTask = new Map(getTimeReport({ project_id: input.project_id, plan_id: input.plan_id, agent_id: input.agent_id, include_open: true }, db).map((entry2) => [entry2.task_id, entry2]));
62912
62933
  const profiles = listCapacityProfiles({ agent_id: input.agent_id, project_id: input.project_id });
62913
62934
  const globalProfiles = input.project_id ? listCapacityProfiles({ agent_id: input.agent_id }).filter((profile) => profile.project_id === null) : [];
@@ -63005,6 +63026,7 @@ function renderPlanningForecastMarkdown(forecast) {
63005
63026
  }
63006
63027
  var LOCAL_CAPACITY_SCHEMA_VERSION = 1;
63007
63028
  var init_capacity_forecasts = __esm(() => {
63029
+ init_database();
63008
63030
  init_tasks();
63009
63031
  init_task_relations();
63010
63032
  init_config2();
@@ -66438,8 +66460,10 @@ function getLatestHandoff(agentId, projectId, db) {
66438
66460
  function createSessionRecoveryHandoff(input, db) {
66439
66461
  const d = db || getDatabase();
66440
66462
  const limit = input.limit || 20;
66441
- const conditions = ["status = 'in_progress'", "(assigned_to = ? OR agent_id = ? OR locked_by = ?)"];
66442
- const params = [input.agent_id, input.agent_id, input.agent_id];
66463
+ const params = [];
66464
+ const assignedInClause = lowerInClause("assigned_to", resolveAssignedToAliases(d, input.agent_id), params);
66465
+ const conditions = ["status = 'in_progress'", `(${assignedInClause} OR agent_id = ? OR locked_by = ?)`];
66466
+ params.push(input.agent_id, input.agent_id);
66443
66467
  if (input.session_id) {
66444
66468
  conditions.push("session_id = ?");
66445
66469
  params.push(input.session_id);
@@ -67031,7 +67055,8 @@ function registerTaskAutoTools(server, ctx) {
67031
67055
  const assigned = listTasks4({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
67032
67056
  const now4 = Date.now();
67033
67057
  const dueSoonCutoff = now4 + 24 * 60 * 60 * 1000;
67034
- const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
67058
+ const blockedAliases = assignedToAliasSet(getDatabase(), effectiveAgentId);
67059
+ const blocked = getBlockedTasks2().filter((t) => blockedAliases.has((t.assigned_to ?? "").toLowerCase()));
67035
67060
  const workload = {
67036
67061
  in_progress: assigned.filter((t) => t.status === "in_progress").length,
67037
67062
  pending: assigned.filter((t) => t.status === "pending").length,
@@ -67068,17 +67093,25 @@ function registerTaskAutoTools(server, ctx) {
67068
67093
  if (agents.length === 0)
67069
67094
  return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
67070
67095
  const activeTasks = listTasks4({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
67071
- const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
67096
+ const agentKeyByAlias = new Map;
67097
+ for (const agent of agents) {
67098
+ agentKeyByAlias.set(String(agent.id).toLowerCase(), agent.id);
67099
+ if (agent.name)
67100
+ agentKeyByAlias.set(String(agent.name).toLowerCase(), agent.id);
67101
+ }
67102
+ const canonicalAgentKey = (assignedTo) => assignedTo ? agentKeyByAlias.get(assignedTo.toLowerCase()) : undefined;
67103
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => canonicalAgentKey(t.assigned_to) === agent.id).length]));
67072
67104
  let moved = 0;
67073
67105
  let skipped = 0;
67074
- for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
67075
- const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
67106
+ for (const task of activeTasks.filter((t) => t.status === "pending" && canonicalAgentKey(t.assigned_to) && (load.get(canonicalAgentKey(t.assigned_to)) ?? 0) > limit)) {
67107
+ const currentAgentKey = canonicalAgentKey(task.assigned_to);
67108
+ const target = agents.filter((agent) => agent.id !== currentAgentKey).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
67076
67109
  if (!target || (load.get(target.id) ?? 0) >= limit) {
67077
67110
  skipped++;
67078
67111
  continue;
67079
67112
  }
67080
67113
  updateTask3(task.id, { assigned_to: target.id, version: task.version });
67081
- load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
67114
+ load.set(currentAgentKey, (load.get(currentAgentKey) ?? 1) - 1);
67082
67115
  load.set(target.id, (load.get(target.id) ?? 0) + 1);
67083
67116
  moved++;
67084
67117
  }
@@ -67257,6 +67290,7 @@ ${lines.join(`
67257
67290
  var init_task_auto_tools = __esm(() => {
67258
67291
  init_zod();
67259
67292
  init_cloud_router();
67293
+ init_database();
67260
67294
  });
67261
67295
 
67262
67296
  // src/lib/context-packs.ts
@@ -68436,7 +68470,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
68436
68470
  }, undefined);
68437
68471
  const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
68438
68472
  const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
68439
- const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
68473
+ const blockedAliases = effectiveAgentId ? assignedToAliasSet(getDatabase(), effectiveAgentId) : null;
68474
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => blockedAliases ? blockedAliases.has((t.assigned_to ?? "").toLowerCase()) : false);
68440
68475
  const lines = [
68441
68476
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
68442
68477
  inProgress.length > 0 ? `
@@ -68664,6 +68699,7 @@ var init_task_adv_tools = __esm(() => {
68664
68699
  init_zod();
68665
68700
  init_token_utils();
68666
68701
  init_cloud_router();
68702
+ init_database();
68667
68703
  });
68668
68704
 
68669
68705
  // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.cjs
@@ -94355,27 +94391,29 @@ function getAgentMetrics(agentId, opts, db) {
94355
94391
  const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
94356
94392
  if (!agent)
94357
94393
  return null;
94394
+ const assignedAliases = resolveAssignedToAliases(d, agent.id).map((a) => a.toLowerCase());
94395
+ const assignedInClause = `LOWER(assigned_to) IN (${assignedAliases.map(() => "?").join(",")})`;
94358
94396
  let projectFilter = "";
94359
- const params = [agent.id, agent.id];
94397
+ const params = [agent.id, ...assignedAliases];
94360
94398
  if (opts?.project_id) {
94361
94399
  projectFilter = " AND project_id = ?";
94362
94400
  params.push(opts.project_id);
94363
94401
  }
94364
- const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
94365
- const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
94366
- const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
94402
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}`).get(...params).count;
94403
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'failed'${projectFilter}`).get(...params).count;
94404
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'in_progress'${projectFilter}`).get(...params).count;
94367
94405
  const total = completed + failed;
94368
94406
  const completionRate = total > 0 ? completed / total : 0;
94369
94407
  const avgTime = d.query(`SELECT AVG(
94370
94408
  (julianday(completed_at) - julianday(created_at)) * 24 * 60
94371
94409
  ) as avg_minutes
94372
94410
  FROM tasks
94373
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
94411
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
94374
94412
  const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
94375
94413
  FROM tasks
94376
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
94414
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
94377
94415
  const reviewTasks = d.query(`SELECT metadata FROM tasks
94378
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
94416
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}
94379
94417
  AND metadata LIKE '%_review_score%'`).all(...params);
94380
94418
  let reviewScoreAvg = null;
94381
94419
  if (reviewTasks.length > 0) {
@@ -94839,14 +94877,15 @@ function millisBetween(start, end) {
94839
94877
  return 0;
94840
94878
  return endMs - startMs;
94841
94879
  }
94842
- function addTaskScope(where, params, options, alias = "t") {
94880
+ function addTaskScope(where, params, options, db, alias = "t") {
94843
94881
  if (options.project_id) {
94844
94882
  where.push(`${alias}.project_id = ?`);
94845
94883
  params.push(options.project_id);
94846
94884
  }
94847
94885
  if (options.agent_id) {
94848
- where.push(`(${alias}.agent_id = ? OR ${alias}.assigned_to = ?)`);
94849
- params.push(options.agent_id, options.agent_id);
94886
+ params.push(options.agent_id);
94887
+ const assignedInClause = lowerInClause(`${alias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
94888
+ where.push(`(${alias}.agent_id = ? OR ${assignedInClause})`);
94850
94889
  }
94851
94890
  if (options.since) {
94852
94891
  where.push(`${alias}.created_at >= ?`);
@@ -94857,14 +94896,15 @@ function addTaskScope(where, params, options, alias = "t") {
94857
94896
  params.push(options.until);
94858
94897
  }
94859
94898
  }
94860
- function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
94899
+ function addRunScope(where, params, options, db, runAlias = "r", taskAlias = "t") {
94861
94900
  if (options.project_id) {
94862
94901
  where.push(`${taskAlias}.project_id = ?`);
94863
94902
  params.push(options.project_id);
94864
94903
  }
94865
94904
  if (options.agent_id) {
94866
- where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${taskAlias}.assigned_to = ?)`);
94867
- params.push(options.agent_id, options.agent_id, options.agent_id);
94905
+ params.push(options.agent_id, options.agent_id);
94906
+ const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
94907
+ where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
94868
94908
  }
94869
94909
  if (options.since) {
94870
94910
  where.push(`${runAlias}.started_at >= ?`);
@@ -94875,14 +94915,15 @@ function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
94875
94915
  params.push(options.until);
94876
94916
  }
94877
94917
  }
94878
- function addTraceScope(where, params, options, traceAlias = "tr", taskAlias = "t") {
94918
+ function addTraceScope(where, params, options, db, traceAlias = "tr", taskAlias = "t") {
94879
94919
  if (options.project_id) {
94880
94920
  where.push(`${taskAlias}.project_id = ?`);
94881
94921
  params.push(options.project_id);
94882
94922
  }
94883
94923
  if (options.agent_id) {
94884
- where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${taskAlias}.assigned_to = ?)`);
94885
- params.push(options.agent_id, options.agent_id, options.agent_id);
94924
+ params.push(options.agent_id, options.agent_id);
94925
+ const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
94926
+ where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
94886
94927
  }
94887
94928
  if (options.since) {
94888
94929
  where.push(`${traceAlias}.created_at >= ?`);
@@ -94942,7 +94983,7 @@ function createLocalUsageLedger(options = {}, db) {
94942
94983
  const generatedAt = options.generated_at || new Date().toISOString();
94943
94984
  const taskWhere = [];
94944
94985
  const taskParams = [];
94945
- addTaskScope(taskWhere, taskParams, options);
94986
+ addTaskScope(taskWhere, taskParams, options, d);
94946
94987
  const taskClause = taskWhere.length ? `WHERE ${taskWhere.join(" AND ")}` : "";
94947
94988
  const taskTotals = queryOne(d, `SELECT COUNT(*) as tasks, COALESCE(SUM(cost_tokens), 0) as task_tokens, COALESCE(SUM(cost_usd), 0) as task_cost_usd FROM tasks t ${taskClause}`, taskParams);
94948
94989
  let projectCount = 0;
@@ -94951,14 +94992,14 @@ function createLocalUsageLedger(options = {}, db) {
94951
94992
  } else if (options.agent_id) {
94952
94993
  const projectWhere = ["t.project_id IS NOT NULL"];
94953
94994
  const projectParams = [];
94954
- addTaskScope(projectWhere, projectParams, options);
94995
+ addTaskScope(projectWhere, projectParams, options, d);
94955
94996
  projectCount = queryOne(d, `SELECT COUNT(DISTINCT t.project_id) as count FROM tasks t WHERE ${projectWhere.join(" AND ")}`, projectParams).count;
94956
94997
  } else {
94957
94998
  projectCount = queryOne(d, "SELECT COUNT(*) as count FROM projects", []).count;
94958
94999
  }
94959
95000
  const runWhere = [];
94960
95001
  const runParams = [];
94961
- addRunScope(runWhere, runParams, options);
95002
+ addRunScope(runWhere, runParams, options, d);
94962
95003
  const runClause = runWhere.length ? `WHERE ${runWhere.join(" AND ")}` : "";
94963
95004
  const runs = queryAll(d, `SELECT r.id, r.started_at, r.completed_at, r.metadata
94964
95005
  FROM task_runs r JOIN tasks t ON t.id = r.task_id
@@ -94975,7 +95016,7 @@ function createLocalUsageLedger(options = {}, db) {
94975
95016
  ${runClause}`, runParams);
94976
95017
  const traceWhere = [];
94977
95018
  const traceParams = [];
94978
- addTraceScope(traceWhere, traceParams, options);
95019
+ addTraceScope(traceWhere, traceParams, options, d);
94979
95020
  const traceClause = traceWhere.length ? `WHERE ${traceWhere.join(" AND ")}` : "";
94980
95021
  const traceTotals = queryOne(d, `SELECT COUNT(*) as traces,
94981
95022
  COALESCE(SUM(tr.tokens), 0) as tokens,
@@ -95119,10 +95160,12 @@ function limitValue(value) {
95119
95160
  function isTerminal2(task) {
95120
95161
  return task.status === "completed" || task.status === "failed" || task.status === "cancelled";
95121
95162
  }
95122
- function sameAgent(task, agentId) {
95163
+ function sameAgent(task, agentId, aliasSet) {
95123
95164
  if (!agentId)
95124
95165
  return true;
95125
- return task.assigned_to === agentId || task.agent_id === agentId;
95166
+ if (task.agent_id === agentId)
95167
+ return true;
95168
+ return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
95126
95169
  }
95127
95170
  function withinTaskWindow(task, options) {
95128
95171
  const time3 = Date.parse(task.updated_at);
@@ -95135,11 +95178,12 @@ function withinTaskWindow(task, options) {
95135
95178
  return true;
95136
95179
  }
95137
95180
  function scopedTasks(options, db) {
95181
+ const aliasSet = options.agent_id ? assignedToAliasSet(db, options.agent_id) : undefined;
95138
95182
  return listTasks2({
95139
95183
  project_id: options.project_id,
95140
95184
  plan_id: options.plan_id,
95141
95185
  include_archived: false
95142
- }, db).filter((task) => sameAgent(task, options.agent_id) && withinTaskWindow(task, options));
95186
+ }, db).filter((task) => sameAgent(task, options.agent_id, aliasSet) && withinTaskWindow(task, options));
95143
95187
  }
95144
95188
  function summarizeTask(task) {
95145
95189
  return {
@@ -95205,7 +95249,7 @@ function initialAgentSummary(agentId) {
95205
95249
  }
95206
95250
  };
95207
95251
  }
95208
- function addScopeClauses(where, params, options, timeColumn) {
95252
+ function addScopeClauses(where, params, options, timeColumn, db) {
95209
95253
  if (options.project_id) {
95210
95254
  where.push("t.project_id = ?");
95211
95255
  params.push(options.project_id);
@@ -95215,8 +95259,9 @@ function addScopeClauses(where, params, options, timeColumn) {
95215
95259
  params.push(options.plan_id);
95216
95260
  }
95217
95261
  if (options.agent_id) {
95218
- where.push("(r.agent_id = ? OR t.agent_id = ? OR t.assigned_to = ?)");
95219
- params.push(options.agent_id, options.agent_id, options.agent_id);
95262
+ params.push(options.agent_id, options.agent_id);
95263
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
95264
+ where.push(`(r.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
95220
95265
  }
95221
95266
  if (options.since) {
95222
95267
  where.push(`${timeColumn} >= ?`);
@@ -95230,7 +95275,7 @@ function addScopeClauses(where, params, options, timeColumn) {
95230
95275
  function loadRuns(options, db) {
95231
95276
  const where = ["t.archived_at IS NULL"];
95232
95277
  const params = [];
95233
- addScopeClauses(where, params, options, "r.started_at");
95278
+ addScopeClauses(where, params, options, "r.started_at", db);
95234
95279
  return db.query(`
95235
95280
  SELECT
95236
95281
  r.id,
@@ -95270,8 +95315,9 @@ function loadVerifications(options, db) {
95270
95315
  params.push(options.plan_id);
95271
95316
  }
95272
95317
  if (options.agent_id) {
95273
- where.push("(v.agent_id = ? OR t.agent_id = ? OR t.assigned_to = ?)");
95274
- params.push(options.agent_id, options.agent_id, options.agent_id);
95318
+ params.push(options.agent_id, options.agent_id);
95319
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
95320
+ where.push(`(v.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
95275
95321
  }
95276
95322
  if (options.since) {
95277
95323
  where.push("v.run_at >= ?");
@@ -95493,7 +95539,6 @@ function renderLocalReportMarkdown(report) {
95493
95539
  }
95494
95540
  var LOCAL_REPORT_SCHEMA_VERSION = 1, LOCAL_REPORT_TYPES;
95495
95541
  var init_local_reports = __esm(() => {
95496
- init_database();
95497
95542
  init_database();
95498
95543
  init_plans();
95499
95544
  init_tasks();
@@ -99596,7 +99641,7 @@ ${lines.join(`
99596
99641
  a.name AS agent_name
99597
99642
  FROM task_files tf
99598
99643
  JOIN tasks t ON tf.task_id = t.id
99599
- LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
99644
+ LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name))))
99600
99645
  WHERE t.status = 'in_progress'
99601
99646
  AND tf.status != 'removed'
99602
99647
  AND t.project_id = ?
@@ -103360,7 +103405,7 @@ ${text2}` }] };
103360
103405
  const oldName = agent.name;
103361
103406
  const updated = updateAgent2(agent.id, { name: new_name });
103362
103407
  const db = getDatabase();
103363
- const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
103408
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE LOWER(assigned_to) = LOWER(?)", [new_name, oldName]);
103364
103409
  const taskNote = tasksResult.changes > 0 ? `
103365
103410
  Updated assigned_to on ${tasksResult.changes} task(s).` : "";
103366
103411
  return {