@hasna/todos 0.10.22 → 0.11.2
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/brains.d.ts +3 -0
- package/dist/cli/brains.d.ts.map +1 -0
- package/dist/cli/index.js +1402 -552
- package/dist/db/budgets.d.ts +27 -0
- package/dist/db/budgets.d.ts.map +1 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/snapshots.d.ts +37 -0
- package/dist/db/snapshots.d.ts.map +1 -0
- package/dist/db/tasks.d.ts +26 -0
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/db/traces.d.ts +38 -0
- package/dist/db/traces.d.ts.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +481 -44
- package/dist/lib/gatherer.d.ts +16 -0
- package/dist/lib/gatherer.d.ts.map +1 -0
- package/dist/lib/model-config.d.ts +14 -0
- package/dist/lib/model-config.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +550 -18
- package/dist/server/index.js +141 -17
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -754,6 +754,58 @@ var MIGRATIONS = [
|
|
|
754
754
|
`
|
|
755
755
|
ALTER TABLE tasks ADD COLUMN task_type TEXT;
|
|
756
756
|
CREATE INDEX IF NOT EXISTS idx_tasks_task_type ON tasks(task_type);
|
|
757
|
+
ALTER TABLE tasks ADD COLUMN cost_tokens INTEGER DEFAULT 0;
|
|
758
|
+
ALTER TABLE tasks ADD COLUMN cost_usd REAL DEFAULT 0;
|
|
759
|
+
ALTER TABLE tasks ADD COLUMN delegated_from TEXT;
|
|
760
|
+
ALTER TABLE tasks ADD COLUMN delegation_depth INTEGER DEFAULT 0;
|
|
761
|
+
ALTER TABLE tasks ADD COLUMN retry_count INTEGER DEFAULT 0;
|
|
762
|
+
ALTER TABLE tasks ADD COLUMN max_retries INTEGER DEFAULT 3;
|
|
763
|
+
ALTER TABLE tasks ADD COLUMN retry_after TEXT;
|
|
764
|
+
ALTER TABLE tasks ADD COLUMN sla_minutes INTEGER;
|
|
765
|
+
|
|
766
|
+
CREATE TABLE IF NOT EXISTS task_traces (
|
|
767
|
+
id TEXT PRIMARY KEY,
|
|
768
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
769
|
+
agent_id TEXT,
|
|
770
|
+
trace_type TEXT NOT NULL CHECK(trace_type IN ('tool_call','llm_call','error','handoff','custom')),
|
|
771
|
+
name TEXT,
|
|
772
|
+
input_summary TEXT,
|
|
773
|
+
output_summary TEXT,
|
|
774
|
+
duration_ms INTEGER,
|
|
775
|
+
tokens INTEGER,
|
|
776
|
+
cost_usd REAL,
|
|
777
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
778
|
+
);
|
|
779
|
+
CREATE INDEX IF NOT EXISTS idx_task_traces_task ON task_traces(task_id);
|
|
780
|
+
CREATE INDEX IF NOT EXISTS idx_task_traces_agent ON task_traces(agent_id);
|
|
781
|
+
|
|
782
|
+
CREATE TABLE IF NOT EXISTS context_snapshots (
|
|
783
|
+
id TEXT PRIMARY KEY,
|
|
784
|
+
agent_id TEXT,
|
|
785
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
786
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
787
|
+
snapshot_type TEXT NOT NULL CHECK(snapshot_type IN ('interrupt','complete','handoff','checkpoint')),
|
|
788
|
+
plan_summary TEXT,
|
|
789
|
+
files_open TEXT DEFAULT '[]',
|
|
790
|
+
attempts TEXT DEFAULT '[]',
|
|
791
|
+
blockers TEXT DEFAULT '[]',
|
|
792
|
+
next_steps TEXT,
|
|
793
|
+
metadata TEXT DEFAULT '{}',
|
|
794
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
795
|
+
);
|
|
796
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_agent ON context_snapshots(agent_id);
|
|
797
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_task ON context_snapshots(task_id);
|
|
798
|
+
|
|
799
|
+
CREATE TABLE IF NOT EXISTS agent_budgets (
|
|
800
|
+
agent_id TEXT PRIMARY KEY,
|
|
801
|
+
max_concurrent INTEGER DEFAULT 5,
|
|
802
|
+
max_cost_usd REAL,
|
|
803
|
+
max_task_minutes INTEGER,
|
|
804
|
+
period_hours INTEGER DEFAULT 24,
|
|
805
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
806
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
807
|
+
);
|
|
808
|
+
|
|
757
809
|
INSERT OR IGNORE INTO _migrations (id) VALUES (35);
|
|
758
810
|
`
|
|
759
811
|
];
|
|
@@ -936,6 +988,15 @@ function ensureSchema(db) {
|
|
|
936
988
|
ensureColumn("tasks", "assigned_by", "TEXT");
|
|
937
989
|
ensureColumn("tasks", "assigned_from_project", "TEXT");
|
|
938
990
|
ensureColumn("tasks", "started_at", "TEXT");
|
|
991
|
+
ensureColumn("tasks", "task_type", "TEXT");
|
|
992
|
+
ensureColumn("tasks", "cost_tokens", "INTEGER DEFAULT 0");
|
|
993
|
+
ensureColumn("tasks", "cost_usd", "REAL DEFAULT 0");
|
|
994
|
+
ensureColumn("tasks", "delegated_from", "TEXT");
|
|
995
|
+
ensureColumn("tasks", "delegation_depth", "INTEGER DEFAULT 0");
|
|
996
|
+
ensureColumn("tasks", "retry_count", "INTEGER DEFAULT 0");
|
|
997
|
+
ensureColumn("tasks", "max_retries", "INTEGER DEFAULT 3");
|
|
998
|
+
ensureColumn("tasks", "retry_after", "TEXT");
|
|
999
|
+
ensureColumn("tasks", "sla_minutes", "INTEGER");
|
|
939
1000
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
940
1001
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
941
1002
|
ensureColumn("agents", "reports_to", "TEXT");
|
|
@@ -2252,6 +2313,20 @@ function completeTask(id, agentId, db, options) {
|
|
|
2252
2313
|
if (spawnedFromTemplate) {
|
|
2253
2314
|
meta._spawned_task = { id: spawnedFromTemplate.id, short_id: spawnedFromTemplate.short_id, title: spawnedFromTemplate.title };
|
|
2254
2315
|
}
|
|
2316
|
+
const unblockedDeps = d.query(`SELECT DISTINCT t.id, t.short_id, t.title FROM tasks t
|
|
2317
|
+
JOIN task_dependencies td ON td.task_id = t.id
|
|
2318
|
+
WHERE td.depends_on = ? AND t.status = 'pending'
|
|
2319
|
+
AND NOT EXISTS (
|
|
2320
|
+
SELECT 1 FROM task_dependencies td2
|
|
2321
|
+
JOIN tasks dep2 ON dep2.id = td2.depends_on
|
|
2322
|
+
WHERE td2.task_id = t.id AND dep2.status NOT IN ('completed', 'cancelled') AND dep2.id != ?
|
|
2323
|
+
)`).all(id, id);
|
|
2324
|
+
if (unblockedDeps.length > 0) {
|
|
2325
|
+
meta._unblocked = unblockedDeps.map((d2) => ({ id: d2.id, short_id: d2.short_id, title: d2.title }));
|
|
2326
|
+
for (const dep of unblockedDeps) {
|
|
2327
|
+
dispatchWebhook("task.unblocked", { id: dep.id, unblocked_by: id, title: dep.title }, d).catch(() => {});
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2255
2330
|
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
2256
2331
|
}
|
|
2257
2332
|
function lockTask(id, agentId, db) {
|
|
@@ -2548,24 +2623,36 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
2548
2623
|
};
|
|
2549
2624
|
let retryTask;
|
|
2550
2625
|
if (options?.retry) {
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2626
|
+
const retryCount = (task.retry_count || 0) + 1;
|
|
2627
|
+
const maxRetries = task.max_retries || 3;
|
|
2628
|
+
if (retryCount > maxRetries) {
|
|
2629
|
+
d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [
|
|
2630
|
+
JSON.stringify({ ...meta, _retry_exhausted: { retry_count: retryCount - 1, max_retries: maxRetries } }),
|
|
2631
|
+
id
|
|
2632
|
+
]);
|
|
2633
|
+
} else {
|
|
2634
|
+
const backoffMinutes = Math.pow(5, retryCount - 1);
|
|
2635
|
+
const retryAfter = options.retry_after || new Date(Date.now() + backoffMinutes * 60 * 1000).toISOString();
|
|
2636
|
+
let title = task.title;
|
|
2637
|
+
if (task.short_id && title.startsWith(task.short_id + ": ")) {
|
|
2638
|
+
title = title.slice(task.short_id.length + 2);
|
|
2639
|
+
}
|
|
2640
|
+
retryTask = createTask({
|
|
2641
|
+
title,
|
|
2642
|
+
description: task.description ?? undefined,
|
|
2643
|
+
priority: task.priority,
|
|
2644
|
+
project_id: task.project_id ?? undefined,
|
|
2645
|
+
task_list_id: task.task_list_id ?? undefined,
|
|
2646
|
+
plan_id: task.plan_id ?? undefined,
|
|
2647
|
+
assigned_to: task.assigned_to ?? undefined,
|
|
2648
|
+
tags: task.tags,
|
|
2649
|
+
metadata: { ...task.metadata, _retry: { original_id: task.id, retry_count: retryCount, max_retries: maxRetries, retry_after: retryAfter, failure_reason: reason } },
|
|
2650
|
+
estimated_minutes: task.estimated_minutes ?? undefined,
|
|
2651
|
+
recurrence_rule: task.recurrence_rule ?? undefined,
|
|
2652
|
+
due_at: retryAfter
|
|
2653
|
+
}, d);
|
|
2654
|
+
d.run("UPDATE tasks SET retry_count = ?, max_retries = ?, retry_after = ? WHERE id = ?", [retryCount, maxRetries, retryAfter, retryTask.id]);
|
|
2554
2655
|
}
|
|
2555
|
-
retryTask = createTask({
|
|
2556
|
-
title,
|
|
2557
|
-
description: task.description ?? undefined,
|
|
2558
|
-
priority: task.priority,
|
|
2559
|
-
project_id: task.project_id ?? undefined,
|
|
2560
|
-
task_list_id: task.task_list_id ?? undefined,
|
|
2561
|
-
plan_id: task.plan_id ?? undefined,
|
|
2562
|
-
assigned_to: task.assigned_to ?? undefined,
|
|
2563
|
-
tags: task.tags,
|
|
2564
|
-
metadata: { ...task.metadata, _retry: { original_id: task.id, retry_after: options.retry_after || null, failure_reason: reason } },
|
|
2565
|
-
estimated_minutes: task.estimated_minutes ?? undefined,
|
|
2566
|
-
recurrence_rule: task.recurrence_rule ?? undefined,
|
|
2567
|
-
due_at: options.retry_after || task.due_at || undefined
|
|
2568
|
-
}, d);
|
|
2569
2656
|
}
|
|
2570
2657
|
return { task: failedTask, retryTask };
|
|
2571
2658
|
}
|
|
@@ -2589,6 +2676,40 @@ function getStaleTasks(staleMinutes = 30, filters, db) {
|
|
|
2589
2676
|
const rows = d.query(`SELECT * FROM tasks WHERE ${where} ORDER BY updated_at ASC`).all(...params);
|
|
2590
2677
|
return rows.map(rowToTask);
|
|
2591
2678
|
}
|
|
2679
|
+
function logCost(taskId, tokens, usd, db) {
|
|
2680
|
+
const d = db || getDatabase();
|
|
2681
|
+
d.run("UPDATE tasks SET cost_tokens = cost_tokens + ?, cost_usd = cost_usd + ?, updated_at = ? WHERE id = ?", [tokens, usd, now(), taskId]);
|
|
2682
|
+
}
|
|
2683
|
+
function stealTask(agentId, opts, db) {
|
|
2684
|
+
const d = db || getDatabase();
|
|
2685
|
+
const staleMinutes = opts?.stale_minutes ?? 30;
|
|
2686
|
+
const staleTasks = getStaleTasks(staleMinutes, { project_id: opts?.project_id, task_list_id: opts?.task_list_id }, d);
|
|
2687
|
+
if (staleTasks.length === 0)
|
|
2688
|
+
return null;
|
|
2689
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
2690
|
+
staleTasks.sort((a, b) => (priorityOrder[a.priority] ?? 9) - (priorityOrder[b.priority] ?? 9));
|
|
2691
|
+
const target = staleTasks[0];
|
|
2692
|
+
const timestamp = now();
|
|
2693
|
+
d.run(`UPDATE tasks SET assigned_to = ?, locked_by = ?, locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ?`, [agentId, agentId, timestamp, timestamp, target.id]);
|
|
2694
|
+
logTaskChange(target.id, "steal", "assigned_to", target.assigned_to, agentId, agentId, d);
|
|
2695
|
+
dispatchWebhook("task.assigned", { id: target.id, agent_id: agentId, title: target.title, stolen_from: target.assigned_to }, d).catch(() => {});
|
|
2696
|
+
return { ...target, assigned_to: agentId, locked_by: agentId, locked_at: timestamp, updated_at: timestamp, version: target.version + 1 };
|
|
2697
|
+
}
|
|
2698
|
+
function claimOrSteal(agentId, filters, db) {
|
|
2699
|
+
const d = db || getDatabase();
|
|
2700
|
+
const tx = d.transaction(() => {
|
|
2701
|
+
const next = getNextTask(agentId, filters, d);
|
|
2702
|
+
if (next) {
|
|
2703
|
+
const started = startTask(next.id, agentId, d);
|
|
2704
|
+
return { task: started, stolen: false };
|
|
2705
|
+
}
|
|
2706
|
+
const stolen = stealTask(agentId, { stale_minutes: filters?.stale_minutes, project_id: filters?.project_id, task_list_id: filters?.task_list_id }, d);
|
|
2707
|
+
if (stolen)
|
|
2708
|
+
return { task: stolen, stolen: true };
|
|
2709
|
+
return null;
|
|
2710
|
+
});
|
|
2711
|
+
return tx();
|
|
2712
|
+
}
|
|
2592
2713
|
function getStatus(filters, agentId, options, db) {
|
|
2593
2714
|
const d = db || getDatabase();
|
|
2594
2715
|
const pending = countTasks({ ...filters, status: "pending" }, d);
|
|
@@ -3330,6 +3451,123 @@ function deleteSession(id, db) {
|
|
|
3330
3451
|
const result = d.run("DELETE FROM sessions WHERE id = ?", [id]);
|
|
3331
3452
|
return result.changes > 0;
|
|
3332
3453
|
}
|
|
3454
|
+
// src/lib/gatherer.ts
|
|
3455
|
+
var SYSTEM_PROMPT = "You are a task management assistant that creates, updates, and tracks tasks and projects.";
|
|
3456
|
+
function taskToCreateExample(task) {
|
|
3457
|
+
const userMsg = `Create a task: ${task.title}${task.description ? `
|
|
3458
|
+
|
|
3459
|
+
Description: ${task.description}` : ""}`;
|
|
3460
|
+
const taskDetails = {
|
|
3461
|
+
id: task.short_id ?? task.id,
|
|
3462
|
+
title: task.title,
|
|
3463
|
+
description: task.description ?? "",
|
|
3464
|
+
status: task.status,
|
|
3465
|
+
priority: task.priority,
|
|
3466
|
+
tags: task.tags,
|
|
3467
|
+
created_at: task.created_at
|
|
3468
|
+
};
|
|
3469
|
+
return {
|
|
3470
|
+
messages: [
|
|
3471
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
3472
|
+
{ role: "user", content: userMsg },
|
|
3473
|
+
{
|
|
3474
|
+
role: "assistant",
|
|
3475
|
+
content: `Created task: ${JSON.stringify(taskDetails, null, 2)}`
|
|
3476
|
+
}
|
|
3477
|
+
]
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
function taskToStatusUpdateExample(task) {
|
|
3481
|
+
if (!task.completed_at && task.status === "pending")
|
|
3482
|
+
return null;
|
|
3483
|
+
const id = task.short_id ?? task.id;
|
|
3484
|
+
return {
|
|
3485
|
+
messages: [
|
|
3486
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
3487
|
+
{ role: "user", content: `Mark task ${id} as ${task.status}` },
|
|
3488
|
+
{
|
|
3489
|
+
role: "assistant",
|
|
3490
|
+
content: `Task ${id} has been updated to status: ${task.status}. ${task.completed_at ? `Completed at: ${task.completed_at}` : ""}`.trim()
|
|
3491
|
+
}
|
|
3492
|
+
]
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
function taskToSearchExample(tasks, query) {
|
|
3496
|
+
const matched = tasks.filter((t) => t.title.toLowerCase().includes(query.toLowerCase())).slice(0, 5);
|
|
3497
|
+
return {
|
|
3498
|
+
messages: [
|
|
3499
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
3500
|
+
{ role: "user", content: `Search tasks for: "${query}"` },
|
|
3501
|
+
{
|
|
3502
|
+
role: "assistant",
|
|
3503
|
+
content: matched.length > 0 ? `Found ${matched.length} task(s):
|
|
3504
|
+
${matched.map((t) => `- [${t.short_id ?? t.id}] ${t.title} (${t.status})`).join(`
|
|
3505
|
+
`)}` : `No tasks found matching "${query}".`
|
|
3506
|
+
}
|
|
3507
|
+
]
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
var gatherTrainingData = async (options = {}) => {
|
|
3511
|
+
const allTasks = listTasks({});
|
|
3512
|
+
const filtered = options.since ? allTasks.filter((t) => new Date(t.created_at) >= options.since) : allTasks;
|
|
3513
|
+
const sorted = filtered.slice().sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
3514
|
+
const fetchSet = options.limit ? sorted.slice(0, options.limit * 2) : sorted;
|
|
3515
|
+
const examples = [];
|
|
3516
|
+
for (const task of fetchSet) {
|
|
3517
|
+
examples.push(taskToCreateExample(task));
|
|
3518
|
+
const statusEx = taskToStatusUpdateExample(task);
|
|
3519
|
+
if (statusEx)
|
|
3520
|
+
examples.push(statusEx);
|
|
3521
|
+
}
|
|
3522
|
+
const searchTerms = ["urgent", "fix", "implement", "create", "update", "review"];
|
|
3523
|
+
for (const term of searchTerms) {
|
|
3524
|
+
examples.push(taskToSearchExample(sorted, term));
|
|
3525
|
+
}
|
|
3526
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
3527
|
+
return {
|
|
3528
|
+
source: "todos",
|
|
3529
|
+
examples: finalExamples,
|
|
3530
|
+
count: finalExamples.length
|
|
3531
|
+
};
|
|
3532
|
+
};
|
|
3533
|
+
// src/lib/model-config.ts
|
|
3534
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3535
|
+
import { homedir } from "os";
|
|
3536
|
+
import { join as join4 } from "path";
|
|
3537
|
+
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
3538
|
+
var CONFIG_DIR = join4(homedir(), ".todos");
|
|
3539
|
+
var CONFIG_PATH = join4(CONFIG_DIR, "config.json");
|
|
3540
|
+
function readConfig() {
|
|
3541
|
+
if (!existsSync4(CONFIG_PATH))
|
|
3542
|
+
return {};
|
|
3543
|
+
try {
|
|
3544
|
+
const raw = readFileSync2(CONFIG_PATH, "utf-8");
|
|
3545
|
+
return JSON.parse(raw);
|
|
3546
|
+
} catch {
|
|
3547
|
+
return {};
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
function writeConfig(config) {
|
|
3551
|
+
if (!existsSync4(CONFIG_DIR)) {
|
|
3552
|
+
mkdirSync3(CONFIG_DIR, { recursive: true });
|
|
3553
|
+
}
|
|
3554
|
+
writeFileSync2(CONFIG_PATH, JSON.stringify(config, null, 2) + `
|
|
3555
|
+
`, "utf-8");
|
|
3556
|
+
}
|
|
3557
|
+
function getActiveModel() {
|
|
3558
|
+
const config = readConfig();
|
|
3559
|
+
return config.activeModel ?? DEFAULT_MODEL;
|
|
3560
|
+
}
|
|
3561
|
+
function setActiveModel(modelId) {
|
|
3562
|
+
const config = readConfig();
|
|
3563
|
+
config.activeModel = modelId;
|
|
3564
|
+
writeConfig(config);
|
|
3565
|
+
}
|
|
3566
|
+
function clearActiveModel() {
|
|
3567
|
+
const config = readConfig();
|
|
3568
|
+
delete config.activeModel;
|
|
3569
|
+
writeConfig(config);
|
|
3570
|
+
}
|
|
3333
3571
|
// src/db/handoffs.ts
|
|
3334
3572
|
function createHandoff(input, db) {
|
|
3335
3573
|
const d = db || getDatabase();
|
|
@@ -4161,16 +4399,16 @@ function searchTasks(options, projectId, taskListId, db) {
|
|
|
4161
4399
|
return rows.map(rowToTask3);
|
|
4162
4400
|
}
|
|
4163
4401
|
// src/lib/claude-tasks.ts
|
|
4164
|
-
import { existsSync as
|
|
4165
|
-
import { join as
|
|
4402
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
4403
|
+
import { join as join5 } from "path";
|
|
4166
4404
|
function getTaskListDir(taskListId) {
|
|
4167
|
-
return
|
|
4405
|
+
return join5(HOME, ".claude", "tasks", taskListId);
|
|
4168
4406
|
}
|
|
4169
4407
|
function readClaudeTask(dir, filename) {
|
|
4170
|
-
return readJsonFile(
|
|
4408
|
+
return readJsonFile(join5(dir, filename));
|
|
4171
4409
|
}
|
|
4172
4410
|
function writeClaudeTask(dir, task) {
|
|
4173
|
-
writeJsonFile(
|
|
4411
|
+
writeJsonFile(join5(dir, `${task.id}.json`), task);
|
|
4174
4412
|
}
|
|
4175
4413
|
function toClaudeStatus(status) {
|
|
4176
4414
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -4182,14 +4420,14 @@ function toSqliteStatus(status) {
|
|
|
4182
4420
|
return status;
|
|
4183
4421
|
}
|
|
4184
4422
|
function readPrefixCounter(dir) {
|
|
4185
|
-
const path =
|
|
4186
|
-
if (!
|
|
4423
|
+
const path = join5(dir, ".prefix-counter");
|
|
4424
|
+
if (!existsSync5(path))
|
|
4187
4425
|
return 0;
|
|
4188
|
-
const val = parseInt(
|
|
4426
|
+
const val = parseInt(readFileSync3(path, "utf-8").trim(), 10);
|
|
4189
4427
|
return isNaN(val) ? 0 : val;
|
|
4190
4428
|
}
|
|
4191
4429
|
function writePrefixCounter(dir, value) {
|
|
4192
|
-
|
|
4430
|
+
writeFileSync3(join5(dir, ".prefix-counter"), String(value));
|
|
4193
4431
|
}
|
|
4194
4432
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
4195
4433
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -4216,7 +4454,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
|
|
|
4216
4454
|
}
|
|
4217
4455
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
4218
4456
|
const dir = getTaskListDir(taskListId);
|
|
4219
|
-
if (!
|
|
4457
|
+
if (!existsSync5(dir))
|
|
4220
4458
|
ensureDir2(dir);
|
|
4221
4459
|
const filter = {};
|
|
4222
4460
|
if (projectId)
|
|
@@ -4225,7 +4463,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
4225
4463
|
const existingByTodosId = new Map;
|
|
4226
4464
|
const files = listJsonFiles(dir);
|
|
4227
4465
|
for (const f of files) {
|
|
4228
|
-
const path =
|
|
4466
|
+
const path = join5(dir, f);
|
|
4229
4467
|
const ct = readClaudeTask(dir, f);
|
|
4230
4468
|
if (ct?.metadata?.["todos_id"]) {
|
|
4231
4469
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -4312,7 +4550,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
4312
4550
|
}
|
|
4313
4551
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
4314
4552
|
const dir = getTaskListDir(taskListId);
|
|
4315
|
-
if (!
|
|
4553
|
+
if (!existsSync5(dir)) {
|
|
4316
4554
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
4317
4555
|
}
|
|
4318
4556
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -4332,7 +4570,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
4332
4570
|
}
|
|
4333
4571
|
for (const f of files) {
|
|
4334
4572
|
try {
|
|
4335
|
-
const filePath =
|
|
4573
|
+
const filePath = join5(dir, f);
|
|
4336
4574
|
const ct = readClaudeTask(dir, f);
|
|
4337
4575
|
if (!ct)
|
|
4338
4576
|
continue;
|
|
@@ -4400,20 +4638,20 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
4400
4638
|
}
|
|
4401
4639
|
|
|
4402
4640
|
// src/lib/agent-tasks.ts
|
|
4403
|
-
import { existsSync as
|
|
4404
|
-
import { join as
|
|
4641
|
+
import { existsSync as existsSync6 } from "fs";
|
|
4642
|
+
import { join as join6 } from "path";
|
|
4405
4643
|
function agentBaseDir(agent) {
|
|
4406
4644
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
4407
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
4645
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join6(HOME, ".todos", "agents");
|
|
4408
4646
|
}
|
|
4409
4647
|
function getTaskListDir2(agent, taskListId) {
|
|
4410
|
-
return
|
|
4648
|
+
return join6(agentBaseDir(agent), agent, taskListId);
|
|
4411
4649
|
}
|
|
4412
4650
|
function readAgentTask(dir, filename) {
|
|
4413
|
-
return readJsonFile(
|
|
4651
|
+
return readJsonFile(join6(dir, filename));
|
|
4414
4652
|
}
|
|
4415
4653
|
function writeAgentTask(dir, task) {
|
|
4416
|
-
writeJsonFile(
|
|
4654
|
+
writeJsonFile(join6(dir, `${task.id}.json`), task);
|
|
4417
4655
|
}
|
|
4418
4656
|
function taskToAgentTask(task, externalId, existingMeta) {
|
|
4419
4657
|
return {
|
|
@@ -4438,7 +4676,7 @@ function metadataKey(agent) {
|
|
|
4438
4676
|
}
|
|
4439
4677
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
4440
4678
|
const dir = getTaskListDir2(agent, taskListId);
|
|
4441
|
-
if (!
|
|
4679
|
+
if (!existsSync6(dir))
|
|
4442
4680
|
ensureDir2(dir);
|
|
4443
4681
|
const filter = {};
|
|
4444
4682
|
if (projectId)
|
|
@@ -4447,7 +4685,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
4447
4685
|
const existingByTodosId = new Map;
|
|
4448
4686
|
const files = listJsonFiles(dir);
|
|
4449
4687
|
for (const f of files) {
|
|
4450
|
-
const path =
|
|
4688
|
+
const path = join6(dir, f);
|
|
4451
4689
|
const at = readAgentTask(dir, f);
|
|
4452
4690
|
if (at?.metadata?.["todos_id"]) {
|
|
4453
4691
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -4521,7 +4759,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
4521
4759
|
}
|
|
4522
4760
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
4523
4761
|
const dir = getTaskListDir2(agent, taskListId);
|
|
4524
|
-
if (!
|
|
4762
|
+
if (!existsSync6(dir)) {
|
|
4525
4763
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
4526
4764
|
}
|
|
4527
4765
|
const files = listJsonFiles(dir);
|
|
@@ -4540,7 +4778,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
4540
4778
|
}
|
|
4541
4779
|
for (const f of files) {
|
|
4542
4780
|
try {
|
|
4543
|
-
const filePath =
|
|
4781
|
+
const filePath = join6(dir, f);
|
|
4544
4782
|
const at = readAgentTask(dir, f);
|
|
4545
4783
|
if (!at)
|
|
4546
4784
|
continue;
|
|
@@ -4677,8 +4915,8 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
4677
4915
|
return { pushed, pulled, errors };
|
|
4678
4916
|
}
|
|
4679
4917
|
// src/lib/extract.ts
|
|
4680
|
-
import { readFileSync as
|
|
4681
|
-
import { relative, resolve as resolve2, join as
|
|
4918
|
+
import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
4919
|
+
import { relative, resolve as resolve2, join as join7 } from "path";
|
|
4682
4920
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
4683
4921
|
var DEFAULT_EXTENSIONS = new Set([
|
|
4684
4922
|
".ts",
|
|
@@ -4812,9 +5050,9 @@ function extractTodos(options, db) {
|
|
|
4812
5050
|
const files = collectFiles(basePath, extensions);
|
|
4813
5051
|
const allComments = [];
|
|
4814
5052
|
for (const file of files) {
|
|
4815
|
-
const fullPath = statSync2(basePath).isFile() ? basePath :
|
|
5053
|
+
const fullPath = statSync2(basePath).isFile() ? basePath : join7(basePath, file);
|
|
4816
5054
|
try {
|
|
4817
|
-
const source =
|
|
5055
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
4818
5056
|
const relPath = statSync2(basePath).isFile() ? relative(resolve2(basePath, ".."), fullPath) : file;
|
|
4819
5057
|
const comments = extractFromSource(source, relPath, tags);
|
|
4820
5058
|
allComments.push(...comments);
|
|
@@ -4998,6 +5236,188 @@ function issueToTask(issue, opts) {
|
|
|
4998
5236
|
agent_id: opts?.agent_id
|
|
4999
5237
|
};
|
|
5000
5238
|
}
|
|
5239
|
+
// src/db/traces.ts
|
|
5240
|
+
function logTrace(input, db) {
|
|
5241
|
+
const d = db || getDatabase();
|
|
5242
|
+
const id = uuid();
|
|
5243
|
+
const timestamp = now();
|
|
5244
|
+
d.run(`INSERT INTO task_traces (id, task_id, agent_id, trace_type, name, input_summary, output_summary, duration_ms, tokens, cost_usd, created_at)
|
|
5245
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5246
|
+
id,
|
|
5247
|
+
input.task_id,
|
|
5248
|
+
input.agent_id || null,
|
|
5249
|
+
input.trace_type,
|
|
5250
|
+
input.name || null,
|
|
5251
|
+
input.input_summary || null,
|
|
5252
|
+
input.output_summary || null,
|
|
5253
|
+
input.duration_ms ?? null,
|
|
5254
|
+
input.tokens ?? null,
|
|
5255
|
+
input.cost_usd ?? null,
|
|
5256
|
+
timestamp
|
|
5257
|
+
]);
|
|
5258
|
+
return {
|
|
5259
|
+
id,
|
|
5260
|
+
task_id: input.task_id,
|
|
5261
|
+
agent_id: input.agent_id || null,
|
|
5262
|
+
trace_type: input.trace_type,
|
|
5263
|
+
name: input.name || null,
|
|
5264
|
+
input_summary: input.input_summary || null,
|
|
5265
|
+
output_summary: input.output_summary || null,
|
|
5266
|
+
duration_ms: input.duration_ms ?? null,
|
|
5267
|
+
tokens: input.tokens ?? null,
|
|
5268
|
+
cost_usd: input.cost_usd ?? null,
|
|
5269
|
+
created_at: timestamp
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5272
|
+
function getTaskTraces(taskId, db) {
|
|
5273
|
+
const d = db || getDatabase();
|
|
5274
|
+
return d.query("SELECT * FROM task_traces WHERE task_id = ? ORDER BY created_at DESC").all(taskId);
|
|
5275
|
+
}
|
|
5276
|
+
function getTraceStats(taskId, db) {
|
|
5277
|
+
const d = db || getDatabase();
|
|
5278
|
+
const row = d.query(`SELECT COUNT(*) as total,
|
|
5279
|
+
SUM(CASE WHEN trace_type = 'tool_call' THEN 1 ELSE 0 END) as tool_calls,
|
|
5280
|
+
SUM(CASE WHEN trace_type = 'llm_call' THEN 1 ELSE 0 END) as llm_calls,
|
|
5281
|
+
SUM(CASE WHEN trace_type = 'error' THEN 1 ELSE 0 END) as errors,
|
|
5282
|
+
COALESCE(SUM(tokens), 0) as total_tokens,
|
|
5283
|
+
COALESCE(SUM(cost_usd), 0) as total_cost_usd,
|
|
5284
|
+
COALESCE(SUM(duration_ms), 0) as total_duration_ms
|
|
5285
|
+
FROM task_traces WHERE task_id = ?`).get(taskId);
|
|
5286
|
+
return row;
|
|
5287
|
+
}
|
|
5288
|
+
// src/db/snapshots.ts
|
|
5289
|
+
function rowToSnapshot(row) {
|
|
5290
|
+
return {
|
|
5291
|
+
...row,
|
|
5292
|
+
snapshot_type: row.snapshot_type,
|
|
5293
|
+
files_open: JSON.parse(row.files_open || "[]"),
|
|
5294
|
+
attempts: JSON.parse(row.attempts || "[]"),
|
|
5295
|
+
blockers: JSON.parse(row.blockers || "[]"),
|
|
5296
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
5297
|
+
};
|
|
5298
|
+
}
|
|
5299
|
+
function saveSnapshot(input, db) {
|
|
5300
|
+
const d = db || getDatabase();
|
|
5301
|
+
const id = uuid();
|
|
5302
|
+
const timestamp = now();
|
|
5303
|
+
d.run(`INSERT INTO context_snapshots (id, agent_id, task_id, project_id, snapshot_type, plan_summary, files_open, attempts, blockers, next_steps, metadata, created_at)
|
|
5304
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5305
|
+
id,
|
|
5306
|
+
input.agent_id || null,
|
|
5307
|
+
input.task_id || null,
|
|
5308
|
+
input.project_id || null,
|
|
5309
|
+
input.snapshot_type,
|
|
5310
|
+
input.plan_summary || null,
|
|
5311
|
+
JSON.stringify(input.files_open || []),
|
|
5312
|
+
JSON.stringify(input.attempts || []),
|
|
5313
|
+
JSON.stringify(input.blockers || []),
|
|
5314
|
+
input.next_steps || null,
|
|
5315
|
+
JSON.stringify(input.metadata || {}),
|
|
5316
|
+
timestamp
|
|
5317
|
+
]);
|
|
5318
|
+
return {
|
|
5319
|
+
id,
|
|
5320
|
+
agent_id: input.agent_id || null,
|
|
5321
|
+
task_id: input.task_id || null,
|
|
5322
|
+
project_id: input.project_id || null,
|
|
5323
|
+
snapshot_type: input.snapshot_type,
|
|
5324
|
+
plan_summary: input.plan_summary || null,
|
|
5325
|
+
files_open: input.files_open || [],
|
|
5326
|
+
attempts: input.attempts || [],
|
|
5327
|
+
blockers: input.blockers || [],
|
|
5328
|
+
next_steps: input.next_steps || null,
|
|
5329
|
+
metadata: input.metadata || {},
|
|
5330
|
+
created_at: timestamp
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5333
|
+
function getLatestSnapshot(agentId, taskId, db) {
|
|
5334
|
+
const d = db || getDatabase();
|
|
5335
|
+
const conditions = [];
|
|
5336
|
+
const params = [];
|
|
5337
|
+
if (agentId) {
|
|
5338
|
+
conditions.push("agent_id = ?");
|
|
5339
|
+
params.push(agentId);
|
|
5340
|
+
}
|
|
5341
|
+
if (taskId) {
|
|
5342
|
+
conditions.push("task_id = ?");
|
|
5343
|
+
params.push(taskId);
|
|
5344
|
+
}
|
|
5345
|
+
if (conditions.length === 0)
|
|
5346
|
+
return null;
|
|
5347
|
+
const where = conditions.join(" AND ");
|
|
5348
|
+
const row = d.query(`SELECT * FROM context_snapshots WHERE ${where} ORDER BY created_at DESC LIMIT 1`).get(...params);
|
|
5349
|
+
return row ? rowToSnapshot(row) : null;
|
|
5350
|
+
}
|
|
5351
|
+
function listSnapshots(opts, db) {
|
|
5352
|
+
const d = db || getDatabase();
|
|
5353
|
+
const conditions = [];
|
|
5354
|
+
const params = [];
|
|
5355
|
+
if (opts.agent_id) {
|
|
5356
|
+
conditions.push("agent_id = ?");
|
|
5357
|
+
params.push(opts.agent_id);
|
|
5358
|
+
}
|
|
5359
|
+
if (opts.task_id) {
|
|
5360
|
+
conditions.push("task_id = ?");
|
|
5361
|
+
params.push(opts.task_id);
|
|
5362
|
+
}
|
|
5363
|
+
if (opts.project_id) {
|
|
5364
|
+
conditions.push("project_id = ?");
|
|
5365
|
+
params.push(opts.project_id);
|
|
5366
|
+
}
|
|
5367
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
5368
|
+
const limit = opts.limit || 20;
|
|
5369
|
+
params.push(limit);
|
|
5370
|
+
return d.query(`SELECT * FROM context_snapshots ${where} ORDER BY created_at DESC LIMIT ?`).all(...params).map(rowToSnapshot);
|
|
5371
|
+
}
|
|
5372
|
+
// src/db/budgets.ts
|
|
5373
|
+
function setBudget(agentId, opts, db) {
|
|
5374
|
+
const d = db || getDatabase();
|
|
5375
|
+
const timestamp = now();
|
|
5376
|
+
d.run(`INSERT INTO agent_budgets (agent_id, max_concurrent, max_cost_usd, max_task_minutes, period_hours, created_at, updated_at)
|
|
5377
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
5378
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
5379
|
+
max_concurrent = COALESCE(?, max_concurrent),
|
|
5380
|
+
max_cost_usd = COALESCE(?, max_cost_usd),
|
|
5381
|
+
max_task_minutes = COALESCE(?, max_task_minutes),
|
|
5382
|
+
period_hours = COALESCE(?, period_hours),
|
|
5383
|
+
updated_at = ?`, [
|
|
5384
|
+
agentId,
|
|
5385
|
+
opts.max_concurrent ?? 5,
|
|
5386
|
+
opts.max_cost_usd ?? null,
|
|
5387
|
+
opts.max_task_minutes ?? null,
|
|
5388
|
+
opts.period_hours ?? 24,
|
|
5389
|
+
timestamp,
|
|
5390
|
+
timestamp,
|
|
5391
|
+
opts.max_concurrent ?? null,
|
|
5392
|
+
opts.max_cost_usd ?? null,
|
|
5393
|
+
opts.max_task_minutes ?? null,
|
|
5394
|
+
opts.period_hours ?? null,
|
|
5395
|
+
timestamp
|
|
5396
|
+
]);
|
|
5397
|
+
return getBudget(agentId, d);
|
|
5398
|
+
}
|
|
5399
|
+
function getBudget(agentId, db) {
|
|
5400
|
+
const d = db || getDatabase();
|
|
5401
|
+
return d.query("SELECT * FROM agent_budgets WHERE agent_id = ?").get(agentId);
|
|
5402
|
+
}
|
|
5403
|
+
function checkBudget(agentId, db) {
|
|
5404
|
+
const d = db || getDatabase();
|
|
5405
|
+
const budget = getBudget(agentId, d);
|
|
5406
|
+
if (!budget)
|
|
5407
|
+
return { allowed: true, current_concurrent: 0, max_concurrent: 999 };
|
|
5408
|
+
const concurrent = countTasks({ status: "in_progress", assigned_to: agentId }, d);
|
|
5409
|
+
if (concurrent >= budget.max_concurrent) {
|
|
5410
|
+
return { allowed: false, reason: `Concurrent limit reached (${concurrent}/${budget.max_concurrent})`, current_concurrent: concurrent, max_concurrent: budget.max_concurrent };
|
|
5411
|
+
}
|
|
5412
|
+
if (budget.max_cost_usd != null) {
|
|
5413
|
+
const periodStart = new Date(Date.now() - budget.period_hours * 60 * 60 * 1000).toISOString();
|
|
5414
|
+
const costRow = d.query("SELECT COALESCE(SUM(cost_usd), 0) as total FROM tasks WHERE (assigned_to = ? OR agent_id = ?) AND updated_at > ?").get(agentId, agentId, periodStart);
|
|
5415
|
+
if (costRow.total >= budget.max_cost_usd) {
|
|
5416
|
+
return { allowed: false, reason: `Cost limit reached ($${costRow.total.toFixed(2)}/$${budget.max_cost_usd.toFixed(2)} in ${budget.period_hours}h)`, current_concurrent: concurrent, max_concurrent: budget.max_concurrent, current_cost_usd: costRow.total, max_cost_usd: budget.max_cost_usd };
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
return { allowed: true, current_concurrent: concurrent, max_concurrent: budget.max_concurrent };
|
|
5420
|
+
}
|
|
5001
5421
|
export {
|
|
5002
5422
|
uuid,
|
|
5003
5423
|
updateTaskList,
|
|
@@ -5017,12 +5437,16 @@ export {
|
|
|
5017
5437
|
syncWithAgents,
|
|
5018
5438
|
syncWithAgent,
|
|
5019
5439
|
syncKgEdges,
|
|
5440
|
+
stealTask,
|
|
5020
5441
|
startTask,
|
|
5021
5442
|
slugify,
|
|
5022
5443
|
setTaskStatus,
|
|
5023
5444
|
setTaskPriority,
|
|
5445
|
+
setBudget,
|
|
5446
|
+
setActiveModel,
|
|
5024
5447
|
searchTasks,
|
|
5025
5448
|
scoreTask,
|
|
5449
|
+
saveSnapshot,
|
|
5026
5450
|
resolvePartialId,
|
|
5027
5451
|
resetDatabase,
|
|
5028
5452
|
removeTaskRelationshipByPair,
|
|
@@ -5044,8 +5468,10 @@ export {
|
|
|
5044
5468
|
nextOccurrence,
|
|
5045
5469
|
moveTask,
|
|
5046
5470
|
matchCapabilities,
|
|
5471
|
+
logTrace,
|
|
5047
5472
|
logTaskChange,
|
|
5048
5473
|
logProgress,
|
|
5474
|
+
logCost,
|
|
5049
5475
|
lockTask,
|
|
5050
5476
|
loadConfig,
|
|
5051
5477
|
listWebhooks,
|
|
@@ -5053,6 +5479,7 @@ export {
|
|
|
5053
5479
|
listTasks,
|
|
5054
5480
|
listTaskLists,
|
|
5055
5481
|
listTaskFiles,
|
|
5482
|
+
listSnapshots,
|
|
5056
5483
|
listSessions,
|
|
5057
5484
|
listProjects,
|
|
5058
5485
|
listProjectSources,
|
|
@@ -5065,9 +5492,11 @@ export {
|
|
|
5065
5492
|
isValidRecurrenceRule,
|
|
5066
5493
|
isAgentConflict,
|
|
5067
5494
|
getWebhook,
|
|
5495
|
+
getTraceStats,
|
|
5068
5496
|
getTemplate,
|
|
5069
5497
|
getTasksChangedSince,
|
|
5070
5498
|
getTaskWithRelations,
|
|
5499
|
+
getTaskTraces,
|
|
5071
5500
|
getTaskStats,
|
|
5072
5501
|
getTaskRelationships,
|
|
5073
5502
|
getTaskRelationship,
|
|
@@ -5096,6 +5525,7 @@ export {
|
|
|
5096
5525
|
getOrg,
|
|
5097
5526
|
getNextTask,
|
|
5098
5527
|
getLeaderboard,
|
|
5528
|
+
getLatestSnapshot,
|
|
5099
5529
|
getLatestHandoff,
|
|
5100
5530
|
getImpactAnalysis,
|
|
5101
5531
|
getDirectReports,
|
|
@@ -5107,11 +5537,14 @@ export {
|
|
|
5107
5537
|
getChecklist,
|
|
5108
5538
|
getCapableAgents,
|
|
5109
5539
|
getBurndown,
|
|
5540
|
+
getBudget,
|
|
5110
5541
|
getBlockingDeps,
|
|
5111
5542
|
getAgentMetrics,
|
|
5112
5543
|
getAgentByName,
|
|
5113
5544
|
getAgent,
|
|
5114
5545
|
getActiveWork,
|
|
5546
|
+
getActiveModel,
|
|
5547
|
+
gatherTrainingData,
|
|
5115
5548
|
findTasksByFile,
|
|
5116
5549
|
findRelatedTaskIds,
|
|
5117
5550
|
findPath,
|
|
@@ -5149,11 +5582,14 @@ export {
|
|
|
5149
5582
|
closeDatabase,
|
|
5150
5583
|
cloneTask,
|
|
5151
5584
|
clearChecklist,
|
|
5585
|
+
clearActiveModel,
|
|
5152
5586
|
cleanExpiredLocks,
|
|
5587
|
+
claimOrSteal,
|
|
5153
5588
|
claimNextTask,
|
|
5154
5589
|
checkLock,
|
|
5155
5590
|
checkCompletionGuard,
|
|
5156
5591
|
checkChecklistItem,
|
|
5592
|
+
checkBudget,
|
|
5157
5593
|
bulkUpdateTasks,
|
|
5158
5594
|
bulkCreateTasks,
|
|
5159
5595
|
bulkAddTaskFiles,
|
|
@@ -5181,6 +5617,7 @@ export {
|
|
|
5181
5617
|
LockError,
|
|
5182
5618
|
EXTRACT_TAGS,
|
|
5183
5619
|
DependencyCycleError,
|
|
5620
|
+
DEFAULT_MODEL,
|
|
5184
5621
|
CompletionGuardError,
|
|
5185
5622
|
AgentNotFoundError
|
|
5186
5623
|
};
|