cross-agent-teams-mcp 0.5.1 → 0.5.3
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.js +67 -49
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/src/daemon/cleanup.ts +21 -26
- package/src/mcp/get-inbox.ts +42 -23
- package/src/mcp/tools.ts +7 -1
- package/src/mcp/transport.ts +1 -4
- package/src/storage/agents-repo.ts +10 -2
- package/src/storage/schema.ts +15 -0
package/dist/cli.js
CHANGED
|
@@ -195,10 +195,18 @@ function migrateMessagesNeedReplyColumn(db) {
|
|
|
195
195
|
if (existing.has("need_reply")) return;
|
|
196
196
|
db.exec(`ALTER TABLE messages ADD COLUMN need_reply INTEGER NOT NULL DEFAULT 1`);
|
|
197
197
|
}
|
|
198
|
+
function migrateAgentsCursorWatermark(db) {
|
|
199
|
+
db.exec(
|
|
200
|
+
`UPDATE agents
|
|
201
|
+
SET last_processed_event_id = COALESCE((SELECT MAX(event_id) FROM events), 0)
|
|
202
|
+
WHERE last_processed_event_id = 0`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
198
205
|
function applySchema(db) {
|
|
199
206
|
for (const sql of DDL) db.exec(sql);
|
|
200
207
|
migrateAgentsDeliveryColumns(db);
|
|
201
208
|
migrateMessagesNeedReplyColumn(db);
|
|
209
|
+
migrateAgentsCursorWatermark(db);
|
|
202
210
|
}
|
|
203
211
|
|
|
204
212
|
// src/daemon/auth.ts
|
|
@@ -425,9 +433,11 @@ var AgentsRepo = class {
|
|
|
425
433
|
this.db.prepare(
|
|
426
434
|
`INSERT INTO agents (
|
|
427
435
|
agent_id, agent_type, agent_type_name, team, role, name, model, registered_at, last_seen_at,
|
|
428
|
-
tmux_pane_id, claude_ui_pid, runtime_ui_pid, delivery_kind, delivery_payload
|
|
436
|
+
tmux_pane_id, claude_ui_pid, runtime_ui_pid, delivery_kind, delivery_payload,
|
|
437
|
+
last_processed_event_id
|
|
429
438
|
)
|
|
430
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
439
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
440
|
+
COALESCE((SELECT MAX(event_id) FROM events), 0))
|
|
431
441
|
ON CONFLICT (team, name) DO UPDATE SET
|
|
432
442
|
agent_type = excluded.agent_type,
|
|
433
443
|
agent_type_name = excluded.agent_type_name,
|
|
@@ -1364,27 +1374,40 @@ var GetInboxService = class {
|
|
|
1364
1374
|
const caller = this.agents.findById(args.caller);
|
|
1365
1375
|
if (!caller) return { messages: [], has_more: false, last_event_id: args.since_event_id ?? 0 };
|
|
1366
1376
|
const callerTeam = caller.team;
|
|
1367
|
-
const
|
|
1377
|
+
const callerRoleRow = this.db.prepare("SELECT role, last_processed_event_id FROM agents WHERE agent_id=?").get(args.caller);
|
|
1378
|
+
const callerRole = callerRoleRow?.role;
|
|
1379
|
+
const storedCursor = callerRoleRow?.last_processed_event_id ?? 0;
|
|
1368
1380
|
const limit = Math.min(args.limit ?? 50, 200);
|
|
1369
|
-
const
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1381
|
+
const implicit = args.since_event_id === void 0;
|
|
1382
|
+
const effectiveSince = implicit ? storedCursor : args.since_event_id;
|
|
1383
|
+
const tx = this.db.transaction(() => {
|
|
1384
|
+
const rows = this.db.prepare(
|
|
1385
|
+
`SELECT m.id, m.event_id, m.from_team, m.to_team, m.from_agent_id, m.to_agent_id, m.to_role, m.subject, m.body, m.need_reply, m.sent_at,
|
|
1386
|
+
a.role as from_role
|
|
1387
|
+
FROM messages m
|
|
1388
|
+
LEFT JOIN agents a ON a.agent_id = m.from_agent_id
|
|
1389
|
+
WHERE m.to_team = ?
|
|
1390
|
+
AND m.event_id > ?
|
|
1391
|
+
AND ( m.to_agent_id = ? OR (m.to_role IS NOT NULL AND m.to_role = ?) )
|
|
1392
|
+
ORDER BY m.event_id ASC
|
|
1393
|
+
LIMIT ?`
|
|
1394
|
+
).all(callerTeam, effectiveSince, args.caller, callerRole ?? "__none__", limit + 1);
|
|
1395
|
+
const has_more = rows.length > limit;
|
|
1396
|
+
const trimmed = (has_more ? rows.slice(0, limit) : rows).map((row) => ({
|
|
1397
|
+
...row,
|
|
1398
|
+
need_reply: row.need_reply === 1
|
|
1399
|
+
}));
|
|
1400
|
+
const last_event_id = trimmed.length > 0 ? trimmed[trimmed.length - 1].event_id : effectiveSince;
|
|
1401
|
+
if (implicit && last_event_id > storedCursor) {
|
|
1402
|
+
this.db.prepare(
|
|
1403
|
+
`UPDATE agents
|
|
1404
|
+
SET last_processed_event_id = ?
|
|
1405
|
+
WHERE agent_id = ? AND last_processed_event_id < ?`
|
|
1406
|
+
).run(last_event_id, args.caller, last_event_id);
|
|
1407
|
+
}
|
|
1408
|
+
return { messages: trimmed, has_more, last_event_id };
|
|
1409
|
+
});
|
|
1410
|
+
return tx();
|
|
1388
1411
|
}
|
|
1389
1412
|
};
|
|
1390
1413
|
|
|
@@ -3701,7 +3724,13 @@ function registerBusinessTools(server, db, getCallerAgentId, fanout, onRegisterS
|
|
|
3701
3724
|
"get_inbox",
|
|
3702
3725
|
{
|
|
3703
3726
|
title: "Get inbox",
|
|
3704
|
-
description:
|
|
3727
|
+
description: [
|
|
3728
|
+
"Return messages addressed to the caller (by agent_id or matching role) within the caller team.",
|
|
3729
|
+
"Default behaviour (since_event_id omitted): the daemon reads the caller's server-side cursor (`agents.last_processed_event_id`), returns mail past it, and ADVANCES the cursor to the highest returned event_id in the same transaction. Subsequent default calls return only newer mail.",
|
|
3730
|
+
"Pagination via `limit` advances the cursor only to the last RETURNED event_id; the next default call resumes from there.",
|
|
3731
|
+
"Explicit `since_event_id` (any number, including 0) is read-only inspection: the daemon uses the supplied value as the lower bound and does NOT advance the stored cursor \u2014 useful for re-reading history or debugging without disturbing live read position.",
|
|
3732
|
+
"Retention: messages older than 30 days are deleted by the cleanup routine regardless of read state. Agents that go offline for more than 30 days forfeit any unread mail in that window."
|
|
3733
|
+
].join(" "),
|
|
3705
3734
|
inputSchema: {
|
|
3706
3735
|
since_event_id: z3.number().int().optional(),
|
|
3707
3736
|
limit: z3.number().int().optional()
|
|
@@ -4196,14 +4225,10 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
4196
4225
|
return reply;
|
|
4197
4226
|
});
|
|
4198
4227
|
function reapOrphanSessions(now, graceMs = 6e4) {
|
|
4199
|
-
for (const
|
|
4228
|
+
for (const session of sessions.values()) {
|
|
4200
4229
|
if (session.agentIdHolder.current !== void 0) continue;
|
|
4201
4230
|
const ageMs = now - session.createdAt;
|
|
4202
4231
|
if (ageMs < graceMs) continue;
|
|
4203
|
-
try {
|
|
4204
|
-
log(`mcp orphan session reap: sid=${sid} age_s=${Math.round(ageMs / 1e3)}`);
|
|
4205
|
-
} catch {
|
|
4206
|
-
}
|
|
4207
4232
|
try {
|
|
4208
4233
|
void session.transport.close();
|
|
4209
4234
|
} catch {
|
|
@@ -4214,30 +4239,23 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
4214
4239
|
}
|
|
4215
4240
|
|
|
4216
4241
|
// src/daemon/cleanup.ts
|
|
4217
|
-
var DELETE_AGED_EVENTS_SQL = `
|
|
4218
|
-
WITH online_cursor AS (
|
|
4219
|
-
SELECT team AS to_team, MIN(last_processed_event_id) AS min_cursor
|
|
4220
|
-
FROM agents
|
|
4221
|
-
WHERE last_seen_at >= :cutoffOnline
|
|
4222
|
-
GROUP BY team
|
|
4223
|
-
)
|
|
4224
|
-
DELETE FROM events
|
|
4225
|
-
WHERE created_at < :ageCutoff
|
|
4226
|
-
AND (
|
|
4227
|
-
events.to_team NOT IN (SELECT to_team FROM online_cursor)
|
|
4228
|
-
OR events.event_id < (
|
|
4229
|
-
SELECT min_cursor FROM online_cursor WHERE online_cursor.to_team = events.to_team
|
|
4230
|
-
)
|
|
4231
|
-
)
|
|
4232
|
-
`;
|
|
4233
4242
|
function runCleanup(db, opts = {}) {
|
|
4234
4243
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
4235
|
-
const maxAgeDays = opts.maxAgeDays ??
|
|
4236
|
-
const onlineWindowMs = opts.onlineWindowMs ?? 5 * 60 * 1e3;
|
|
4244
|
+
const maxAgeDays = opts.maxAgeDays ?? 30;
|
|
4237
4245
|
const ageCutoff = new Date(now.getTime() - maxAgeDays * 86400 * 1e3).toISOString();
|
|
4238
|
-
const
|
|
4239
|
-
|
|
4240
|
-
|
|
4246
|
+
const deleteStatus = db.prepare(
|
|
4247
|
+
`DELETE FROM message_delivery_status
|
|
4248
|
+
WHERE message_id IN (SELECT id FROM messages WHERE sent_at < ?)`
|
|
4249
|
+
);
|
|
4250
|
+
const deleteMessages = db.prepare(`DELETE FROM messages WHERE sent_at < ?`);
|
|
4251
|
+
const deleteEvents = db.prepare(`DELETE FROM events WHERE created_at < ?`);
|
|
4252
|
+
const tx = db.transaction(() => {
|
|
4253
|
+
const s = deleteStatus.run(ageCutoff);
|
|
4254
|
+
const m = deleteMessages.run(ageCutoff);
|
|
4255
|
+
const e = deleteEvents.run(ageCutoff);
|
|
4256
|
+
return Number(s.changes) + Number(m.changes) + Number(e.changes);
|
|
4257
|
+
});
|
|
4258
|
+
return { deleted: tx() };
|
|
4241
4259
|
}
|
|
4242
4260
|
|
|
4243
4261
|
// src/daemon/sse-fanout.ts
|