@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
package/dist/cli/index.js CHANGED
@@ -2123,7 +2123,7 @@ var package_default;
2123
2123
  var init_package = __esm(() => {
2124
2124
  package_default = {
2125
2125
  name: "@hasna/todos",
2126
- version: "0.13.10",
2126
+ version: "0.13.12",
2127
2127
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
2128
2128
  type: "module",
2129
2129
  main: "dist/index.js",
@@ -6045,8 +6045,10 @@ async function cloudEscalatedTasks(client, opts = {}, at = new Date) {
6045
6045
  const filter = {};
6046
6046
  if (opts.project_id)
6047
6047
  filter.project_id = opts.project_id;
6048
+ if (opts.agent_id)
6049
+ filter.assigned_to = opts.agent_id;
6048
6050
  const active = await cloudActiveTasks(client, filter);
6049
- return active.filter((t) => !t.archived_at && (opts.agent_id ? t.assigned_to === opts.agent_id : true)).map((task) => {
6051
+ return active.filter((t) => !t.archived_at).map((task) => {
6050
6052
  const reasons = [];
6051
6053
  const breachedTimes = [];
6052
6054
  if (task.due_at) {
@@ -6258,7 +6260,11 @@ async function cloudRecap(client, hours, projectId) {
6258
6260
  const blocked = pending.filter((t) => blockedMap.has(t.id));
6259
6261
  const sinceMs = new Date(since).getTime();
6260
6262
  const agentSummaries = agents.map((agent) => {
6261
- const owned = all.filter((t) => t.assigned_to === agent.id || t.agent_id === agent.id);
6263
+ const assignedName = agent.name?.toLowerCase();
6264
+ const owned = all.filter((t) => {
6265
+ const assignedTo = t.assigned_to?.toLowerCase();
6266
+ return assignedTo === agent.id.toLowerCase() || assignedName && assignedTo === assignedName || t.agent_id === agent.id;
6267
+ });
6262
6268
  return {
6263
6269
  name: agent.name,
6264
6270
  completed_count: owned.filter((t) => t.status === "completed" && t.completed_at != null && t.completed_at > since).length,
@@ -10714,14 +10720,17 @@ var exports_database = {};
10714
10720
  __export(exports_database, {
10715
10721
  uuid: () => uuid,
10716
10722
  resolvePartialId: () => resolvePartialId,
10723
+ resolveAssignedToAliases: () => resolveAssignedToAliases,
10717
10724
  resetDatabase: () => resetDatabase,
10718
10725
  now: () => now,
10726
+ lowerInClause: () => lowerInClause,
10719
10727
  lockExpiryCutoff: () => lockExpiryCutoff,
10720
10728
  isLockExpired: () => isLockExpired,
10721
10729
  getDatabasePath: () => getDatabasePath,
10722
10730
  getDatabase: () => getDatabase,
10723
10731
  closeDatabase: () => closeDatabase,
10724
10732
  clearExpiredLocks: () => clearExpiredLocks,
10733
+ assignedToAliasSet: () => assignedToAliasSet,
10725
10734
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
10726
10735
  });
10727
10736
  import { Database } from "bun:sqlite";
@@ -10950,6 +10959,33 @@ function resolvePartialId(db, table, partialId) {
10950
10959
  }
10951
10960
  return null;
10952
10961
  }
10962
+ function resolveAssignedToAliases(db, ref) {
10963
+ const aliases = new Set([ref]);
10964
+ let agentId;
10965
+ try {
10966
+ agentId = resolvePartialId(db, "agents", ref);
10967
+ } catch (err) {
10968
+ if (!(err instanceof IdentityAliasAmbiguousError))
10969
+ throw err;
10970
+ agentId = null;
10971
+ }
10972
+ if (agentId) {
10973
+ aliases.add(agentId);
10974
+ const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
10975
+ if (row?.name)
10976
+ aliases.add(row.name);
10977
+ }
10978
+ return [...aliases];
10979
+ }
10980
+ function assignedToAliasSet(db, ref) {
10981
+ return new Set(resolveAssignedToAliases(db, ref).map((a) => a.toLowerCase()));
10982
+ }
10983
+ function lowerInClause(column, values, params) {
10984
+ if (values.length === 0)
10985
+ return "1=0";
10986
+ params.push(...values.map((v) => v.toLowerCase()));
10987
+ return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
10988
+ }
10953
10989
  var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
10954
10990
  var init_database = __esm(() => {
10955
10991
  init_schema();
@@ -11740,17 +11776,19 @@ function checkCompletionGuard(task, agentId, db, configOverride) {
11740
11776
  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);
11741
11777
  }
11742
11778
  }
11779
+ const assignedAliasParams = [];
11780
+ const assignedInClause = agent ? lowerInClause("assigned_to", resolveAssignedToAliases(db, agent), assignedAliasParams) : "";
11743
11781
  if (agent && config.max_completions_per_window && config.window_minutes) {
11744
11782
  const windowStart = new Date(Date.now() - config.window_minutes * 60 * 1000).toISOString();
11745
11783
  const result = db.query(`SELECT COUNT(*) as count FROM tasks
11746
- WHERE completed_at > ? AND (assigned_to = ? OR agent_id = ?)`).get(windowStart, agent, agent);
11784
+ WHERE completed_at > ? AND (${assignedInClause} OR agent_id = ?)`).get(windowStart, ...assignedAliasParams, agent);
11747
11785
  if (result.count >= config.max_completions_per_window) {
11748
11786
  throw new CompletionGuardError(`Rate limit: ${result.count} tasks completed in the last ${config.window_minutes} minutes (max ${config.max_completions_per_window}). Slow down.`);
11749
11787
  }
11750
11788
  }
11751
11789
  if (agent && config.cooldown_seconds) {
11752
11790
  const result = db.query(`SELECT MAX(completed_at) as last_completed FROM tasks
11753
- WHERE completed_at IS NOT NULL AND (assigned_to = ? OR agent_id = ?) AND id != ?`).get(agent, agent, task.id);
11791
+ WHERE completed_at IS NOT NULL AND (${assignedInClause} OR agent_id = ?) AND id != ?`).get(...assignedAliasParams, agent, task.id);
11754
11792
  if (result.last_completed) {
11755
11793
  const elapsedSeconds = (Date.now() - new Date(result.last_completed).getTime()) / 1000;
11756
11794
  if (elapsedSeconds < config.cooldown_seconds) {
@@ -11764,6 +11802,7 @@ var init_completion_guard = __esm(() => {
11764
11802
  init_types();
11765
11803
  init_config();
11766
11804
  init_projects();
11805
+ init_database();
11767
11806
  });
11768
11807
 
11769
11808
  // src/lib/event-emission-safety.ts
@@ -13862,7 +13901,7 @@ function getRecap(hours = 8, projectId, db) {
13862
13901
  const in_progress = projectId ? d.query(`SELECT id, short_id, title, assigned_to, started_at FROM tasks WHERE status = 'in_progress' AND project_id = ? ORDER BY updated_at DESC`).all(projectId) : d.query(`SELECT id, short_id, title, assigned_to, started_at FROM tasks WHERE status = 'in_progress' ORDER BY updated_at DESC`).all();
13863
13902
  const blocked = projectId ? d.query(`SELECT DISTINCT t.id, t.short_id, t.title, t.assigned_to FROM tasks t JOIN task_dependencies td ON td.task_id = t.id JOIN tasks dep ON dep.id = td.depends_on AND dep.status NOT IN ('completed','cancelled') WHERE t.status = 'pending'${tpf}`).all(projectId) : d.query(`SELECT DISTINCT t.id, t.short_id, t.title, t.assigned_to FROM tasks t JOIN task_dependencies td ON td.task_id = t.id JOIN tasks dep ON dep.id = td.depends_on AND dep.status NOT IN ('completed','cancelled') WHERE t.status = 'pending'`).all();
13864
13903
  const stale = projectId ? d.query(`SELECT id, short_id, title, assigned_to, updated_at FROM tasks WHERE status = 'in_progress' AND updated_at < ? AND project_id = ? ORDER BY updated_at ASC`).all(staleWindow, projectId) : d.query(`SELECT id, short_id, title, assigned_to, updated_at FROM tasks WHERE status = 'in_progress' AND updated_at < ? ORDER BY updated_at ASC`).all(staleWindow);
13865
- const agents = projectId ? d.query(`SELECT a.name, a.last_seen_at, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR t.agent_id = a.id) AND t.status = 'completed' AND t.completed_at > ?${tpf}) as completed_count, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR t.agent_id = a.id) AND t.status = 'in_progress'${tpf}) as in_progress_count FROM agents a WHERE a.status = 'active' AND a.last_seen_at > ? ORDER BY completed_count DESC`).all(since, projectId, projectId, since) : d.query(`SELECT a.name, a.last_seen_at, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR t.agent_id = a.id) AND t.status = 'completed' AND t.completed_at > ?) as completed_count, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR t.agent_id = a.id) AND t.status = 'in_progress') as in_progress_count FROM agents a WHERE a.status = 'active' AND a.last_seen_at > ? ORDER BY completed_count DESC`).all(since, since);
13904
+ const agents = projectId ? d.query(`SELECT a.name, a.last_seen_at, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name) OR t.agent_id = a.id) AND t.status = 'completed' AND t.completed_at > ?${tpf}) as completed_count, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name) OR t.agent_id = a.id) AND t.status = 'in_progress'${tpf}) as in_progress_count FROM agents a WHERE a.status = 'active' AND a.last_seen_at > ? ORDER BY completed_count DESC`).all(since, projectId, projectId, since) : d.query(`SELECT a.name, a.last_seen_at, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name) OR t.agent_id = a.id) AND t.status = 'completed' AND t.completed_at > ?) as completed_count, (SELECT COUNT(*) FROM tasks t WHERE (t.assigned_to = a.id OR LOWER(t.assigned_to) = LOWER(a.name) OR t.agent_id = a.id) AND t.status = 'in_progress') as in_progress_count FROM agents a WHERE a.status = 'active' AND a.last_seen_at > ? ORDER BY completed_count DESC`).all(since, since);
13866
13905
  return {
13867
13906
  hours,
13868
13907
  since,
@@ -14008,13 +14047,17 @@ function logDelivery(d, webhookId, event, payload, statusCode, response, attempt
14008
14047
  const id = uuid();
14009
14048
  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()]);
14010
14049
  }
14011
- function matchesScope(wh, payload) {
14050
+ function matchesScope(wh, payload, db) {
14012
14051
  if (wh.project_id && payload.project_id !== wh.project_id)
14013
14052
  return false;
14014
14053
  if (wh.task_list_id && payload.task_list_id !== wh.task_list_id)
14015
14054
  return false;
14016
- if (wh.agent_id && payload.agent_id !== wh.agent_id && payload.assigned_to !== wh.agent_id)
14017
- return false;
14055
+ if (wh.agent_id && payload.agent_id !== wh.agent_id) {
14056
+ const aliasSet = assignedToAliasSet(db, wh.agent_id);
14057
+ const assignedTo = typeof payload.assigned_to === "string" ? payload.assigned_to.toLowerCase() : "";
14058
+ if (!aliasSet.has(assignedTo))
14059
+ return false;
14060
+ }
14018
14061
  if (wh.task_id && payload.id !== wh.task_id)
14019
14062
  return false;
14020
14063
  return true;
@@ -14081,7 +14124,7 @@ async function dispatchWebhook2(event, payload, db) {
14081
14124
  const webhooks = listWebhooks(d).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
14082
14125
  const payloadObj = typeof payload === "object" && payload !== null ? payload : {};
14083
14126
  for (const wh of webhooks) {
14084
- if (!matchesScope(wh, payloadObj))
14127
+ if (!matchesScope(wh, payloadObj, d))
14085
14128
  continue;
14086
14129
  const body = JSON.stringify({ event, payload, timestamp: now() });
14087
14130
  deliverWebhook(wh, event, body, 1, d).catch((err) => {
@@ -15229,14 +15272,16 @@ function getNextTask(agentId, filters, db) {
15229
15272
  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')");
15230
15273
  const where = conditions.join(" AND ");
15231
15274
  let recentProjectIds = [];
15275
+ const assignedAliasParams = [];
15276
+ const assignedInClause = agentId ? lowerInClause("assigned_to", resolveAssignedToAliases(d, agentId), assignedAliasParams) : "";
15232
15277
  if (agentId) {
15233
- 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);
15278
+ 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);
15234
15279
  recentProjectIds = recentRows.map((r) => r.project_id);
15235
15280
  }
15236
15281
  let sql = `SELECT * FROM tasks WHERE ${where} ORDER BY `;
15237
15282
  if (agentId) {
15238
- sql += `CASE WHEN assigned_to = ? THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
15239
- params.push(agentId);
15283
+ sql += `CASE WHEN ${assignedInClause} THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
15284
+ params.push(...assignedAliasParams);
15240
15285
  }
15241
15286
  if (recentProjectIds.length > 0) {
15242
15287
  const placeholders = recentProjectIds.map(() => "?").join(",");
@@ -15630,30 +15675,6 @@ function getTaskWithRelations(id, db) {
15630
15675
  checklist
15631
15676
  };
15632
15677
  }
15633
- function resolveAssignedToAliases(db, ref) {
15634
- const aliases = new Set([ref]);
15635
- let agentId;
15636
- try {
15637
- agentId = resolvePartialId(db, "agents", ref);
15638
- } catch (err) {
15639
- if (!(err instanceof IdentityAliasAmbiguousError))
15640
- throw err;
15641
- agentId = null;
15642
- }
15643
- if (agentId) {
15644
- aliases.add(agentId);
15645
- const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
15646
- if (row?.name)
15647
- aliases.add(row.name);
15648
- }
15649
- return [...aliases];
15650
- }
15651
- function lowerInClause(column, values, params) {
15652
- if (values.length === 0)
15653
- return "1=0";
15654
- params.push(...values.map((v) => v.toLowerCase()));
15655
- return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
15656
- }
15657
15678
  function listTasks(filter = {}, db) {
15658
15679
  const d = db || getDatabase();
15659
15680
  const { clearExpiredLocks: clearExpiredLocks2 } = (init_database(), __toCommonJS(exports_database));
@@ -16126,6 +16147,7 @@ function deleteTask(id, db) {
16126
16147
  var init_task_crud = __esm(() => {
16127
16148
  init_types();
16128
16149
  init_database();
16150
+ init_database();
16129
16151
  init_completion_guard();
16130
16152
  init_event_emission_safety();
16131
16153
  init_event_hooks();
@@ -16288,8 +16310,8 @@ function getTaskStats(filters, db) {
16288
16310
  params.push(filters.task_list_id);
16289
16311
  }
16290
16312
  if (filters?.agent_id) {
16291
- conditions.push("(agent_id = ? OR assigned_to = ?)");
16292
- params.push(filters.agent_id, filters.agent_id);
16313
+ params.push(filters.agent_id);
16314
+ conditions.push(`(agent_id = ? OR ${lowerInClause("assigned_to", resolveAssignedToAliases(d, filters.agent_id), params)})`);
16293
16315
  }
16294
16316
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
16295
16317
  const totalRow = d.query(`SELECT COUNT(*) as count FROM tasks ${where}`).get(...params);
@@ -16472,8 +16494,7 @@ function getEscalatedTasks(opts = {}, db, at = new Date) {
16472
16494
  params.push(opts.project_id);
16473
16495
  }
16474
16496
  if (opts.agent_id) {
16475
- conditions.push("assigned_to = ?");
16476
- params.push(opts.agent_id);
16497
+ conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
16477
16498
  }
16478
16499
  const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC, created_at ASC`).all(...params);
16479
16500
  return rows.map(rowToTask).map((task) => {
@@ -16521,8 +16542,7 @@ function notifyUpcomingDeadlines(opts = {}, db) {
16521
16542
  params.push(opts.project_id);
16522
16543
  }
16523
16544
  if (opts.agent_id) {
16524
- conditions.push("assigned_to = ?");
16525
- params.push(opts.agent_id);
16545
+ conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
16526
16546
  }
16527
16547
  const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
16528
16548
  return rows.map(rowToTask);
@@ -16780,13 +16800,14 @@ function getTimeReport(opts, db) {
16780
16800
  params.push(opts.plan_id);
16781
16801
  }
16782
16802
  if (opts?.agent_id) {
16803
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.agent_id), params);
16783
16804
  conditions.push(`(
16784
- t.assigned_to = ?
16805
+ ${assignedInClause}
16785
16806
  OR t.agent_id = ?
16786
16807
  OR EXISTS (SELECT 1 FROM task_time_logs ttl WHERE ttl.task_id = t.id AND ttl.agent_id = ?)
16787
16808
  OR EXISTS (SELECT 1 FROM focus_sessions fs WHERE fs.task_id = t.id AND fs.agent_id = ?)
16788
16809
  )`);
16789
- params.push(opts.agent_id, opts.agent_id, opts.agent_id, opts.agent_id);
16810
+ params.push(opts.agent_id, opts.agent_id, opts.agent_id);
16790
16811
  }
16791
16812
  if (opts?.since) {
16792
16813
  conditions.push("(t.completed_at >= ? OR t.updated_at >= ?)");
@@ -17799,7 +17820,7 @@ function listActiveFiles(db) {
17799
17820
  a.name AS agent_name
17800
17821
  FROM task_files tf
17801
17822
  JOIN tasks t ON tf.task_id = t.id
17802
- LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
17823
+ 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))))
17803
17824
  WHERE t.status = 'in_progress'
17804
17825
  AND tf.status != 'removed'
17805
17826
  ORDER BY tf.updated_at DESC
@@ -19090,10 +19111,11 @@ function validateAssignee(input, ctx) {
19090
19111
  if (ctx.allowSeat) {
19091
19112
  return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
19092
19113
  }
19114
+ const hint = ctx.seatHint?.(raw);
19093
19115
  return {
19094
19116
  ok: false,
19095
19117
  reason: "seat",
19096
- 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.`
19118
+ 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).")
19097
19119
  };
19098
19120
  }
19099
19121
  if (byId) {
@@ -19821,10 +19843,10 @@ var init_agents = __esm(() => {
19821
19843
 
19822
19844
  // src/cli/assignee-guard.ts
19823
19845
  import chalk2 from "chalk";
19824
- async function resolveValidatedAssignee(value, allowSeat) {
19846
+ async function resolveValidatedAssignee(value, allowSeat, buildSeatHint) {
19825
19847
  const cloud = getTodosCloudClient();
19826
19848
  const ctx = await loadAssigneeContext(() => cloud ? cloudListAgents(cloud) : listAgents(), allowSeat);
19827
- const verdict = validateAssignee(value, ctx);
19849
+ const verdict = validateAssignee(value, { ...ctx, seatHint: buildSeatHint });
19828
19850
  if (!verdict.ok) {
19829
19851
  handleError(new Error(`Cannot assign to '${value}'. ${verdict.message}`));
19830
19852
  }
@@ -20344,7 +20366,7 @@ function registerTaskCommands(program2) {
20344
20366
  opts.list = opts.list || opts.taskList;
20345
20367
  const creator = resolveCreatorIdentity(opts.createdBy || globalOpts.agent);
20346
20368
  const router = resolveWritableIdentity(opts.createdBy || globalOpts.agent);
20347
- const requestedAssign = opts.assign ? await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat)) : undefined;
20369
+ const requestedAssign = opts.assign ? await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`) : undefined;
20348
20370
  const assignee = requestedAssign || (opts.unassigned ? undefined : router.agent_id || undefined);
20349
20371
  if (!assignee && !opts.unassigned) {
20350
20372
  console.error(chalk3.yellow("Warning: task is ownerless and unattributable \u2014 export TODOS_AGENT_ID=<name> for this session, or pass --agent/--assign <agent> or --unassigned."));
@@ -20447,7 +20469,7 @@ function registerTaskCommands(program2) {
20447
20469
  opts.tags = opts.tags || opts.tag;
20448
20470
  opts.list = opts.list || opts.taskList;
20449
20471
  if (opts.assign) {
20450
- opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat));
20472
+ opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`);
20451
20473
  }
20452
20474
  const explicitProject = opts.project || globalOpts.project;
20453
20475
  const cloud = getTodosCloudClient();
@@ -21108,7 +21130,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
21108
21130
  handleError(new Error("Use either --working-dir or --clear-working-dir, not both."));
21109
21131
  }
21110
21132
  if (opts.assign) {
21111
- opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat));
21133
+ opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`);
21112
21134
  }
21113
21135
  const cloud = getTodosCloudClient();
21114
21136
  if (cloud) {
@@ -23367,8 +23389,7 @@ function searchTasks(options, projectId, taskListId, db) {
23367
23389
  }
23368
23390
  }
23369
23391
  if (opts.assigned_to) {
23370
- sql += " AND t.assigned_to = ?";
23371
- params.push(opts.assigned_to);
23392
+ sql += ` AND ${lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.assigned_to), params)}`;
23372
23393
  }
23373
23394
  if (opts.agent_id) {
23374
23395
  sql += " AND t.agent_id = ?";
@@ -34898,8 +34919,9 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
34898
34919
  }
34899
34920
  }
34900
34921
  function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
34922
+ const aliasSet = assignedToAliasSet(getDatabase(), agentId);
34901
34923
  const pending = listTasks({ status: "pending" });
34902
- const queue = pending.filter((t) => t.assigned_to === agentId || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
34924
+ const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
34903
34925
  const order = { critical: 0, high: 1, medium: 2, low: 3 };
34904
34926
  queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
34905
34927
  return json2(queue.map((t) => taskToSummary2(t)));
@@ -44586,11 +44608,12 @@ function runAlerts(input, db, checkedAt, quieted) {
44586
44608
  if (input.include_runs === false)
44587
44609
  return [];
44588
44610
  const since = Date.parse(input.run_since || new Date(Date.parse(checkedAt) - 24 * 60 * 60000).toISOString());
44611
+ const runAlertAliases = input.agent_id ? assignedToAliasSet(db, input.agent_id) : null;
44589
44612
  return listTaskRuns(undefined, db).filter((run) => run.completed_at && Date.parse(run.completed_at) >= since).map((run) => {
44590
44613
  const task = getTask(run.task_id, db);
44591
44614
  if (input.project_id && task?.project_id !== input.project_id)
44592
44615
  return null;
44593
- if (input.agent_id && run.agent_id !== input.agent_id && task?.assigned_to !== input.agent_id)
44616
+ if (input.agent_id && run.agent_id !== input.agent_id && !(runAlertAliases && runAlertAliases.has((task?.assigned_to ?? "").toLowerCase())))
44594
44617
  return null;
44595
44618
  const failed = ["failed", "cancelled"].includes(run.status);
44596
44619
  return {
@@ -44635,6 +44658,7 @@ async function checkLocalNotifications(input = {}, db) {
44635
44658
  const warnings = [];
44636
44659
  if (Number.isNaN(checkedMs))
44637
44660
  warnings.push("Invalid check timestamp; due-soon window may be incomplete.");
44661
+ const notifyAgentAliases = input.agent_id ? assignedToAliasSet(d, input.agent_id) : null;
44638
44662
  const alerts = [];
44639
44663
  for (const escalation of getEscalatedTasks({ project_id: input.project_id, agent_id: input.agent_id }, d, new Date(checkedAt))) {
44640
44664
  for (const reason of escalation.reasons) {
@@ -44646,7 +44670,7 @@ async function checkLocalNotifications(input = {}, db) {
44646
44670
  for (const task of listTasks({ project_id: input.project_id, include_archived: false, limit: 5000 }, d)) {
44647
44671
  if (!unfinished(task) || !task.due_at)
44648
44672
  continue;
44649
- if (input.agent_id && task.assigned_to !== input.agent_id && task.agent_id !== input.agent_id)
44673
+ if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
44650
44674
  continue;
44651
44675
  if (task.due_at > checkedAt && task.due_at <= dueSoonUntil) {
44652
44676
  alerts.push(taskAlert(task, "task_due_soon", "task.due_soon", "warning", `Due soon: ${task.title}`, task.due_at, quieted));
@@ -44654,7 +44678,7 @@ async function checkLocalNotifications(input = {}, db) {
44654
44678
  }
44655
44679
  }
44656
44680
  for (const task of getStaleTasks(staleMinutes, { project_id: input.project_id }, d)) {
44657
- if (input.agent_id && task.assigned_to !== input.agent_id && task.agent_id !== input.agent_id)
44681
+ if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
44658
44682
  continue;
44659
44683
  alerts.push(taskAlert(task, "task_stale", "task.stale", "warning", `Stale task: ${task.title}`, checkedAt, quieted, { stale_minutes: staleMinutes }));
44660
44684
  }
@@ -46479,10 +46503,12 @@ function matchesProfile(profile, query) {
46479
46503
  return false;
46480
46504
  return true;
46481
46505
  }
46482
- function taskMatchesAgent(task, agentId) {
46506
+ function taskMatchesAgent(task, agentId, aliasSet) {
46483
46507
  if (!agentId)
46484
46508
  return true;
46485
- return task.assigned_to === agentId || task.agent_id === agentId;
46509
+ if (task.agent_id === agentId)
46510
+ return true;
46511
+ return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
46486
46512
  }
46487
46513
  function estimateRemaining(task) {
46488
46514
  if (task.status === "completed" || task.status === "cancelled")
@@ -46552,7 +46578,8 @@ function removeCapacityProfile(idOrAgent, projectId) {
46552
46578
  }
46553
46579
  function getPlanningForecast(input = {}, db) {
46554
46580
  const startDate = input.start_date ? dateOnly(input.start_date) : todayIso();
46555
- const tasks = listTasks({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id));
46581
+ const aliasSet = input.agent_id ? assignedToAliasSet(db || getDatabase(), input.agent_id) : undefined;
46582
+ const tasks = listTasks({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id, aliasSet));
46556
46583
  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]));
46557
46584
  const profiles = listCapacityProfiles({ agent_id: input.agent_id, project_id: input.project_id });
46558
46585
  const globalProfiles = input.project_id ? listCapacityProfiles({ agent_id: input.agent_id }).filter((profile) => profile.project_id === null) : [];
@@ -46650,6 +46677,7 @@ function renderPlanningForecastMarkdown(forecast) {
46650
46677
  }
46651
46678
  var LOCAL_CAPACITY_SCHEMA_VERSION = 1;
46652
46679
  var init_capacity_forecasts = __esm(() => {
46680
+ init_database();
46653
46681
  init_tasks();
46654
46682
  init_task_relations();
46655
46683
  init_config();
@@ -49759,8 +49787,10 @@ function getLatestHandoff(agentId, projectId, db) {
49759
49787
  function createSessionRecoveryHandoff(input, db) {
49760
49788
  const d = db || getDatabase();
49761
49789
  const limit = input.limit || 20;
49762
- const conditions = ["status = 'in_progress'", "(assigned_to = ? OR agent_id = ? OR locked_by = ?)"];
49763
- const params = [input.agent_id, input.agent_id, input.agent_id];
49790
+ const params = [];
49791
+ const assignedInClause = lowerInClause("assigned_to", resolveAssignedToAliases(d, input.agent_id), params);
49792
+ const conditions = ["status = 'in_progress'", `(${assignedInClause} OR agent_id = ? OR locked_by = ?)`];
49793
+ params.push(input.agent_id, input.agent_id);
49764
49794
  if (input.session_id) {
49765
49795
  conditions.push("session_id = ?");
49766
49796
  params.push(input.session_id);
@@ -50352,7 +50382,8 @@ function registerTaskAutoTools(server, ctx) {
50352
50382
  const assigned = listTasks4({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
50353
50383
  const now4 = Date.now();
50354
50384
  const dueSoonCutoff = now4 + 24 * 60 * 60 * 1000;
50355
- const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
50385
+ const blockedAliases = assignedToAliasSet(getDatabase(), effectiveAgentId);
50386
+ const blocked = getBlockedTasks2().filter((t) => blockedAliases.has((t.assigned_to ?? "").toLowerCase()));
50356
50387
  const workload = {
50357
50388
  in_progress: assigned.filter((t) => t.status === "in_progress").length,
50358
50389
  pending: assigned.filter((t) => t.status === "pending").length,
@@ -50389,17 +50420,25 @@ function registerTaskAutoTools(server, ctx) {
50389
50420
  if (agents.length === 0)
50390
50421
  return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
50391
50422
  const activeTasks = listTasks4({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
50392
- const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
50423
+ const agentKeyByAlias = new Map;
50424
+ for (const agent of agents) {
50425
+ agentKeyByAlias.set(String(agent.id).toLowerCase(), agent.id);
50426
+ if (agent.name)
50427
+ agentKeyByAlias.set(String(agent.name).toLowerCase(), agent.id);
50428
+ }
50429
+ const canonicalAgentKey = (assignedTo) => assignedTo ? agentKeyByAlias.get(assignedTo.toLowerCase()) : undefined;
50430
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => canonicalAgentKey(t.assigned_to) === agent.id).length]));
50393
50431
  let moved = 0;
50394
50432
  let skipped = 0;
50395
- for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
50396
- const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
50433
+ for (const task of activeTasks.filter((t) => t.status === "pending" && canonicalAgentKey(t.assigned_to) && (load.get(canonicalAgentKey(t.assigned_to)) ?? 0) > limit)) {
50434
+ const currentAgentKey = canonicalAgentKey(task.assigned_to);
50435
+ const target = agents.filter((agent) => agent.id !== currentAgentKey).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
50397
50436
  if (!target || (load.get(target.id) ?? 0) >= limit) {
50398
50437
  skipped++;
50399
50438
  continue;
50400
50439
  }
50401
50440
  updateTask3(task.id, { assigned_to: target.id, version: task.version });
50402
- load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
50441
+ load.set(currentAgentKey, (load.get(currentAgentKey) ?? 1) - 1);
50403
50442
  load.set(target.id, (load.get(target.id) ?? 0) + 1);
50404
50443
  moved++;
50405
50444
  }
@@ -50578,6 +50617,7 @@ ${lines.join(`
50578
50617
  var init_task_auto_tools = __esm(() => {
50579
50618
  init_zod();
50580
50619
  init_cloud_router();
50620
+ init_database();
50581
50621
  });
50582
50622
 
50583
50623
  // src/lib/context-packs.ts
@@ -51757,7 +51797,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
51757
51797
  }, undefined);
51758
51798
  const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
51759
51799
  const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
51760
- const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
51800
+ const blockedAliases = effectiveAgentId ? assignedToAliasSet(getDatabase(), effectiveAgentId) : null;
51801
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => blockedAliases ? blockedAliases.has((t.assigned_to ?? "").toLowerCase()) : false);
51761
51802
  const lines = [
51762
51803
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
51763
51804
  inProgress.length > 0 ? `
@@ -51985,6 +52026,7 @@ var init_task_adv_tools = __esm(() => {
51985
52026
  init_zod();
51986
52027
  init_token_utils();
51987
52028
  init_cloud_router();
52029
+ init_database();
51988
52030
  });
51989
52031
 
51990
52032
  // src/mcp/tools/task-meta-tools.ts
@@ -54181,27 +54223,29 @@ function getAgentMetrics(agentId, opts, db) {
54181
54223
  const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
54182
54224
  if (!agent)
54183
54225
  return null;
54226
+ const assignedAliases = resolveAssignedToAliases(d, agent.id).map((a) => a.toLowerCase());
54227
+ const assignedInClause = `LOWER(assigned_to) IN (${assignedAliases.map(() => "?").join(",")})`;
54184
54228
  let projectFilter = "";
54185
- const params = [agent.id, agent.id];
54229
+ const params = [agent.id, ...assignedAliases];
54186
54230
  if (opts?.project_id) {
54187
54231
  projectFilter = " AND project_id = ?";
54188
54232
  params.push(opts.project_id);
54189
54233
  }
54190
- const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
54191
- const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
54192
- const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
54234
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}`).get(...params).count;
54235
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'failed'${projectFilter}`).get(...params).count;
54236
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'in_progress'${projectFilter}`).get(...params).count;
54193
54237
  const total = completed + failed;
54194
54238
  const completionRate = total > 0 ? completed / total : 0;
54195
54239
  const avgTime = d.query(`SELECT AVG(
54196
54240
  (julianday(completed_at) - julianday(created_at)) * 24 * 60
54197
54241
  ) as avg_minutes
54198
54242
  FROM tasks
54199
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
54243
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
54200
54244
  const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
54201
54245
  FROM tasks
54202
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
54246
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
54203
54247
  const reviewTasks = d.query(`SELECT metadata FROM tasks
54204
- WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
54248
+ WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}
54205
54249
  AND metadata LIKE '%_review_score%'`).all(...params);
54206
54250
  let reviewScoreAvg = null;
54207
54251
  if (reviewTasks.length > 0) {
@@ -54671,14 +54715,15 @@ function millisBetween(start, end) {
54671
54715
  return 0;
54672
54716
  return endMs - startMs;
54673
54717
  }
54674
- function addTaskScope(where, params, options, alias = "t") {
54718
+ function addTaskScope(where, params, options, db, alias = "t") {
54675
54719
  if (options.project_id) {
54676
54720
  where.push(`${alias}.project_id = ?`);
54677
54721
  params.push(options.project_id);
54678
54722
  }
54679
54723
  if (options.agent_id) {
54680
- where.push(`(${alias}.agent_id = ? OR ${alias}.assigned_to = ?)`);
54681
- params.push(options.agent_id, options.agent_id);
54724
+ params.push(options.agent_id);
54725
+ const assignedInClause = lowerInClause(`${alias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
54726
+ where.push(`(${alias}.agent_id = ? OR ${assignedInClause})`);
54682
54727
  }
54683
54728
  if (options.since) {
54684
54729
  where.push(`${alias}.created_at >= ?`);
@@ -54689,14 +54734,15 @@ function addTaskScope(where, params, options, alias = "t") {
54689
54734
  params.push(options.until);
54690
54735
  }
54691
54736
  }
54692
- function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
54737
+ function addRunScope(where, params, options, db, runAlias = "r", taskAlias = "t") {
54693
54738
  if (options.project_id) {
54694
54739
  where.push(`${taskAlias}.project_id = ?`);
54695
54740
  params.push(options.project_id);
54696
54741
  }
54697
54742
  if (options.agent_id) {
54698
- where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${taskAlias}.assigned_to = ?)`);
54699
- params.push(options.agent_id, options.agent_id, options.agent_id);
54743
+ params.push(options.agent_id, options.agent_id);
54744
+ const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
54745
+ where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
54700
54746
  }
54701
54747
  if (options.since) {
54702
54748
  where.push(`${runAlias}.started_at >= ?`);
@@ -54707,14 +54753,15 @@ function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
54707
54753
  params.push(options.until);
54708
54754
  }
54709
54755
  }
54710
- function addTraceScope(where, params, options, traceAlias = "tr", taskAlias = "t") {
54756
+ function addTraceScope(where, params, options, db, traceAlias = "tr", taskAlias = "t") {
54711
54757
  if (options.project_id) {
54712
54758
  where.push(`${taskAlias}.project_id = ?`);
54713
54759
  params.push(options.project_id);
54714
54760
  }
54715
54761
  if (options.agent_id) {
54716
- where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${taskAlias}.assigned_to = ?)`);
54717
- params.push(options.agent_id, options.agent_id, options.agent_id);
54762
+ params.push(options.agent_id, options.agent_id);
54763
+ const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
54764
+ where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
54718
54765
  }
54719
54766
  if (options.since) {
54720
54767
  where.push(`${traceAlias}.created_at >= ?`);
@@ -54774,7 +54821,7 @@ function createLocalUsageLedger(options = {}, db) {
54774
54821
  const generatedAt = options.generated_at || new Date().toISOString();
54775
54822
  const taskWhere = [];
54776
54823
  const taskParams = [];
54777
- addTaskScope(taskWhere, taskParams, options);
54824
+ addTaskScope(taskWhere, taskParams, options, d);
54778
54825
  const taskClause = taskWhere.length ? `WHERE ${taskWhere.join(" AND ")}` : "";
54779
54826
  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);
54780
54827
  let projectCount = 0;
@@ -54783,14 +54830,14 @@ function createLocalUsageLedger(options = {}, db) {
54783
54830
  } else if (options.agent_id) {
54784
54831
  const projectWhere = ["t.project_id IS NOT NULL"];
54785
54832
  const projectParams = [];
54786
- addTaskScope(projectWhere, projectParams, options);
54833
+ addTaskScope(projectWhere, projectParams, options, d);
54787
54834
  projectCount = queryOne(d, `SELECT COUNT(DISTINCT t.project_id) as count FROM tasks t WHERE ${projectWhere.join(" AND ")}`, projectParams).count;
54788
54835
  } else {
54789
54836
  projectCount = queryOne(d, "SELECT COUNT(*) as count FROM projects", []).count;
54790
54837
  }
54791
54838
  const runWhere = [];
54792
54839
  const runParams = [];
54793
- addRunScope(runWhere, runParams, options);
54840
+ addRunScope(runWhere, runParams, options, d);
54794
54841
  const runClause = runWhere.length ? `WHERE ${runWhere.join(" AND ")}` : "";
54795
54842
  const runs = queryAll(d, `SELECT r.id, r.started_at, r.completed_at, r.metadata
54796
54843
  FROM task_runs r JOIN tasks t ON t.id = r.task_id
@@ -54807,7 +54854,7 @@ function createLocalUsageLedger(options = {}, db) {
54807
54854
  ${runClause}`, runParams);
54808
54855
  const traceWhere = [];
54809
54856
  const traceParams = [];
54810
- addTraceScope(traceWhere, traceParams, options);
54857
+ addTraceScope(traceWhere, traceParams, options, d);
54811
54858
  const traceClause = traceWhere.length ? `WHERE ${traceWhere.join(" AND ")}` : "";
54812
54859
  const traceTotals = queryOne(d, `SELECT COUNT(*) as traces,
54813
54860
  COALESCE(SUM(tr.tokens), 0) as tokens,
@@ -54951,10 +54998,12 @@ function limitValue(value) {
54951
54998
  function isTerminal2(task) {
54952
54999
  return task.status === "completed" || task.status === "failed" || task.status === "cancelled";
54953
55000
  }
54954
- function sameAgent(task, agentId) {
55001
+ function sameAgent(task, agentId, aliasSet) {
54955
55002
  if (!agentId)
54956
55003
  return true;
54957
- return task.assigned_to === agentId || task.agent_id === agentId;
55004
+ if (task.agent_id === agentId)
55005
+ return true;
55006
+ return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
54958
55007
  }
54959
55008
  function withinTaskWindow(task, options) {
54960
55009
  const time = Date.parse(task.updated_at);
@@ -54967,11 +55016,12 @@ function withinTaskWindow(task, options) {
54967
55016
  return true;
54968
55017
  }
54969
55018
  function scopedTasks(options, db) {
55019
+ const aliasSet = options.agent_id ? assignedToAliasSet(db, options.agent_id) : undefined;
54970
55020
  return listTasks({
54971
55021
  project_id: options.project_id,
54972
55022
  plan_id: options.plan_id,
54973
55023
  include_archived: false
54974
- }, db).filter((task) => sameAgent(task, options.agent_id) && withinTaskWindow(task, options));
55024
+ }, db).filter((task) => sameAgent(task, options.agent_id, aliasSet) && withinTaskWindow(task, options));
54975
55025
  }
54976
55026
  function summarizeTask(task) {
54977
55027
  return {
@@ -55037,7 +55087,7 @@ function initialAgentSummary(agentId) {
55037
55087
  }
55038
55088
  };
55039
55089
  }
55040
- function addScopeClauses(where, params, options, timeColumn) {
55090
+ function addScopeClauses(where, params, options, timeColumn, db) {
55041
55091
  if (options.project_id) {
55042
55092
  where.push("t.project_id = ?");
55043
55093
  params.push(options.project_id);
@@ -55047,8 +55097,9 @@ function addScopeClauses(where, params, options, timeColumn) {
55047
55097
  params.push(options.plan_id);
55048
55098
  }
55049
55099
  if (options.agent_id) {
55050
- where.push("(r.agent_id = ? OR t.agent_id = ? OR t.assigned_to = ?)");
55051
- params.push(options.agent_id, options.agent_id, options.agent_id);
55100
+ params.push(options.agent_id, options.agent_id);
55101
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
55102
+ where.push(`(r.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
55052
55103
  }
55053
55104
  if (options.since) {
55054
55105
  where.push(`${timeColumn} >= ?`);
@@ -55062,7 +55113,7 @@ function addScopeClauses(where, params, options, timeColumn) {
55062
55113
  function loadRuns(options, db) {
55063
55114
  const where = ["t.archived_at IS NULL"];
55064
55115
  const params = [];
55065
- addScopeClauses(where, params, options, "r.started_at");
55116
+ addScopeClauses(where, params, options, "r.started_at", db);
55066
55117
  return db.query(`
55067
55118
  SELECT
55068
55119
  r.id,
@@ -55102,8 +55153,9 @@ function loadVerifications(options, db) {
55102
55153
  params.push(options.plan_id);
55103
55154
  }
55104
55155
  if (options.agent_id) {
55105
- where.push("(v.agent_id = ? OR t.agent_id = ? OR t.assigned_to = ?)");
55106
- params.push(options.agent_id, options.agent_id, options.agent_id);
55156
+ params.push(options.agent_id, options.agent_id);
55157
+ const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
55158
+ where.push(`(v.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
55107
55159
  }
55108
55160
  if (options.since) {
55109
55161
  where.push("v.run_at >= ?");
@@ -55325,7 +55377,6 @@ function renderLocalReportMarkdown(report) {
55325
55377
  }
55326
55378
  var LOCAL_REPORT_SCHEMA_VERSION = 1, LOCAL_REPORT_TYPES;
55327
55379
  var init_local_reports = __esm(() => {
55328
- init_database();
55329
55380
  init_database();
55330
55381
  init_plans();
55331
55382
  init_tasks();
@@ -58381,7 +58432,7 @@ ${lines.join(`
58381
58432
  a.name AS agent_name
58382
58433
  FROM task_files tf
58383
58434
  JOIN tasks t ON tf.task_id = t.id
58384
- LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
58435
+ 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))))
58385
58436
  WHERE t.status = 'in_progress'
58386
58437
  AND tf.status != 'removed'
58387
58438
  AND t.project_id = ?
@@ -61761,7 +61812,7 @@ ${text2}` }] };
61761
61812
  const oldName = agent.name;
61762
61813
  const updated = updateAgent(agent.id, { name: new_name });
61763
61814
  const db = getDatabase();
61764
- const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
61815
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE LOWER(assigned_to) = LOWER(?)", [new_name, oldName]);
61765
61816
  const taskNote = tasksResult.changes > 0 ? `
61766
61817
  Updated assigned_to on ${tasksResult.changes} task(s).` : "";
61767
61818
  return {
@@ -68355,7 +68406,7 @@ No task claimed (nothing available).`));
68355
68406
  });
68356
68407
  program2.command("assign <id> <agent>").description("Assign a task to an agent").option("-j, --json", "Output as JSON").option("--assign-seat", "Allow <agent> to name a durable seat (a seat queue has no session watching it)").action(async (id, agentInput, opts) => {
68357
68408
  const globalOpts = program2.opts();
68358
- const agent = await resolveValidatedAssignee(agentInput, Boolean(opts.assignSeat));
68409
+ const agent = await resolveValidatedAssignee(agentInput, Boolean(opts.assignSeat), (v) => `${id} ${v} --assign-seat`);
68359
68410
  const cloud = getTodosCloudClient();
68360
68411
  if (cloud) {
68361
68412
  try {