@hasna/todos 0.10.21 → 0.11.1
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/index.js +613 -47
- 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 +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +332 -17
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +568 -35
- package/dist/server/index.js +157 -22
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -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);
|
|
@@ -4998,6 +5119,188 @@ function issueToTask(issue, opts) {
|
|
|
4998
5119
|
agent_id: opts?.agent_id
|
|
4999
5120
|
};
|
|
5000
5121
|
}
|
|
5122
|
+
// src/db/traces.ts
|
|
5123
|
+
function logTrace(input, db) {
|
|
5124
|
+
const d = db || getDatabase();
|
|
5125
|
+
const id = uuid();
|
|
5126
|
+
const timestamp = now();
|
|
5127
|
+
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)
|
|
5128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5129
|
+
id,
|
|
5130
|
+
input.task_id,
|
|
5131
|
+
input.agent_id || null,
|
|
5132
|
+
input.trace_type,
|
|
5133
|
+
input.name || null,
|
|
5134
|
+
input.input_summary || null,
|
|
5135
|
+
input.output_summary || null,
|
|
5136
|
+
input.duration_ms ?? null,
|
|
5137
|
+
input.tokens ?? null,
|
|
5138
|
+
input.cost_usd ?? null,
|
|
5139
|
+
timestamp
|
|
5140
|
+
]);
|
|
5141
|
+
return {
|
|
5142
|
+
id,
|
|
5143
|
+
task_id: input.task_id,
|
|
5144
|
+
agent_id: input.agent_id || null,
|
|
5145
|
+
trace_type: input.trace_type,
|
|
5146
|
+
name: input.name || null,
|
|
5147
|
+
input_summary: input.input_summary || null,
|
|
5148
|
+
output_summary: input.output_summary || null,
|
|
5149
|
+
duration_ms: input.duration_ms ?? null,
|
|
5150
|
+
tokens: input.tokens ?? null,
|
|
5151
|
+
cost_usd: input.cost_usd ?? null,
|
|
5152
|
+
created_at: timestamp
|
|
5153
|
+
};
|
|
5154
|
+
}
|
|
5155
|
+
function getTaskTraces(taskId, db) {
|
|
5156
|
+
const d = db || getDatabase();
|
|
5157
|
+
return d.query("SELECT * FROM task_traces WHERE task_id = ? ORDER BY created_at DESC").all(taskId);
|
|
5158
|
+
}
|
|
5159
|
+
function getTraceStats(taskId, db) {
|
|
5160
|
+
const d = db || getDatabase();
|
|
5161
|
+
const row = d.query(`SELECT COUNT(*) as total,
|
|
5162
|
+
SUM(CASE WHEN trace_type = 'tool_call' THEN 1 ELSE 0 END) as tool_calls,
|
|
5163
|
+
SUM(CASE WHEN trace_type = 'llm_call' THEN 1 ELSE 0 END) as llm_calls,
|
|
5164
|
+
SUM(CASE WHEN trace_type = 'error' THEN 1 ELSE 0 END) as errors,
|
|
5165
|
+
COALESCE(SUM(tokens), 0) as total_tokens,
|
|
5166
|
+
COALESCE(SUM(cost_usd), 0) as total_cost_usd,
|
|
5167
|
+
COALESCE(SUM(duration_ms), 0) as total_duration_ms
|
|
5168
|
+
FROM task_traces WHERE task_id = ?`).get(taskId);
|
|
5169
|
+
return row;
|
|
5170
|
+
}
|
|
5171
|
+
// src/db/snapshots.ts
|
|
5172
|
+
function rowToSnapshot(row) {
|
|
5173
|
+
return {
|
|
5174
|
+
...row,
|
|
5175
|
+
snapshot_type: row.snapshot_type,
|
|
5176
|
+
files_open: JSON.parse(row.files_open || "[]"),
|
|
5177
|
+
attempts: JSON.parse(row.attempts || "[]"),
|
|
5178
|
+
blockers: JSON.parse(row.blockers || "[]"),
|
|
5179
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
function saveSnapshot(input, db) {
|
|
5183
|
+
const d = db || getDatabase();
|
|
5184
|
+
const id = uuid();
|
|
5185
|
+
const timestamp = now();
|
|
5186
|
+
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)
|
|
5187
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5188
|
+
id,
|
|
5189
|
+
input.agent_id || null,
|
|
5190
|
+
input.task_id || null,
|
|
5191
|
+
input.project_id || null,
|
|
5192
|
+
input.snapshot_type,
|
|
5193
|
+
input.plan_summary || null,
|
|
5194
|
+
JSON.stringify(input.files_open || []),
|
|
5195
|
+
JSON.stringify(input.attempts || []),
|
|
5196
|
+
JSON.stringify(input.blockers || []),
|
|
5197
|
+
input.next_steps || null,
|
|
5198
|
+
JSON.stringify(input.metadata || {}),
|
|
5199
|
+
timestamp
|
|
5200
|
+
]);
|
|
5201
|
+
return {
|
|
5202
|
+
id,
|
|
5203
|
+
agent_id: input.agent_id || null,
|
|
5204
|
+
task_id: input.task_id || null,
|
|
5205
|
+
project_id: input.project_id || null,
|
|
5206
|
+
snapshot_type: input.snapshot_type,
|
|
5207
|
+
plan_summary: input.plan_summary || null,
|
|
5208
|
+
files_open: input.files_open || [],
|
|
5209
|
+
attempts: input.attempts || [],
|
|
5210
|
+
blockers: input.blockers || [],
|
|
5211
|
+
next_steps: input.next_steps || null,
|
|
5212
|
+
metadata: input.metadata || {},
|
|
5213
|
+
created_at: timestamp
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
function getLatestSnapshot(agentId, taskId, db) {
|
|
5217
|
+
const d = db || getDatabase();
|
|
5218
|
+
const conditions = [];
|
|
5219
|
+
const params = [];
|
|
5220
|
+
if (agentId) {
|
|
5221
|
+
conditions.push("agent_id = ?");
|
|
5222
|
+
params.push(agentId);
|
|
5223
|
+
}
|
|
5224
|
+
if (taskId) {
|
|
5225
|
+
conditions.push("task_id = ?");
|
|
5226
|
+
params.push(taskId);
|
|
5227
|
+
}
|
|
5228
|
+
if (conditions.length === 0)
|
|
5229
|
+
return null;
|
|
5230
|
+
const where = conditions.join(" AND ");
|
|
5231
|
+
const row = d.query(`SELECT * FROM context_snapshots WHERE ${where} ORDER BY created_at DESC LIMIT 1`).get(...params);
|
|
5232
|
+
return row ? rowToSnapshot(row) : null;
|
|
5233
|
+
}
|
|
5234
|
+
function listSnapshots(opts, db) {
|
|
5235
|
+
const d = db || getDatabase();
|
|
5236
|
+
const conditions = [];
|
|
5237
|
+
const params = [];
|
|
5238
|
+
if (opts.agent_id) {
|
|
5239
|
+
conditions.push("agent_id = ?");
|
|
5240
|
+
params.push(opts.agent_id);
|
|
5241
|
+
}
|
|
5242
|
+
if (opts.task_id) {
|
|
5243
|
+
conditions.push("task_id = ?");
|
|
5244
|
+
params.push(opts.task_id);
|
|
5245
|
+
}
|
|
5246
|
+
if (opts.project_id) {
|
|
5247
|
+
conditions.push("project_id = ?");
|
|
5248
|
+
params.push(opts.project_id);
|
|
5249
|
+
}
|
|
5250
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
5251
|
+
const limit = opts.limit || 20;
|
|
5252
|
+
params.push(limit);
|
|
5253
|
+
return d.query(`SELECT * FROM context_snapshots ${where} ORDER BY created_at DESC LIMIT ?`).all(...params).map(rowToSnapshot);
|
|
5254
|
+
}
|
|
5255
|
+
// src/db/budgets.ts
|
|
5256
|
+
function setBudget(agentId, opts, db) {
|
|
5257
|
+
const d = db || getDatabase();
|
|
5258
|
+
const timestamp = now();
|
|
5259
|
+
d.run(`INSERT INTO agent_budgets (agent_id, max_concurrent, max_cost_usd, max_task_minutes, period_hours, created_at, updated_at)
|
|
5260
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
5261
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
5262
|
+
max_concurrent = COALESCE(?, max_concurrent),
|
|
5263
|
+
max_cost_usd = COALESCE(?, max_cost_usd),
|
|
5264
|
+
max_task_minutes = COALESCE(?, max_task_minutes),
|
|
5265
|
+
period_hours = COALESCE(?, period_hours),
|
|
5266
|
+
updated_at = ?`, [
|
|
5267
|
+
agentId,
|
|
5268
|
+
opts.max_concurrent ?? 5,
|
|
5269
|
+
opts.max_cost_usd ?? null,
|
|
5270
|
+
opts.max_task_minutes ?? null,
|
|
5271
|
+
opts.period_hours ?? 24,
|
|
5272
|
+
timestamp,
|
|
5273
|
+
timestamp,
|
|
5274
|
+
opts.max_concurrent ?? null,
|
|
5275
|
+
opts.max_cost_usd ?? null,
|
|
5276
|
+
opts.max_task_minutes ?? null,
|
|
5277
|
+
opts.period_hours ?? null,
|
|
5278
|
+
timestamp
|
|
5279
|
+
]);
|
|
5280
|
+
return getBudget(agentId, d);
|
|
5281
|
+
}
|
|
5282
|
+
function getBudget(agentId, db) {
|
|
5283
|
+
const d = db || getDatabase();
|
|
5284
|
+
return d.query("SELECT * FROM agent_budgets WHERE agent_id = ?").get(agentId);
|
|
5285
|
+
}
|
|
5286
|
+
function checkBudget(agentId, db) {
|
|
5287
|
+
const d = db || getDatabase();
|
|
5288
|
+
const budget = getBudget(agentId, d);
|
|
5289
|
+
if (!budget)
|
|
5290
|
+
return { allowed: true, current_concurrent: 0, max_concurrent: 999 };
|
|
5291
|
+
const concurrent = countTasks({ status: "in_progress", assigned_to: agentId }, d);
|
|
5292
|
+
if (concurrent >= budget.max_concurrent) {
|
|
5293
|
+
return { allowed: false, reason: `Concurrent limit reached (${concurrent}/${budget.max_concurrent})`, current_concurrent: concurrent, max_concurrent: budget.max_concurrent };
|
|
5294
|
+
}
|
|
5295
|
+
if (budget.max_cost_usd != null) {
|
|
5296
|
+
const periodStart = new Date(Date.now() - budget.period_hours * 60 * 60 * 1000).toISOString();
|
|
5297
|
+
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);
|
|
5298
|
+
if (costRow.total >= budget.max_cost_usd) {
|
|
5299
|
+
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 };
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
return { allowed: true, current_concurrent: concurrent, max_concurrent: budget.max_concurrent };
|
|
5303
|
+
}
|
|
5001
5304
|
export {
|
|
5002
5305
|
uuid,
|
|
5003
5306
|
updateTaskList,
|
|
@@ -5017,12 +5320,15 @@ export {
|
|
|
5017
5320
|
syncWithAgents,
|
|
5018
5321
|
syncWithAgent,
|
|
5019
5322
|
syncKgEdges,
|
|
5323
|
+
stealTask,
|
|
5020
5324
|
startTask,
|
|
5021
5325
|
slugify,
|
|
5022
5326
|
setTaskStatus,
|
|
5023
5327
|
setTaskPriority,
|
|
5328
|
+
setBudget,
|
|
5024
5329
|
searchTasks,
|
|
5025
5330
|
scoreTask,
|
|
5331
|
+
saveSnapshot,
|
|
5026
5332
|
resolvePartialId,
|
|
5027
5333
|
resetDatabase,
|
|
5028
5334
|
removeTaskRelationshipByPair,
|
|
@@ -5044,8 +5350,10 @@ export {
|
|
|
5044
5350
|
nextOccurrence,
|
|
5045
5351
|
moveTask,
|
|
5046
5352
|
matchCapabilities,
|
|
5353
|
+
logTrace,
|
|
5047
5354
|
logTaskChange,
|
|
5048
5355
|
logProgress,
|
|
5356
|
+
logCost,
|
|
5049
5357
|
lockTask,
|
|
5050
5358
|
loadConfig,
|
|
5051
5359
|
listWebhooks,
|
|
@@ -5053,6 +5361,7 @@ export {
|
|
|
5053
5361
|
listTasks,
|
|
5054
5362
|
listTaskLists,
|
|
5055
5363
|
listTaskFiles,
|
|
5364
|
+
listSnapshots,
|
|
5056
5365
|
listSessions,
|
|
5057
5366
|
listProjects,
|
|
5058
5367
|
listProjectSources,
|
|
@@ -5065,9 +5374,11 @@ export {
|
|
|
5065
5374
|
isValidRecurrenceRule,
|
|
5066
5375
|
isAgentConflict,
|
|
5067
5376
|
getWebhook,
|
|
5377
|
+
getTraceStats,
|
|
5068
5378
|
getTemplate,
|
|
5069
5379
|
getTasksChangedSince,
|
|
5070
5380
|
getTaskWithRelations,
|
|
5381
|
+
getTaskTraces,
|
|
5071
5382
|
getTaskStats,
|
|
5072
5383
|
getTaskRelationships,
|
|
5073
5384
|
getTaskRelationship,
|
|
@@ -5096,6 +5407,7 @@ export {
|
|
|
5096
5407
|
getOrg,
|
|
5097
5408
|
getNextTask,
|
|
5098
5409
|
getLeaderboard,
|
|
5410
|
+
getLatestSnapshot,
|
|
5099
5411
|
getLatestHandoff,
|
|
5100
5412
|
getImpactAnalysis,
|
|
5101
5413
|
getDirectReports,
|
|
@@ -5107,6 +5419,7 @@ export {
|
|
|
5107
5419
|
getChecklist,
|
|
5108
5420
|
getCapableAgents,
|
|
5109
5421
|
getBurndown,
|
|
5422
|
+
getBudget,
|
|
5110
5423
|
getBlockingDeps,
|
|
5111
5424
|
getAgentMetrics,
|
|
5112
5425
|
getAgentByName,
|
|
@@ -5150,10 +5463,12 @@ export {
|
|
|
5150
5463
|
cloneTask,
|
|
5151
5464
|
clearChecklist,
|
|
5152
5465
|
cleanExpiredLocks,
|
|
5466
|
+
claimOrSteal,
|
|
5153
5467
|
claimNextTask,
|
|
5154
5468
|
checkLock,
|
|
5155
5469
|
checkCompletionGuard,
|
|
5156
5470
|
checkChecklistItem,
|
|
5471
|
+
checkBudget,
|
|
5157
5472
|
bulkUpdateTasks,
|
|
5158
5473
|
bulkCreateTasks,
|
|
5159
5474
|
bulkAddTaskFiles,
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AA+IA,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9E"}
|