@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/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.
|
|
2126
|
+
version: "0.14.0",
|
|
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",
|
|
@@ -2258,7 +2258,10 @@ var init_package_version = __esm(() => {
|
|
|
2258
2258
|
function isBlockingDependencyStatus(status) {
|
|
2259
2259
|
return status !== "completed" && status !== "cancelled";
|
|
2260
2260
|
}
|
|
2261
|
-
|
|
2261
|
+
function isTerminalStatus(status) {
|
|
2262
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
2263
|
+
}
|
|
2264
|
+
var TASK_STATUSES, TASK_PRIORITIES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
2262
2265
|
var init_types = __esm(() => {
|
|
2263
2266
|
TASK_STATUSES = [
|
|
2264
2267
|
"pending",
|
|
@@ -2427,6 +2430,7 @@ var init_types = __esm(() => {
|
|
|
2427
2430
|
this.name = "CompletionGuardError";
|
|
2428
2431
|
}
|
|
2429
2432
|
};
|
|
2433
|
+
DISPATCH_STATUSES = ["pending", "sent", "failed", "cancelled"];
|
|
2430
2434
|
DispatchNotFoundError = class DispatchNotFoundError extends Error {
|
|
2431
2435
|
dispatchId;
|
|
2432
2436
|
static code = "DISPATCH_NOT_FOUND";
|
|
@@ -6045,8 +6049,10 @@ async function cloudEscalatedTasks(client, opts = {}, at = new Date) {
|
|
|
6045
6049
|
const filter = {};
|
|
6046
6050
|
if (opts.project_id)
|
|
6047
6051
|
filter.project_id = opts.project_id;
|
|
6052
|
+
if (opts.agent_id)
|
|
6053
|
+
filter.assigned_to = opts.agent_id;
|
|
6048
6054
|
const active = await cloudActiveTasks(client, filter);
|
|
6049
|
-
return active.filter((t) => !t.archived_at
|
|
6055
|
+
return active.filter((t) => !t.archived_at).map((task) => {
|
|
6050
6056
|
const reasons = [];
|
|
6051
6057
|
const breachedTimes = [];
|
|
6052
6058
|
if (task.due_at) {
|
|
@@ -6258,7 +6264,11 @@ async function cloudRecap(client, hours, projectId) {
|
|
|
6258
6264
|
const blocked = pending.filter((t) => blockedMap.has(t.id));
|
|
6259
6265
|
const sinceMs = new Date(since).getTime();
|
|
6260
6266
|
const agentSummaries = agents.map((agent) => {
|
|
6261
|
-
const
|
|
6267
|
+
const assignedName = agent.name?.toLowerCase();
|
|
6268
|
+
const owned = all.filter((t) => {
|
|
6269
|
+
const assignedTo = t.assigned_to?.toLowerCase();
|
|
6270
|
+
return assignedTo === agent.id.toLowerCase() || assignedName && assignedTo === assignedName || t.agent_id === agent.id;
|
|
6271
|
+
});
|
|
6262
6272
|
return {
|
|
6263
6273
|
name: agent.name,
|
|
6264
6274
|
completed_count: owned.filter((t) => t.status === "completed" && t.completed_at != null && t.completed_at > since).length,
|
|
@@ -10714,14 +10724,17 @@ var exports_database = {};
|
|
|
10714
10724
|
__export(exports_database, {
|
|
10715
10725
|
uuid: () => uuid,
|
|
10716
10726
|
resolvePartialId: () => resolvePartialId,
|
|
10727
|
+
resolveAssignedToAliases: () => resolveAssignedToAliases,
|
|
10717
10728
|
resetDatabase: () => resetDatabase,
|
|
10718
10729
|
now: () => now,
|
|
10730
|
+
lowerInClause: () => lowerInClause,
|
|
10719
10731
|
lockExpiryCutoff: () => lockExpiryCutoff,
|
|
10720
10732
|
isLockExpired: () => isLockExpired,
|
|
10721
10733
|
getDatabasePath: () => getDatabasePath,
|
|
10722
10734
|
getDatabase: () => getDatabase,
|
|
10723
10735
|
closeDatabase: () => closeDatabase,
|
|
10724
10736
|
clearExpiredLocks: () => clearExpiredLocks,
|
|
10737
|
+
assignedToAliasSet: () => assignedToAliasSet,
|
|
10725
10738
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
10726
10739
|
});
|
|
10727
10740
|
import { Database } from "bun:sqlite";
|
|
@@ -10950,6 +10963,33 @@ function resolvePartialId(db, table, partialId) {
|
|
|
10950
10963
|
}
|
|
10951
10964
|
return null;
|
|
10952
10965
|
}
|
|
10966
|
+
function resolveAssignedToAliases(db, ref) {
|
|
10967
|
+
const aliases = new Set([ref]);
|
|
10968
|
+
let agentId;
|
|
10969
|
+
try {
|
|
10970
|
+
agentId = resolvePartialId(db, "agents", ref);
|
|
10971
|
+
} catch (err) {
|
|
10972
|
+
if (!(err instanceof IdentityAliasAmbiguousError))
|
|
10973
|
+
throw err;
|
|
10974
|
+
agentId = null;
|
|
10975
|
+
}
|
|
10976
|
+
if (agentId) {
|
|
10977
|
+
aliases.add(agentId);
|
|
10978
|
+
const row = db.query("SELECT name FROM agents WHERE id = ?").get(agentId);
|
|
10979
|
+
if (row?.name)
|
|
10980
|
+
aliases.add(row.name);
|
|
10981
|
+
}
|
|
10982
|
+
return [...aliases];
|
|
10983
|
+
}
|
|
10984
|
+
function assignedToAliasSet(db, ref) {
|
|
10985
|
+
return new Set(resolveAssignedToAliases(db, ref).map((a) => a.toLowerCase()));
|
|
10986
|
+
}
|
|
10987
|
+
function lowerInClause(column, values, params) {
|
|
10988
|
+
if (values.length === 0)
|
|
10989
|
+
return "1=0";
|
|
10990
|
+
params.push(...values.map((v) => v.toLowerCase()));
|
|
10991
|
+
return `LOWER(${column}) IN (${values.map(() => "?").join(",")})`;
|
|
10992
|
+
}
|
|
10953
10993
|
var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
|
|
10954
10994
|
var init_database = __esm(() => {
|
|
10955
10995
|
init_schema();
|
|
@@ -11420,6 +11460,96 @@ var init_lock_display = __esm(() => {
|
|
|
11420
11460
|
init_database();
|
|
11421
11461
|
});
|
|
11422
11462
|
|
|
11463
|
+
// src/lib/enum-vocabulary.ts
|
|
11464
|
+
function editDistance2(a, b) {
|
|
11465
|
+
if (a === b)
|
|
11466
|
+
return 0;
|
|
11467
|
+
if (!a.length)
|
|
11468
|
+
return b.length;
|
|
11469
|
+
if (!b.length)
|
|
11470
|
+
return a.length;
|
|
11471
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
11472
|
+
for (let i = 1;i <= a.length; i += 1) {
|
|
11473
|
+
const current = [i];
|
|
11474
|
+
for (let j = 1;j <= b.length; j += 1) {
|
|
11475
|
+
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
11476
|
+
}
|
|
11477
|
+
previous = current;
|
|
11478
|
+
}
|
|
11479
|
+
return previous[b.length];
|
|
11480
|
+
}
|
|
11481
|
+
function suggestVocabularyMatches(value, vocabulary, limit = 3) {
|
|
11482
|
+
const needle = value.trim().toLowerCase();
|
|
11483
|
+
if (!needle)
|
|
11484
|
+
return [];
|
|
11485
|
+
const scored = [];
|
|
11486
|
+
for (const member of vocabulary) {
|
|
11487
|
+
const candidate = member.toLowerCase();
|
|
11488
|
+
if (candidate.startsWith(needle) || needle.startsWith(candidate)) {
|
|
11489
|
+
scored.push({ member, score: 0 });
|
|
11490
|
+
continue;
|
|
11491
|
+
}
|
|
11492
|
+
if (candidate.includes(needle) || needle.includes(candidate)) {
|
|
11493
|
+
scored.push({ member, score: 1 });
|
|
11494
|
+
continue;
|
|
11495
|
+
}
|
|
11496
|
+
const distance = editDistance2(needle, candidate);
|
|
11497
|
+
if (distance <= Math.max(1, Math.floor(candidate.length / 3))) {
|
|
11498
|
+
scored.push({ member, score: 1 + distance });
|
|
11499
|
+
}
|
|
11500
|
+
}
|
|
11501
|
+
return scored.sort((a, b) => a.score - b.score || a.member.localeCompare(b.member)).slice(0, limit).map((entry) => entry.member);
|
|
11502
|
+
}
|
|
11503
|
+
function resolveEnumVocabulary(raw, spec) {
|
|
11504
|
+
const allowList = spec.allowList !== false;
|
|
11505
|
+
const rawElements = (allowList ? raw.split(",") : [raw]).map((element) => element.trim()).filter((element) => element.length > 0);
|
|
11506
|
+
if (rawElements.length === 0) {
|
|
11507
|
+
return {
|
|
11508
|
+
ok: false,
|
|
11509
|
+
message: `${spec.name} requires a value. Allowed values: ${spec.vocabulary.join(", ")}.`,
|
|
11510
|
+
invalid: []
|
|
11511
|
+
};
|
|
11512
|
+
}
|
|
11513
|
+
const normalized = rawElements.map((element) => spec.normalize ? spec.normalize(element) : element);
|
|
11514
|
+
const allowed = new Set(spec.vocabulary);
|
|
11515
|
+
const invalid2 = [];
|
|
11516
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
11517
|
+
if (!allowed.has(normalized[i]))
|
|
11518
|
+
invalid2.push(rawElements[i]);
|
|
11519
|
+
}
|
|
11520
|
+
if (invalid2.length === 0) {
|
|
11521
|
+
return { ok: true, values: [...new Set(normalized)] };
|
|
11522
|
+
}
|
|
11523
|
+
const label = invalid2.length === 1 ? "value" : "values";
|
|
11524
|
+
const parts = [
|
|
11525
|
+
`Invalid ${spec.name} ${label}: ${invalid2.join(", ")}.`,
|
|
11526
|
+
`Allowed values: ${spec.vocabulary.join(", ")}.`
|
|
11527
|
+
];
|
|
11528
|
+
const hints = new Set;
|
|
11529
|
+
const suggestions = new Set;
|
|
11530
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
11531
|
+
const canonical = normalized[i];
|
|
11532
|
+
if (allowed.has(canonical))
|
|
11533
|
+
continue;
|
|
11534
|
+
const hint = spec.hints?.[canonical.toLowerCase()];
|
|
11535
|
+
if (hint) {
|
|
11536
|
+
hints.add(hint);
|
|
11537
|
+
continue;
|
|
11538
|
+
}
|
|
11539
|
+
for (const match of suggestVocabularyMatches(canonical, spec.vocabulary)) {
|
|
11540
|
+
suggestions.add(match);
|
|
11541
|
+
}
|
|
11542
|
+
}
|
|
11543
|
+
if (suggestions.size > 0)
|
|
11544
|
+
parts.push(`Did you mean ${[...suggestions].join(", ")}?`);
|
|
11545
|
+
for (const hint of hints)
|
|
11546
|
+
parts.push(hint);
|
|
11547
|
+
return { ok: false, message: parts.join(" "), invalid: invalid2 };
|
|
11548
|
+
}
|
|
11549
|
+
function collapseEnumValues(values) {
|
|
11550
|
+
return values.length === 1 ? values[0] : values;
|
|
11551
|
+
}
|
|
11552
|
+
|
|
11423
11553
|
// src/cli/helpers.ts
|
|
11424
11554
|
var exports_helpers = {};
|
|
11425
11555
|
__export(exports_helpers, {
|
|
@@ -11430,9 +11560,13 @@ __export(exports_helpers, {
|
|
|
11430
11560
|
resolveExplicitProject: () => resolveExplicitProject,
|
|
11431
11561
|
priorityColors: () => priorityColors,
|
|
11432
11562
|
printJson: () => printJson,
|
|
11563
|
+
parseEnumFlagList: () => parseEnumFlagList,
|
|
11564
|
+
parseEnumFlag: () => parseEnumFlag,
|
|
11565
|
+
outputRecord: () => outputRecord,
|
|
11433
11566
|
output: () => output,
|
|
11434
11567
|
normalizeStatusList: () => normalizeStatusList,
|
|
11435
11568
|
normalizeStatus: () => normalizeStatus,
|
|
11569
|
+
normalizePriority: () => normalizePriority,
|
|
11436
11570
|
jsonModeRequested: () => jsonModeRequested,
|
|
11437
11571
|
handleError: () => handleError,
|
|
11438
11572
|
getPackageVersion: () => getPackageVersion,
|
|
@@ -11440,7 +11574,10 @@ __export(exports_helpers, {
|
|
|
11440
11574
|
detectGitRoot: () => detectGitRoot,
|
|
11441
11575
|
cacheCloudTaskForIdResolution: () => cacheCloudTaskForIdResolution,
|
|
11442
11576
|
autoProject: () => autoProject,
|
|
11443
|
-
autoDetectProject: () => autoDetectProject
|
|
11577
|
+
autoDetectProject: () => autoDetectProject,
|
|
11578
|
+
TASK_STATUS_FLAG_HINTS: () => TASK_STATUS_FLAG_HINTS,
|
|
11579
|
+
TASK_STATUS_FLAG: () => TASK_STATUS_FLAG,
|
|
11580
|
+
TASK_PRIORITY_FLAG: () => TASK_PRIORITY_FLAG
|
|
11444
11581
|
});
|
|
11445
11582
|
import chalk from "chalk";
|
|
11446
11583
|
import { execSync } from "child_process";
|
|
@@ -11450,8 +11587,19 @@ import { dirname as dirname3, join as join4, resolve as resolve3, sep } from "pa
|
|
|
11450
11587
|
function jsonModeRequested(argv = process.argv) {
|
|
11451
11588
|
return argv.some((arg) => arg === "--json" || /^-[a-z]+$/i.test(arg) && arg.includes("j"));
|
|
11452
11589
|
}
|
|
11590
|
+
function remoteErrorDetail(e) {
|
|
11591
|
+
if (!e || typeof e !== "object")
|
|
11592
|
+
return null;
|
|
11593
|
+
const body = e.body;
|
|
11594
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
11595
|
+
return null;
|
|
11596
|
+
const detail = body.error;
|
|
11597
|
+
return typeof detail === "string" && detail.trim() ? detail.trim() : null;
|
|
11598
|
+
}
|
|
11453
11599
|
function handleError(e) {
|
|
11454
|
-
const
|
|
11600
|
+
const baseMessage = e instanceof Error ? e.message : String(e);
|
|
11601
|
+
const detail = remoteErrorDetail(e);
|
|
11602
|
+
const message = detail && !baseMessage.includes(detail) ? `${baseMessage}: ${detail}` : baseMessage;
|
|
11455
11603
|
console.error(chalk.red(message));
|
|
11456
11604
|
if (jsonModeRequested()) {
|
|
11457
11605
|
console.log(JSON.stringify({ error: message }));
|
|
@@ -11629,7 +11777,8 @@ function autoProject(opts) {
|
|
|
11629
11777
|
return autoDetectProject(opts)?.id;
|
|
11630
11778
|
}
|
|
11631
11779
|
function normalizeStatus(s) {
|
|
11632
|
-
|
|
11780
|
+
const folded = s.toLowerCase().trim();
|
|
11781
|
+
switch (folded) {
|
|
11633
11782
|
case "done":
|
|
11634
11783
|
return "completed";
|
|
11635
11784
|
case "complete":
|
|
@@ -11643,14 +11792,33 @@ function normalizeStatus(s) {
|
|
|
11643
11792
|
case "canceled":
|
|
11644
11793
|
return "cancelled";
|
|
11645
11794
|
default:
|
|
11646
|
-
return
|
|
11795
|
+
return folded;
|
|
11647
11796
|
}
|
|
11648
11797
|
}
|
|
11798
|
+
function normalizePriority(p) {
|
|
11799
|
+
return p.toLowerCase().trim();
|
|
11800
|
+
}
|
|
11649
11801
|
function normalizeStatusList(statuses) {
|
|
11650
11802
|
if (Array.isArray(statuses))
|
|
11651
11803
|
return statuses.map(normalizeStatus);
|
|
11652
11804
|
return normalizeStatus(statuses);
|
|
11653
11805
|
}
|
|
11806
|
+
function parseEnumFlag(raw, spec) {
|
|
11807
|
+
if (raw === undefined || raw === null)
|
|
11808
|
+
return;
|
|
11809
|
+
const result = resolveEnumVocabulary(String(raw), spec);
|
|
11810
|
+
if (!result.ok)
|
|
11811
|
+
handleError(new Error(result.message));
|
|
11812
|
+
return collapseEnumValues(result.values);
|
|
11813
|
+
}
|
|
11814
|
+
function parseEnumFlagList(raw, spec) {
|
|
11815
|
+
if (raw === undefined || raw === null)
|
|
11816
|
+
return;
|
|
11817
|
+
const result = resolveEnumVocabulary(String(raw), spec);
|
|
11818
|
+
if (!result.ok)
|
|
11819
|
+
handleError(new Error(result.message));
|
|
11820
|
+
return result.values;
|
|
11821
|
+
}
|
|
11654
11822
|
function writeStdoutSync(text) {
|
|
11655
11823
|
const buffer = Buffer.from(text);
|
|
11656
11824
|
let offset = 0;
|
|
@@ -11682,6 +11850,41 @@ function output(data, jsonMode) {
|
|
|
11682
11850
|
printJson(data);
|
|
11683
11851
|
}
|
|
11684
11852
|
}
|
|
11853
|
+
function formatRecordLines(data) {
|
|
11854
|
+
if (data === null || data === undefined)
|
|
11855
|
+
return [];
|
|
11856
|
+
if (typeof data !== "object")
|
|
11857
|
+
return [String(data)];
|
|
11858
|
+
if (Array.isArray(data)) {
|
|
11859
|
+
return data.flatMap((entry, index) => [
|
|
11860
|
+
chalk.dim(`[${index}]`),
|
|
11861
|
+
...formatRecordLines(entry).map((line) => ` ${line}`)
|
|
11862
|
+
]);
|
|
11863
|
+
}
|
|
11864
|
+
const lines = [];
|
|
11865
|
+
for (const [key, value] of Object.entries(data)) {
|
|
11866
|
+
if (value === undefined)
|
|
11867
|
+
continue;
|
|
11868
|
+
const rendered = value === null ? chalk.dim("\u2014") : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
11869
|
+
lines.push(` ${chalk.dim(`${key}:`)} ${rendered}`);
|
|
11870
|
+
}
|
|
11871
|
+
return lines;
|
|
11872
|
+
}
|
|
11873
|
+
function outputRecord(data, jsonMode, heading) {
|
|
11874
|
+
if (jsonMode) {
|
|
11875
|
+
printJson(data);
|
|
11876
|
+
return;
|
|
11877
|
+
}
|
|
11878
|
+
if (heading)
|
|
11879
|
+
console.log(chalk.bold(heading));
|
|
11880
|
+
const lines = formatRecordLines(data);
|
|
11881
|
+
if (lines.length === 0) {
|
|
11882
|
+
console.log(chalk.dim("(empty)"));
|
|
11883
|
+
return;
|
|
11884
|
+
}
|
|
11885
|
+
for (const line of lines)
|
|
11886
|
+
console.log(line);
|
|
11887
|
+
}
|
|
11685
11888
|
function formatTaskLine(t) {
|
|
11686
11889
|
const statusFn = statusColors[t.status] || chalk.white;
|
|
11687
11890
|
const priorityFn = priorityColors[t.priority] || chalk.white;
|
|
@@ -11692,16 +11895,33 @@ function formatTaskLine(t) {
|
|
|
11692
11895
|
const plan = t.plan_id ? chalk.magenta(` [plan:${t.plan_id.slice(0, 8)}]`) : "";
|
|
11693
11896
|
return `${chalk.dim(t.id.slice(0, 8))} ${statusFn(t.status.padEnd(11))} ${priorityFn(t.priority.padEnd(8))} ${t.title}${assigned}${lock}${tags}${plan}`;
|
|
11694
11897
|
}
|
|
11695
|
-
var stdoutRetryBuffer, stdoutRetrySignal, TASK_UUID_RE, statusColors, priorityColors;
|
|
11898
|
+
var stdoutRetryBuffer, stdoutRetrySignal, TASK_UUID_RE, TASK_STATUS_FLAG_HINTS, TASK_STATUS_FLAG, TASK_PRIORITY_FLAG, statusColors, priorityColors;
|
|
11696
11899
|
var init_helpers = __esm(() => {
|
|
11697
11900
|
init_cloud_router();
|
|
11698
11901
|
init_database();
|
|
11699
11902
|
init_projects();
|
|
11700
11903
|
init_lock_display();
|
|
11701
11904
|
init_package_version();
|
|
11905
|
+
init_types();
|
|
11702
11906
|
stdoutRetryBuffer = new SharedArrayBuffer(4);
|
|
11703
11907
|
stdoutRetrySignal = new Int32Array(stdoutRetryBuffer);
|
|
11704
11908
|
TASK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11909
|
+
TASK_STATUS_FLAG_HINTS = {
|
|
11910
|
+
all: "To include every status, use -a/--all instead of a --status value.",
|
|
11911
|
+
any: "To include every status, use -a/--all instead of a --status value.",
|
|
11912
|
+
open: "Unstarted and running work is pending and in_progress (the default when no --status is given)."
|
|
11913
|
+
};
|
|
11914
|
+
TASK_STATUS_FLAG = {
|
|
11915
|
+
name: "--status",
|
|
11916
|
+
vocabulary: TASK_STATUSES,
|
|
11917
|
+
normalize: normalizeStatus,
|
|
11918
|
+
hints: TASK_STATUS_FLAG_HINTS
|
|
11919
|
+
};
|
|
11920
|
+
TASK_PRIORITY_FLAG = {
|
|
11921
|
+
name: "--priority",
|
|
11922
|
+
vocabulary: TASK_PRIORITIES,
|
|
11923
|
+
normalize: normalizePriority
|
|
11924
|
+
};
|
|
11705
11925
|
statusColors = {
|
|
11706
11926
|
pending: chalk.yellow,
|
|
11707
11927
|
in_progress: chalk.blue,
|
|
@@ -11740,17 +11960,19 @@ function checkCompletionGuard(task, agentId, db, configOverride) {
|
|
|
11740
11960
|
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
11961
|
}
|
|
11742
11962
|
}
|
|
11963
|
+
const assignedAliasParams = [];
|
|
11964
|
+
const assignedInClause = agent ? lowerInClause("assigned_to", resolveAssignedToAliases(db, agent), assignedAliasParams) : "";
|
|
11743
11965
|
if (agent && config.max_completions_per_window && config.window_minutes) {
|
|
11744
11966
|
const windowStart = new Date(Date.now() - config.window_minutes * 60 * 1000).toISOString();
|
|
11745
11967
|
const result = db.query(`SELECT COUNT(*) as count FROM tasks
|
|
11746
|
-
WHERE completed_at > ? AND (
|
|
11968
|
+
WHERE completed_at > ? AND (${assignedInClause} OR agent_id = ?)`).get(windowStart, ...assignedAliasParams, agent);
|
|
11747
11969
|
if (result.count >= config.max_completions_per_window) {
|
|
11748
11970
|
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
11971
|
}
|
|
11750
11972
|
}
|
|
11751
11973
|
if (agent && config.cooldown_seconds) {
|
|
11752
11974
|
const result = db.query(`SELECT MAX(completed_at) as last_completed FROM tasks
|
|
11753
|
-
WHERE completed_at IS NOT NULL AND (
|
|
11975
|
+
WHERE completed_at IS NOT NULL AND (${assignedInClause} OR agent_id = ?) AND id != ?`).get(...assignedAliasParams, agent, task.id);
|
|
11754
11976
|
if (result.last_completed) {
|
|
11755
11977
|
const elapsedSeconds = (Date.now() - new Date(result.last_completed).getTime()) / 1000;
|
|
11756
11978
|
if (elapsedSeconds < config.cooldown_seconds) {
|
|
@@ -11764,6 +11986,7 @@ var init_completion_guard = __esm(() => {
|
|
|
11764
11986
|
init_types();
|
|
11765
11987
|
init_config();
|
|
11766
11988
|
init_projects();
|
|
11989
|
+
init_database();
|
|
11767
11990
|
});
|
|
11768
11991
|
|
|
11769
11992
|
// src/lib/event-emission-safety.ts
|
|
@@ -13862,7 +14085,7 @@ function getRecap(hours = 8, projectId, db) {
|
|
|
13862
14085
|
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
14086
|
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
14087
|
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);
|
|
14088
|
+
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
14089
|
return {
|
|
13867
14090
|
hours,
|
|
13868
14091
|
since,
|
|
@@ -14008,13 +14231,17 @@ function logDelivery(d, webhookId, event, payload, statusCode, response, attempt
|
|
|
14008
14231
|
const id = uuid();
|
|
14009
14232
|
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
14233
|
}
|
|
14011
|
-
function matchesScope(wh, payload) {
|
|
14234
|
+
function matchesScope(wh, payload, db) {
|
|
14012
14235
|
if (wh.project_id && payload.project_id !== wh.project_id)
|
|
14013
14236
|
return false;
|
|
14014
14237
|
if (wh.task_list_id && payload.task_list_id !== wh.task_list_id)
|
|
14015
14238
|
return false;
|
|
14016
|
-
if (wh.agent_id && payload.agent_id !== wh.agent_id
|
|
14017
|
-
|
|
14239
|
+
if (wh.agent_id && payload.agent_id !== wh.agent_id) {
|
|
14240
|
+
const aliasSet = assignedToAliasSet(db, wh.agent_id);
|
|
14241
|
+
const assignedTo = typeof payload.assigned_to === "string" ? payload.assigned_to.toLowerCase() : "";
|
|
14242
|
+
if (!aliasSet.has(assignedTo))
|
|
14243
|
+
return false;
|
|
14244
|
+
}
|
|
14018
14245
|
if (wh.task_id && payload.id !== wh.task_id)
|
|
14019
14246
|
return false;
|
|
14020
14247
|
return true;
|
|
@@ -14081,7 +14308,7 @@ async function dispatchWebhook2(event, payload, db) {
|
|
|
14081
14308
|
const webhooks = listWebhooks(d).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
|
|
14082
14309
|
const payloadObj = typeof payload === "object" && payload !== null ? payload : {};
|
|
14083
14310
|
for (const wh of webhooks) {
|
|
14084
|
-
if (!matchesScope(wh, payloadObj))
|
|
14311
|
+
if (!matchesScope(wh, payloadObj, d))
|
|
14085
14312
|
continue;
|
|
14086
14313
|
const body = JSON.stringify({ event, payload, timestamp: now() });
|
|
14087
14314
|
deliverWebhook(wh, event, body, 1, d).catch((err) => {
|
|
@@ -15229,14 +15456,16 @@ function getNextTask(agentId, filters, db) {
|
|
|
15229
15456
|
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
15457
|
const where = conditions.join(" AND ");
|
|
15231
15458
|
let recentProjectIds = [];
|
|
15459
|
+
const assignedAliasParams = [];
|
|
15460
|
+
const assignedInClause = agentId ? lowerInClause("assigned_to", resolveAssignedToAliases(d, agentId), assignedAliasParams) : "";
|
|
15232
15461
|
if (agentId) {
|
|
15233
|
-
const recentRows = d.query(`SELECT DISTINCT project_id FROM tasks WHERE
|
|
15462
|
+
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
15463
|
recentProjectIds = recentRows.map((r) => r.project_id);
|
|
15235
15464
|
}
|
|
15236
15465
|
let sql = `SELECT * FROM tasks WHERE ${where} ORDER BY `;
|
|
15237
15466
|
if (agentId) {
|
|
15238
|
-
sql += `CASE WHEN
|
|
15239
|
-
params.push(
|
|
15467
|
+
sql += `CASE WHEN ${assignedInClause} THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
|
|
15468
|
+
params.push(...assignedAliasParams);
|
|
15240
15469
|
}
|
|
15241
15470
|
if (recentProjectIds.length > 0) {
|
|
15242
15471
|
const placeholders = recentProjectIds.map(() => "?").join(",");
|
|
@@ -15630,30 +15859,6 @@ function getTaskWithRelations(id, db) {
|
|
|
15630
15859
|
checklist
|
|
15631
15860
|
};
|
|
15632
15861
|
}
|
|
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
15862
|
function listTasks(filter = {}, db) {
|
|
15658
15863
|
const d = db || getDatabase();
|
|
15659
15864
|
const { clearExpiredLocks: clearExpiredLocks2 } = (init_database(), __toCommonJS(exports_database));
|
|
@@ -15939,11 +16144,13 @@ function updateTask(id, input, db) {
|
|
|
15939
16144
|
}
|
|
15940
16145
|
sets.push("status = ?");
|
|
15941
16146
|
params.push(input.status);
|
|
16147
|
+
if (isTerminalStatus(input.status)) {
|
|
16148
|
+
sets.push("locked_by = NULL");
|
|
16149
|
+
sets.push("locked_at = NULL");
|
|
16150
|
+
}
|
|
15942
16151
|
if (input.status === "completed") {
|
|
15943
16152
|
sets.push("completed_at = ?");
|
|
15944
16153
|
params.push(completionTimestamp);
|
|
15945
|
-
sets.push("locked_by = NULL");
|
|
15946
|
-
sets.push("locked_at = NULL");
|
|
15947
16154
|
} else if (task.status === "completed" && input.completed_at === undefined) {
|
|
15948
16155
|
sets.push("completed_at = NULL");
|
|
15949
16156
|
}
|
|
@@ -16067,6 +16274,7 @@ function updateTask(id, input, db) {
|
|
|
16067
16274
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
16068
16275
|
const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
|
|
16069
16276
|
const completedNow = input.status === "completed";
|
|
16277
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
16070
16278
|
const updatedTask = {
|
|
16071
16279
|
...task,
|
|
16072
16280
|
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
@@ -16074,8 +16282,8 @@ function updateTask(id, input, db) {
|
|
|
16074
16282
|
metadata: input.metadata ?? task.metadata,
|
|
16075
16283
|
version: task.version + 1,
|
|
16076
16284
|
updated_at: timestamp2,
|
|
16077
|
-
locked_by:
|
|
16078
|
-
locked_at:
|
|
16285
|
+
locked_by: terminalNow ? null : task.locked_by,
|
|
16286
|
+
locked_at: terminalNow ? null : task.locked_at,
|
|
16079
16287
|
completed_at: completedNow ? completionTimestamp : reopened ? null : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
16080
16288
|
sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
|
|
16081
16289
|
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
@@ -16126,6 +16334,7 @@ function deleteTask(id, db) {
|
|
|
16126
16334
|
var init_task_crud = __esm(() => {
|
|
16127
16335
|
init_types();
|
|
16128
16336
|
init_database();
|
|
16337
|
+
init_database();
|
|
16129
16338
|
init_completion_guard();
|
|
16130
16339
|
init_event_emission_safety();
|
|
16131
16340
|
init_event_hooks();
|
|
@@ -16288,8 +16497,8 @@ function getTaskStats(filters, db) {
|
|
|
16288
16497
|
params.push(filters.task_list_id);
|
|
16289
16498
|
}
|
|
16290
16499
|
if (filters?.agent_id) {
|
|
16291
|
-
|
|
16292
|
-
|
|
16500
|
+
params.push(filters.agent_id);
|
|
16501
|
+
conditions.push(`(agent_id = ? OR ${lowerInClause("assigned_to", resolveAssignedToAliases(d, filters.agent_id), params)})`);
|
|
16293
16502
|
}
|
|
16294
16503
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
16295
16504
|
const totalRow = d.query(`SELECT COUNT(*) as count FROM tasks ${where}`).get(...params);
|
|
@@ -16472,8 +16681,7 @@ function getEscalatedTasks(opts = {}, db, at = new Date) {
|
|
|
16472
16681
|
params.push(opts.project_id);
|
|
16473
16682
|
}
|
|
16474
16683
|
if (opts.agent_id) {
|
|
16475
|
-
conditions.push("assigned_to
|
|
16476
|
-
params.push(opts.agent_id);
|
|
16684
|
+
conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
|
|
16477
16685
|
}
|
|
16478
16686
|
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC, created_at ASC`).all(...params);
|
|
16479
16687
|
return rows.map(rowToTask).map((task) => {
|
|
@@ -16521,8 +16729,7 @@ function notifyUpcomingDeadlines(opts = {}, db) {
|
|
|
16521
16729
|
params.push(opts.project_id);
|
|
16522
16730
|
}
|
|
16523
16731
|
if (opts.agent_id) {
|
|
16524
|
-
conditions.push("assigned_to
|
|
16525
|
-
params.push(opts.agent_id);
|
|
16732
|
+
conditions.push(lowerInClause("assigned_to", resolveAssignedToAliases(d, opts.agent_id), params));
|
|
16526
16733
|
}
|
|
16527
16734
|
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
|
|
16528
16735
|
return rows.map(rowToTask);
|
|
@@ -16780,13 +16987,14 @@ function getTimeReport(opts, db) {
|
|
|
16780
16987
|
params.push(opts.plan_id);
|
|
16781
16988
|
}
|
|
16782
16989
|
if (opts?.agent_id) {
|
|
16990
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.agent_id), params);
|
|
16783
16991
|
conditions.push(`(
|
|
16784
|
-
|
|
16992
|
+
${assignedInClause}
|
|
16785
16993
|
OR t.agent_id = ?
|
|
16786
16994
|
OR EXISTS (SELECT 1 FROM task_time_logs ttl WHERE ttl.task_id = t.id AND ttl.agent_id = ?)
|
|
16787
16995
|
OR EXISTS (SELECT 1 FROM focus_sessions fs WHERE fs.task_id = t.id AND fs.agent_id = ?)
|
|
16788
16996
|
)`);
|
|
16789
|
-
params.push(opts.agent_id, opts.agent_id, opts.agent_id
|
|
16997
|
+
params.push(opts.agent_id, opts.agent_id, opts.agent_id);
|
|
16790
16998
|
}
|
|
16791
16999
|
if (opts?.since) {
|
|
16792
17000
|
conditions.push("(t.completed_at >= ? OR t.updated_at >= ?)");
|
|
@@ -17799,7 +18007,7 @@ function listActiveFiles(db) {
|
|
|
17799
18007
|
a.name AS agent_name
|
|
17800
18008
|
FROM task_files tf
|
|
17801
18009
|
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))
|
|
18010
|
+
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
18011
|
WHERE t.status = 'in_progress'
|
|
17804
18012
|
AND tf.status != 'removed'
|
|
17805
18013
|
ORDER BY tf.updated_at DESC
|
|
@@ -20193,11 +20401,7 @@ function resolveProjectIdOrSlug(input) {
|
|
|
20193
20401
|
function parseStatus(value) {
|
|
20194
20402
|
if (!value)
|
|
20195
20403
|
return;
|
|
20196
|
-
|
|
20197
|
-
if (!TASK_STATUSES.includes(normalized)) {
|
|
20198
|
-
handleError(new Error(`--status must be one of: ${TASK_STATUSES.join(", ")}`));
|
|
20199
|
-
}
|
|
20200
|
-
return normalized;
|
|
20404
|
+
return parseEnumFlagList(value, { ...TASK_STATUS_FLAG, allowList: false })?.[0];
|
|
20201
20405
|
}
|
|
20202
20406
|
function parseIntOption(value, flag) {
|
|
20203
20407
|
if (value === undefined)
|
|
@@ -20222,10 +20426,7 @@ function resolvePlanId(input) {
|
|
|
20222
20426
|
function parsePriority(value) {
|
|
20223
20427
|
if (!value)
|
|
20224
20428
|
return;
|
|
20225
|
-
|
|
20226
|
-
handleError(new Error("--priority must be one of: low, medium, high, critical"));
|
|
20227
|
-
}
|
|
20228
|
-
return value;
|
|
20429
|
+
return parseEnumFlagList(value, { ...TASK_PRIORITY_FLAG, allowList: false })?.[0];
|
|
20229
20430
|
}
|
|
20230
20431
|
function parseJsonObject3(value, flag) {
|
|
20231
20432
|
if (!value)
|
|
@@ -20610,12 +20811,12 @@ function registerTaskCommands(program2) {
|
|
|
20610
20811
|
filter["task_list_id"] = listId;
|
|
20611
20812
|
}
|
|
20612
20813
|
if (opts.status) {
|
|
20613
|
-
filter["status"] =
|
|
20814
|
+
filter["status"] = parseEnumFlag(opts.status, TASK_STATUS_FLAG);
|
|
20614
20815
|
} else if (!opts.all) {
|
|
20615
20816
|
filter["status"] = ["pending", "in_progress"];
|
|
20616
20817
|
}
|
|
20617
20818
|
if (opts.priority)
|
|
20618
|
-
filter["priority"] = opts.priority;
|
|
20819
|
+
filter["priority"] = parseEnumFlag(opts.priority, TASK_PRIORITY_FLAG);
|
|
20619
20820
|
if (opts.assigned)
|
|
20620
20821
|
filter["assigned_to"] = opts.assigned;
|
|
20621
20822
|
if (opts.createdBy)
|
|
@@ -21588,7 +21789,6 @@ var init_task_commands = __esm(() => {
|
|
|
21588
21789
|
init_assignee_guard();
|
|
21589
21790
|
init_helpers();
|
|
21590
21791
|
init_output_redaction();
|
|
21591
|
-
init_types();
|
|
21592
21792
|
});
|
|
21593
21793
|
|
|
21594
21794
|
// src/lib/plan-artifacts.ts
|
|
@@ -23368,8 +23568,7 @@ function searchTasks(options, projectId, taskListId, db) {
|
|
|
23368
23568
|
}
|
|
23369
23569
|
}
|
|
23370
23570
|
if (opts.assigned_to) {
|
|
23371
|
-
sql +=
|
|
23372
|
-
params.push(opts.assigned_to);
|
|
23571
|
+
sql += ` AND ${lowerInClause("t.assigned_to", resolveAssignedToAliases(d, opts.assigned_to), params)}`;
|
|
23373
23572
|
}
|
|
23374
23573
|
if (opts.agent_id) {
|
|
23375
23574
|
sql += " AND t.agent_id = ?";
|
|
@@ -23421,7 +23620,8 @@ __export(exports_saved_search_views, {
|
|
|
23421
23620
|
normalizeScope: () => normalizeScope,
|
|
23422
23621
|
listSearchViews: () => listSearchViews,
|
|
23423
23622
|
getSearchView: () => getSearchView,
|
|
23424
|
-
deleteSearchView: () => deleteSearchView
|
|
23623
|
+
deleteSearchView: () => deleteSearchView,
|
|
23624
|
+
SAVED_SEARCH_SCOPES: () => SAVED_SEARCH_SCOPES
|
|
23425
23625
|
});
|
|
23426
23626
|
function parseFilters(value) {
|
|
23427
23627
|
if (!value)
|
|
@@ -23441,10 +23641,8 @@ function rowToSavedSearchView(row) {
|
|
|
23441
23641
|
};
|
|
23442
23642
|
}
|
|
23443
23643
|
function normalizeScope(scope) {
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
}
|
|
23447
|
-
return "tasks";
|
|
23644
|
+
const candidate = (scope ?? "").trim().toLowerCase();
|
|
23645
|
+
return SAVED_SEARCH_SCOPES.includes(candidate) ? candidate : "tasks";
|
|
23448
23646
|
}
|
|
23449
23647
|
function normalizeName(name) {
|
|
23450
23648
|
const normalized = name.trim();
|
|
@@ -23747,10 +23945,12 @@ function runSearchView(idOrName, db) {
|
|
|
23747
23945
|
throw new Error(`Saved search view not found: ${idOrName}`);
|
|
23748
23946
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
23749
23947
|
}
|
|
23948
|
+
var SAVED_SEARCH_SCOPES;
|
|
23750
23949
|
var init_saved_search_views = __esm(() => {
|
|
23751
23950
|
init_database();
|
|
23752
23951
|
init_local_fields();
|
|
23753
23952
|
init_search();
|
|
23953
|
+
SAVED_SEARCH_SCOPES = ["all", "tasks", "projects", "plans", "runs", "comments"];
|
|
23754
23954
|
});
|
|
23755
23955
|
|
|
23756
23956
|
// src/lib/claude-tasks.ts
|
|
@@ -26320,12 +26520,11 @@ function buildSearchFilters(query, opts, projectId) {
|
|
|
26320
26520
|
const customFields = parseJsonObjectOption(opts.fieldCustom, "--field-custom");
|
|
26321
26521
|
const labels = splitList(opts.fieldLabel);
|
|
26322
26522
|
const tags = splitList(opts.tag);
|
|
26323
|
-
const statuses = splitList(opts.status)?.map(normalizeStatus);
|
|
26324
26523
|
const filters = {
|
|
26325
26524
|
query: query || opts.query,
|
|
26326
26525
|
project_id: opts.allProjects ? undefined : projectId,
|
|
26327
|
-
status:
|
|
26328
|
-
priority:
|
|
26526
|
+
status: parseEnumFlagList(opts.status, TASK_STATUS_FLAG),
|
|
26527
|
+
priority: parseEnumFlagList(opts.priority, TASK_PRIORITY_FLAG),
|
|
26329
26528
|
assigned_to: opts.assigned,
|
|
26330
26529
|
agent_id: opts.agentId,
|
|
26331
26530
|
task_list_id: filterPatch?.task_list_id === undefined ? resolveTaskListFilter(opts.taskList, projectId) : undefined,
|
|
@@ -26430,7 +26629,7 @@ function registerProjectCommands(program2) {
|
|
|
26430
26629
|
try {
|
|
26431
26630
|
const cloud = getTodosCloudClient();
|
|
26432
26631
|
const projectId = opts.allProjects ? undefined : cloud ? undefined : autoProject(globalOpts);
|
|
26433
|
-
const scope =
|
|
26632
|
+
const scope = parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG) ?? "tasks";
|
|
26434
26633
|
const searchOpts = buildSearchFilters(query, opts, projectId);
|
|
26435
26634
|
if (opts.saveAs) {
|
|
26436
26635
|
const view = saveSearchView({
|
|
@@ -26522,7 +26721,7 @@ function registerProjectCommands(program2) {
|
|
|
26522
26721
|
const view = saveSearchView({
|
|
26523
26722
|
name,
|
|
26524
26723
|
description: opts.description,
|
|
26525
|
-
scope:
|
|
26724
|
+
scope: parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG) ?? "tasks",
|
|
26526
26725
|
filters: buildSearchFilters(opts.query, opts, projectId)
|
|
26527
26726
|
});
|
|
26528
26727
|
output(view, Boolean(globalOpts.json));
|
|
@@ -26535,7 +26734,7 @@ function registerProjectCommands(program2) {
|
|
|
26535
26734
|
views.command("list").description("List local saved search views").option("--scope <scope>", "Filter by scope").action((opts) => {
|
|
26536
26735
|
const globalOpts = program2.opts();
|
|
26537
26736
|
try {
|
|
26538
|
-
const rows = listSearchViews(
|
|
26737
|
+
const rows = listSearchViews(parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG));
|
|
26539
26738
|
output(rows, Boolean(globalOpts.json));
|
|
26540
26739
|
if (!globalOpts.json) {
|
|
26541
26740
|
if (rows.length === 0) {
|
|
@@ -26731,7 +26930,7 @@ function registerProjectCommands(program2) {
|
|
|
26731
26930
|
const cloud = getTodosCloudClient();
|
|
26732
26931
|
if (opts.show) {
|
|
26733
26932
|
const project = cloud ? await cloudResolveProject(cloud, opts.show) : resolveExplicitProject(opts.show);
|
|
26734
|
-
|
|
26933
|
+
outputRecord(project, Boolean(globalOpts.json), "Project:");
|
|
26735
26934
|
return;
|
|
26736
26935
|
}
|
|
26737
26936
|
if (opts.update) {
|
|
@@ -26745,7 +26944,7 @@ function registerProjectCommands(program2) {
|
|
|
26745
26944
|
}
|
|
26746
26945
|
const current = cloud ? await cloudResolveProject(cloud, opts.update) : resolveExplicitProject(opts.update);
|
|
26747
26946
|
const project = cloud ? await cloudUpdateProject(cloud, current.id, patch) : updateProject(current.id, patch);
|
|
26748
|
-
|
|
26947
|
+
outputRecord(project, Boolean(globalOpts.json), "Project updated:");
|
|
26749
26948
|
return;
|
|
26750
26949
|
}
|
|
26751
26950
|
if (opts.deregister) {
|
|
@@ -27053,6 +27252,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
|
|
|
27053
27252
|
program2.command("export").description("Export tasks").option("-f, --format <format>", "Format: json, md, todos.md, or bridge", "json").option("-o, --output <path>", "Write export output to a file").option("--encrypt", "Encrypt bridge exports with a local encryption profile").option("--encryption-profile <name>", "Encryption profile name", "default").option("--allow-plaintext-sensitive", "Deprecated; bridge exports are redacted unless --encrypt is used").action(async (opts) => {
|
|
27054
27253
|
const { listTasks: listTasks2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
27055
27254
|
const globalOpts = program2.opts();
|
|
27255
|
+
opts.format = parseEnumFlag(opts.format, EXPORT_FORMAT_FLAG) ?? "json";
|
|
27056
27256
|
const projectId = autoProject(globalOpts);
|
|
27057
27257
|
const writeOutput = async (content) => {
|
|
27058
27258
|
if (opts.output) {
|
|
@@ -27214,7 +27414,7 @@ function resolveTaskListId(partialId) {
|
|
|
27214
27414
|
}
|
|
27215
27415
|
return id;
|
|
27216
27416
|
}
|
|
27217
|
-
var CLOUD_GRAPH_EDGE_CONCURRENCY = 6;
|
|
27417
|
+
var SEARCH_SCOPE_FLAG, EXPORT_FORMATS, EXPORT_FORMAT_FLAG, CLOUD_GRAPH_EDGE_CONCURRENCY = 6;
|
|
27218
27418
|
var init_project_commands = __esm(() => {
|
|
27219
27419
|
init_database();
|
|
27220
27420
|
init_projects();
|
|
@@ -27226,12 +27426,26 @@ var init_project_commands = __esm(() => {
|
|
|
27226
27426
|
init_config();
|
|
27227
27427
|
init_helpers();
|
|
27228
27428
|
init_output_redaction();
|
|
27429
|
+
SEARCH_SCOPE_FLAG = {
|
|
27430
|
+
name: "--scope",
|
|
27431
|
+
vocabulary: SAVED_SEARCH_SCOPES,
|
|
27432
|
+
normalize: (value) => value.toLowerCase().trim(),
|
|
27433
|
+
allowList: false
|
|
27434
|
+
};
|
|
27435
|
+
EXPORT_FORMATS = ["json", "md", "markdown", "todos.md", "todos-md", "bridge"];
|
|
27436
|
+
EXPORT_FORMAT_FLAG = {
|
|
27437
|
+
name: "--format",
|
|
27438
|
+
vocabulary: EXPORT_FORMATS,
|
|
27439
|
+
normalize: (value) => value.toLowerCase().trim(),
|
|
27440
|
+
allowList: false
|
|
27441
|
+
};
|
|
27229
27442
|
});
|
|
27230
27443
|
|
|
27231
27444
|
// src/cli/commands/agent-commands.ts
|
|
27232
27445
|
var exports_agent_commands = {};
|
|
27233
27446
|
__export(exports_agent_commands, {
|
|
27234
|
-
registerAgentCommands: () => registerAgentCommands
|
|
27447
|
+
registerAgentCommands: () => registerAgentCommands,
|
|
27448
|
+
findCaseVariantRows: () => findCaseVariantRows
|
|
27235
27449
|
});
|
|
27236
27450
|
import chalk6 from "chalk";
|
|
27237
27451
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -27245,6 +27459,15 @@ function resolveCloudAgentByNameOrId(agents, nameOrId) {
|
|
|
27245
27459
|
return null;
|
|
27246
27460
|
return matches.reduce((freshest, candidate) => new Date(candidate.last_seen_at).getTime() > new Date(freshest.last_seen_at).getTime() ? candidate : freshest);
|
|
27247
27461
|
}
|
|
27462
|
+
function findCaseVariantRows(agents, requested) {
|
|
27463
|
+
const raw = requested.trim();
|
|
27464
|
+
const target = normalizeAgentNameInput(raw);
|
|
27465
|
+
if (!target)
|
|
27466
|
+
return [];
|
|
27467
|
+
if (agents.some((agent) => agent.name === raw))
|
|
27468
|
+
return [];
|
|
27469
|
+
return agents.filter((agent) => normalizeAgentNameInput(agent.name) === target);
|
|
27470
|
+
}
|
|
27248
27471
|
function clearIdentityIfMine(agentId, agentName) {
|
|
27249
27472
|
const persisted = readPersistedIdentity();
|
|
27250
27473
|
if (!persisted)
|
|
@@ -27258,7 +27481,22 @@ function registerAgentCommands(program2) {
|
|
|
27258
27481
|
const globalOpts = program2.opts();
|
|
27259
27482
|
try {
|
|
27260
27483
|
const cloud = getTodosCloudClient();
|
|
27261
|
-
const
|
|
27484
|
+
const registrationName = name.trim();
|
|
27485
|
+
if (cloud) {
|
|
27486
|
+
let roster = null;
|
|
27487
|
+
try {
|
|
27488
|
+
roster = await cloudListAgents(cloud);
|
|
27489
|
+
} catch {
|
|
27490
|
+
console.error(chalk6.yellow("Warning: could not read the shared roster, so the case-variant check was skipped for this registration."));
|
|
27491
|
+
}
|
|
27492
|
+
const variants = roster ? findCaseVariantRows(roster, registrationName) : [];
|
|
27493
|
+
if (variants.length > 0) {
|
|
27494
|
+
const listed = variants.map((a) => `${a.name} (${a.id})`).join(", ");
|
|
27495
|
+
console.error(chalk6.red(`'${name}' is a case variant of an agent that already exists: ${listed}. ` + `Agent names are ONE case-insensitive identity, so registering this spelling would create a second row ` + `and split the identity in two \u2014 tasks assigned to one spelling are invisible to the other. ` + `Register with the existing spelling instead.`));
|
|
27496
|
+
process.exit(1);
|
|
27497
|
+
}
|
|
27498
|
+
}
|
|
27499
|
+
const result = cloud ? await cloudRegisterAgent(cloud, { name: registrationName, description: opts.description }) : (await Promise.resolve().then(() => (init_agents(), exports_agents))).registerAgent({ name: registrationName, description: opts.description });
|
|
27262
27500
|
const { isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
27263
27501
|
if (isAgentConflict2(result)) {
|
|
27264
27502
|
console.error(chalk6.red("CONFLICT:"), result.message);
|
|
@@ -27603,7 +27841,7 @@ ${isOnline ? chalk6.green("\u25CF") : chalk6.dim("\u25CB")} ${chalk6.bold(agent.
|
|
|
27603
27841
|
const list2 = cloud ? await cloudGetTaskList(cloud, resolved) : getTaskList(resolved);
|
|
27604
27842
|
if (!list2)
|
|
27605
27843
|
throw new Error(`Task list not found: ${ref}`);
|
|
27606
|
-
|
|
27844
|
+
outputRecord(list2, Boolean(globalOpts.json), "Task list:");
|
|
27607
27845
|
return;
|
|
27608
27846
|
}
|
|
27609
27847
|
const patch = {
|
|
@@ -27614,7 +27852,7 @@ ${isOnline ? chalk6.green("\u25CF") : chalk6.dim("\u25CB")} ${chalk6.bold(agent.
|
|
|
27614
27852
|
if (Object.keys(patch).length === 0)
|
|
27615
27853
|
throw new Error("lists --update requires --name, --slug, or --description");
|
|
27616
27854
|
const list = cloud ? await cloudUpdateTaskList(cloud, resolved, patch) : updateTaskList(resolved, patch);
|
|
27617
|
-
|
|
27855
|
+
outputRecord(list, Boolean(globalOpts.json), "Task list updated:");
|
|
27618
27856
|
return;
|
|
27619
27857
|
}
|
|
27620
27858
|
if (opts.delete) {
|
|
@@ -32165,9 +32403,11 @@ async function updateTask2(id, input, store) {
|
|
|
32165
32403
|
throw new Error(`Task ${id} version conflict: expected ${existing.version}, got ${input.version}`);
|
|
32166
32404
|
}
|
|
32167
32405
|
const reopened = existing.status === "completed" && input.status !== undefined && input.status !== "completed" && input.completed_at === undefined;
|
|
32406
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
32168
32407
|
const task = {
|
|
32169
32408
|
...existing,
|
|
32170
32409
|
...definedPatch(input),
|
|
32410
|
+
...terminalNow ? { locked_by: null, locked_at: null } : {},
|
|
32171
32411
|
version: existing.version + 1,
|
|
32172
32412
|
updated_at: new Date().toISOString(),
|
|
32173
32413
|
tags: input.tags ?? existing.tags,
|
|
@@ -34508,8 +34748,19 @@ function handleStats(_ctx, json2) {
|
|
|
34508
34748
|
recurring_tasks: countRecurringTasks()
|
|
34509
34749
|
});
|
|
34510
34750
|
}
|
|
34751
|
+
function taskStatusQueryParam(url) {
|
|
34752
|
+
const raw = url.searchParams.get("status");
|
|
34753
|
+
if (!raw)
|
|
34754
|
+
return { ok: true, value: undefined };
|
|
34755
|
+
const result = resolveEnumVocabulary(raw, { name: "status", vocabulary: TASK_STATUSES });
|
|
34756
|
+
if (!result.ok)
|
|
34757
|
+
return { ok: false, message: result.message };
|
|
34758
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
34759
|
+
}
|
|
34511
34760
|
async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
34512
|
-
const
|
|
34761
|
+
const statusParam = taskStatusQueryParam(url);
|
|
34762
|
+
if (!statusParam.ok)
|
|
34763
|
+
return json2({ error: statusParam.message }, 400);
|
|
34513
34764
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
34514
34765
|
const sessionId = url.searchParams.get("session_id") || undefined;
|
|
34515
34766
|
const agentId = url.searchParams.get("agent_id") || undefined;
|
|
@@ -34517,7 +34768,7 @@ async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
34517
34768
|
const offsetParam = url.searchParams.get("offset");
|
|
34518
34769
|
const fields = parseFieldsParam(url);
|
|
34519
34770
|
const tasks = listTasks({
|
|
34520
|
-
status,
|
|
34771
|
+
status: statusParam.value,
|
|
34521
34772
|
project_id: projectId,
|
|
34522
34773
|
session_id: sessionId,
|
|
34523
34774
|
agent_id: agentId,
|
|
@@ -34582,9 +34833,19 @@ async function handleUpsertTask(req, ctx, json2, taskToSummary2) {
|
|
|
34582
34833
|
}
|
|
34583
34834
|
function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
|
|
34584
34835
|
const format = url.searchParams.get("format") || "json";
|
|
34585
|
-
const
|
|
34836
|
+
const statusParam = taskStatusQueryParam(url);
|
|
34837
|
+
if (!statusParam.ok) {
|
|
34838
|
+
return new Response(JSON.stringify({ error: statusParam.message }), {
|
|
34839
|
+
status: 400,
|
|
34840
|
+
headers: { "Content-Type": "application/json" }
|
|
34841
|
+
});
|
|
34842
|
+
}
|
|
34586
34843
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
34587
|
-
const tasks = listTasks({
|
|
34844
|
+
const tasks = listTasks({
|
|
34845
|
+
status: statusParam.value,
|
|
34846
|
+
project_id: projectId,
|
|
34847
|
+
limit: 1e4
|
|
34848
|
+
});
|
|
34588
34849
|
const summaries = tasks.map((t) => taskToSummary2(t));
|
|
34589
34850
|
if (format === "csv") {
|
|
34590
34851
|
const headers = ["id", "short_id", "title", "status", "priority", "project_id", "assigned_to", "agent_id", "created_at", "updated_at", "completed_at", "due_at"];
|
|
@@ -34899,8 +35160,9 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
34899
35160
|
}
|
|
34900
35161
|
}
|
|
34901
35162
|
function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
|
|
35163
|
+
const aliasSet = assignedToAliasSet(getDatabase(), agentId);
|
|
34902
35164
|
const pending = listTasks({ status: "pending" });
|
|
34903
|
-
const queue = pending.filter((t) => t.assigned_to
|
|
35165
|
+
const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
|
|
34904
35166
|
const order = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
34905
35167
|
queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
34906
35168
|
return json2(queue.map((t) => taskToSummary2(t)));
|
|
@@ -35165,6 +35427,7 @@ var init_routes = __esm(() => {
|
|
|
35165
35427
|
init_tasks();
|
|
35166
35428
|
init_database();
|
|
35167
35429
|
init_types();
|
|
35430
|
+
init_types();
|
|
35168
35431
|
init_projects();
|
|
35169
35432
|
init_agents();
|
|
35170
35433
|
init_plans();
|
|
@@ -37049,8 +37312,38 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
37049
37312
|
operationId: "listTasks",
|
|
37050
37313
|
summary: "List tasks",
|
|
37051
37314
|
parameters: [
|
|
37052
|
-
{
|
|
37053
|
-
|
|
37315
|
+
{
|
|
37316
|
+
name: "status",
|
|
37317
|
+
in: "query",
|
|
37318
|
+
description: `Task status, or a comma-separated list of statuses. Allowed values: ${TASK_STATUSES.join(", ")}.`,
|
|
37319
|
+
style: "form",
|
|
37320
|
+
explode: false,
|
|
37321
|
+
schema: {
|
|
37322
|
+
oneOf: [
|
|
37323
|
+
{ type: "string", enum: [...TASK_STATUSES] },
|
|
37324
|
+
{
|
|
37325
|
+
type: "array",
|
|
37326
|
+
items: { type: "string", enum: [...TASK_STATUSES] }
|
|
37327
|
+
}
|
|
37328
|
+
]
|
|
37329
|
+
}
|
|
37330
|
+
},
|
|
37331
|
+
{
|
|
37332
|
+
name: "priority",
|
|
37333
|
+
in: "query",
|
|
37334
|
+
description: `Task priority, or a comma-separated list of priorities. Allowed values: ${TASK_PRIORITIES.join(", ")}.`,
|
|
37335
|
+
style: "form",
|
|
37336
|
+
explode: false,
|
|
37337
|
+
schema: {
|
|
37338
|
+
oneOf: [
|
|
37339
|
+
{ type: "string", enum: [...TASK_PRIORITIES] },
|
|
37340
|
+
{
|
|
37341
|
+
type: "array",
|
|
37342
|
+
items: { type: "string", enum: [...TASK_PRIORITIES] }
|
|
37343
|
+
}
|
|
37344
|
+
]
|
|
37345
|
+
}
|
|
37346
|
+
},
|
|
37054
37347
|
{ name: "project_id", in: "query", schema: { type: "string" } },
|
|
37055
37348
|
{ name: "parent_id", in: "query", schema: { type: "string", nullable: true } },
|
|
37056
37349
|
{ name: "include_subtasks", in: "query", schema: { type: "boolean" } },
|
|
@@ -37562,6 +37855,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
37562
37855
|
var taskSchema, projectSchema, taskListSchema, taskCommentSchema, planSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
|
|
37563
37856
|
var init_openapi = __esm(() => {
|
|
37564
37857
|
init_package_version();
|
|
37858
|
+
init_types();
|
|
37565
37859
|
taskSchema = {
|
|
37566
37860
|
type: "object",
|
|
37567
37861
|
properties: {
|
|
@@ -37829,6 +38123,15 @@ function json3(body, status = 200) {
|
|
|
37829
38123
|
function error(status, message, extra) {
|
|
37830
38124
|
return json3({ error: message, ...extra ?? {} }, status);
|
|
37831
38125
|
}
|
|
38126
|
+
function enumQueryParam(url, name, vocabulary) {
|
|
38127
|
+
const raw = url.searchParams.get(name);
|
|
38128
|
+
if (raw === null || raw === "")
|
|
38129
|
+
return { ok: true, value: undefined };
|
|
38130
|
+
const result = resolveEnumVocabulary(raw, { name, vocabulary });
|
|
38131
|
+
if (!result.ok)
|
|
38132
|
+
return { ok: false, response: error(400, result.message) };
|
|
38133
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
38134
|
+
}
|
|
37832
38135
|
function validateTaskCompletion(value) {
|
|
37833
38136
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
37834
38137
|
return { ok: false, message: "completion body must be an object" };
|
|
@@ -38249,14 +38552,16 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
38249
38552
|
return error(400, "include_subtasks must be true or false");
|
|
38250
38553
|
}
|
|
38251
38554
|
const hasParentFilter = url.searchParams.has("parent_id");
|
|
38555
|
+
const statusParam = enumQueryParam(url, "status", TASK_STATUSES);
|
|
38556
|
+
if (!statusParam.ok)
|
|
38557
|
+
return statusParam.response;
|
|
38558
|
+
const priorityParam = enumQueryParam(url, "priority", TASK_PRIORITIES);
|
|
38559
|
+
if (!priorityParam.ok)
|
|
38560
|
+
return priorityParam.response;
|
|
38252
38561
|
const filter = {
|
|
38253
38562
|
...url.searchParams.get("q") ? { query: url.searchParams.get("q") } : {},
|
|
38254
|
-
...
|
|
38255
|
-
|
|
38256
|
-
} : {},
|
|
38257
|
-
...url.searchParams.get("priority") ? {
|
|
38258
|
-
priority: url.searchParams.get("priority").includes(",") ? url.searchParams.get("priority").split(",") : url.searchParams.get("priority")
|
|
38259
|
-
} : {},
|
|
38563
|
+
...statusParam.value !== undefined ? { status: statusParam.value } : {},
|
|
38564
|
+
...priorityParam.value !== undefined ? { priority: priorityParam.value } : {},
|
|
38260
38565
|
...url.searchParams.get("project_id") ? { project_id: url.searchParams.get("project_id") } : {},
|
|
38261
38566
|
...hasParentFilter ? { parent_id: url.searchParams.get("parent_id") || null, include_subtasks: true } : includeSubtasks !== null ? { include_subtasks: includeSubtasks === "true" } : {},
|
|
38262
38567
|
...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {},
|
|
@@ -44587,11 +44892,12 @@ function runAlerts(input, db, checkedAt, quieted) {
|
|
|
44587
44892
|
if (input.include_runs === false)
|
|
44588
44893
|
return [];
|
|
44589
44894
|
const since = Date.parse(input.run_since || new Date(Date.parse(checkedAt) - 24 * 60 * 60000).toISOString());
|
|
44895
|
+
const runAlertAliases = input.agent_id ? assignedToAliasSet(db, input.agent_id) : null;
|
|
44590
44896
|
return listTaskRuns(undefined, db).filter((run) => run.completed_at && Date.parse(run.completed_at) >= since).map((run) => {
|
|
44591
44897
|
const task = getTask(run.task_id, db);
|
|
44592
44898
|
if (input.project_id && task?.project_id !== input.project_id)
|
|
44593
44899
|
return null;
|
|
44594
|
-
if (input.agent_id && run.agent_id !== input.agent_id && task?.assigned_to
|
|
44900
|
+
if (input.agent_id && run.agent_id !== input.agent_id && !(runAlertAliases && runAlertAliases.has((task?.assigned_to ?? "").toLowerCase())))
|
|
44595
44901
|
return null;
|
|
44596
44902
|
const failed = ["failed", "cancelled"].includes(run.status);
|
|
44597
44903
|
return {
|
|
@@ -44636,6 +44942,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
44636
44942
|
const warnings = [];
|
|
44637
44943
|
if (Number.isNaN(checkedMs))
|
|
44638
44944
|
warnings.push("Invalid check timestamp; due-soon window may be incomplete.");
|
|
44945
|
+
const notifyAgentAliases = input.agent_id ? assignedToAliasSet(d, input.agent_id) : null;
|
|
44639
44946
|
const alerts = [];
|
|
44640
44947
|
for (const escalation of getEscalatedTasks({ project_id: input.project_id, agent_id: input.agent_id }, d, new Date(checkedAt))) {
|
|
44641
44948
|
for (const reason of escalation.reasons) {
|
|
@@ -44647,7 +44954,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
44647
44954
|
for (const task of listTasks({ project_id: input.project_id, include_archived: false, limit: 5000 }, d)) {
|
|
44648
44955
|
if (!unfinished(task) || !task.due_at)
|
|
44649
44956
|
continue;
|
|
44650
|
-
if (input.agent_id && task.
|
|
44957
|
+
if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
|
|
44651
44958
|
continue;
|
|
44652
44959
|
if (task.due_at > checkedAt && task.due_at <= dueSoonUntil) {
|
|
44653
44960
|
alerts.push(taskAlert(task, "task_due_soon", "task.due_soon", "warning", `Due soon: ${task.title}`, task.due_at, quieted));
|
|
@@ -44655,7 +44962,7 @@ async function checkLocalNotifications(input = {}, db) {
|
|
|
44655
44962
|
}
|
|
44656
44963
|
}
|
|
44657
44964
|
for (const task of getStaleTasks(staleMinutes, { project_id: input.project_id }, d)) {
|
|
44658
|
-
if (input.agent_id && task.
|
|
44965
|
+
if (input.agent_id && task.agent_id !== input.agent_id && !(notifyAgentAliases && notifyAgentAliases.has((task.assigned_to ?? "").toLowerCase())))
|
|
44659
44966
|
continue;
|
|
44660
44967
|
alerts.push(taskAlert(task, "task_stale", "task.stale", "warning", `Stale task: ${task.title}`, checkedAt, quieted, { stale_minutes: staleMinutes }));
|
|
44661
44968
|
}
|
|
@@ -46480,10 +46787,12 @@ function matchesProfile(profile, query) {
|
|
|
46480
46787
|
return false;
|
|
46481
46788
|
return true;
|
|
46482
46789
|
}
|
|
46483
|
-
function taskMatchesAgent(task, agentId) {
|
|
46790
|
+
function taskMatchesAgent(task, agentId, aliasSet) {
|
|
46484
46791
|
if (!agentId)
|
|
46485
46792
|
return true;
|
|
46486
|
-
|
|
46793
|
+
if (task.agent_id === agentId)
|
|
46794
|
+
return true;
|
|
46795
|
+
return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
|
|
46487
46796
|
}
|
|
46488
46797
|
function estimateRemaining(task) {
|
|
46489
46798
|
if (task.status === "completed" || task.status === "cancelled")
|
|
@@ -46553,7 +46862,8 @@ function removeCapacityProfile(idOrAgent, projectId) {
|
|
|
46553
46862
|
}
|
|
46554
46863
|
function getPlanningForecast(input = {}, db) {
|
|
46555
46864
|
const startDate = input.start_date ? dateOnly(input.start_date) : todayIso();
|
|
46556
|
-
const
|
|
46865
|
+
const aliasSet = input.agent_id ? assignedToAliasSet(db || getDatabase(), input.agent_id) : undefined;
|
|
46866
|
+
const tasks = listTasks({ project_id: input.project_id, plan_id: input.plan_id, limit: 1e4 }, db).filter((task) => taskMatchesAgent(task, input.agent_id, aliasSet));
|
|
46557
46867
|
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]));
|
|
46558
46868
|
const profiles = listCapacityProfiles({ agent_id: input.agent_id, project_id: input.project_id });
|
|
46559
46869
|
const globalProfiles = input.project_id ? listCapacityProfiles({ agent_id: input.agent_id }).filter((profile) => profile.project_id === null) : [];
|
|
@@ -46651,6 +46961,7 @@ function renderPlanningForecastMarkdown(forecast) {
|
|
|
46651
46961
|
}
|
|
46652
46962
|
var LOCAL_CAPACITY_SCHEMA_VERSION = 1;
|
|
46653
46963
|
var init_capacity_forecasts = __esm(() => {
|
|
46964
|
+
init_database();
|
|
46654
46965
|
init_tasks();
|
|
46655
46966
|
init_task_relations();
|
|
46656
46967
|
init_config();
|
|
@@ -49760,8 +50071,10 @@ function getLatestHandoff(agentId, projectId, db) {
|
|
|
49760
50071
|
function createSessionRecoveryHandoff(input, db) {
|
|
49761
50072
|
const d = db || getDatabase();
|
|
49762
50073
|
const limit = input.limit || 20;
|
|
49763
|
-
const
|
|
49764
|
-
const
|
|
50074
|
+
const params = [];
|
|
50075
|
+
const assignedInClause = lowerInClause("assigned_to", resolveAssignedToAliases(d, input.agent_id), params);
|
|
50076
|
+
const conditions = ["status = 'in_progress'", `(${assignedInClause} OR agent_id = ? OR locked_by = ?)`];
|
|
50077
|
+
params.push(input.agent_id, input.agent_id);
|
|
49765
50078
|
if (input.session_id) {
|
|
49766
50079
|
conditions.push("session_id = ?");
|
|
49767
50080
|
params.push(input.session_id);
|
|
@@ -50353,7 +50666,8 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
50353
50666
|
const assigned = listTasks4({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
|
|
50354
50667
|
const now4 = Date.now();
|
|
50355
50668
|
const dueSoonCutoff = now4 + 24 * 60 * 60 * 1000;
|
|
50356
|
-
const
|
|
50669
|
+
const blockedAliases = assignedToAliasSet(getDatabase(), effectiveAgentId);
|
|
50670
|
+
const blocked = getBlockedTasks2().filter((t) => blockedAliases.has((t.assigned_to ?? "").toLowerCase()));
|
|
50357
50671
|
const workload = {
|
|
50358
50672
|
in_progress: assigned.filter((t) => t.status === "in_progress").length,
|
|
50359
50673
|
pending: assigned.filter((t) => t.status === "pending").length,
|
|
@@ -50390,17 +50704,38 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
50390
50704
|
if (agents.length === 0)
|
|
50391
50705
|
return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
|
|
50392
50706
|
const activeTasks = listTasks4({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
|
|
50393
|
-
const
|
|
50707
|
+
const agentKeyByAlias = new Map;
|
|
50708
|
+
const ambiguousAgentAliases = new Set;
|
|
50709
|
+
const indexAgentAlias = (alias, agentId) => {
|
|
50710
|
+
const normalizedAlias2 = alias.toLowerCase();
|
|
50711
|
+
if (ambiguousAgentAliases.has(normalizedAlias2))
|
|
50712
|
+
return;
|
|
50713
|
+
const existingAgentId = agentKeyByAlias.get(normalizedAlias2);
|
|
50714
|
+
if (existingAgentId !== undefined && existingAgentId !== agentId) {
|
|
50715
|
+
agentKeyByAlias.delete(normalizedAlias2);
|
|
50716
|
+
ambiguousAgentAliases.add(normalizedAlias2);
|
|
50717
|
+
return;
|
|
50718
|
+
}
|
|
50719
|
+
agentKeyByAlias.set(normalizedAlias2, agentId);
|
|
50720
|
+
};
|
|
50721
|
+
for (const agent of agents) {
|
|
50722
|
+
indexAgentAlias(String(agent.id), agent.id);
|
|
50723
|
+
if (agent.name)
|
|
50724
|
+
indexAgentAlias(String(agent.name), agent.id);
|
|
50725
|
+
}
|
|
50726
|
+
const canonicalAgentKey = (assignedTo) => assignedTo ? agentKeyByAlias.get(assignedTo.toLowerCase()) : undefined;
|
|
50727
|
+
const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => canonicalAgentKey(t.assigned_to) === agent.id).length]));
|
|
50394
50728
|
let moved = 0;
|
|
50395
50729
|
let skipped = 0;
|
|
50396
|
-
for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
|
|
50397
|
-
const
|
|
50730
|
+
for (const task of activeTasks.filter((t) => t.status === "pending" && canonicalAgentKey(t.assigned_to) && (load.get(canonicalAgentKey(t.assigned_to)) ?? 0) > limit)) {
|
|
50731
|
+
const currentAgentKey = canonicalAgentKey(task.assigned_to);
|
|
50732
|
+
const target = agents.filter((agent) => agent.id !== currentAgentKey).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
|
|
50398
50733
|
if (!target || (load.get(target.id) ?? 0) >= limit) {
|
|
50399
50734
|
skipped++;
|
|
50400
50735
|
continue;
|
|
50401
50736
|
}
|
|
50402
50737
|
updateTask3(task.id, { assigned_to: target.id, version: task.version });
|
|
50403
|
-
load.set(
|
|
50738
|
+
load.set(currentAgentKey, (load.get(currentAgentKey) ?? 1) - 1);
|
|
50404
50739
|
load.set(target.id, (load.get(target.id) ?? 0) + 1);
|
|
50405
50740
|
moved++;
|
|
50406
50741
|
}
|
|
@@ -50579,6 +50914,7 @@ ${lines.join(`
|
|
|
50579
50914
|
var init_task_auto_tools = __esm(() => {
|
|
50580
50915
|
init_zod();
|
|
50581
50916
|
init_cloud_router();
|
|
50917
|
+
init_database();
|
|
50582
50918
|
});
|
|
50583
50919
|
|
|
50584
50920
|
// src/lib/context-packs.ts
|
|
@@ -51758,7 +52094,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
|
|
|
51758
52094
|
}, undefined);
|
|
51759
52095
|
const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
|
|
51760
52096
|
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
51761
|
-
const
|
|
52097
|
+
const blockedAliases = effectiveAgentId ? assignedToAliasSet(getDatabase(), effectiveAgentId) : null;
|
|
52098
|
+
const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => blockedAliases ? blockedAliases.has((t.assigned_to ?? "").toLowerCase()) : false);
|
|
51762
52099
|
const lines = [
|
|
51763
52100
|
`Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
|
|
51764
52101
|
inProgress.length > 0 ? `
|
|
@@ -51986,6 +52323,7 @@ var init_task_adv_tools = __esm(() => {
|
|
|
51986
52323
|
init_zod();
|
|
51987
52324
|
init_token_utils();
|
|
51988
52325
|
init_cloud_router();
|
|
52326
|
+
init_database();
|
|
51989
52327
|
});
|
|
51990
52328
|
|
|
51991
52329
|
// src/mcp/tools/task-meta-tools.ts
|
|
@@ -54182,27 +54520,29 @@ function getAgentMetrics(agentId, opts, db) {
|
|
|
54182
54520
|
const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
54183
54521
|
if (!agent)
|
|
54184
54522
|
return null;
|
|
54523
|
+
const assignedAliases = resolveAssignedToAliases(d, agent.id).map((a) => a.toLowerCase());
|
|
54524
|
+
const assignedInClause = `LOWER(assigned_to) IN (${assignedAliases.map(() => "?").join(",")})`;
|
|
54185
54525
|
let projectFilter = "";
|
|
54186
|
-
const params = [agent.id,
|
|
54526
|
+
const params = [agent.id, ...assignedAliases];
|
|
54187
54527
|
if (opts?.project_id) {
|
|
54188
54528
|
projectFilter = " AND project_id = ?";
|
|
54189
54529
|
params.push(opts.project_id);
|
|
54190
54530
|
}
|
|
54191
|
-
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
54192
|
-
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
54193
|
-
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR
|
|
54531
|
+
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}`).get(...params).count;
|
|
54532
|
+
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'failed'${projectFilter}`).get(...params).count;
|
|
54533
|
+
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'in_progress'${projectFilter}`).get(...params).count;
|
|
54194
54534
|
const total = completed + failed;
|
|
54195
54535
|
const completionRate = total > 0 ? completed / total : 0;
|
|
54196
54536
|
const avgTime = d.query(`SELECT AVG(
|
|
54197
54537
|
(julianday(completed_at) - julianday(created_at)) * 24 * 60
|
|
54198
54538
|
) as avg_minutes
|
|
54199
54539
|
FROM tasks
|
|
54200
|
-
WHERE (agent_id = ? OR
|
|
54540
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
|
|
54201
54541
|
const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
|
|
54202
54542
|
FROM tasks
|
|
54203
|
-
WHERE (agent_id = ? OR
|
|
54543
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
|
|
54204
54544
|
const reviewTasks = d.query(`SELECT metadata FROM tasks
|
|
54205
|
-
WHERE (agent_id = ? OR
|
|
54545
|
+
WHERE (agent_id = ? OR ${assignedInClause}) AND status = 'completed'${projectFilter}
|
|
54206
54546
|
AND metadata LIKE '%_review_score%'`).all(...params);
|
|
54207
54547
|
let reviewScoreAvg = null;
|
|
54208
54548
|
if (reviewTasks.length > 0) {
|
|
@@ -54672,14 +55012,15 @@ function millisBetween(start, end) {
|
|
|
54672
55012
|
return 0;
|
|
54673
55013
|
return endMs - startMs;
|
|
54674
55014
|
}
|
|
54675
|
-
function addTaskScope(where, params, options, alias = "t") {
|
|
55015
|
+
function addTaskScope(where, params, options, db, alias = "t") {
|
|
54676
55016
|
if (options.project_id) {
|
|
54677
55017
|
where.push(`${alias}.project_id = ?`);
|
|
54678
55018
|
params.push(options.project_id);
|
|
54679
55019
|
}
|
|
54680
55020
|
if (options.agent_id) {
|
|
54681
|
-
|
|
54682
|
-
|
|
55021
|
+
params.push(options.agent_id);
|
|
55022
|
+
const assignedInClause = lowerInClause(`${alias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
55023
|
+
where.push(`(${alias}.agent_id = ? OR ${assignedInClause})`);
|
|
54683
55024
|
}
|
|
54684
55025
|
if (options.since) {
|
|
54685
55026
|
where.push(`${alias}.created_at >= ?`);
|
|
@@ -54690,14 +55031,15 @@ function addTaskScope(where, params, options, alias = "t") {
|
|
|
54690
55031
|
params.push(options.until);
|
|
54691
55032
|
}
|
|
54692
55033
|
}
|
|
54693
|
-
function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
|
|
55034
|
+
function addRunScope(where, params, options, db, runAlias = "r", taskAlias = "t") {
|
|
54694
55035
|
if (options.project_id) {
|
|
54695
55036
|
where.push(`${taskAlias}.project_id = ?`);
|
|
54696
55037
|
params.push(options.project_id);
|
|
54697
55038
|
}
|
|
54698
55039
|
if (options.agent_id) {
|
|
54699
|
-
|
|
54700
|
-
|
|
55040
|
+
params.push(options.agent_id, options.agent_id);
|
|
55041
|
+
const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
55042
|
+
where.push(`(${runAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
|
|
54701
55043
|
}
|
|
54702
55044
|
if (options.since) {
|
|
54703
55045
|
where.push(`${runAlias}.started_at >= ?`);
|
|
@@ -54708,14 +55050,15 @@ function addRunScope(where, params, options, runAlias = "r", taskAlias = "t") {
|
|
|
54708
55050
|
params.push(options.until);
|
|
54709
55051
|
}
|
|
54710
55052
|
}
|
|
54711
|
-
function addTraceScope(where, params, options, traceAlias = "tr", taskAlias = "t") {
|
|
55053
|
+
function addTraceScope(where, params, options, db, traceAlias = "tr", taskAlias = "t") {
|
|
54712
55054
|
if (options.project_id) {
|
|
54713
55055
|
where.push(`${taskAlias}.project_id = ?`);
|
|
54714
55056
|
params.push(options.project_id);
|
|
54715
55057
|
}
|
|
54716
55058
|
if (options.agent_id) {
|
|
54717
|
-
|
|
54718
|
-
|
|
55059
|
+
params.push(options.agent_id, options.agent_id);
|
|
55060
|
+
const assignedInClause = lowerInClause(`${taskAlias}.assigned_to`, resolveAssignedToAliases(db, options.agent_id), params);
|
|
55061
|
+
where.push(`(${traceAlias}.agent_id = ? OR ${taskAlias}.agent_id = ? OR ${assignedInClause})`);
|
|
54719
55062
|
}
|
|
54720
55063
|
if (options.since) {
|
|
54721
55064
|
where.push(`${traceAlias}.created_at >= ?`);
|
|
@@ -54775,7 +55118,7 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
54775
55118
|
const generatedAt = options.generated_at || new Date().toISOString();
|
|
54776
55119
|
const taskWhere = [];
|
|
54777
55120
|
const taskParams = [];
|
|
54778
|
-
addTaskScope(taskWhere, taskParams, options);
|
|
55121
|
+
addTaskScope(taskWhere, taskParams, options, d);
|
|
54779
55122
|
const taskClause = taskWhere.length ? `WHERE ${taskWhere.join(" AND ")}` : "";
|
|
54780
55123
|
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);
|
|
54781
55124
|
let projectCount = 0;
|
|
@@ -54784,14 +55127,14 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
54784
55127
|
} else if (options.agent_id) {
|
|
54785
55128
|
const projectWhere = ["t.project_id IS NOT NULL"];
|
|
54786
55129
|
const projectParams = [];
|
|
54787
|
-
addTaskScope(projectWhere, projectParams, options);
|
|
55130
|
+
addTaskScope(projectWhere, projectParams, options, d);
|
|
54788
55131
|
projectCount = queryOne(d, `SELECT COUNT(DISTINCT t.project_id) as count FROM tasks t WHERE ${projectWhere.join(" AND ")}`, projectParams).count;
|
|
54789
55132
|
} else {
|
|
54790
55133
|
projectCount = queryOne(d, "SELECT COUNT(*) as count FROM projects", []).count;
|
|
54791
55134
|
}
|
|
54792
55135
|
const runWhere = [];
|
|
54793
55136
|
const runParams = [];
|
|
54794
|
-
addRunScope(runWhere, runParams, options);
|
|
55137
|
+
addRunScope(runWhere, runParams, options, d);
|
|
54795
55138
|
const runClause = runWhere.length ? `WHERE ${runWhere.join(" AND ")}` : "";
|
|
54796
55139
|
const runs = queryAll(d, `SELECT r.id, r.started_at, r.completed_at, r.metadata
|
|
54797
55140
|
FROM task_runs r JOIN tasks t ON t.id = r.task_id
|
|
@@ -54808,7 +55151,7 @@ function createLocalUsageLedger(options = {}, db) {
|
|
|
54808
55151
|
${runClause}`, runParams);
|
|
54809
55152
|
const traceWhere = [];
|
|
54810
55153
|
const traceParams = [];
|
|
54811
|
-
addTraceScope(traceWhere, traceParams, options);
|
|
55154
|
+
addTraceScope(traceWhere, traceParams, options, d);
|
|
54812
55155
|
const traceClause = traceWhere.length ? `WHERE ${traceWhere.join(" AND ")}` : "";
|
|
54813
55156
|
const traceTotals = queryOne(d, `SELECT COUNT(*) as traces,
|
|
54814
55157
|
COALESCE(SUM(tr.tokens), 0) as tokens,
|
|
@@ -54952,10 +55295,12 @@ function limitValue(value) {
|
|
|
54952
55295
|
function isTerminal2(task) {
|
|
54953
55296
|
return task.status === "completed" || task.status === "failed" || task.status === "cancelled";
|
|
54954
55297
|
}
|
|
54955
|
-
function sameAgent(task, agentId) {
|
|
55298
|
+
function sameAgent(task, agentId, aliasSet) {
|
|
54956
55299
|
if (!agentId)
|
|
54957
55300
|
return true;
|
|
54958
|
-
|
|
55301
|
+
if (task.agent_id === agentId)
|
|
55302
|
+
return true;
|
|
55303
|
+
return aliasSet ? aliasSet.has((task.assigned_to ?? "").toLowerCase()) : task.assigned_to === agentId;
|
|
54959
55304
|
}
|
|
54960
55305
|
function withinTaskWindow(task, options) {
|
|
54961
55306
|
const time = Date.parse(task.updated_at);
|
|
@@ -54968,11 +55313,12 @@ function withinTaskWindow(task, options) {
|
|
|
54968
55313
|
return true;
|
|
54969
55314
|
}
|
|
54970
55315
|
function scopedTasks(options, db) {
|
|
55316
|
+
const aliasSet = options.agent_id ? assignedToAliasSet(db, options.agent_id) : undefined;
|
|
54971
55317
|
return listTasks({
|
|
54972
55318
|
project_id: options.project_id,
|
|
54973
55319
|
plan_id: options.plan_id,
|
|
54974
55320
|
include_archived: false
|
|
54975
|
-
}, db).filter((task) => sameAgent(task, options.agent_id) && withinTaskWindow(task, options));
|
|
55321
|
+
}, db).filter((task) => sameAgent(task, options.agent_id, aliasSet) && withinTaskWindow(task, options));
|
|
54976
55322
|
}
|
|
54977
55323
|
function summarizeTask(task) {
|
|
54978
55324
|
return {
|
|
@@ -55038,7 +55384,7 @@ function initialAgentSummary(agentId) {
|
|
|
55038
55384
|
}
|
|
55039
55385
|
};
|
|
55040
55386
|
}
|
|
55041
|
-
function addScopeClauses(where, params, options, timeColumn) {
|
|
55387
|
+
function addScopeClauses(where, params, options, timeColumn, db) {
|
|
55042
55388
|
if (options.project_id) {
|
|
55043
55389
|
where.push("t.project_id = ?");
|
|
55044
55390
|
params.push(options.project_id);
|
|
@@ -55048,8 +55394,9 @@ function addScopeClauses(where, params, options, timeColumn) {
|
|
|
55048
55394
|
params.push(options.plan_id);
|
|
55049
55395
|
}
|
|
55050
55396
|
if (options.agent_id) {
|
|
55051
|
-
|
|
55052
|
-
|
|
55397
|
+
params.push(options.agent_id, options.agent_id);
|
|
55398
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
|
|
55399
|
+
where.push(`(r.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
|
|
55053
55400
|
}
|
|
55054
55401
|
if (options.since) {
|
|
55055
55402
|
where.push(`${timeColumn} >= ?`);
|
|
@@ -55063,7 +55410,7 @@ function addScopeClauses(where, params, options, timeColumn) {
|
|
|
55063
55410
|
function loadRuns(options, db) {
|
|
55064
55411
|
const where = ["t.archived_at IS NULL"];
|
|
55065
55412
|
const params = [];
|
|
55066
|
-
addScopeClauses(where, params, options, "r.started_at");
|
|
55413
|
+
addScopeClauses(where, params, options, "r.started_at", db);
|
|
55067
55414
|
return db.query(`
|
|
55068
55415
|
SELECT
|
|
55069
55416
|
r.id,
|
|
@@ -55103,8 +55450,9 @@ function loadVerifications(options, db) {
|
|
|
55103
55450
|
params.push(options.plan_id);
|
|
55104
55451
|
}
|
|
55105
55452
|
if (options.agent_id) {
|
|
55106
|
-
|
|
55107
|
-
|
|
55453
|
+
params.push(options.agent_id, options.agent_id);
|
|
55454
|
+
const assignedInClause = lowerInClause("t.assigned_to", resolveAssignedToAliases(db, options.agent_id), params);
|
|
55455
|
+
where.push(`(v.agent_id = ? OR t.agent_id = ? OR ${assignedInClause})`);
|
|
55108
55456
|
}
|
|
55109
55457
|
if (options.since) {
|
|
55110
55458
|
where.push("v.run_at >= ?");
|
|
@@ -55326,7 +55674,6 @@ function renderLocalReportMarkdown(report) {
|
|
|
55326
55674
|
}
|
|
55327
55675
|
var LOCAL_REPORT_SCHEMA_VERSION = 1, LOCAL_REPORT_TYPES;
|
|
55328
55676
|
var init_local_reports = __esm(() => {
|
|
55329
|
-
init_database();
|
|
55330
55677
|
init_database();
|
|
55331
55678
|
init_plans();
|
|
55332
55679
|
init_tasks();
|
|
@@ -58382,7 +58729,7 @@ ${lines.join(`
|
|
|
58382
58729
|
a.name AS agent_name
|
|
58383
58730
|
FROM task_files tf
|
|
58384
58731
|
JOIN tasks t ON tf.task_id = t.id
|
|
58385
|
-
LEFT JOIN agents a ON (tf.agent_id = a.id OR (tf.agent_id IS NULL AND t.assigned_to = a.id))
|
|
58732
|
+
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))))
|
|
58386
58733
|
WHERE t.status = 'in_progress'
|
|
58387
58734
|
AND tf.status != 'removed'
|
|
58388
58735
|
AND t.project_id = ?
|
|
@@ -61760,9 +62107,16 @@ ${text2}` }] };
|
|
|
61760
62107
|
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
61761
62108
|
}
|
|
61762
62109
|
const oldName = agent.name;
|
|
61763
|
-
const updated = updateAgent(agent.id, { name: new_name });
|
|
61764
62110
|
const db = getDatabase();
|
|
61765
|
-
|
|
62111
|
+
let oldNameUniquelyIdentifiesAgent = false;
|
|
62112
|
+
try {
|
|
62113
|
+
oldNameUniquelyIdentifiesAgent = resolvePartialId(db, "agents", oldName) === agent.id;
|
|
62114
|
+
} catch (error2) {
|
|
62115
|
+
if (!(error2 instanceof IdentityAliasAmbiguousError))
|
|
62116
|
+
throw error2;
|
|
62117
|
+
}
|
|
62118
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
62119
|
+
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]);
|
|
61766
62120
|
const taskNote = tasksResult.changes > 0 ? `
|
|
61767
62121
|
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
61768
62122
|
return {
|
|
@@ -61946,6 +62300,7 @@ var init_agents2 = __esm(() => {
|
|
|
61946
62300
|
init_agents();
|
|
61947
62301
|
init_config();
|
|
61948
62302
|
init_database();
|
|
62303
|
+
init_types();
|
|
61949
62304
|
init_cloud_router();
|
|
61950
62305
|
});
|
|
61951
62306
|
|
|
@@ -72111,7 +72466,11 @@ function registerDispatchCommands(program2) {
|
|
|
72111
72466
|
process.exit(1);
|
|
72112
72467
|
}
|
|
72113
72468
|
}
|
|
72114
|
-
const statuses = opts.status
|
|
72469
|
+
const statuses = parseEnumFlagList(opts.status, {
|
|
72470
|
+
name: "--status",
|
|
72471
|
+
vocabulary: DISPATCH_STATUSES,
|
|
72472
|
+
normalize: (value) => value.toLowerCase().trim()
|
|
72473
|
+
});
|
|
72115
72474
|
const dispatches = listDispatches({ status: statuses, limit: opts.limit ?? 20 });
|
|
72116
72475
|
if (useJson) {
|
|
72117
72476
|
console.log(JSON.stringify(dispatches, null, 2));
|
|
@@ -72144,6 +72503,8 @@ var init_dispatch3 = __esm(() => {
|
|
|
72144
72503
|
init_dispatch();
|
|
72145
72504
|
init_dispatch_formatter();
|
|
72146
72505
|
init_tmux();
|
|
72506
|
+
init_types();
|
|
72507
|
+
init_helpers();
|
|
72147
72508
|
});
|
|
72148
72509
|
|
|
72149
72510
|
// src/cli/commands/machines.tsx
|