@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/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 resolveAgent(parsed, db) {
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
- () => resolveAgent({ ...parsed, kind: "agent" }, db),
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 resolveAgent(parsed, d);
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 resolveAgent2(agentId, db) {
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 = resolveAgent2(agentId, d);
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 ? resolveAgent2(options.agent_id, d) ? [resolveAgent2(options.agent_id, d)] : [] : d.query("SELECT id, name FROM agents ORDER BY name").all();
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,CA6cjJ"}
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.84",
3
+ "packageVersion": "0.11.86",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "f7fcea3d57bf87853fcc48b009019733b2206ca7",
6
- "generatedAt": "2026-07-08T15:23:54.541Z"
5
+ "gitCommit": "25cf0e9f66c7cac1e5e4d312ae8cd7f901e8a9b9",
6
+ "generatedAt": "2026-07-08T17:12:30.188Z"
7
7
  }