@sleep2agi/commhub-server 0.5.0-preview.5 → 0.5.0-preview.6
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/package.json +1 -1
- package/src/db.ts +1 -1
- package/src/index.ts +6 -1
- package/src/tools.ts +53 -2
package/package.json
CHANGED
package/src/db.ts
CHANGED
|
@@ -147,7 +147,7 @@ db.exec(`
|
|
|
147
147
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
148
148
|
);
|
|
149
149
|
|
|
150
|
-
CREATE INDEX IF NOT EXISTS
|
|
150
|
+
CREATE INDEX IF NOT EXISTS idx_task_events_task_time ON task_events(task_id, created_at DESC);
|
|
151
151
|
CREATE INDEX IF NOT EXISTS idx_task_events_created ON task_events(created_at);
|
|
152
152
|
`);
|
|
153
153
|
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
3
3
|
import { z } from "zod/v4";
|
|
4
4
|
import { registerTools } from "./tools.js";
|
|
5
|
-
import { db } from "./db.js";
|
|
5
|
+
import { db, logTaskEvent } from "./db.js";
|
|
6
6
|
import { createSSEStream, pushEvent, pushBroadcast, getSSEStats } from "./push.js";
|
|
7
7
|
|
|
8
8
|
const PORT = Number(process.env.PORT) || 9200;
|
|
@@ -84,6 +84,11 @@ setInterval(() => {
|
|
|
84
84
|
);
|
|
85
85
|
if (result.changes > 0) {
|
|
86
86
|
console.log(`[patrol] expired ${result.changes} stale task(s)`);
|
|
87
|
+
// Log events for expired tasks
|
|
88
|
+
const expired = db.query<{ task_id: string }, []>(
|
|
89
|
+
"SELECT task_id FROM tasks WHERE status = 'expired' AND completed_at >= datetime('now', '-1 minute')"
|
|
90
|
+
).all();
|
|
91
|
+
for (const t of expired) logTaskEvent(t.task_id, null, "expired", "patrol");
|
|
87
92
|
}
|
|
88
93
|
} catch {}
|
|
89
94
|
}, 5 * 60 * 1000);
|
package/src/tools.ts
CHANGED
|
@@ -78,11 +78,18 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
78
78
|
// V2: sync tasks table — report_status(working) → tasks.running
|
|
79
79
|
if (status === "working" && task) {
|
|
80
80
|
try {
|
|
81
|
-
db.run(
|
|
81
|
+
const runResult = db.run(
|
|
82
82
|
`UPDATE tasks SET status = 'running', started_at = datetime('now')
|
|
83
83
|
WHERE to_name = ?1 AND status IN ('delivered', 'acked') AND content = ?2`,
|
|
84
84
|
[alias, task]
|
|
85
85
|
);
|
|
86
|
+
if (runResult.changes > 0) {
|
|
87
|
+
// Find task_id for logging
|
|
88
|
+
const t = db.query<{ task_id: string }, [string, string]>(
|
|
89
|
+
"SELECT task_id FROM tasks WHERE to_name = ?1 AND content = ?2 AND status = 'running' ORDER BY started_at DESC LIMIT 1"
|
|
90
|
+
).get(alias, task);
|
|
91
|
+
if (t) logTaskEvent(t.task_id, null, "running", alias);
|
|
92
|
+
}
|
|
86
93
|
} catch {}
|
|
87
94
|
}
|
|
88
95
|
|
|
@@ -180,6 +187,11 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
db.run("COMMIT");
|
|
190
|
+
// Log event after commit
|
|
191
|
+
const updatedTaskId = taskUpdate.changes > 0 ? task : (db.query<{ task_id: string }, [string]>(
|
|
192
|
+
"SELECT task_id FROM tasks WHERE to_name = ?1 AND status = 'replied' ORDER BY completed_at DESC LIMIT 1"
|
|
193
|
+
).get(alias)?.task_id);
|
|
194
|
+
if (updatedTaskId) logTaskEvent(updatedTaskId, null, "replied", alias, "report_completion");
|
|
183
195
|
} catch (e) {
|
|
184
196
|
try { db.run("ROLLBACK"); } catch {}
|
|
185
197
|
throw e;
|
|
@@ -419,6 +431,7 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
419
431
|
async ({ alias, text, in_reply_to, status: replyStatus, from_session }) => {
|
|
420
432
|
console.log(`[${ts()}] ${from_session} → send_reply (${replyStatus}) → ${alias}: ${text.slice(0, 60)}`);
|
|
421
433
|
const id = uuidv4();
|
|
434
|
+
let replyLogged = false;
|
|
422
435
|
try {
|
|
423
436
|
db.run("BEGIN IMMEDIATE");
|
|
424
437
|
db.run(
|
|
@@ -437,7 +450,7 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
437
450
|
if (result.changes === 0) {
|
|
438
451
|
console.log(`[${ts()}] ⚠ send_reply: task ${in_reply_to?.slice(0, 8)} not found or already terminal`);
|
|
439
452
|
} else {
|
|
440
|
-
|
|
453
|
+
replyLogged = true;
|
|
441
454
|
}
|
|
442
455
|
}
|
|
443
456
|
db.run("COMMIT");
|
|
@@ -446,6 +459,9 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
446
459
|
throw e;
|
|
447
460
|
}
|
|
448
461
|
|
|
462
|
+
// Log event after commit (outside transaction)
|
|
463
|
+
if (replyLogged && in_reply_to) logTaskEvent(in_reply_to, null, replyStatus, from_session, text.slice(0, 200));
|
|
464
|
+
|
|
449
465
|
const session = db.query<any, [string]>("SELECT status FROM sessions WHERE alias = ?1").get(alias);
|
|
450
466
|
pushEvent(alias, { type: "new_reply", from: from_session, message_id: id, in_reply_to, status: replyStatus });
|
|
451
467
|
|
|
@@ -472,6 +488,7 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
472
488
|
`UPDATE tasks SET status = 'acked' WHERE task_id = ?1 AND status IN ('created', 'delivered')`,
|
|
473
489
|
[task_id]
|
|
474
490
|
);
|
|
491
|
+
if (result.changes > 0) logTaskEvent(task_id, "delivered", "acked", from_session);
|
|
475
492
|
return {
|
|
476
493
|
content: [{
|
|
477
494
|
type: "text" as const,
|
|
@@ -548,6 +565,40 @@ export function registerTools(server: McpServer, clientIP?: string) {
|
|
|
548
565
|
}
|
|
549
566
|
);
|
|
550
567
|
|
|
568
|
+
// ── V2: reassign_task (转移任务到另一个 agent) ──
|
|
569
|
+
server.tool(
|
|
570
|
+
"reassign_task",
|
|
571
|
+
"Reassign a task to a different agent. Works on any non-terminal task (delivered/acked/running).",
|
|
572
|
+
{
|
|
573
|
+
task_id: z.string().min(1).max(200).describe("Task ID to reassign"),
|
|
574
|
+
new_alias: z.string().min(1).max(200).describe("Target agent alias"),
|
|
575
|
+
from_session: z.string().max(200).optional().default("hub"),
|
|
576
|
+
},
|
|
577
|
+
async ({ task_id, new_alias, from_session }) => {
|
|
578
|
+
console.log(`[${ts()}] ${from_session} → reassign_task → ${task_id.slice(0, 8)} → ${new_alias}`);
|
|
579
|
+
const task = db.query<any, [string]>("SELECT * FROM tasks WHERE task_id = ?1").get(task_id);
|
|
580
|
+
if (!task) return { content: [{ type: "text" as const, text: JSON.stringify({ ok: false, error: "task not found" }) }] };
|
|
581
|
+
if (["replied", "failed", "cancelled", "expired"].includes(task.status)) {
|
|
582
|
+
return { content: [{ type: "text" as const, text: JSON.stringify({ ok: false, error: `task is terminal (${task.status})` }) }] };
|
|
583
|
+
}
|
|
584
|
+
const oldAlias = task.to_name;
|
|
585
|
+
try {
|
|
586
|
+
db.run("BEGIN IMMEDIATE");
|
|
587
|
+
db.run("UPDATE tasks SET to_name = ?1, status = 'delivered', started_at = NULL, delivered_at = datetime('now') WHERE task_id = ?2", [new_alias, task_id]);
|
|
588
|
+
const newInboxId = uuidv4();
|
|
589
|
+
db.run("INSERT INTO inbox (id, session_name, type, priority, content, from_session, requires_response) VALUES (?1, ?2, 'task', ?3, ?4, ?5, 'reply')",
|
|
590
|
+
[newInboxId, new_alias, task.priority, task.content, from_session]);
|
|
591
|
+
db.run("COMMIT");
|
|
592
|
+
logTaskEvent(task_id, task.status, "delivered", from_session, `reassign: ${oldAlias} → ${new_alias}`);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
try { db.run("ROLLBACK"); } catch {}
|
|
595
|
+
throw e;
|
|
596
|
+
}
|
|
597
|
+
pushEvent(new_alias, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
|
|
598
|
+
return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, reassigned_from: oldAlias, reassigned_to: new_alias }) }] };
|
|
599
|
+
}
|
|
600
|
+
);
|
|
601
|
+
|
|
551
602
|
server.tool(
|
|
552
603
|
"broadcast",
|
|
553
604
|
"Send a message to multiple sessions.",
|