@hasna/todos 0.13.11 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts +39 -0
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/dispatch.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +64 -2
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +510 -149
- package/dist/contracts.js +102 -71
- package/dist/db/agent-metrics.d.ts.map +1 -1
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/budgets.d.ts.map +1 -1
- package/dist/db/database.d.ts +44 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/handoffs.d.ts.map +1 -1
- package/dist/db/task-crud.d.ts +1 -0
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/task-files.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/db/task-relations.d.ts.map +1 -1
- package/dist/db/task-status.d.ts.map +1 -1
- package/dist/db/webhooks.d.ts.map +1 -1
- package/dist/index.js +134 -93
- package/dist/lib/capacity-forecasts.d.ts.map +1 -1
- package/dist/lib/completion-guard.d.ts.map +1 -1
- package/dist/lib/enum-vocabulary.d.ts +72 -0
- package/dist/lib/enum-vocabulary.d.ts.map +1 -0
- package/dist/lib/local-notifications.d.ts.map +1 -1
- package/dist/lib/local-reports.d.ts.map +1 -1
- package/dist/lib/notification-reminders.d.ts.map +1 -1
- package/dist/lib/saved-search-views.d.ts +12 -1
- package/dist/lib/saved-search-views.d.ts.map +1 -1
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/usage-ledger.d.ts.map +1 -1
- package/dist/mcp/index.js +349 -115
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-resources.d.ts.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/registry.js +102 -71
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/v1.generated.d.ts +2 -2
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +349 -115
- package/dist/server/openapi.d.ts +58 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +69 -46
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -44,7 +44,10 @@ var __require = import.meta.require;
|
|
|
44
44
|
function isBlockingDependencyStatus(status) {
|
|
45
45
|
return status !== "completed" && status !== "cancelled";
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
function isTerminalStatus(status) {
|
|
48
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
49
|
+
}
|
|
50
|
+
var TASK_STATUSES, TASK_PRIORITIES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DispatchNotFoundError;
|
|
48
51
|
var init_types = __esm(() => {
|
|
49
52
|
TASK_STATUSES = [
|
|
50
53
|
"pending",
|
|
@@ -53,6 +56,12 @@ var init_types = __esm(() => {
|
|
|
53
56
|
"failed",
|
|
54
57
|
"cancelled"
|
|
55
58
|
];
|
|
59
|
+
TASK_PRIORITIES = [
|
|
60
|
+
"low",
|
|
61
|
+
"medium",
|
|
62
|
+
"high",
|
|
63
|
+
"critical"
|
|
64
|
+
];
|
|
56
65
|
VersionConflictError = class VersionConflictError extends Error {
|
|
57
66
|
taskId;
|
|
58
67
|
expectedVersion;
|
|
@@ -4111,14 +4120,17 @@ var exports_database = {};
|
|
|
4111
4120
|
__export(exports_database, {
|
|
4112
4121
|
uuid: () => uuid,
|
|
4113
4122
|
resolvePartialId: () => resolvePartialId,
|
|
4123
|
+
resolveAssignedToAliases: () => resolveAssignedToAliases,
|
|
4114
4124
|
resetDatabase: () => resetDatabase,
|
|
4115
4125
|
now: () => now,
|
|
4126
|
+
lowerInClause: () => lowerInClause,
|
|
4116
4127
|
lockExpiryCutoff: () => lockExpiryCutoff,
|
|
4117
4128
|
isLockExpired: () => isLockExpired,
|
|
4118
4129
|
getDatabasePath: () => getDatabasePath,
|
|
4119
4130
|
getDatabase: () => getDatabase,
|
|
4120
4131
|
closeDatabase: () => closeDatabase,
|
|
4121
4132
|
clearExpiredLocks: () => clearExpiredLocks,
|
|
4133
|
+
assignedToAliasSet: () => assignedToAliasSet,
|
|
4122
4134
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
4123
4135
|
});
|
|
4124
4136
|
import { Database } from "bun:sqlite";
|
|
@@ -4347,6 +4359,33 @@ function resolvePartialId(db, table, partialId) {
|
|
|
4347
4359
|
}
|
|
4348
4360
|
return null;
|
|
4349
4361
|
}
|
|
4362
|
+
function resolveAssignedToAliases(db, ref) {
|
|
4363
|
+
const aliases = new Set([ref]);
|
|
4364
|
+
let agentId;
|
|
4365
|
+
try {
|
|
4366
|
+
agentId = resolvePartialId(db, "agents", ref);
|
|
4367
|
+
} catch (err) {
|
|
4368
|
+
if (!(err instanceof IdentityAliasAmbiguousError))
|
|
4369
|
+
throw err;
|
|
4370
|
+
agentId = null;
|
|
4371
|
+
}
|
|
4372
|
+
if (agentId) {
|
|
4373
|
+
aliases.add(agentId);
|
|
4374
|
+
const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
|
|
4375
|
+
if (row?.name)
|
|
4376
|
+
aliases.add(row.name);
|
|
4377
|
+
}
|
|
4378
|
+
return [...aliases];
|
|
4379
|
+
}
|
|
4380
|
+
function assignedToAliasSet(db, ref) {
|
|
4381
|
+
return new Set(resolveAssignedToAliases(db, ref).map((a) => a.toLowerCase()));
|
|
4382
|
+
}
|
|
4383
|
+
function lowerInClause(column, values, params) {
|
|
4384
|
+
if (values.length === 0)
|
|
4385
|
+
return "1=0";
|
|
4386
|
+
params.push(...values.map((v) => v.toLowerCase()));
|
|
4387
|
+
return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
|
|
4388
|
+
}
|
|
4350
4389
|
var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
|
|
4351
4390
|
var init_database = __esm(() => {
|
|
4352
4391
|
init_schema();
|
|
@@ -9991,17 +10030,19 @@ function checkCompletionGuard(task, agentId, db, configOverride) {
|
|
|
9991
10030
|
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);
|
|
9992
10031
|
}
|
|
9993
10032
|
}
|
|
10033
|
+
const assignedAliasParams = [];
|
|
10034
|
+
const assignedInClause = agent ? lowerInClause("assigned_to", resolveAssignedToAliases(db, agent), assignedAliasParams) : "";
|
|
9994
10035
|
if (agent && config.max_completions_per_window && config.window_minutes) {
|
|
9995
10036
|
const windowStart = new Date(Date.now() - config.window_minutes * 60 * 1000).toISOString();
|
|
9996
10037
|
const result = db.query(`SELECT COUNT(*) as count FROM tasks
|
|
9997
|
-
WHERE completed_at > ? AND (
|
|
10038
|
+
WHERE completed_at > ? AND (${assignedInClause} OR agent_id = ?)`).get(windowStart, ...assignedAliasParams, agent);
|
|
9998
10039
|
if (result.count >= config.max_completions_per_window) {
|
|
9999
10040
|
throw new CompletionGuardError(`Rate limit: ${result.count} tasks completed in the last ${config.window_minutes} minutes (max ${config.max_completions_per_window}). Slow down.`);
|
|
10000
10041
|
}
|
|
10001
10042
|
}
|
|
10002
10043
|
if (agent && config.cooldown_seconds) {
|
|
10003
10044
|
const result = db.query(`SELECT MAX(completed_at) as last_completed FROM tasks
|
|
10004
|
-
WHERE completed_at IS NOT NULL AND (
|
|
10045
|
+
WHERE completed_at IS NOT NULL AND (${assignedInClause} OR agent_id = ?) AND id != ?`).get(...assignedAliasParams, agent, task.id);
|
|
10005
10046
|
if (result.last_completed) {
|
|
10006
10047
|
const elapsedSeconds = (Date.now() - new Date(result.last_completed).getTime()) / 1000;
|
|
10007
10048
|
if (elapsedSeconds < config.cooldown_seconds) {
|
|
@@ -10015,6 +10056,7 @@ var init_completion_guard = __esm(() => {
|
|
|
10015
10056
|
init_types();
|
|
10016
10057
|
init_config2();
|
|
10017
10058
|
init_projects();
|
|
10059
|
+
init_database();
|
|
10018
10060
|
});
|
|
10019
10061
|
|
|
10020
10062
|
// src/lib/event-emission-safety.ts
|
|
@@ -12013,13 +12055,17 @@ function logDelivery(d, webhookId, event, payload, statusCode, response, attempt
|
|
|
12013
12055
|
const id = uuid();
|
|
12014
12056
|
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()]);
|
|
12015
12057
|
}
|
|
12016
|
-
function matchesScope(wh, payload) {
|
|
12058
|
+
function matchesScope(wh, payload, db) {
|
|
12017
12059
|
if (wh.project_id && payload.project_id !== wh.project_id)
|
|
12018
12060
|
return false;
|
|
12019
12061
|
if (wh.task_list_id && payload.task_list_id !== wh.task_list_id)
|
|
12020
12062
|
return false;
|
|
12021
|
-
if (wh.agent_id && payload.agent_id !== wh.agent_id
|
|
12022
|
-
|
|
12063
|
+
if (wh.agent_id && payload.agent_id !== wh.agent_id) {
|
|
12064
|
+
const aliasSet = assignedToAliasSet(db, wh.agent_id);
|
|
12065
|
+
const assignedTo = typeof payload.assigned_to === "string" ? payload.assigned_to.toLowerCase() : "";
|
|
12066
|
+
if (!aliasSet.has(assignedTo))
|
|
12067
|
+
return false;
|
|
12068
|
+
}
|
|
12023
12069
|
if (wh.task_id && payload.id !== wh.task_id)
|
|
12024
12070
|
return false;
|
|
12025
12071
|
return true;
|
|
@@ -12086,7 +12132,7 @@ async function dispatchWebhook2(event, payload, db) {
|
|
|
12086
12132
|
const webhooks = listWebhooks(d).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
|
|
12087
12133
|
const payloadObj = typeof payload === "object" && payload !== null ? payload : {};
|
|
12088
12134
|
for (const wh of webhooks) {
|
|
12089
|
-
if (!matchesScope(wh, payloadObj))
|
|
12135
|
+
if (!matchesScope(wh, payloadObj, d))
|
|
12090
12136
|
continue;
|
|
12091
12137
|
const body = JSON.stringify({ event, payload, timestamp: now() });
|
|
12092
12138
|
deliverWebhook(wh, event, body, 1, d).catch((err) => {
|
|
@@ -13202,14 +13248,16 @@ function getNextTask(agentId, filters, db) {
|
|
|
13202
13248
|
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')");
|
|
13203
13249
|
const where = conditions.join(" AND ");
|
|
13204
13250
|
let recentProjectIds = [];
|
|
13251
|
+
const assignedAliasParams = [];
|
|
13252
|
+
const assignedInClause = agentId ? lowerInClause("assigned_to", resolveAssignedToAliases(d, agentId), assignedAliasParams) : "";
|
|
13205
13253
|
if (agentId) {
|
|
13206
|
-
const recentRows = d.query(`SELECT DISTINCT project_id FROM tasks WHERE
|
|
13254
|
+
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);
|
|
13207
13255
|
recentProjectIds = recentRows.map((r) => r.project_id);
|
|
13208
13256
|
}
|
|
13209
13257
|
let sql = `SELECT * FROM tasks WHERE ${where} ORDER BY `;
|
|
13210
13258
|
if (agentId) {
|
|
13211
|
-
sql += `CASE WHEN
|
|
13212
|
-
params.push(
|
|
13259
|
+
sql += `CASE WHEN ${assignedInClause} THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
|
|
13260
|
+
params.push(...assignedAliasParams);
|
|
13213
13261
|
}
|
|
13214
13262
|
if (recentProjectIds.length > 0) {
|
|
13215
13263
|
const placeholders = recentProjectIds.map(() => "?").join(",");
|
|
@@ -13603,30 +13651,6 @@ function getTaskWithRelations(id, db) {
|
|
|
13603
13651
|
checklist
|
|
13604
13652
|
};
|
|
13605
13653
|
}
|
|
13606
|
-
function resolveAssignedToAliases(db, ref) {
|
|
13607
|
-
const aliases = new Set([ref]);
|
|
13608
|
-
let agentId;
|
|
13609
|
-
try {
|
|
13610
|
-
agentId = resolvePartialId(db, "agents", ref);
|
|
13611
|
-
} catch (err) {
|
|
13612
|
-
if (!(err instanceof IdentityAliasAmbiguousError))
|
|
13613
|
-
throw err;
|
|
13614
|
-
agentId = null;
|
|
13615
|
-
}
|
|
13616
|
-
if (agentId) {
|
|
13617
|
-
aliases.add(agentId);
|
|
13618
|
-
const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
|
|
13619
|
-
if (row?.name)
|
|
13620
|
-
aliases.add(row.name);
|
|
13621
|
-
}
|
|
13622
|
-
return [...aliases];
|
|
13623
|
-
}
|
|
13624
|
-
function lowerInClause(column, values, params) {
|
|
13625
|
-
if (values.length === 0)
|
|
13626
|
-
return "1=0";
|
|
13627
|
-
params.push(...values.map((v) => v.toLowerCase()));
|
|
13628
|
-
return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
|
|
13629
|
-
}
|
|
13630
13654
|
function listTasks(filter = {}, db) {
|
|
13631
13655
|
const d = db || getDatabase();
|
|
13632
13656
|
const { clearExpiredLocks: clearExpiredLocks2 } = (init_database(), __toCommonJS(exports_database));
|
|
@@ -13912,11 +13936,13 @@ function updateTask(id, input, db) {
|
|
|
13912
13936
|
}
|
|
13913
13937
|
sets.push("status = ?");
|
|
13914
13938
|
params.push(input.status);
|
|
13939
|
+
if (isTerminalStatus(input.status)) {
|
|
13940
|
+
sets.push("locked_by = NULL");
|
|
13941
|
+
sets.push("locked_at = NULL");
|
|
13942
|
+
}
|
|
13915
13943
|
if (input.status === "completed") {
|
|
13916
13944
|
sets.push("completed_at = ?");
|
|
13917
13945
|
params.push(completionTimestamp);
|
|
13918
|
-
sets.push("locked_by = NULL");
|
|
13919
|
-
sets.push("locked_at = NULL");
|
|
13920
13946
|
} else if (task.status === "completed" && input.completed_at === undefined) {
|
|
13921
13947
|
sets.push("completed_at = NULL");
|
|
13922
13948
|
}
|
|
@@ -14040,6 +14066,7 @@ function updateTask(id, input, db) {
|
|
|
14040
14066
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
14041
14067
|
const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
|
|
14042
14068
|
const completedNow = input.status === "completed";
|
|
14069
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
14043
14070
|
const updatedTask = {
|
|
14044
14071
|
...task,
|
|
14045
14072
|
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
@@ -14047,8 +14074,8 @@ function updateTask(id, input, db) {
|
|
|
14047
14074
|
metadata: input.metadata ?? task.metadata,
|
|
14048
14075
|
version: task.version + 1,
|
|
14049
14076
|
updated_at: timestamp2,
|
|
14050
|
-
locked_by:
|
|
14051
|
-
locked_at:
|
|
14077
|
+
locked_by: terminalNow ? null : task.locked_by,
|
|
14078
|
+
locked_at: terminalNow ? null : task.locked_at,
|
|
14052
14079
|
completed_at: completedNow ? completionTimestamp : reopened ? null : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
14053
14080
|
sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
|
|
14054
14081
|
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
@@ -14099,6 +14126,7 @@ function deleteTask(id, db) {
|
|
|
14099
14126
|
var init_task_crud = __esm(() => {
|
|
14100
14127
|
init_types();
|
|
14101
14128
|
init_database();
|
|
14129
|
+
init_database();
|
|
14102
14130
|
init_completion_guard();
|
|
14103
14131
|
init_event_emission_safety();
|
|
14104
14132
|
init_event_hooks();
|
|
@@ -14261,8 +14289,8 @@ function getTaskStats(filters, db) {
|
|
|
14261
14289
|
params.push(filters.task_list_id);
|
|
14262
14290
|
}
|
|
14263
14291
|
if (filters?.agent_id) {
|
|
14264
|
-
|
|
14265
|
-
|
|
14292
|
+
params.push(filters.agent_id);
|
|
14293
|
+
conditions.push(`(agent_id = ? OR ${lowerInClause("assigned_to", resolveAssignedToAliases(d, filters.agent_id), params)})`);
|
|
14266
14294
|
}
|
|
14267
14295
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
14268
14296
|
const totalRow = d.query(`SELECT COUNT(*) as count FROM tasks ${where}`).get(...params);
|
|
@@ -14445,8 +14473,7 @@ function getEscalatedTasks(opts = {}, db, at = new Date) {
|
|
|
14445
14473
|
params.push(opts.project_id);
|
|
14446
14474
|
}
|
|
14447
14475
|
if (opts.agent_id) {
|
|
14448
|
-
conditions.push("assigned_to
|
|
14449
|
-
params.push(opts.agent_id);
|
|
14476
|
+
conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
|
|
14450
14477
|
}
|
|
14451
14478
|
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC, created_at ASC`).all(...params);
|
|
14452
14479
|
return rows.map(rowToTask).map((task) => {
|
|
@@ -14494,8 +14521,7 @@ function notifyUpcomingDeadlines(opts = {}, db) {
|
|
|
14494
14521
|
params.push(opts.project_id);
|
|
14495
14522
|
}
|
|
14496
14523
|
if (opts.agent_id) {
|
|
14497
|
-
conditions.push("assigned_to
|
|
14498
|
-
params.push(opts.agent_id);
|
|
14524
|
+
conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
|
|
14499
14525
|
}
|
|
14500
14526
|
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
|
|
14501
14527
|
return rows.map(rowToTask);
|
|
@@ -14753,13 +14779,14 @@ function getTimeReport(opts, db) {
|
|
|
14753
14779
|
params.push(opts.plan_id);
|
|
14754
14780
|
}
|
|
14755
14781
|
if (opts?.agent_id) {
|
|
14782
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.agent_id), params);
|
|
14756
14783
|
conditions.push(`(
|
|
14757
|
-
|
|
14784
|
+
${assignedInClause}
|
|
14758
14785
|
OR t.agent_id = ?
|
|
14759
14786
|
OR EXISTS (SELECT 1 FROM task_time_logs ttl WHERE ttl.task_id = t.id AND ttl.agent_id = ?)
|
|
14760
14787
|
OR EXISTS (SELECT 1 FROM focus_sessions fs WHERE fs.task_id = t.id AND fs.agent_id = ?)
|
|
14761
14788
|
)`);
|
|
14762
|
-
params.push(opts.agent_id, opts.agent_id, opts.agent_id
|
|
14789
|
+
params.push(opts.agent_id, opts.agent_id, opts.agent_id);
|
|
14763
14790
|
}
|
|
14764
14791
|
if (opts?.since) {
|
|
14765
14792
|
conditions.push("(t.completed_at >= ? OR t.updated_at >= ?)");
|
|
@@ -15755,7 +15782,7 @@ function listActiveFiles(db) {
|
|
|
15755
15782
|
a.name AS agent_name
|
|
15756
15783
|
FROM task_files tf
|
|
15757
15784
|
JOIN tasks t ON tf.task_id = t.id
|
|
15758
|
-
LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
|
|
15785
|
+
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))))
|
|
15759
15786
|
WHERE t.status = 'in_progress'
|
|
15760
15787
|
AND tf.status != 'removed'
|
|
15761
15788
|
ORDER BY tf.updated_at DESC
|
|
@@ -23056,11 +23083,12 @@ function runAlerts(input, db, checkedAt, quieted) {
|
|
|
23056
23083
|
if (input.include_runs === false)
|
|
23057
23084
|
return [];
|
|
23058
23085
|
const since = Date.parse(input.run_since || new Date(Date.parse(checkedAt) - 24 * 60 * 60000).toISOString());
|
|
23086
|
+
const runAlertAliases = input.agent_id ? assignedToAliasSet(db, input.agent_id) : null;
|
|
23059
23087
|
return listTaskRuns(undefined, db).filter((run) => run.completed_at && Date.parse(run.completed_at) >= since).map((run) => {
|
|
23060
23088
|
const task = getTask(run.task_id, db);
|
|
23061
23089
|
if (input.project_id && task?.project_id !== input.project_id)
|
|
23062
23090
|
return null;
|
|
23063
|
-
if (input.agent_id && run.agent_id !== input.agent_id && task?.assigned_to
|
|
23091
|
+
if (input.agent_id && run.agent_id !== input.agent_id && !(runAlertAliases && runAlertAliases.has((task?.assigned_to ?? "").toLowerCase())))
|
|
23064
23092
|
return null;
|
|
23065
23093
|
const failed = ["failed", "cancelled"].includes(run.status);
|
|
23066
23094
|
return {
|
|
@@ -23105,6 +23133,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
23105
23133
|
const warnings = [];
|
|
23106
23134
|
if (Number.isNaN(checkedMs))
|
|
23107
23135
|
warnings.push("Invalid check timestamp; due-soon window may be incomplete.");
|
|
23136
|
+
const notifyAgentAliases = input.agent_id ? assignedToAliasSet(d, input.agent_id) : null;
|
|
23108
23137
|
const alerts = [];
|
|
23109
23138
|
for (const escalation of getEscalatedTasks({ project_id: input.project_id, agent_id: input.agent_id }, d, new Date(checkedAt))) {
|
|
23110
23139
|
for (const reason of escalation.reasons) {
|
|
@@ -23116,7 +23145,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
23116
23145
|
for (const task of listTasks({ project_id: input.project_id, include_archived: false, limit: 5000 }, d)) {
|
|
23117
23146
|
if (!unfinished(task) || !task.due_at)
|
|
23118
23147
|
continue;
|
|
23119
|
-
if (input.agent_id && task.
|
|
23148
|
+
if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
|
|
23120
23149
|
continue;
|
|
23121
23150
|
if (task.due_at > checkedAt && task.due_at <= dueSoonUntil) {
|
|
23122
23151
|
alerts.push(taskAlert(task, "task_due_soon", "task.due_soon", "warning", `Due soon: ${task.title}`, task.due_at, quieted));
|
|
@@ -23124,7 +23153,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
23124
23153
|
}
|
|
23125
23154
|
}
|
|
23126
23155
|
for (const task of getStaleTasks(staleMinutes, { project_id: input.project_id }, d)) {
|
|
23127
|
-
if (input.agent_id && task.
|
|
23156
|
+
if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
|
|
23128
23157
|
continue;
|
|
23129
23158
|
alerts.push(taskAlert(task, "task_stale", "task.stale", "warning", `Stale task: ${task.title}`, checkedAt, quieted, { stale_minutes: staleMinutes }));
|
|
23130
23159
|
}
|
|
@@ -25144,10 +25173,12 @@ function matchesProfile(profile, query) {
|
|
|
25144
25173
|
return false;
|
|
25145
25174
|
return true;
|
|
25146
25175
|
}
|
|
25147
|
-
function taskMatchesAgent(task, agentId) {
|
|
25176
|
+
function taskMatchesAgent(task, agentId, aliasSet) {
|
|
25148
25177
|
if (!agentId)
|
|
25149
25178
|
return true;
|
|
25150
|
-
|
|
25179
|
+
if (task.agent_id === agentId)
|
|
25180
|
+
return true;
|
|
25181
|
+
return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
|
|
25151
25182
|
}
|
|
25152
25183
|
function estimateRemaining(task) {
|
|
25153
25184
|
if (task.status === "completed" || task.status === "cancelled")
|
|
@@ -25217,7 +25248,8 @@ function removeCapacityProfile(idOrAgent, projectId) {
|
|
|
25217
25248
|
}
|
|
25218
25249
|
function getPlanningForecast(input = {}, db) {
|
|
25219
25250
|
const startDate = input.start_date ? dateOnly(input.start_date) : todayIso();
|
|
25220
|
-
const
|
|
25251
|
+
const aliasSet = input.agent_id ? assignedToAliasSet(db || getDatabase(), input.agent_id) : undefined;
|
|
25252
|
+
const tasks = listTasks({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id, aliasSet));
|
|
25221
25253
|
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((entry) => [entry.task_id, entry]));
|
|
25222
25254
|
const profiles = listCapacityProfiles({ agent_id: input.agent_id, project_id: input.project_id });
|
|
25223
25255
|
const globalProfiles = input.project_id ? listCapacityProfiles({ agent_id: input.agent_id }).filter((profile) => profile.project_id === null) : [];
|
|
@@ -25315,6 +25347,7 @@ function renderPlanningForecastMarkdown(forecast) {
|
|
|
25315
25347
|
}
|
|
25316
25348
|
var LOCAL_CAPACITY_SCHEMA_VERSION = 1;
|
|
25317
25349
|
var init_capacity_forecasts = __esm(() => {
|
|
25350
|
+
init_database();
|
|
25318
25351
|
init_tasks();
|
|
25319
25352
|
init_task_relations();
|
|
25320
25353
|
init_config2();
|
|
@@ -25976,8 +26009,7 @@ function searchTasks(options, projectId, taskListId, db) {
|
|
|
25976
26009
|
}
|
|
25977
26010
|
}
|
|
25978
26011
|
if (opts.assigned_to) {
|
|
25979
|
-
sql +=
|
|
25980
|
-
params.push(opts.assigned_to);
|
|
26012
|
+
sql += ` AND ${lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.assigned_to), params)}`;
|
|
25981
26013
|
}
|
|
25982
26014
|
if (opts.agent_id) {
|
|
25983
26015
|
sql += " AND t.agent_id = ?";
|
|
@@ -26029,7 +26061,8 @@ __export(exports_saved_search_views, {
|
|
|
26029
26061
|
normalizeScope: () => normalizeScope,
|
|
26030
26062
|
listSearchViews: () => listSearchViews,
|
|
26031
26063
|
getSearchView: () => getSearchView,
|
|
26032
|
-
deleteSearchView: () => deleteSearchView
|
|
26064
|
+
deleteSearchView: () => deleteSearchView,
|
|
26065
|
+
SAVED_SEARCH_SCOPES: () => SAVED_SEARCH_SCOPES
|
|
26033
26066
|
});
|
|
26034
26067
|
function parseFilters(value) {
|
|
26035
26068
|
if (!value)
|
|
@@ -26049,10 +26082,8 @@ function rowToSavedSearchView(row) {
|
|
|
26049
26082
|
};
|
|
26050
26083
|
}
|
|
26051
26084
|
function normalizeScope(scope) {
|
|
26052
|
-
|
|
26053
|
-
|
|
26054
|
-
}
|
|
26055
|
-
return "tasks";
|
|
26085
|
+
const candidate = (scope ?? "").trim().toLowerCase();
|
|
26086
|
+
return SAVED_SEARCH_SCOPES.includes(candidate) ? candidate : "tasks";
|
|
26056
26087
|
}
|
|
26057
26088
|
function normalizeName4(name) {
|
|
26058
26089
|
const normalized = name.trim();
|
|
@@ -26355,10 +26386,12 @@ function runSearchView(idOrName, db) {
|
|
|
26355
26386
|
throw new Error(`Saved search view not found: ${idOrName}`);
|
|
26356
26387
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
26357
26388
|
}
|
|
26389
|
+
var SAVED_SEARCH_SCOPES;
|
|
26358
26390
|
var init_saved_search_views = __esm(() => {
|
|
26359
26391
|
init_database();
|
|
26360
26392
|
init_local_fields();
|
|
26361
26393
|
init_search();
|
|
26394
|
+
SAVED_SEARCH_SCOPES = ["all", "tasks", "projects", "plans", "runs", "comments"];
|
|
26362
26395
|
});
|
|
26363
26396
|
|
|
26364
26397
|
// src/mcp/tools/task-project-tools.ts
|
|
@@ -28908,8 +28941,10 @@ function getLatestHandoff(agentId, projectId, db) {
|
|
|
28908
28941
|
function createSessionRecoveryHandoff(input, db) {
|
|
28909
28942
|
const d = db || getDatabase();
|
|
28910
28943
|
const limit = input.limit || 20;
|
|
28911
|
-
const
|
|
28912
|
-
const
|
|
28944
|
+
const params = [];
|
|
28945
|
+
const assignedInClause = lowerInClause("assigned_to", resolveAssignedToAliases(d, input.agent_id), params);
|
|
28946
|
+
const conditions = ["status = 'in_progress'", `(${assignedInClause} OR agent_id = ? OR locked_by = ?)`];
|
|
28947
|
+
params.push(input.agent_id, input.agent_id);
|
|
28913
28948
|
if (input.session_id) {
|
|
28914
28949
|
conditions.push("session_id = ?");
|
|
28915
28950
|
params.push(input.session_id);
|
|
@@ -30133,7 +30168,8 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
30133
30168
|
const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
|
|
30134
30169
|
const now4 = Date.now();
|
|
30135
30170
|
const dueSoonCutoff = now4 + 24 * 60 * 60 * 1000;
|
|
30136
|
-
const
|
|
30171
|
+
const blockedAliases = assignedToAliasSet(getDatabase(), effectiveAgentId);
|
|
30172
|
+
const blocked = getBlockedTasks2().filter((t) => blockedAliases.has((t.assigned_to ?? "").toLowerCase()));
|
|
30137
30173
|
const workload = {
|
|
30138
30174
|
in_progress: assigned.filter((t) => t.status === "in_progress").length,
|
|
30139
30175
|
pending: assigned.filter((t) => t.status === "pending").length,
|
|
@@ -30170,17 +30206,38 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
30170
30206
|
if (agents.length === 0)
|
|
30171
30207
|
return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
|
|
30172
30208
|
const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
|
|
30173
|
-
const
|
|
30209
|
+
const agentKeyByAlias = new Map;
|
|
30210
|
+
const ambiguousAgentAliases = new Set;
|
|
30211
|
+
const indexAgentAlias = (alias, agentId) => {
|
|
30212
|
+
const normalizedAlias2 = alias.toLowerCase();
|
|
30213
|
+
if (ambiguousAgentAliases.has(normalizedAlias2))
|
|
30214
|
+
return;
|
|
30215
|
+
const existingAgentId = agentKeyByAlias.get(normalizedAlias2);
|
|
30216
|
+
if (existingAgentId !== undefined && existingAgentId !== agentId) {
|
|
30217
|
+
agentKeyByAlias.delete(normalizedAlias2);
|
|
30218
|
+
ambiguousAgentAliases.add(normalizedAlias2);
|
|
30219
|
+
return;
|
|
30220
|
+
}
|
|
30221
|
+
agentKeyByAlias.set(normalizedAlias2, agentId);
|
|
30222
|
+
};
|
|
30223
|
+
for (const agent of agents) {
|
|
30224
|
+
indexAgentAlias(String(agent.id), agent.id);
|
|
30225
|
+
if (agent.name)
|
|
30226
|
+
indexAgentAlias(String(agent.name), agent.id);
|
|
30227
|
+
}
|
|
30228
|
+
const canonicalAgentKey = (assignedTo) => assignedTo ? agentKeyByAlias.get(assignedTo.toLowerCase()) : undefined;
|
|
30229
|
+
const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => canonicalAgentKey(t.assigned_to) === agent.id).length]));
|
|
30174
30230
|
let moved = 0;
|
|
30175
30231
|
let skipped = 0;
|
|
30176
|
-
for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
|
|
30177
|
-
const
|
|
30232
|
+
for (const task of activeTasks.filter((t) => t.status === "pending" && canonicalAgentKey(t.assigned_to) && (load.get(canonicalAgentKey(t.assigned_to)) ?? 0) > limit)) {
|
|
30233
|
+
const currentAgentKey = canonicalAgentKey(task.assigned_to);
|
|
30234
|
+
const target = agents.filter((agent) => agent.id !== currentAgentKey).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
|
|
30178
30235
|
if (!target || (load.get(target.id) ?? 0) >= limit) {
|
|
30179
30236
|
skipped++;
|
|
30180
30237
|
continue;
|
|
30181
30238
|
}
|
|
30182
30239
|
updateTask2(task.id, { assigned_to: target.id, version: task.version });
|
|
30183
|
-
load.set(
|
|
30240
|
+
load.set(currentAgentKey, (load.get(currentAgentKey) ?? 1) - 1);
|
|
30184
30241
|
load.set(target.id, (load.get(target.id) ?? 0) + 1);
|
|
30185
30242
|
moved++;
|
|
30186
30243
|
}
|
|
@@ -30359,6 +30416,7 @@ ${lines.join(`
|
|
|
30359
30416
|
var init_task_auto_tools = __esm(() => {
|
|
30360
30417
|
init_zod();
|
|
30361
30418
|
init_cloud_router();
|
|
30419
|
+
init_database();
|
|
30362
30420
|
});
|
|
30363
30421
|
|
|
30364
30422
|
// src/lib/context-packs.ts
|
|
@@ -31538,7 +31596,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
|
|
|
31538
31596
|
}, undefined);
|
|
31539
31597
|
const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
|
|
31540
31598
|
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
31541
|
-
const
|
|
31599
|
+
const blockedAliases = effectiveAgentId ? assignedToAliasSet(getDatabase(), effectiveAgentId) : null;
|
|
31600
|
+
const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => blockedAliases ? blockedAliases.has((t.assigned_to ?? "").toLowerCase()) : false);
|
|
31542
31601
|
const lines = [
|
|
31543
31602
|
`Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
|
|
31544
31603
|
inProgress.length > 0 ? `
|
|
@@ -31766,6 +31825,7 @@ var init_task_adv_tools = __esm(() => {
|
|
|
31766
31825
|
init_zod();
|
|
31767
31826
|
init_token_utils();
|
|
31768
31827
|
init_cloud_router();
|
|
31828
|
+
init_database();
|
|
31769
31829
|
});
|
|
31770
31830
|
|
|
31771
31831
|
// src/mcp/tools/task-meta-tools.ts
|
|
@@ -33683,27 +33743,29 @@ function getAgentMetrics(agentId, opts, db) {
|
|
|
33683
33743
|
const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
33684
33744
|
if (!agent)
|
|
33685
33745
|
return null;
|
|
33746
|
+
const assignedAliases = resolveAssignedToAliases(d, agent.id).map((a) => a.toLowerCase());
|
|
33747
|
+
const assignedInClause = `LOWER(assigned_to) IN (${assignedAliases.map(() => "?").join(",")})`;
|
|
33686
33748
|
let projectFilter = "";
|
|
33687
|
-
const params = [agent.id,
|
|
33749
|
+
const params = [agent.id, ...assignedAliases];
|
|
33688
33750
|
if (opts?.project_id) {
|
|
33689
33751
|
projectFilter = " AND project_id = ?";
|
|
33690
33752
|
params.push(opts.project_id);
|
|
33691
33753
|
}
|
|
33692
|
-
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
33693
|
-
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
33694
|
-
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
33754
|
+
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}`).get(...params).count;
|
|
33755
|
+
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'failed'${projectFilter}`).get(...params).count;
|
|
33756
|
+
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'in_progress'${projectFilter}`).get(...params).count;
|
|
33695
33757
|
const total = completed + failed;
|
|
33696
33758
|
const completionRate = total > 0 ? completed / total : 0;
|
|
33697
33759
|
const avgTime = d.query(`SELECT AVG(
|
|
33698
33760
|
(julianday(completed_at) - julianday(created_at)) * 24 * 60
|
|
33699
33761
|
) as avg_minutes
|
|
33700
33762
|
FROM tasks
|
|
33701
|
-
WHERE (agent_id = ? OR
|
|
33763
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
|
|
33702
33764
|
const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
|
|
33703
33765
|
FROM tasks
|
|
33704
|
-
WHERE (agent_id = ? OR
|
|
33766
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
|
|
33705
33767
|
const reviewTasks = d.query(`SELECT metadata FROM tasks
|
|
33706
|
-
WHERE (agent_id = ? OR
|
|
33768
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}
|
|
33707
33769
|
AND metadata LIKE '%_review_score%'`).all(...params);
|
|
33708
33770
|
let reviewScoreAvg = null;
|
|
33709
33771
|
if (reviewTasks.length > 0) {
|
|
@@ -34167,14 +34229,15 @@ function millisBetween(start, end) {
|
|
|
34167
34229
|
return 0;
|
|
34168
34230
|
return endMs - startMs;
|
|
34169
34231
|
}
|
|
34170
|
-
function addTaskScope(where, params, options, alias = "t") {
|
|
34232
|
+
function addTaskScope(where, params, options, db, alias = "t") {
|
|
34171
34233
|
if (options.project_id) {
|
|
34172
34234
|
where.push(`${alias}.project_id = ?`);
|
|
34173
34235
|
params.push(options.project_id);
|
|
34174
34236
|
}
|
|
34175
34237
|
if (options.agent_id) {
|
|
34176
|
-
|
|
34177
|
-
|
|
34238
|
+
params.push(options.agent_id);
|
|
34239
|
+
const assignedInClause = lowerInClause(`${alias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
34240
|
+
where.push(`(${alias}.agent_id = ? OR ${assignedInClause})`);
|
|
34178
34241
|
}
|
|
34179
34242
|
if (options.since) {
|
|
34180
34243
|
where.push(`${alias}.created_at >= ?`);
|
|
@@ -34185,14 +34248,15 @@ function addTaskScope(where, params, options, alias = "t") {
|
|
|
34185
34248
|
params.push(options.until);
|
|
34186
34249
|
}
|
|
34187
34250
|
}
|
|
34188
|
-
function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
|
|
34251
|
+
function addRunScope(where, params, options, db, runAlias = "r", taskAlias = "t") {
|
|
34189
34252
|
if (options.project_id) {
|
|
34190
34253
|
where.push(`${taskAlias}.project_id = ?`);
|
|
34191
34254
|
params.push(options.project_id);
|
|
34192
34255
|
}
|
|
34193
34256
|
if (options.agent_id) {
|
|
34194
|
-
|
|
34195
|
-
|
|
34257
|
+
params.push(options.agent_id, options.agent_id);
|
|
34258
|
+
const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
34259
|
+
where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
|
|
34196
34260
|
}
|
|
34197
34261
|
if (options.since) {
|
|
34198
34262
|
where.push(`${runAlias}.started_at >= ?`);
|
|
@@ -34203,14 +34267,15 @@ function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
|
|
|
34203
34267
|
params.push(options.until);
|
|
34204
34268
|
}
|
|
34205
34269
|
}
|
|
34206
|
-
function addTraceScope(where, params, options, traceAlias = "tr", taskAlias = "t") {
|
|
34270
|
+
function addTraceScope(where, params, options, db, traceAlias = "tr", taskAlias = "t") {
|
|
34207
34271
|
if (options.project_id) {
|
|
34208
34272
|
where.push(`${taskAlias}.project_id = ?`);
|
|
34209
34273
|
params.push(options.project_id);
|
|
34210
34274
|
}
|
|
34211
34275
|
if (options.agent_id) {
|
|
34212
|
-
|
|
34213
|
-
|
|
34276
|
+
params.push(options.agent_id, options.agent_id);
|
|
34277
|
+
const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
34278
|
+
where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
|
|
34214
34279
|
}
|
|
34215
34280
|
if (options.since) {
|
|
34216
34281
|
where.push(`${traceAlias}.created_at >= ?`);
|
|
@@ -34270,7 +34335,7 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
34270
34335
|
const generatedAt = options.generated_at || new Date().toISOString();
|
|
34271
34336
|
const taskWhere = [];
|
|
34272
34337
|
const taskParams = [];
|
|
34273
|
-
addTaskScope(taskWhere, taskParams, options);
|
|
34338
|
+
addTaskScope(taskWhere, taskParams, options, d);
|
|
34274
34339
|
const taskClause = taskWhere.length ? `WHERE ${taskWhere.join(" AND ")}` : "";
|
|
34275
34340
|
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);
|
|
34276
34341
|
let projectCount = 0;
|
|
@@ -34279,14 +34344,14 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
34279
34344
|
} else if (options.agent_id) {
|
|
34280
34345
|
const projectWhere = ["t.project_id IS NOT NULL"];
|
|
34281
34346
|
const projectParams = [];
|
|
34282
|
-
addTaskScope(projectWhere, projectParams, options);
|
|
34347
|
+
addTaskScope(projectWhere, projectParams, options, d);
|
|
34283
34348
|
projectCount = queryOne(d, `SELECT COUNT(DISTINCT t.project_id) as count FROM tasks t WHERE ${projectWhere.join(" AND ")}`, projectParams).count;
|
|
34284
34349
|
} else {
|
|
34285
34350
|
projectCount = queryOne(d, "SELECT COUNT(*) as count FROM projects", []).count;
|
|
34286
34351
|
}
|
|
34287
34352
|
const runWhere = [];
|
|
34288
34353
|
const runParams = [];
|
|
34289
|
-
addRunScope(runWhere, runParams, options);
|
|
34354
|
+
addRunScope(runWhere, runParams, options, d);
|
|
34290
34355
|
const runClause = runWhere.length ? `WHERE ${runWhere.join(" AND ")}` : "";
|
|
34291
34356
|
const runs = queryAll(d, `SELECT r.id, r.started_at, r.completed_at, r.metadata
|
|
34292
34357
|
FROM task_runs r JOIN tasks t ON t.id = r.task_id
|
|
@@ -34303,7 +34368,7 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
34303
34368
|
${runClause}`, runParams);
|
|
34304
34369
|
const traceWhere = [];
|
|
34305
34370
|
const traceParams = [];
|
|
34306
|
-
addTraceScope(traceWhere, traceParams, options);
|
|
34371
|
+
addTraceScope(traceWhere, traceParams, options, d);
|
|
34307
34372
|
const traceClause = traceWhere.length ? `WHERE ${traceWhere.join(" AND ")}` : "";
|
|
34308
34373
|
const traceTotals = queryOne(d, `SELECT COUNT(*) as traces,
|
|
34309
34374
|
COALESCE(SUM(tr.tokens), 0) as tokens,
|
|
@@ -34447,10 +34512,12 @@ function limitValue(value) {
|
|
|
34447
34512
|
function isTerminal(task) {
|
|
34448
34513
|
return task.status === "completed" || task.status === "failed" || task.status === "cancelled";
|
|
34449
34514
|
}
|
|
34450
|
-
function sameAgent(task, agentId) {
|
|
34515
|
+
function sameAgent(task, agentId, aliasSet) {
|
|
34451
34516
|
if (!agentId)
|
|
34452
34517
|
return true;
|
|
34453
|
-
|
|
34518
|
+
if (task.agent_id === agentId)
|
|
34519
|
+
return true;
|
|
34520
|
+
return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
|
|
34454
34521
|
}
|
|
34455
34522
|
function withinTaskWindow(task, options) {
|
|
34456
34523
|
const time = Date.parse(task.updated_at);
|
|
@@ -34463,11 +34530,12 @@ function withinTaskWindow(task, options) {
|
|
|
34463
34530
|
return true;
|
|
34464
34531
|
}
|
|
34465
34532
|
function scopedTasks(options, db) {
|
|
34533
|
+
const aliasSet = options.agent_id ? assignedToAliasSet(db, options.agent_id) : undefined;
|
|
34466
34534
|
return listTasks({
|
|
34467
34535
|
project_id: options.project_id,
|
|
34468
34536
|
plan_id: options.plan_id,
|
|
34469
34537
|
include_archived: false
|
|
34470
|
-
}, db).filter((task) => sameAgent(task, options.agent_id) && withinTaskWindow(task, options));
|
|
34538
|
+
}, db).filter((task) => sameAgent(task, options.agent_id, aliasSet) && withinTaskWindow(task, options));
|
|
34471
34539
|
}
|
|
34472
34540
|
function summarizeTask(task) {
|
|
34473
34541
|
return {
|
|
@@ -34533,7 +34601,7 @@ function initialAgentSummary(agentId) {
|
|
|
34533
34601
|
}
|
|
34534
34602
|
};
|
|
34535
34603
|
}
|
|
34536
|
-
function addScopeClauses(where, params, options, timeColumn) {
|
|
34604
|
+
function addScopeClauses(where, params, options, timeColumn, db) {
|
|
34537
34605
|
if (options.project_id) {
|
|
34538
34606
|
where.push("t.project_id = ?");
|
|
34539
34607
|
params.push(options.project_id);
|
|
@@ -34543,8 +34611,9 @@ function addScopeClauses(where, params, options, timeColumn) {
|
|
|
34543
34611
|
params.push(options.plan_id);
|
|
34544
34612
|
}
|
|
34545
34613
|
if (options.agent_id) {
|
|
34546
|
-
|
|
34547
|
-
|
|
34614
|
+
params.push(options.agent_id, options.agent_id);
|
|
34615
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
|
|
34616
|
+
where.push(`(r.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
|
|
34548
34617
|
}
|
|
34549
34618
|
if (options.since) {
|
|
34550
34619
|
where.push(`${timeColumn} >= ?`);
|
|
@@ -34558,7 +34627,7 @@ function addScopeClauses(where, params, options, timeColumn) {
|
|
|
34558
34627
|
function loadRuns(options, db) {
|
|
34559
34628
|
const where = ["t.archived_at IS NULL"];
|
|
34560
34629
|
const params = [];
|
|
34561
|
-
addScopeClauses(where, params, options, "r.started_at");
|
|
34630
|
+
addScopeClauses(where, params, options, "r.started_at", db);
|
|
34562
34631
|
return db.query(`
|
|
34563
34632
|
SELECT
|
|
34564
34633
|
r.id,
|
|
@@ -34598,8 +34667,9 @@ function loadVerifications(options, db) {
|
|
|
34598
34667
|
params.push(options.plan_id);
|
|
34599
34668
|
}
|
|
34600
34669
|
if (options.agent_id) {
|
|
34601
|
-
|
|
34602
|
-
|
|
34670
|
+
params.push(options.agent_id, options.agent_id);
|
|
34671
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
|
|
34672
|
+
where.push(`(v.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
|
|
34603
34673
|
}
|
|
34604
34674
|
if (options.since) {
|
|
34605
34675
|
where.push("v.run_at >= ?");
|
|
@@ -34821,7 +34891,6 @@ function renderLocalReportMarkdown(report) {
|
|
|
34821
34891
|
}
|
|
34822
34892
|
var LOCAL_REPORT_SCHEMA_VERSION = 1, LOCAL_REPORT_TYPES;
|
|
34823
34893
|
var init_local_reports = __esm(() => {
|
|
34824
|
-
init_database();
|
|
34825
34894
|
init_database();
|
|
34826
34895
|
init_plans();
|
|
34827
34896
|
init_tasks();
|
|
@@ -34843,7 +34912,7 @@ var package_default;
|
|
|
34843
34912
|
var init_package = __esm(() => {
|
|
34844
34913
|
package_default = {
|
|
34845
34914
|
name: "@hasna/todos",
|
|
34846
|
-
version: "0.
|
|
34915
|
+
version: "0.14.0",
|
|
34847
34916
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
34848
34917
|
type: "module",
|
|
34849
34918
|
main: "dist/index.js",
|
|
@@ -39060,7 +39129,7 @@ ${lines.join(`
|
|
|
39060
39129
|
a.name AS agent_name
|
|
39061
39130
|
FROM task_files tf
|
|
39062
39131
|
JOIN tasks t ON tf.task_id = t.id
|
|
39063
|
-
LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
|
|
39132
|
+
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))))
|
|
39064
39133
|
WHERE t.status = 'in_progress'
|
|
39065
39134
|
AND tf.status != 'removed'
|
|
39066
39135
|
AND t.project_id = ?
|
|
@@ -42822,9 +42891,16 @@ ${text2}` }] };
|
|
|
42822
42891
|
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
42823
42892
|
}
|
|
42824
42893
|
const oldName = agent.name;
|
|
42825
|
-
const updated = updateAgent(agent.id, { name: new_name });
|
|
42826
42894
|
const db = getDatabase();
|
|
42827
|
-
|
|
42895
|
+
let oldNameUniquelyIdentifiesAgent = false;
|
|
42896
|
+
try {
|
|
42897
|
+
oldNameUniquelyIdentifiesAgent = resolvePartialId(db, "agents", oldName) === agent.id;
|
|
42898
|
+
} catch (error) {
|
|
42899
|
+
if (!(error instanceof IdentityAliasAmbiguousError))
|
|
42900
|
+
throw error;
|
|
42901
|
+
}
|
|
42902
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
42903
|
+
const tasksResult = db.run(oldNameUniquelyIdentifiesAgent ? "UPDATE tasks SET assigned_to = ? WHERE LOWER(assigned_to) = LOWER(?)" : "UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
|
|
42828
42904
|
const taskNote = tasksResult.changes > 0 ? `
|
|
42829
42905
|
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
42830
42906
|
return {
|
|
@@ -43008,6 +43084,7 @@ var init_agents2 = __esm(() => {
|
|
|
43008
43084
|
init_agents();
|
|
43009
43085
|
init_config2();
|
|
43010
43086
|
init_database();
|
|
43087
|
+
init_types();
|
|
43011
43088
|
init_cloud_router();
|
|
43012
43089
|
});
|
|
43013
43090
|
|
|
@@ -46416,9 +46493,11 @@ async function updateTask2(id, input, store) {
|
|
|
46416
46493
|
throw new Error(`Task ${id} version conflict: expected ${existing.version}, got ${input.version}`);
|
|
46417
46494
|
}
|
|
46418
46495
|
const reopened = existing.status === "completed" && input.status !== undefined && input.status !== "completed" && input.completed_at === undefined;
|
|
46496
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
46419
46497
|
const task2 = {
|
|
46420
46498
|
...existing,
|
|
46421
46499
|
...definedPatch(input),
|
|
46500
|
+
...terminalNow ? { locked_by: null, locked_at: null } : {},
|
|
46422
46501
|
version: existing.version + 1,
|
|
46423
46502
|
updated_at: new Date().toISOString(),
|
|
46424
46503
|
tags: input.tags ?? existing.tags,
|
|
@@ -47991,6 +48070,96 @@ var init_auth_posture = __esm(() => {
|
|
|
47991
48070
|
LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "[::1]", "ip6-localhost"]);
|
|
47992
48071
|
});
|
|
47993
48072
|
|
|
48073
|
+
// src/lib/enum-vocabulary.ts
|
|
48074
|
+
function editDistance(a, b) {
|
|
48075
|
+
if (a === b)
|
|
48076
|
+
return 0;
|
|
48077
|
+
if (!a.length)
|
|
48078
|
+
return b.length;
|
|
48079
|
+
if (!b.length)
|
|
48080
|
+
return a.length;
|
|
48081
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
48082
|
+
for (let i = 1;i <= a.length; i += 1) {
|
|
48083
|
+
const current = [i];
|
|
48084
|
+
for (let j = 1;j <= b.length; j += 1) {
|
|
48085
|
+
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
48086
|
+
}
|
|
48087
|
+
previous = current;
|
|
48088
|
+
}
|
|
48089
|
+
return previous[b.length];
|
|
48090
|
+
}
|
|
48091
|
+
function suggestVocabularyMatches(value, vocabulary, limit = 3) {
|
|
48092
|
+
const needle = value.trim().toLowerCase();
|
|
48093
|
+
if (!needle)
|
|
48094
|
+
return [];
|
|
48095
|
+
const scored = [];
|
|
48096
|
+
for (const member of vocabulary) {
|
|
48097
|
+
const candidate = member.toLowerCase();
|
|
48098
|
+
if (candidate.startsWith(needle) || needle.startsWith(candidate)) {
|
|
48099
|
+
scored.push({ member, score: 0 });
|
|
48100
|
+
continue;
|
|
48101
|
+
}
|
|
48102
|
+
if (candidate.includes(needle) || needle.includes(candidate)) {
|
|
48103
|
+
scored.push({ member, score: 1 });
|
|
48104
|
+
continue;
|
|
48105
|
+
}
|
|
48106
|
+
const distance = editDistance(needle, candidate);
|
|
48107
|
+
if (distance <= Math.max(1, Math.floor(candidate.length / 3))) {
|
|
48108
|
+
scored.push({ member, score: 1 + distance });
|
|
48109
|
+
}
|
|
48110
|
+
}
|
|
48111
|
+
return scored.sort((a, b) => a.score - b.score || a.member.localeCompare(b.member)).slice(0, limit).map((entry2) => entry2.member);
|
|
48112
|
+
}
|
|
48113
|
+
function resolveEnumVocabulary(raw, spec) {
|
|
48114
|
+
const allowList = spec.allowList !== false;
|
|
48115
|
+
const rawElements = (allowList ? raw.split(",") : [raw]).map((element) => element.trim()).filter((element) => element.length > 0);
|
|
48116
|
+
if (rawElements.length === 0) {
|
|
48117
|
+
return {
|
|
48118
|
+
ok: false,
|
|
48119
|
+
message: `${spec.name} requires a value. Allowed values: ${spec.vocabulary.join(", ")}.`,
|
|
48120
|
+
invalid: []
|
|
48121
|
+
};
|
|
48122
|
+
}
|
|
48123
|
+
const normalized = rawElements.map((element) => spec.normalize ? spec.normalize(element) : element);
|
|
48124
|
+
const allowed = new Set(spec.vocabulary);
|
|
48125
|
+
const invalid2 = [];
|
|
48126
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
48127
|
+
if (!allowed.has(normalized[i]))
|
|
48128
|
+
invalid2.push(rawElements[i]);
|
|
48129
|
+
}
|
|
48130
|
+
if (invalid2.length === 0) {
|
|
48131
|
+
return { ok: true, values: [...new Set(normalized)] };
|
|
48132
|
+
}
|
|
48133
|
+
const label = invalid2.length === 1 ? "value" : "values";
|
|
48134
|
+
const parts = [
|
|
48135
|
+
`Invalid ${spec.name} ${label}: ${invalid2.join(", ")}.`,
|
|
48136
|
+
`Allowed values: ${spec.vocabulary.join(", ")}.`
|
|
48137
|
+
];
|
|
48138
|
+
const hints = new Set;
|
|
48139
|
+
const suggestions = new Set;
|
|
48140
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
48141
|
+
const canonical = normalized[i];
|
|
48142
|
+
if (allowed.has(canonical))
|
|
48143
|
+
continue;
|
|
48144
|
+
const hint = spec.hints?.[canonical.toLowerCase()];
|
|
48145
|
+
if (hint) {
|
|
48146
|
+
hints.add(hint);
|
|
48147
|
+
continue;
|
|
48148
|
+
}
|
|
48149
|
+
for (const match of suggestVocabularyMatches(canonical, spec.vocabulary)) {
|
|
48150
|
+
suggestions.add(match);
|
|
48151
|
+
}
|
|
48152
|
+
}
|
|
48153
|
+
if (suggestions.size > 0)
|
|
48154
|
+
parts.push(`Did you mean ${[...suggestions].join(", ")}?`);
|
|
48155
|
+
for (const hint of hints)
|
|
48156
|
+
parts.push(hint);
|
|
48157
|
+
return { ok: false, message: parts.join(" "), invalid: invalid2 };
|
|
48158
|
+
}
|
|
48159
|
+
function collapseEnumValues(values) {
|
|
48160
|
+
return values.length === 1 ? values[0] : values;
|
|
48161
|
+
}
|
|
48162
|
+
|
|
47994
48163
|
// src/db/orgs.ts
|
|
47995
48164
|
function rowToOrg(row) {
|
|
47996
48165
|
return { ...row, metadata: JSON.parse(row.metadata || "{}") };
|
|
@@ -48291,8 +48460,19 @@ function handleStats(_ctx, json2) {
|
|
|
48291
48460
|
recurring_tasks: countRecurringTasks()
|
|
48292
48461
|
});
|
|
48293
48462
|
}
|
|
48463
|
+
function taskStatusQueryParam(url) {
|
|
48464
|
+
const raw = url.searchParams.get("status");
|
|
48465
|
+
if (!raw)
|
|
48466
|
+
return { ok: true, value: undefined };
|
|
48467
|
+
const result = resolveEnumVocabulary(raw, { name: "status", vocabulary: TASK_STATUSES });
|
|
48468
|
+
if (!result.ok)
|
|
48469
|
+
return { ok: false, message: result.message };
|
|
48470
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
48471
|
+
}
|
|
48294
48472
|
async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
48295
|
-
const
|
|
48473
|
+
const statusParam = taskStatusQueryParam(url);
|
|
48474
|
+
if (!statusParam.ok)
|
|
48475
|
+
return json2({ error: statusParam.message }, 400);
|
|
48296
48476
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
48297
48477
|
const sessionId = url.searchParams.get("session_id") || undefined;
|
|
48298
48478
|
const agentId = url.searchParams.get("agent_id") || undefined;
|
|
@@ -48300,7 +48480,7 @@ async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
48300
48480
|
const offsetParam = url.searchParams.get("offset");
|
|
48301
48481
|
const fields = parseFieldsParam(url);
|
|
48302
48482
|
const tasks = listTasks({
|
|
48303
|
-
status,
|
|
48483
|
+
status: statusParam.value,
|
|
48304
48484
|
project_id: projectId,
|
|
48305
48485
|
session_id: sessionId,
|
|
48306
48486
|
agent_id: agentId,
|
|
@@ -48365,9 +48545,19 @@ async function handleUpsertTask(req, ctx, json2, taskToSummary2) {
|
|
|
48365
48545
|
}
|
|
48366
48546
|
function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
|
|
48367
48547
|
const format = url.searchParams.get("format") || "json";
|
|
48368
|
-
const
|
|
48548
|
+
const statusParam = taskStatusQueryParam(url);
|
|
48549
|
+
if (!statusParam.ok) {
|
|
48550
|
+
return new Response(JSON.stringify({ error: statusParam.message }), {
|
|
48551
|
+
status: 400,
|
|
48552
|
+
headers: { "Content-Type": "application/json" }
|
|
48553
|
+
});
|
|
48554
|
+
}
|
|
48369
48555
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
48370
|
-
const tasks = listTasks({
|
|
48556
|
+
const tasks = listTasks({
|
|
48557
|
+
status: statusParam.value,
|
|
48558
|
+
project_id: projectId,
|
|
48559
|
+
limit: 1e4
|
|
48560
|
+
});
|
|
48371
48561
|
const summaries = tasks.map((t) => taskToSummary2(t));
|
|
48372
48562
|
if (format === "csv") {
|
|
48373
48563
|
const headers = ["id", "short_id", "title", "status", "priority", "project_id", "assigned_to", "agent_id", "created_at", "updated_at", "completed_at", "due_at"];
|
|
@@ -48682,8 +48872,9 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
48682
48872
|
}
|
|
48683
48873
|
}
|
|
48684
48874
|
function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
|
|
48875
|
+
const aliasSet = assignedToAliasSet(getDatabase(), agentId);
|
|
48685
48876
|
const pending = listTasks({ status: "pending" });
|
|
48686
|
-
const queue = pending.filter((t) => t.assigned_to
|
|
48877
|
+
const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
|
|
48687
48878
|
const order = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
48688
48879
|
queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
48689
48880
|
return json2(queue.map((t) => taskToSummary2(t)));
|
|
@@ -48948,6 +49139,7 @@ var init_routes = __esm(() => {
|
|
|
48948
49139
|
init_tasks();
|
|
48949
49140
|
init_database();
|
|
48950
49141
|
init_types();
|
|
49142
|
+
init_types();
|
|
48951
49143
|
init_projects();
|
|
48952
49144
|
init_agents();
|
|
48953
49145
|
init_plans();
|
|
@@ -49901,8 +50093,38 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
49901
50093
|
operationId: "listTasks",
|
|
49902
50094
|
summary: "List tasks",
|
|
49903
50095
|
parameters: [
|
|
49904
|
-
{
|
|
49905
|
-
|
|
50096
|
+
{
|
|
50097
|
+
name: "status",
|
|
50098
|
+
in: "query",
|
|
50099
|
+
description: `Task status, or a comma-separated list of statuses. Allowed values: ${TASK_STATUSES.join(", ")}.`,
|
|
50100
|
+
style: "form",
|
|
50101
|
+
explode: false,
|
|
50102
|
+
schema: {
|
|
50103
|
+
oneOf: [
|
|
50104
|
+
{ type: "string", enum: [...TASK_STATUSES] },
|
|
50105
|
+
{
|
|
50106
|
+
type: "array",
|
|
50107
|
+
items: { type: "string", enum: [...TASK_STATUSES] }
|
|
50108
|
+
}
|
|
50109
|
+
]
|
|
50110
|
+
}
|
|
50111
|
+
},
|
|
50112
|
+
{
|
|
50113
|
+
name: "priority",
|
|
50114
|
+
in: "query",
|
|
50115
|
+
description: `Task priority, or a comma-separated list of priorities. Allowed values: ${TASK_PRIORITIES.join(", ")}.`,
|
|
50116
|
+
style: "form",
|
|
50117
|
+
explode: false,
|
|
50118
|
+
schema: {
|
|
50119
|
+
oneOf: [
|
|
50120
|
+
{ type: "string", enum: [...TASK_PRIORITIES] },
|
|
50121
|
+
{
|
|
50122
|
+
type: "array",
|
|
50123
|
+
items: { type: "string", enum: [...TASK_PRIORITIES] }
|
|
50124
|
+
}
|
|
50125
|
+
]
|
|
50126
|
+
}
|
|
50127
|
+
},
|
|
49906
50128
|
{ name: "project_id", in: "query", schema: { type: "string" } },
|
|
49907
50129
|
{ name: "parent_id", in: "query", schema: { type: "string", nullable: true } },
|
|
49908
50130
|
{ name: "include_subtasks", in: "query", schema: { type: "boolean" } },
|
|
@@ -50414,6 +50636,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
50414
50636
|
var taskSchema, projectSchema, taskListSchema, taskCommentSchema, planSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
|
|
50415
50637
|
var init_openapi = __esm(() => {
|
|
50416
50638
|
init_package_version();
|
|
50639
|
+
init_types();
|
|
50417
50640
|
taskSchema = {
|
|
50418
50641
|
type: "object",
|
|
50419
50642
|
properties: {
|
|
@@ -50681,6 +50904,15 @@ function json3(body, status = 200) {
|
|
|
50681
50904
|
function error(status, message, extra) {
|
|
50682
50905
|
return json3({ error: message, ...extra ?? {} }, status);
|
|
50683
50906
|
}
|
|
50907
|
+
function enumQueryParam(url, name, vocabulary) {
|
|
50908
|
+
const raw = url.searchParams.get(name);
|
|
50909
|
+
if (raw === null || raw === "")
|
|
50910
|
+
return { ok: true, value: undefined };
|
|
50911
|
+
const result = resolveEnumVocabulary(raw, { name, vocabulary });
|
|
50912
|
+
if (!result.ok)
|
|
50913
|
+
return { ok: false, response: error(400, result.message) };
|
|
50914
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
50915
|
+
}
|
|
50684
50916
|
function validateTaskCompletion(value) {
|
|
50685
50917
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
50686
50918
|
return { ok: false, message: "completion body must be an object" };
|
|
@@ -51101,14 +51333,16 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
51101
51333
|
return error(400, "include_subtasks must be true or false");
|
|
51102
51334
|
}
|
|
51103
51335
|
const hasParentFilter = url.searchParams.has("parent_id");
|
|
51336
|
+
const statusParam = enumQueryParam(url, "status", TASK_STATUSES);
|
|
51337
|
+
if (!statusParam.ok)
|
|
51338
|
+
return statusParam.response;
|
|
51339
|
+
const priorityParam = enumQueryParam(url, "priority", TASK_PRIORITIES);
|
|
51340
|
+
if (!priorityParam.ok)
|
|
51341
|
+
return priorityParam.response;
|
|
51104
51342
|
const filter = {
|
|
51105
51343
|
...url.searchParams.get("q") ? { query: url.searchParams.get("q") } : {},
|
|
51106
|
-
...
|
|
51107
|
-
|
|
51108
|
-
} : {},
|
|
51109
|
-
...url.searchParams.get("priority") ? {
|
|
51110
|
-
priority: url.searchParams.get("priority").includes(",") ? url.searchParams.get("priority").split(",") : url.searchParams.get("priority")
|
|
51111
|
-
} : {},
|
|
51344
|
+
...statusParam.value !== undefined ? { status: statusParam.value } : {},
|
|
51345
|
+
...priorityParam.value !== undefined ? { priority: priorityParam.value } : {},
|
|
51112
51346
|
...url.searchParams.get("project_id") ? { project_id: url.searchParams.get("project_id") } : {},
|
|
51113
51347
|
...hasParentFilter ? { parent_id: url.searchParams.get("parent_id") || null, include_subtasks: true } : includeSubtasks !== null ? { include_subtasks: includeSubtasks === "true" } : {},
|
|
51114
51348
|
...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {},
|