agent-coord-mcp 0.13.0 → 0.14.0
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/README.md +1 -1
- package/dist/server.js +0 -0
- package/hooks/tier.mjs +18 -10
- package/package.json +1 -1
- package/scripts/coord-pusher.mjs +15 -5
package/README.md
CHANGED
|
@@ -293,7 +293,7 @@ Either way, `list_agents` will show the agent with `transport: "tmux-push"` so p
|
|
|
293
293
|
|
|
294
294
|
Under the hood: [`hooks/tmux-pusher.mjs`](./hooks/tmux-pusher.mjs) is the daemon. It watches `~/agent-coord/inbox/<id>.jsonl` (and, when room delivery is on, every channel the agent has joined), debounces bursts (1s default), drops self-posts and `/`-prefixed text (except allowlisted control commands — see [below](#clearing-sub-agent-context-send_command)), optionally enforces the peer allowlist, then pastes batches via `tmux load-buffer` → `paste-buffer -d` → `send-keys Enter`. Single-flight so two batches never overlap.
|
|
295
295
|
|
|
296
|
-
**Delivery tiers.** Not every message deserves an agent turn. Only push-now traffic wakes the pane: `BLOCKER:`, `DAVID_DECISION:`, literal `GO:` work orders, `SCOPE:`/`SCOPE CHANGE:` from a trusted sender (coordinator/gate ids resolved from the registry), `DONE:` when this agent is a gate runner (QA/coordinator — from its registry role, re-resolved every 30s, overridable with `AGENT_COORD_GATE_RUNNER=1|0`), control commands, and server-flagged messages like the post-`/clear` identity reminder. Prefixes are literal and case-sensitive. Everything else (`FYI:`, `AGENT_ACTION:`, `RISK:`, chatter) queues silently and rides the next urgent push as one coalesced `[agent-coord]
|
|
296
|
+
**Delivery tiers.** Not every message deserves an agent turn. Only push-now traffic wakes the pane: `BLOCKER:`, `DAVID_DECISION:`, literal `GO:` work orders, `SCOPE:`/`SCOPE CHANGE:` from a trusted sender (coordinator/gate ids resolved from the registry), `DONE:` when this agent is a gate runner (QA/coordinator — from its registry role, re-resolved every 30s, overridable with `AGENT_COORD_GATE_RUNNER=1|0`), control commands, and server-flagged messages like the post-`/clear` identity reminder. Prefixes are literal and case-sensitive. Everything else (`FYI:`, `AGENT_ACTION:`, `RISK:`, chatter) queues silently and rides the next urgent push as one coalesced digest block (banner `[agent-coord] +N routine (pre-consumed, FYI, no reply):`). The on-disk cursor (shared with `read_messages`) advances only **after** a paste is confirmed, so a crash or `SIGTERM` rewinds and redelivers (at-least-once) — queued traffic stays **unread** and is never lost. An idle agent with only routine backlog is never woken; routine ops cost zero tokens. `AGENT_COORD_TIERS=0` restores legacy push-everything. The classifier and queue are pure ([`hooks/tier.mjs`](./hooks/tier.mjs)). Known trade-offs: routine messages get their delivery receipt only when their digest lands; a digest can arrive without the banner if a push carries only queued routine after a control command; within one paste, urgent lines precede the digest regardless of arrival order.
|
|
297
297
|
|
|
298
298
|
**Caveats — read these.**
|
|
299
299
|
|
package/dist/server.js
CHANGED
|
File without changes
|
package/hooks/tier.mjs
CHANGED
|
@@ -68,25 +68,33 @@ export class TierQueue {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// The per-message PARSE CONTRACT line. Agent harnesses read from/room/text
|
|
72
|
+
// back out of this, so its shape is load-bearing and MUST stay byte-identical
|
|
73
|
+
// to coord-pusher.mjs's `injectLine`. Compact form (v0.14.0, salvaged from
|
|
74
|
+
// v0.8.10): ` [<kind> <HH:MM> <from>] <text>` where kind drops the leading
|
|
75
|
+
// "room " ("room #general" → "#general"), the timestamp is HH:MM UTC, and the
|
|
76
|
+
// "from=" label is dropped (bare id). kind/time/from never contain spaces
|
|
77
|
+
// (ids are sanitized), so a parser splits on the first "] " unambiguously.
|
|
78
|
+
export function injectLine(m) {
|
|
79
|
+
const tag = String(m.kind ?? "").replace(/^room /, "");
|
|
80
|
+
const d = new Date(m.ts ?? 0);
|
|
81
|
+
const hhmm = `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`;
|
|
82
|
+
return ` [${tag} ${hhmm} ${m.from}] ${m.text ?? ""}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
// Render one delivery: urgent verbatim under the banner, then at most ONE
|
|
72
86
|
// coalesced digest block carrying the queued routine messages.
|
|
73
87
|
export function formatBatch(batch) {
|
|
74
|
-
const line = (m) =>
|
|
75
|
-
` [${m.kind} ${new Date(m.ts ?? 0).toISOString()} from=${m.from}] ${m.text ?? ""}`;
|
|
76
88
|
const urgent = batch.filter((m) => m.tier !== "routine");
|
|
77
89
|
const routine = batch.filter((m) => m.tier === "routine");
|
|
78
90
|
const lines = [];
|
|
79
91
|
if (urgent.length > 0) {
|
|
80
|
-
lines.push(
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
for (const m of urgent) lines.push(line(m));
|
|
92
|
+
lines.push("[agent-coord] msgs (pre-consumed, don't re-read):");
|
|
93
|
+
for (const m of urgent) lines.push(injectLine(m));
|
|
84
94
|
}
|
|
85
95
|
if (routine.length > 0) {
|
|
86
|
-
lines.push(
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
for (const m of routine) lines.push(line(m));
|
|
96
|
+
lines.push(`[agent-coord] +${routine.length} routine (pre-consumed, FYI, no reply):`);
|
|
97
|
+
for (const m of routine) lines.push(injectLine(m));
|
|
90
98
|
}
|
|
91
99
|
return lines.join("\n");
|
|
92
100
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-coord-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "File-backed MCP server for coordinating multiple AI coding agents (Claude Code, Cursor, Cline, etc.). Local stdio or networked over Streamable HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/scripts/coord-pusher.mjs
CHANGED
|
@@ -168,13 +168,23 @@ async function flush() {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
// The per-message PARSE CONTRACT line — MUST stay byte-identical to
|
|
172
|
+
// hooks/tier.mjs's injectLine (agent harnesses parse from/room/text out of
|
|
173
|
+
// it). Compact form (v0.14.0): ` [<kind> <HH:MM> <from>] <text>`, kind drops
|
|
174
|
+
// the leading "room ", timestamp is HH:MM UTC, no "from=" label. This pusher
|
|
175
|
+
// is standalone (may deploy without hooks/), so the helper is duplicated
|
|
176
|
+
// rather than imported; test/tier.test.mjs locks both to the same shape.
|
|
177
|
+
function injectLine(m) {
|
|
178
|
+
const tag = String(m.kind ?? "").replace(/^room /, "");
|
|
179
|
+
const d = new Date(m.ts ?? 0);
|
|
180
|
+
const hhmm = `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`;
|
|
181
|
+
return ` [${tag} ${hhmm} ${m.from}] ${m.text ?? ""}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
171
184
|
function formatBatch(batch) {
|
|
172
|
-
const lines = [
|
|
173
|
-
"[agent-coord] incoming peer messages — already consumed from your inbox, do not call read_messages for them:",
|
|
174
|
-
];
|
|
185
|
+
const lines = ["[agent-coord] msgs (pre-consumed, don't re-read):"];
|
|
175
186
|
for (const m of batch) {
|
|
176
|
-
|
|
177
|
-
lines.push(` [${m.kind} ${ts} from=${m.from}] ${m.text ?? ""}`);
|
|
187
|
+
lines.push(injectLine(m));
|
|
178
188
|
}
|
|
179
189
|
return lines.join("\n");
|
|
180
190
|
}
|