@sleep2agi/commhub-server 0.5.0-preview.3 → 0.5.0-preview.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.ts +66 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/commhub-server",
3
- "version": "0.5.0-preview.3",
3
+ "version": "0.5.0-preview.4",
4
4
  "description": "CommHub MCP Server — AI Agent communication hub with SSE push, MCP protocol, and REST API",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/tools.ts CHANGED
@@ -477,6 +477,72 @@ export function registerTools(server: McpServer, clientIP?: string) {
477
477
  }
478
478
  );
479
479
 
480
+ // ── V2: retry_task (重新投递失败/过期任务) ──
481
+ server.tool(
482
+ "retry_task",
483
+ "Retry a failed, expired, or cancelled task. Resets status to delivered and re-queues in inbox.",
484
+ {
485
+ task_id: z.string().min(1).max(200).describe("Task ID to retry"),
486
+ from_session: z.string().max(200).optional().default("hub"),
487
+ },
488
+ async ({ task_id, from_session }) => {
489
+ console.log(`[${ts()}] ${from_session} → retry_task → ${task_id.slice(0, 8)}`);
490
+ // Find the original task
491
+ const task = db.query<any, [string]>(
492
+ "SELECT * FROM tasks WHERE task_id = ?1"
493
+ ).get(task_id);
494
+ if (!task) {
495
+ return { content: [{ type: "text" as const, text: JSON.stringify({ ok: false, error: "task not found" }) }] };
496
+ }
497
+ if (!["failed", "expired", "cancelled"].includes(task.status)) {
498
+ return { content: [{ type: "text" as const, text: JSON.stringify({ ok: false, error: `task status is ${task.status}, not retryable` }) }] };
499
+ }
500
+ try {
501
+ db.run("BEGIN IMMEDIATE");
502
+ // Reset task status
503
+ db.run(
504
+ `UPDATE tasks SET status = 'delivered', result = NULL, completed_at = NULL, started_at = NULL, delivered_at = datetime('now'), expires_at = datetime('now', '+1 hour')
505
+ WHERE task_id = ?1`,
506
+ [task_id]
507
+ );
508
+ // Re-queue in inbox with new ID (original ID may already exist)
509
+ const retryInboxId = uuidv4();
510
+ db.run(
511
+ `INSERT INTO inbox (id, session_name, type, priority, content, from_session, requires_response)
512
+ VALUES (?1, ?2, 'task', ?3, ?4, ?5, 'reply')`,
513
+ [retryInboxId, task.to_name, task.priority, task.content, from_session]
514
+ );
515
+ db.run("COMMIT");
516
+ } catch (e) {
517
+ try { db.run("ROLLBACK"); } catch {}
518
+ throw e;
519
+ }
520
+ // SSE push
521
+ pushEvent(task.to_name, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
522
+ return {
523
+ content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, retried_to: task.to_name }) }],
524
+ };
525
+ }
526
+ );
527
+
528
+ // ── V2: get_task (查询任务状态) ──
529
+ server.tool(
530
+ "get_task",
531
+ "Get task details by task_id. Returns status, result, timestamps.",
532
+ {
533
+ task_id: z.string().min(1).max(200).describe("Task ID to query"),
534
+ },
535
+ async ({ task_id }) => {
536
+ const task = db.query<any, [string]>("SELECT * FROM tasks WHERE task_id = ?1").get(task_id);
537
+ return {
538
+ content: [{
539
+ type: "text" as const,
540
+ text: JSON.stringify(task ? { ok: true, task } : { ok: false, error: "task not found" }),
541
+ }],
542
+ };
543
+ }
544
+ );
545
+
480
546
  server.tool(
481
547
  "broadcast",
482
548
  "Send a message to multiple sessions.",