@hasna/todos 0.11.84 → 0.11.86
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 +95 -0
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +701 -110
- package/dist/index.js +101 -8
- package/dist/mcp/index.js +303 -2
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +312 -11
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/interfaces.d.ts +74 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage.js +95 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23527,7 +23527,8 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23527
23527
|
getActiveWork: (filters) => getActiveWork2(filters, store),
|
|
23528
23528
|
getChangedSince: (since, filters) => getChangedSince(since, filters, store),
|
|
23529
23529
|
lock: (id, agentId) => lockTask2(id, agentId, store),
|
|
23530
|
-
unlock: (id, agentId) => unlockTask2(id, agentId, store)
|
|
23530
|
+
unlock: (id, agentId) => unlockTask2(id, agentId, store),
|
|
23531
|
+
getByFingerprint: (fingerprint2) => store.getTaskByFingerprint(fingerprint2)
|
|
23531
23532
|
},
|
|
23532
23533
|
dependencies: {
|
|
23533
23534
|
add: (taskId, dependsOn, context) => addDependency2(taskId, dependsOn, store, context),
|
|
@@ -23539,6 +23540,16 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23539
23540
|
add: (input, context) => addVerification(input, store, context),
|
|
23540
23541
|
list: (taskId) => listVerifications(taskId, store)
|
|
23541
23542
|
},
|
|
23543
|
+
commits: {
|
|
23544
|
+
add: (input, context) => addCommit(input, store, context),
|
|
23545
|
+
list: (taskId) => listCommits(taskId, store),
|
|
23546
|
+
find: (sha) => findCommit(sha, store)
|
|
23547
|
+
},
|
|
23548
|
+
gitRefs: {
|
|
23549
|
+
add: (input, context) => addGitRef(input, store, context),
|
|
23550
|
+
list: (taskId) => listGitRefs(taskId, store),
|
|
23551
|
+
find: (ref) => findGitRefs(ref, store)
|
|
23552
|
+
},
|
|
23542
23553
|
projects: {
|
|
23543
23554
|
create: (input, context) => createProject2(input, store, context),
|
|
23544
23555
|
get: (id) => store.get("projects", id),
|
|
@@ -23559,7 +23570,9 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23559
23570
|
get: (id) => store.get("agents", id),
|
|
23560
23571
|
getByName: async (name) => (await store.list("agents")).find((agent) => agent.name === name) ?? null,
|
|
23561
23572
|
list: async (options2) => (await store.list("agents")).filter((agent) => options2?.include_archived || agent.status !== "archived").sort((a, b) => a.name.localeCompare(b.name)),
|
|
23562
|
-
update: (id, input) => updateAgent2(id, input, store)
|
|
23573
|
+
update: (id, input) => updateAgent2(id, input, store),
|
|
23574
|
+
heartbeat: (idOrName, context) => heartbeatAgent(idOrName, store, context),
|
|
23575
|
+
release: (idOrName, sessionId, context) => releaseAgent2(idOrName, sessionId, store, context)
|
|
23563
23576
|
},
|
|
23564
23577
|
taskLists: {
|
|
23565
23578
|
create: (input, context) => createTaskList2(input, store, context),
|
|
@@ -23704,6 +23717,17 @@ class PostgresJsonRecordStore {
|
|
|
23704
23717
|
const result = await this.options.client.query(sql, params);
|
|
23705
23718
|
return result.rows.map((row) => payloadRecord2(row.payload));
|
|
23706
23719
|
}
|
|
23720
|
+
async getTaskByFingerprint(fingerprint2) {
|
|
23721
|
+
await this.ensureSchema();
|
|
23722
|
+
const sql = `/* todos:task-by-fingerprint */ SELECT payload FROM ${this.tableName}
|
|
23723
|
+
WHERE service = $1 AND object_type = $2 AND deleted_at IS NULL
|
|
23724
|
+
AND payload->'metadata'->>'fingerprint' = $3
|
|
23725
|
+
ORDER BY payload->>'created_at' ASC
|
|
23726
|
+
LIMIT 1`;
|
|
23727
|
+
const result = await this.options.client.query(sql, [this.service, "tasks", fingerprint2]);
|
|
23728
|
+
const row = result.rows[0];
|
|
23729
|
+
return row ? payloadRecord2(row.payload) : null;
|
|
23730
|
+
}
|
|
23707
23731
|
async countTasks(filter) {
|
|
23708
23732
|
await this.ensureSchema();
|
|
23709
23733
|
const { where, params } = this.buildTaskFilterSql(filter);
|
|
@@ -24090,6 +24114,53 @@ async function addVerification(input, store, context) {
|
|
|
24090
24114
|
async function listVerifications(taskId, store) {
|
|
24091
24115
|
return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
|
|
24092
24116
|
}
|
|
24117
|
+
async function addCommit(input, store, context) {
|
|
24118
|
+
if (!await store.get("tasks", input.task_id))
|
|
24119
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
24120
|
+
const timestamp2 = new Date().toISOString();
|
|
24121
|
+
const commit = {
|
|
24122
|
+
id: randomUUID3(),
|
|
24123
|
+
task_id: input.task_id,
|
|
24124
|
+
sha: input.sha,
|
|
24125
|
+
message: input.message ?? null,
|
|
24126
|
+
author: input.author ?? null,
|
|
24127
|
+
files_changed: input.files_changed ?? null,
|
|
24128
|
+
created_at: timestamp2
|
|
24129
|
+
};
|
|
24130
|
+
await store.upsert("commits", { ...commit, updated_at: timestamp2 }, context);
|
|
24131
|
+
return commit;
|
|
24132
|
+
}
|
|
24133
|
+
async function listCommits(taskId, store) {
|
|
24134
|
+
return (await store.list("commits")).filter((commit) => commit.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
24135
|
+
}
|
|
24136
|
+
async function findCommit(sha, store) {
|
|
24137
|
+
const matches = (await store.list("commits")).filter((commit) => commit.sha === sha || commit.sha.startsWith(sha) || sha.startsWith(commit.sha)).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
24138
|
+
return matches[0] ?? null;
|
|
24139
|
+
}
|
|
24140
|
+
async function addGitRef(input, store, context) {
|
|
24141
|
+
if (!await store.get("tasks", input.task_id))
|
|
24142
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
24143
|
+
const timestamp2 = new Date().toISOString();
|
|
24144
|
+
const gitRef = {
|
|
24145
|
+
id: randomUUID3(),
|
|
24146
|
+
task_id: input.task_id,
|
|
24147
|
+
ref_type: input.ref_type,
|
|
24148
|
+
name: input.name,
|
|
24149
|
+
url: input.url ?? null,
|
|
24150
|
+
provider: input.provider ?? null,
|
|
24151
|
+
metadata: input.metadata ?? {},
|
|
24152
|
+
created_at: timestamp2,
|
|
24153
|
+
updated_at: timestamp2
|
|
24154
|
+
};
|
|
24155
|
+
await store.upsert("refs", gitRef, context);
|
|
24156
|
+
return gitRef;
|
|
24157
|
+
}
|
|
24158
|
+
async function listGitRefs(taskId, store) {
|
|
24159
|
+
return (await store.list("refs")).filter((ref) => ref.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
24160
|
+
}
|
|
24161
|
+
async function findGitRefs(ref, store) {
|
|
24162
|
+
return (await store.list("refs")).filter((r) => r.name === ref).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
24163
|
+
}
|
|
24093
24164
|
var TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
|
|
24094
24165
|
function toFilterArray(value) {
|
|
24095
24166
|
return Array.isArray(value) ? value : [value];
|
|
@@ -24237,6 +24308,28 @@ async function updateAgent2(id, input, store) {
|
|
|
24237
24308
|
last_seen_at: new Date().toISOString()
|
|
24238
24309
|
});
|
|
24239
24310
|
}
|
|
24311
|
+
async function resolveAgent(idOrName, store) {
|
|
24312
|
+
const byId = await store.get("agents", idOrName);
|
|
24313
|
+
if (byId)
|
|
24314
|
+
return byId;
|
|
24315
|
+
return (await store.list("agents")).find((agent) => agent.name === idOrName) ?? null;
|
|
24316
|
+
}
|
|
24317
|
+
async function heartbeatAgent(idOrName, store, context) {
|
|
24318
|
+
const agent = await resolveAgent(idOrName, store);
|
|
24319
|
+
if (!agent)
|
|
24320
|
+
return null;
|
|
24321
|
+
return store.upsert("agents", { ...agent, last_seen_at: new Date().toISOString() }, context);
|
|
24322
|
+
}
|
|
24323
|
+
async function releaseAgent2(idOrName, sessionId, store, context) {
|
|
24324
|
+
const agent = await resolveAgent(idOrName, store);
|
|
24325
|
+
if (!agent)
|
|
24326
|
+
return null;
|
|
24327
|
+
if (sessionId && agent.session_id && agent.session_id !== sessionId) {
|
|
24328
|
+
return { agent, released: false };
|
|
24329
|
+
}
|
|
24330
|
+
const updated = await store.upsert("agents", { ...agent, session_id: null, last_seen_at: new Date().toISOString() }, context);
|
|
24331
|
+
return { agent: updated, released: true };
|
|
24332
|
+
}
|
|
24240
24333
|
async function createTaskList2(input, store, context) {
|
|
24241
24334
|
const timestamp2 = new Date().toISOString();
|
|
24242
24335
|
return store.upsert("task_lists", {
|
|
@@ -31317,7 +31410,7 @@ function resolveRun(parsed, db) {
|
|
|
31317
31410
|
}
|
|
31318
31411
|
return resolution;
|
|
31319
31412
|
}
|
|
31320
|
-
function
|
|
31413
|
+
function resolveAgent2(parsed, db) {
|
|
31321
31414
|
const resolution = blankResolution(parsed);
|
|
31322
31415
|
const id = resolvePartialId(db, "agents", parsed.target);
|
|
31323
31416
|
const agent = id ? getAgent(id, db) : getAgentByName(parsed.target, db);
|
|
@@ -31354,7 +31447,7 @@ function resolveTask(parsed, db) {
|
|
|
31354
31447
|
function resolveUnknown(parsed, workspace, db, maxSymbolMatches) {
|
|
31355
31448
|
const attempts = [
|
|
31356
31449
|
() => resolveTask({ ...parsed, kind: "task" }, db),
|
|
31357
|
-
() =>
|
|
31450
|
+
() => resolveAgent2({ ...parsed, kind: "agent" }, db),
|
|
31358
31451
|
() => resolvePlan({ ...parsed, kind: "plan" }, db),
|
|
31359
31452
|
() => resolveSymbol({ ...parsed, kind: "symbol" }, workspace, maxSymbolMatches)
|
|
31360
31453
|
];
|
|
@@ -31389,7 +31482,7 @@ function resolveMentions(input, db) {
|
|
|
31389
31482
|
case "run":
|
|
31390
31483
|
return resolveRun(parsed, d);
|
|
31391
31484
|
case "agent":
|
|
31392
|
-
return
|
|
31485
|
+
return resolveAgent2(parsed, d);
|
|
31393
31486
|
case "task":
|
|
31394
31487
|
return resolveTask(parsed, d);
|
|
31395
31488
|
default:
|
|
@@ -42207,7 +42300,7 @@ function resolveProjectId(value, db) {
|
|
|
42207
42300
|
return null;
|
|
42208
42301
|
return resolvePartialId(db, "projects", value) || value;
|
|
42209
42302
|
}
|
|
42210
|
-
function
|
|
42303
|
+
function resolveAgent3(agentId, db) {
|
|
42211
42304
|
return db.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
42212
42305
|
}
|
|
42213
42306
|
function agentTaskWhere(agent, options, db) {
|
|
@@ -42251,7 +42344,7 @@ function scoreHandoffs(created, withTaskRefs, withBlockers) {
|
|
|
42251
42344
|
}
|
|
42252
42345
|
function getAgentReliabilityScorecard(agentId, options = {}, db) {
|
|
42253
42346
|
const d = db || getDatabase();
|
|
42254
|
-
const agent =
|
|
42347
|
+
const agent = resolveAgent3(agentId, d);
|
|
42255
42348
|
if (!agent)
|
|
42256
42349
|
return null;
|
|
42257
42350
|
const generatedAt = now();
|
|
@@ -42420,7 +42513,7 @@ function getAgentReliabilityScorecard(agentId, options = {}, db) {
|
|
|
42420
42513
|
}
|
|
42421
42514
|
function listAgentReliabilityScorecards(options = {}, db) {
|
|
42422
42515
|
const d = db || getDatabase();
|
|
42423
|
-
const agents = options.agent_id ?
|
|
42516
|
+
const agents = options.agent_id ? resolveAgent3(options.agent_id, d) ? [resolveAgent3(options.agent_id, d)] : [] : d.query("SELECT id, name FROM agents ORDER BY name").all();
|
|
42424
42517
|
const limit = Number.isFinite(options.limit) && options.limit > 0 ? Math.min(Math.floor(options.limit), 500) : 50;
|
|
42425
42518
|
return agents.map((agent) => getAgentReliabilityScorecard(agent.id, options, d)).filter((scorecard) => Boolean(scorecard)).filter((scorecard) => {
|
|
42426
42519
|
const signals = scorecard.signals;
|
package/dist/mcp/index.js
CHANGED
|
@@ -22870,6 +22870,18 @@ async function cloudRegisterAgent(client, input) {
|
|
|
22870
22870
|
}
|
|
22871
22871
|
return raw;
|
|
22872
22872
|
}
|
|
22873
|
+
async function cloudHeartbeatAgent(client, idOrName) {
|
|
22874
|
+
const raw = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/heartbeat`, {});
|
|
22875
|
+
if (raw && typeof raw === "object" && "agent" in raw) {
|
|
22876
|
+
return raw.agent;
|
|
22877
|
+
}
|
|
22878
|
+
return raw ?? null;
|
|
22879
|
+
}
|
|
22880
|
+
async function cloudReleaseAgent(client, idOrName, sessionId) {
|
|
22881
|
+
const raw = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/release`, sessionId ? { session_id: sessionId } : {});
|
|
22882
|
+
const env = raw ?? {};
|
|
22883
|
+
return { agent: env.agent ?? null, released: env.released !== false };
|
|
22884
|
+
}
|
|
22873
22885
|
var _cache;
|
|
22874
22886
|
var init_cloud_router = __esm(() => {
|
|
22875
22887
|
init_storage();
|
|
@@ -44496,6 +44508,19 @@ ID: ${updated.id}${taskNote}`
|
|
|
44496
44508
|
agent_id: exports_external.string().describe("Your agent ID or name.")
|
|
44497
44509
|
}, async ({ agent_id }) => {
|
|
44498
44510
|
try {
|
|
44511
|
+
const cloud = getTodosCloudClient();
|
|
44512
|
+
if (cloud) {
|
|
44513
|
+
const a = await cloudHeartbeatAgent(cloud, agent_id);
|
|
44514
|
+
if (!a) {
|
|
44515
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
44516
|
+
}
|
|
44517
|
+
return {
|
|
44518
|
+
content: [{
|
|
44519
|
+
type: "text",
|
|
44520
|
+
text: `Heartbeat: ${a.name} (${a.id}) \u2014 last_seen_at updated to ${a.last_seen_at}`
|
|
44521
|
+
}]
|
|
44522
|
+
};
|
|
44523
|
+
}
|
|
44499
44524
|
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
44500
44525
|
if (!agent) {
|
|
44501
44526
|
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
@@ -44518,6 +44543,22 @@ ID: ${updated.id}${taskNote}`
|
|
|
44518
44543
|
session_id: exports_external.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
|
|
44519
44544
|
}, async ({ agent_id, session_id }) => {
|
|
44520
44545
|
try {
|
|
44546
|
+
const cloud = getTodosCloudClient();
|
|
44547
|
+
if (cloud) {
|
|
44548
|
+
const result = await cloudReleaseAgent(cloud, agent_id, session_id);
|
|
44549
|
+
if (!result.agent) {
|
|
44550
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
44551
|
+
}
|
|
44552
|
+
if (!result.released) {
|
|
44553
|
+
return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
|
|
44554
|
+
}
|
|
44555
|
+
return {
|
|
44556
|
+
content: [{
|
|
44557
|
+
type: "text",
|
|
44558
|
+
text: `Agent released: ${result.agent.name} (${result.agent.id}) \u2014 session cleared, name is now available.`
|
|
44559
|
+
}]
|
|
44560
|
+
};
|
|
44561
|
+
}
|
|
44521
44562
|
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
44522
44563
|
if (!agent) {
|
|
44523
44564
|
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
@@ -48130,7 +48171,8 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
48130
48171
|
getActiveWork: (filters) => getActiveWork2(filters, store),
|
|
48131
48172
|
getChangedSince: (since, filters) => getChangedSince(since, filters, store),
|
|
48132
48173
|
lock: (id, agentId) => lockTask2(id, agentId, store),
|
|
48133
|
-
unlock: (id, agentId) => unlockTask2(id, agentId, store)
|
|
48174
|
+
unlock: (id, agentId) => unlockTask2(id, agentId, store),
|
|
48175
|
+
getByFingerprint: (fingerprint3) => store.getTaskByFingerprint(fingerprint3)
|
|
48134
48176
|
},
|
|
48135
48177
|
dependencies: {
|
|
48136
48178
|
add: (taskId, dependsOn, context) => addDependency2(taskId, dependsOn, store, context),
|
|
@@ -48142,6 +48184,16 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
48142
48184
|
add: (input, context) => addVerification(input, store, context),
|
|
48143
48185
|
list: (taskId) => listVerifications(taskId, store)
|
|
48144
48186
|
},
|
|
48187
|
+
commits: {
|
|
48188
|
+
add: (input, context) => addCommit(input, store, context),
|
|
48189
|
+
list: (taskId) => listCommits(taskId, store),
|
|
48190
|
+
find: (sha) => findCommit(sha, store)
|
|
48191
|
+
},
|
|
48192
|
+
gitRefs: {
|
|
48193
|
+
add: (input, context) => addGitRef(input, store, context),
|
|
48194
|
+
list: (taskId) => listGitRefs(taskId, store),
|
|
48195
|
+
find: (ref) => findGitRefs(ref, store)
|
|
48196
|
+
},
|
|
48145
48197
|
projects: {
|
|
48146
48198
|
create: (input, context) => createProject2(input, store, context),
|
|
48147
48199
|
get: (id) => store.get("projects", id),
|
|
@@ -48162,7 +48214,9 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
48162
48214
|
get: (id) => store.get("agents", id),
|
|
48163
48215
|
getByName: async (name) => (await store.list("agents")).find((agent) => agent.name === name) ?? null,
|
|
48164
48216
|
list: async (options2) => (await store.list("agents")).filter((agent) => options2?.include_archived || agent.status !== "archived").sort((a, b) => a.name.localeCompare(b.name)),
|
|
48165
|
-
update: (id, input) => updateAgent2(id, input, store)
|
|
48217
|
+
update: (id, input) => updateAgent2(id, input, store),
|
|
48218
|
+
heartbeat: (idOrName, context) => heartbeatAgent(idOrName, store, context),
|
|
48219
|
+
release: (idOrName, sessionId, context) => releaseAgent2(idOrName, sessionId, store, context)
|
|
48166
48220
|
},
|
|
48167
48221
|
taskLists: {
|
|
48168
48222
|
create: (input, context) => createTaskList2(input, store, context),
|
|
@@ -48307,6 +48361,17 @@ class PostgresJsonRecordStore {
|
|
|
48307
48361
|
const result = await this.options.client.query(sql, params);
|
|
48308
48362
|
return result.rows.map((row) => payloadRecord2(row.payload));
|
|
48309
48363
|
}
|
|
48364
|
+
async getTaskByFingerprint(fingerprint3) {
|
|
48365
|
+
await this.ensureSchema();
|
|
48366
|
+
const sql = `/* todos:task-by-fingerprint */ SELECT payload FROM ${this.tableName}
|
|
48367
|
+
WHERE service = $1 AND object_type = $2 AND deleted_at IS NULL
|
|
48368
|
+
AND payload->'metadata'->>'fingerprint' = $3
|
|
48369
|
+
ORDER BY payload->>'created_at' ASC
|
|
48370
|
+
LIMIT 1`;
|
|
48371
|
+
const result = await this.options.client.query(sql, [this.service, "tasks", fingerprint3]);
|
|
48372
|
+
const row = result.rows[0];
|
|
48373
|
+
return row ? payloadRecord2(row.payload) : null;
|
|
48374
|
+
}
|
|
48310
48375
|
async countTasks(filter) {
|
|
48311
48376
|
await this.ensureSchema();
|
|
48312
48377
|
const { where, params } = this.buildTaskFilterSql(filter);
|
|
@@ -48692,6 +48757,53 @@ async function addVerification(input, store, context) {
|
|
|
48692
48757
|
async function listVerifications(taskId, store) {
|
|
48693
48758
|
return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
|
|
48694
48759
|
}
|
|
48760
|
+
async function addCommit(input, store, context) {
|
|
48761
|
+
if (!await store.get("tasks", input.task_id))
|
|
48762
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
48763
|
+
const timestamp3 = new Date().toISOString();
|
|
48764
|
+
const commit = {
|
|
48765
|
+
id: randomUUID3(),
|
|
48766
|
+
task_id: input.task_id,
|
|
48767
|
+
sha: input.sha,
|
|
48768
|
+
message: input.message ?? null,
|
|
48769
|
+
author: input.author ?? null,
|
|
48770
|
+
files_changed: input.files_changed ?? null,
|
|
48771
|
+
created_at: timestamp3
|
|
48772
|
+
};
|
|
48773
|
+
await store.upsert("commits", { ...commit, updated_at: timestamp3 }, context);
|
|
48774
|
+
return commit;
|
|
48775
|
+
}
|
|
48776
|
+
async function listCommits(taskId, store) {
|
|
48777
|
+
return (await store.list("commits")).filter((commit) => commit.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
48778
|
+
}
|
|
48779
|
+
async function findCommit(sha, store) {
|
|
48780
|
+
const matches = (await store.list("commits")).filter((commit) => commit.sha === sha || commit.sha.startsWith(sha) || sha.startsWith(commit.sha)).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
48781
|
+
return matches[0] ?? null;
|
|
48782
|
+
}
|
|
48783
|
+
async function addGitRef(input, store, context) {
|
|
48784
|
+
if (!await store.get("tasks", input.task_id))
|
|
48785
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
48786
|
+
const timestamp3 = new Date().toISOString();
|
|
48787
|
+
const gitRef = {
|
|
48788
|
+
id: randomUUID3(),
|
|
48789
|
+
task_id: input.task_id,
|
|
48790
|
+
ref_type: input.ref_type,
|
|
48791
|
+
name: input.name,
|
|
48792
|
+
url: input.url ?? null,
|
|
48793
|
+
provider: input.provider ?? null,
|
|
48794
|
+
metadata: input.metadata ?? {},
|
|
48795
|
+
created_at: timestamp3,
|
|
48796
|
+
updated_at: timestamp3
|
|
48797
|
+
};
|
|
48798
|
+
await store.upsert("refs", gitRef, context);
|
|
48799
|
+
return gitRef;
|
|
48800
|
+
}
|
|
48801
|
+
async function listGitRefs(taskId, store) {
|
|
48802
|
+
return (await store.list("refs")).filter((ref) => ref.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
48803
|
+
}
|
|
48804
|
+
async function findGitRefs(ref, store) {
|
|
48805
|
+
return (await store.list("refs")).filter((r) => r.name === ref).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
48806
|
+
}
|
|
48695
48807
|
function toFilterArray(value) {
|
|
48696
48808
|
return Array.isArray(value) ? value : [value];
|
|
48697
48809
|
}
|
|
@@ -48838,6 +48950,28 @@ async function updateAgent2(id, input, store) {
|
|
|
48838
48950
|
last_seen_at: new Date().toISOString()
|
|
48839
48951
|
});
|
|
48840
48952
|
}
|
|
48953
|
+
async function resolveAgent3(idOrName, store) {
|
|
48954
|
+
const byId = await store.get("agents", idOrName);
|
|
48955
|
+
if (byId)
|
|
48956
|
+
return byId;
|
|
48957
|
+
return (await store.list("agents")).find((agent) => agent.name === idOrName) ?? null;
|
|
48958
|
+
}
|
|
48959
|
+
async function heartbeatAgent(idOrName, store, context) {
|
|
48960
|
+
const agent = await resolveAgent3(idOrName, store);
|
|
48961
|
+
if (!agent)
|
|
48962
|
+
return null;
|
|
48963
|
+
return store.upsert("agents", { ...agent, last_seen_at: new Date().toISOString() }, context);
|
|
48964
|
+
}
|
|
48965
|
+
async function releaseAgent2(idOrName, sessionId, store, context) {
|
|
48966
|
+
const agent = await resolveAgent3(idOrName, store);
|
|
48967
|
+
if (!agent)
|
|
48968
|
+
return null;
|
|
48969
|
+
if (sessionId && agent.session_id && agent.session_id !== sessionId) {
|
|
48970
|
+
return { agent, released: false };
|
|
48971
|
+
}
|
|
48972
|
+
const updated = await store.upsert("agents", { ...agent, session_id: null, last_seen_at: new Date().toISOString() }, context);
|
|
48973
|
+
return { agent: updated, released: true };
|
|
48974
|
+
}
|
|
48841
48975
|
async function createTaskList2(input, store, context) {
|
|
48842
48976
|
const timestamp3 = new Date().toISOString();
|
|
48843
48977
|
return store.upsert("task_lists", {
|
|
@@ -49586,6 +49720,61 @@ async function handleV1Request(req, url) {
|
|
|
49586
49720
|
missing
|
|
49587
49721
|
});
|
|
49588
49722
|
}
|
|
49723
|
+
if (id === "upsert" && !action) {
|
|
49724
|
+
if (method !== "POST")
|
|
49725
|
+
return error(405, `method ${method} not allowed on /v1/tasks/upsert`);
|
|
49726
|
+
if (typeof store.tasks.getByFingerprint !== "function") {
|
|
49727
|
+
return error(501, "fingerprint upsert is not supported by this storage backend");
|
|
49728
|
+
}
|
|
49729
|
+
const body = await readJson(req) ?? {};
|
|
49730
|
+
const fingerprint3 = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
|
|
49731
|
+
if (!fingerprint3)
|
|
49732
|
+
return error(400, "fingerprint is required");
|
|
49733
|
+
if (typeof body.title !== "string" || !body.title.trim())
|
|
49734
|
+
return error(400, "title is required");
|
|
49735
|
+
const existing = await store.tasks.getByFingerprint(fingerprint3);
|
|
49736
|
+
const metadata = {
|
|
49737
|
+
...existing?.metadata ?? {},
|
|
49738
|
+
...body.metadata ?? {},
|
|
49739
|
+
fingerprint: fingerprint3
|
|
49740
|
+
};
|
|
49741
|
+
const fields = { metadata };
|
|
49742
|
+
for (const key of [
|
|
49743
|
+
"title",
|
|
49744
|
+
"description",
|
|
49745
|
+
"priority",
|
|
49746
|
+
"status",
|
|
49747
|
+
"project_id",
|
|
49748
|
+
"assigned_to",
|
|
49749
|
+
"working_dir",
|
|
49750
|
+
"plan_id",
|
|
49751
|
+
"task_list_id",
|
|
49752
|
+
"tags",
|
|
49753
|
+
"due_at",
|
|
49754
|
+
"estimated_minutes",
|
|
49755
|
+
"sla_minutes",
|
|
49756
|
+
"requires_approval",
|
|
49757
|
+
"recurrence_rule",
|
|
49758
|
+
"task_type"
|
|
49759
|
+
]) {
|
|
49760
|
+
const bag = body;
|
|
49761
|
+
if (bag[key] !== undefined)
|
|
49762
|
+
fields[key] = bag[key];
|
|
49763
|
+
}
|
|
49764
|
+
if (!existing) {
|
|
49765
|
+
const task2 = await store.tasks.create({ ...fields, title: body.title }, contextFromPrincipal(principal, body));
|
|
49766
|
+
return json2({ task: task2, created: true }, 201);
|
|
49767
|
+
}
|
|
49768
|
+
try {
|
|
49769
|
+
const task2 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body));
|
|
49770
|
+
return json2({ task: task2, created: false });
|
|
49771
|
+
} catch (e) {
|
|
49772
|
+
const msg = e.message || "";
|
|
49773
|
+
if (msg.includes("version conflict"))
|
|
49774
|
+
return error(409, msg);
|
|
49775
|
+
throw e;
|
|
49776
|
+
}
|
|
49777
|
+
}
|
|
49589
49778
|
if (!id) {
|
|
49590
49779
|
if (method === "GET") {
|
|
49591
49780
|
const filter = {
|
|
@@ -49639,6 +49828,14 @@ async function handleV1Request(req, url) {
|
|
|
49639
49828
|
}
|
|
49640
49829
|
return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
|
|
49641
49830
|
}
|
|
49831
|
+
if (action === "history") {
|
|
49832
|
+
if (method !== "GET")
|
|
49833
|
+
return error(405, `method ${method} not allowed on /v1/tasks/:id/history`);
|
|
49834
|
+
if (!await store.tasks.get(id))
|
|
49835
|
+
return error(404, "task not found");
|
|
49836
|
+
const history = await store.audit.getTaskHistory(id);
|
|
49837
|
+
return json2({ history, count: history.length });
|
|
49838
|
+
}
|
|
49642
49839
|
if (action === "lock" || action === "unlock") {
|
|
49643
49840
|
if (method !== "POST")
|
|
49644
49841
|
return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
|
|
@@ -49722,6 +49919,70 @@ async function handleV1Request(req, url) {
|
|
|
49722
49919
|
}
|
|
49723
49920
|
return error(405, `method ${method} not allowed on /v1/tasks/:id/verifications`);
|
|
49724
49921
|
}
|
|
49922
|
+
if (action === "commits") {
|
|
49923
|
+
if (!store.commits)
|
|
49924
|
+
return error(501, "commit links are not supported by this storage backend");
|
|
49925
|
+
if (method === "GET") {
|
|
49926
|
+
if (!await store.tasks.get(id))
|
|
49927
|
+
return error(404, "task not found");
|
|
49928
|
+
const commits = await store.commits.list(id);
|
|
49929
|
+
return json2({ commits, count: commits.length });
|
|
49930
|
+
}
|
|
49931
|
+
if (method === "POST") {
|
|
49932
|
+
const body2 = await readJson(req) ?? {};
|
|
49933
|
+
if (typeof body2.sha !== "string" || !body2.sha.trim())
|
|
49934
|
+
return error(400, "sha is required");
|
|
49935
|
+
try {
|
|
49936
|
+
const commit = await store.commits.add({
|
|
49937
|
+
task_id: id,
|
|
49938
|
+
sha: body2.sha,
|
|
49939
|
+
message: body2.message,
|
|
49940
|
+
author: body2.author,
|
|
49941
|
+
files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
|
|
49942
|
+
}, contextFromPrincipal(principal));
|
|
49943
|
+
return json2({ commit }, 201);
|
|
49944
|
+
} catch (e) {
|
|
49945
|
+
const msg = e.message || "";
|
|
49946
|
+
if (msg.includes("not found"))
|
|
49947
|
+
return error(404, msg);
|
|
49948
|
+
throw e;
|
|
49949
|
+
}
|
|
49950
|
+
}
|
|
49951
|
+
return error(405, `method ${method} not allowed on /v1/tasks/:id/commits`);
|
|
49952
|
+
}
|
|
49953
|
+
if (action === "refs") {
|
|
49954
|
+
if (!store.gitRefs)
|
|
49955
|
+
return error(501, "git ref links are not supported by this storage backend");
|
|
49956
|
+
if (method === "GET") {
|
|
49957
|
+
if (!await store.tasks.get(id))
|
|
49958
|
+
return error(404, "task not found");
|
|
49959
|
+
const refs = await store.gitRefs.list(id);
|
|
49960
|
+
return json2({ refs, count: refs.length });
|
|
49961
|
+
}
|
|
49962
|
+
if (method === "POST") {
|
|
49963
|
+
const body2 = await readJson(req) ?? {};
|
|
49964
|
+
const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
|
|
49965
|
+
if (typeof body2.name !== "string" || !body2.name.trim())
|
|
49966
|
+
return error(400, "name is required");
|
|
49967
|
+
try {
|
|
49968
|
+
const ref = await store.gitRefs.add({
|
|
49969
|
+
task_id: id,
|
|
49970
|
+
ref_type: refType,
|
|
49971
|
+
name: body2.name,
|
|
49972
|
+
url: body2.url,
|
|
49973
|
+
provider: body2.provider,
|
|
49974
|
+
metadata: body2.metadata
|
|
49975
|
+
}, contextFromPrincipal(principal));
|
|
49976
|
+
return json2({ ref }, 201);
|
|
49977
|
+
} catch (e) {
|
|
49978
|
+
const msg = e.message || "";
|
|
49979
|
+
if (msg.includes("not found"))
|
|
49980
|
+
return error(404, msg);
|
|
49981
|
+
throw e;
|
|
49982
|
+
}
|
|
49983
|
+
}
|
|
49984
|
+
return error(405, `method ${method} not allowed on /v1/tasks/:id/refs`);
|
|
49985
|
+
}
|
|
49725
49986
|
const body = await readJson(req) ?? {};
|
|
49726
49987
|
const agentId = body.agent_id || principal.agent || "todos-serve";
|
|
49727
49988
|
if (action === "start" && method === "POST") {
|
|
@@ -49834,6 +50095,30 @@ async function handleV1Request(req, url) {
|
|
|
49834
50095
|
}
|
|
49835
50096
|
return json2({ agent: result }, 201);
|
|
49836
50097
|
}
|
|
50098
|
+
if (id && action === "heartbeat") {
|
|
50099
|
+
if (method !== "POST")
|
|
50100
|
+
return error(405, `method ${method} not allowed on /v1/agents/:id/heartbeat`);
|
|
50101
|
+
if (typeof store.agents.heartbeat !== "function") {
|
|
50102
|
+
return error(501, "agent heartbeat is not supported by this storage backend");
|
|
50103
|
+
}
|
|
50104
|
+
const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
|
|
50105
|
+
return agent ? json2({ agent }) : error(404, "agent not found");
|
|
50106
|
+
}
|
|
50107
|
+
if (id && action === "release") {
|
|
50108
|
+
if (method !== "POST")
|
|
50109
|
+
return error(405, `method ${method} not allowed on /v1/agents/:id/release`);
|
|
50110
|
+
if (typeof store.agents.release !== "function") {
|
|
50111
|
+
return error(501, "agent release is not supported by this storage backend");
|
|
50112
|
+
}
|
|
50113
|
+
const body = await readJson(req) ?? {};
|
|
50114
|
+
const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
|
|
50115
|
+
if (!result)
|
|
50116
|
+
return error(404, "agent not found");
|
|
50117
|
+
if (!result.released) {
|
|
50118
|
+
return error(409, "release denied: session_id does not match agent's current session", { released: false });
|
|
50119
|
+
}
|
|
50120
|
+
return json2({ agent: result.agent, released: true });
|
|
50121
|
+
}
|
|
49837
50122
|
if (id && method === "GET") {
|
|
49838
50123
|
const agent = await store.agents.get(id);
|
|
49839
50124
|
return agent ? json2({ agent }) : error(404, "agent not found");
|
|
@@ -49863,6 +50148,22 @@ async function handleV1Request(req, url) {
|
|
|
49863
50148
|
const dependencies = await store.dependencies.listAll();
|
|
49864
50149
|
return json2({ dependencies, count: dependencies.length });
|
|
49865
50150
|
}
|
|
50151
|
+
if (resource === "commits" && id) {
|
|
50152
|
+
if (method !== "GET")
|
|
50153
|
+
return error(405, `method ${method} not allowed on /v1/commits/:sha`);
|
|
50154
|
+
if (!store.commits)
|
|
50155
|
+
return error(501, "commit links are not supported by this storage backend");
|
|
50156
|
+
const commit = await store.commits.find(id);
|
|
50157
|
+
return json2({ commit: commit ?? null });
|
|
50158
|
+
}
|
|
50159
|
+
if (resource === "refs" && id) {
|
|
50160
|
+
if (method !== "GET")
|
|
50161
|
+
return error(405, `method ${method} not allowed on /v1/refs/:ref`);
|
|
50162
|
+
if (!store.gitRefs)
|
|
50163
|
+
return error(501, "git ref links are not supported by this storage backend");
|
|
50164
|
+
const refs = await store.gitRefs.find(id);
|
|
50165
|
+
return json2({ refs, count: refs.length });
|
|
50166
|
+
}
|
|
49866
50167
|
if (resource === "next" && !id) {
|
|
49867
50168
|
if (method !== "GET")
|
|
49868
50169
|
return error(405, `method ${method} not allowed on /v1/next`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,OAAO,GAAG;IACb,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;IACpC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;CAC5D,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,OAAO,GAAG;IACb,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;IACpC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;CAC5D,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,OAAO,GAAG,IAAI,CA+ejJ"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageName": "@hasna/todos",
|
|
3
|
-
"packageVersion": "0.11.
|
|
3
|
+
"packageVersion": "0.11.86",
|
|
4
4
|
"repository": "https://github.com/hasna/todos.git",
|
|
5
|
-
"gitCommit": "
|
|
6
|
-
"generatedAt": "2026-07-
|
|
5
|
+
"gitCommit": "25cf0e9f66c7cac1e5e4d312ae8cd7f901e8a9b9",
|
|
6
|
+
"generatedAt": "2026-07-08T17:12:30.188Z"
|
|
7
7
|
}
|