agent-coord-mcp 0.25.4 → 0.25.5
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/server.js +1 -1
- package/hooks/submit.mjs +33 -7
- package/hooks/tmux-pusher.mjs +6 -2
- package/package.json +1 -1
- package/scripts/check-test-count.mjs +1 -1
- package/scripts/coord-pusher.mjs +6 -7
- package/src/server.ts +1 -1
package/dist/server.js
CHANGED
|
@@ -160,7 +160,7 @@ function buildServer(initialBound, opts = {}) {
|
|
|
160
160
|
}
|
|
161
161
|
const server = new McpServer({
|
|
162
162
|
name: "agent-coord",
|
|
163
|
-
version: "0.25.
|
|
163
|
+
version: "0.25.5",
|
|
164
164
|
});
|
|
165
165
|
const addTool = (name, description, inputSchema, cb) => {
|
|
166
166
|
server.registerTool(name, { description, inputSchema: z.object(inputSchema) }, async (args) => cb((args ?? {})));
|
package/hooks/submit.mjs
CHANGED
|
@@ -182,6 +182,20 @@ export function partitionStyledLine(styledLine) {
|
|
|
182
182
|
// lesson is narrower than "test on a real TUI": a check that reads scrollback
|
|
183
183
|
// has to be tested against a pane that HAS scrollback.
|
|
184
184
|
//
|
|
185
|
+
// Claude Code collapses a long bracketed paste into a chip in the input:
|
|
186
|
+
// ❯ [Pasted text #14 +9 lines]
|
|
187
|
+
// That chip IS the landed paste — the full payload never appears as plain
|
|
188
|
+
// text. Treating it as "not landed" was the intermittent Enter miss David hit:
|
|
189
|
+
// landedInInput returned false → Enter was refused → chip sat forever. The
|
|
190
|
+
// opposite failure (chip still sitting after Enter, verifier saying "gone"
|
|
191
|
+
// because the payload string isn't in the chip label) is handled in
|
|
192
|
+
// stillInInput below.
|
|
193
|
+
export const PASTE_CHIP_RE = /\[Pasted text #\d+/i;
|
|
194
|
+
|
|
195
|
+
export function isPasteChip(draft) {
|
|
196
|
+
return PASTE_CHIP_RE.test(String(draft ?? ""));
|
|
197
|
+
}
|
|
198
|
+
|
|
185
199
|
// Returns true = still waiting in the input, false = gone (submitted),
|
|
186
200
|
// null = UNKNOWN. Unknown is never upgraded to a confirmation: no pane text,
|
|
187
201
|
// or no line matching AGENT_COORD_PROMPT_PATTERN, means we cannot tell.
|
|
@@ -191,7 +205,12 @@ export function stillInInput(paneText, payload) {
|
|
|
191
205
|
if (!needle) return false;
|
|
192
206
|
const { draft } = readPaneState(paneText);
|
|
193
207
|
if (draft === null) return null; // no recognizable input line — cannot tell
|
|
194
|
-
|
|
208
|
+
const d = squash(draft);
|
|
209
|
+
if (!d) return false;
|
|
210
|
+
// A paste chip still in the input means Enter did not submit — regardless
|
|
211
|
+
// of whether the full payload string is visible inside the chip label.
|
|
212
|
+
if (isPasteChip(d)) return true;
|
|
213
|
+
return d.includes(needle);
|
|
195
214
|
}
|
|
196
215
|
|
|
197
216
|
// Has the PASTED payload appeared in the input line yet? Distinct from
|
|
@@ -209,6 +228,8 @@ export function landedInInput(paneText, payload) {
|
|
|
209
228
|
if (draft === null) return null;
|
|
210
229
|
const d = squash(draft);
|
|
211
230
|
if (!d) return false;
|
|
231
|
+
// Claude Code paste chip = landed (collapsed). See PASTE_CHIP_RE.
|
|
232
|
+
if (isPasteChip(d)) return true;
|
|
212
233
|
if (d.includes(needle.slice(0, 32))) return true; // draft carries the payload head
|
|
213
234
|
return needle.includes(d) && d.length >= 8; // visible draft is a genuine fragment of the payload
|
|
214
235
|
}
|
|
@@ -336,12 +357,17 @@ export async function submitControl(deps, payload) {
|
|
|
336
357
|
// run(args) -> {status, stdout, stderr} // spawnSync-shaped
|
|
337
358
|
// target, buffer // tmux -t target, buffer name
|
|
338
359
|
//
|
|
339
|
-
// Returns {submitted, verified, reason?, attempts}.
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
|
|
360
|
+
// Returns {submitted, verified, reason?, attempts}.
|
|
361
|
+
//
|
|
362
|
+
// Bracketed peer content verifies by DEFAULT. The old hot-path shortcut
|
|
363
|
+
// (`verify:false` → fire-and-forget Enter) left Claude Code paste chips
|
|
364
|
+
// (`[Pasted text #N +X lines]`) sitting unsubmitted whenever Enter was
|
|
365
|
+
// dropped or refused — no retry, no receipt of failure. Control commands
|
|
366
|
+
// already verified; peer traffic gets the same loop now. Pass
|
|
367
|
+
// `verify:false` explicitly to opt out (unknown-TUI / test escape hatch).
|
|
368
|
+
export async function pasteAndSubmit(deps, payload, { bracketed = false, verify } = {}) {
|
|
344
369
|
const { run, runStdin, target, buffer } = deps;
|
|
370
|
+
const doVerify = verify === undefined ? bracketed : verify;
|
|
345
371
|
|
|
346
372
|
await runStdin(["load-buffer", "-b", buffer, "-"], payload);
|
|
347
373
|
const paste = run(["paste-buffer", ...(bracketed ? ["-p"] : []), "-b", buffer, "-t", target, "-d"]);
|
|
@@ -378,7 +404,7 @@ export async function pasteAndSubmit(deps, payload, { bracketed = false, verify
|
|
|
378
404
|
await sleep(ENTER_GAP_MS());
|
|
379
405
|
run(["send-keys", "-t", target, "Enter"]);
|
|
380
406
|
|
|
381
|
-
if (!
|
|
407
|
+
if (!doVerify) return { submitted: true, verified: false, attempts: 1 };
|
|
382
408
|
|
|
383
409
|
const retries = ENTER_RETRIES();
|
|
384
410
|
for (let attempt = 1; ; attempt++) {
|
package/hooks/tmux-pusher.mjs
CHANGED
|
@@ -428,8 +428,12 @@ async function injectViaTmux(batch) {
|
|
|
428
428
|
let run = [];
|
|
429
429
|
const flushRun = async () => {
|
|
430
430
|
if (run.length === 0) return;
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
// bracketed=true → submit.mjs verifies by default (paste-chip land + Enter retry).
|
|
432
|
+
const outcome = await pasteAndSubmit(formatBatch(run), true);
|
|
433
|
+
if (outcome && outcome.submitted === false) {
|
|
434
|
+
process.stderr.write(`[tmux-pusher] peer paste NOT submitted: ${outcome.reason}\n`);
|
|
435
|
+
}
|
|
436
|
+
writeReceipts(run, outcome); // stamp only AFTER the paste+submit resolves
|
|
433
437
|
run = [];
|
|
434
438
|
};
|
|
435
439
|
for (const m of batch) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-coord-mcp",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.5",
|
|
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": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { spawn } from "node:child_process";
|
|
24
24
|
|
|
25
|
-
const EXPECTED_TESTS =
|
|
25
|
+
const EXPECTED_TESTS = 291;
|
|
26
26
|
|
|
27
27
|
const expected = Number(process.env.AGENT_COORD_EXPECTED_TESTS ?? EXPECTED_TESTS);
|
|
28
28
|
// Same glob the suite always used — `--test test/` would recurse differently
|
package/scripts/coord-pusher.mjs
CHANGED
|
@@ -300,8 +300,9 @@ async function injectViaTmux(batch) {
|
|
|
300
300
|
let run = [];
|
|
301
301
|
const flushRun = async () => {
|
|
302
302
|
if (run.length === 0) return;
|
|
303
|
-
|
|
304
|
-
await
|
|
303
|
+
// bracketed=true → submit.mjs verifies by default (paste-chip land + Enter retry).
|
|
304
|
+
const outcome = await pasteAndSubmit(formatBatch(run), true);
|
|
305
|
+
await reportReceipts(run, outcome); // stamp only AFTER the paste+submit resolves
|
|
305
306
|
run = [];
|
|
306
307
|
};
|
|
307
308
|
for (const m of batch) {
|
|
@@ -328,11 +329,9 @@ async function injectViaTmux(batch) {
|
|
|
328
329
|
// Wire counterpart to tmux-pusher's writeReceipts: this pusher cannot append
|
|
329
330
|
// to receipts/<id>.jsonl on the server's filesystem, so it reports each
|
|
330
331
|
// delivery over MCP and the server writes the same receipt line the local
|
|
331
|
-
// path does. `outcome`
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
// `submitted` means "typed but unverified" and must never be upgraded to a
|
|
335
|
-
// claim of execution this pusher did not make.
|
|
332
|
+
// path does. `outcome` carries what submit verification observed —
|
|
333
|
+
// submitted/verified/reason are forwarded for both control commands and
|
|
334
|
+
// (as of 0.25.5) bracketed peer pastes.
|
|
336
335
|
//
|
|
337
336
|
// Best-effort by design: a failed report must not break delivery or crash the
|
|
338
337
|
// inject loop (the message IS in the pane by the time we get here). The
|
package/src/server.ts
CHANGED