agent-coord-mcp 0.26.31 → 0.26.33
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 +5 -2
- package/dist/capabilities.js +52 -0
- package/dist/capabilities.js.map +1 -1
- package/dist/seat-identity.js +116 -0
- package/dist/seat-identity.js.map +1 -0
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/pane.js +46 -0
- package/dist/tools/pane.js.map +1 -0
- package/dist/transports/herdr.js +29 -0
- package/dist/transports/herdr.js.map +1 -1
- package/dist/transports/types.js.map +1 -1
- package/package.json +1 -1
- package/scripts/coord-seat.mjs +90 -1
- package/scripts/coord-token.mjs +9 -62
- package/scripts/lib/seat-identity.mjs +14 -0
- package/src/capabilities.ts +50 -0
- package/src/seat-identity.ts +125 -0
- package/src/server.ts +9 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/pane.ts +49 -0
- package/src/transports/herdr.ts +28 -1
- package/src/transports/types.ts +24 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⟨q-927e28cd⟩ ADR-023 d4, NARROW SLICE — A CONSUMER READS A SEAT'S PANE THROUGH THE BUS.
|
|
3
|
+
*
|
|
4
|
+
* The console showed every herdr seat's pane as `410 "pane not found (session gone?)"` for a
|
|
5
|
+
* live seat, because it captured panes itself through tmux and a herdr pane is not a tmux
|
|
6
|
+
* pane. ADR-023 d4 says a consumer asks the bus rather than learning each transport; this is
|
|
7
|
+
* the one question it could not ask, because no bus verb returned what a pane shows.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ READ THROUGH THE TRANSPORT, NEVER AROUND IT (the coordinator's GO, and ADR-023 d1). This
|
|
10
|
+
* module names no field of the marker: it hands the whole marker to `Transport.readPane`,
|
|
11
|
+
* which decides which pane it is, whether it is its own kind, and whether it is still there.
|
|
12
|
+
* qa counted 47 marker-internal reads across 5 modules outside src/transports/; this is not
|
|
13
|
+
* the 48th, and a test says so by reading this file.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ READ-ONLY, INCLUDING OF THE STATE DIR. `loadLiveTransports` deletes markers it judges
|
|
16
|
+
* dead, which is a write; this reads the one marker file and deletes nothing. Whether the
|
|
17
|
+
* pane is gone is the transport's answer, and it answers UNKNOWN, never an empty screen.
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import type { PaneRead, Transport, TransportMarker } from "../transports/types.js";
|
|
21
|
+
import { activeTransport } from "../transports/index.js";
|
|
22
|
+
import { readJson, transportFile } from "../store.js";
|
|
23
|
+
|
|
24
|
+
export const readPaneSchema = {
|
|
25
|
+
agentId: z.string().min(1),
|
|
26
|
+
/** The seat whose pane to read. */
|
|
27
|
+
targetAgentId: z.string().min(1),
|
|
28
|
+
lines: z.number().int().positive().max(2000).optional(),
|
|
29
|
+
/** Keep the terminal's styling (ANSI escapes) rather than plain text. */
|
|
30
|
+
colors: z.boolean().optional(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type ReadPaneResult = { ok: true; agentId: string } & PaneRead;
|
|
34
|
+
|
|
35
|
+
export async function readPaneTool(
|
|
36
|
+
args: { agentId: string; targetAgentId: string; lines?: number; colors?: boolean },
|
|
37
|
+
transport: Transport | null | undefined = activeTransport(),
|
|
38
|
+
): Promise<ReadPaneResult> {
|
|
39
|
+
const agentId = args.targetAgentId;
|
|
40
|
+
const marker = await readJson<TransportMarker | null>(transportFile(agentId), null);
|
|
41
|
+
if (!marker) return { ok: true, agentId, state: "unknown", why: "no transport marker — the seat is not attached, so there is no pane on record to read" };
|
|
42
|
+
if (!transport) return { ok: true, agentId, state: "unknown", why: "no transport is wired in this server process, so nothing can read a pane" };
|
|
43
|
+
if (typeof transport.readPane !== "function") return { ok: true, agentId, state: "unknown", why: `the ${transport.kind} transport cannot read a pane from this host` };
|
|
44
|
+
try {
|
|
45
|
+
return { ok: true, agentId, ...(await transport.readPane(marker, { lines: args.lines, format: args.colors ? "ansi" : "text" })) };
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return { ok: true, agentId, state: "unknown", why: `the transport threw while reading the pane: ${(e as Error).message}` };
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/transports/herdr.ts
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* same reason (identical evidence for a typo and a default).
|
|
35
35
|
*/
|
|
36
36
|
import { spawnSync } from "node:child_process";
|
|
37
|
-
import type { ControlCommand, Liveness, Transport, TransportKind, TransportMarker, TickReading, TickState } from "./types.js";
|
|
37
|
+
import type { ControlCommand, Liveness, PaneRead, Transport, TransportKind, TransportMarker, TickReading, TickState } from "./types.js";
|
|
38
38
|
import { HERDR, TICK_READS_AS, TICK_STORED_AS, targetOf } from "./types.js";
|
|
39
39
|
// ⟨q-dc83023a⟩ The chip grammar and the pane-unsafe byte class are single-sourced in hooks/, shared
|
|
40
40
|
// with both pushers. `hooks/` ships beside `dist/`, so these resolve the same when installed.
|
|
@@ -587,6 +587,33 @@ export class HerdrTransport implements Transport {
|
|
|
587
587
|
return { readable: false, why: `herdr reports agent_status "${status}" for pane ${target} — no agent state is published there, which is a MISSING signal, not a calm seat` };
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
+
/**
|
|
591
|
+
* ⟨q-927e28cd⟩ THE SCREEN, FOR A CONSUMER — herdr's own `pane read`, the same read `push`
|
|
592
|
+
* verifies with. Same guards, in the same order, as `readTick`: a marker that is not herdr's,
|
|
593
|
+
* one with no target, or herdr absent answers UNKNOWN naming which; a pane herdr cannot find
|
|
594
|
+
* is UNKNOWN too — it is gone, and a gone pane has no screen, not an empty one.
|
|
595
|
+
*/
|
|
596
|
+
async readPane(marker: TransportMarker, opts: { lines?: number; format?: "text" | "ansi" } = {}): Promise<PaneRead> {
|
|
597
|
+
return this.readPaneNow(marker, opts);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** The whole rule, synchronous — `readPane` and the capabilities probe both call THIS, so the probe cannot pass while the verb behaves differently. */
|
|
601
|
+
readPaneNow(marker: TransportMarker, opts: { lines?: number; format?: "text" | "ansi" } = {}): PaneRead {
|
|
602
|
+
if (marker.transport !== HERDR) return { state: "unknown", why: `transport "${marker.transport}" is not herdr — this transport reads only its own panes` };
|
|
603
|
+
const target = targetOf(marker);
|
|
604
|
+
if (!target) return { state: "unknown", why: "no target recorded on the marker" };
|
|
605
|
+
const avail = this.availability();
|
|
606
|
+
if (!avail.available) return { state: "unknown", why: avail.reason };
|
|
607
|
+
const lines = Math.min(Math.max(Math.trunc(opts.lines ?? 200), 1), 2000);
|
|
608
|
+
const format = opts.format === "ansi" ? "ansi" : "text";
|
|
609
|
+
const read = this.#run(["pane", "read", target, "--source", "visible", "--lines", String(lines), "--format", format]);
|
|
610
|
+
if (!read.ok) {
|
|
611
|
+
if (read.error?.code === "pane_not_found") return { state: "unknown", why: `herdr reports pane ${target} not found — a gone pane has no screen to read` };
|
|
612
|
+
return { state: "unknown", why: `herdr could not read pane ${target}: ${read.error?.message ?? read.stderr}` };
|
|
613
|
+
}
|
|
614
|
+
return { state: "read", text: read.stdout, lines, format, source: `herdr pane ${target}` };
|
|
615
|
+
}
|
|
616
|
+
|
|
590
617
|
/**
|
|
591
618
|
* ⟨q-1c95f7d4⟩ Task 1's open question — PUBLISH, and it needs a receipt it does not get.
|
|
592
619
|
*
|
package/src/transports/types.ts
CHANGED
|
@@ -249,6 +249,18 @@ export type TickReading =
|
|
|
249
249
|
| { readable: true; state: TickState; source: string }
|
|
250
250
|
| { readable: false; why: string };
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* ⟨q-927e28cd⟩ ADR-023 d4, narrow slice — WHAT A PANE SHOWS, READ THROUGH THE TRANSPORT.
|
|
254
|
+
*
|
|
255
|
+
* Two answers and no third: the screen, or UNKNOWN with the reason. A pane that is gone, a
|
|
256
|
+
* binary that is absent, a marker of another kind — each is `unknown`, never an empty screen
|
|
257
|
+
* and never a stale one: an empty string would read as "the seat is showing nothing", which
|
|
258
|
+
* is a claim about the seat that nothing measured.
|
|
259
|
+
*/
|
|
260
|
+
export type PaneRead =
|
|
261
|
+
| { state: "read"; text: string; lines: number; format: "text" | "ansi"; source: string }
|
|
262
|
+
| { state: "unknown"; why: string };
|
|
263
|
+
|
|
252
264
|
/** Keystroke-shaped commands a transport must deliver to an interactive pane. */
|
|
253
265
|
export const CONTROL_COMMANDS = ["clear", "compact", "reload-skills"] as const;
|
|
254
266
|
export type ControlCommand = (typeof CONTROL_COMMANDS)[number];
|
|
@@ -313,6 +325,18 @@ export interface Transport {
|
|
|
313
325
|
*/
|
|
314
326
|
publishTick?(marker: TransportMarker, state: TickState, opts?: { message?: string; source?: string }): Promise<{ ok: boolean; error?: string }>;
|
|
315
327
|
|
|
328
|
+
/**
|
|
329
|
+
* ⟨q-927e28cd⟩ OPTIONAL, AND ITS ABSENCE IS AN ANSWER: a transport that cannot see the
|
|
330
|
+
* seat's screen from this host (tmux-push-remote: the pane is on another machine) does not
|
|
331
|
+
* implement it, and every caller reports `unknown` for it rather than guessing.
|
|
332
|
+
*
|
|
333
|
+
* ⛔ THE ONE PLACE A PANE IS READ FOR A CONSUMER. Callers pass the WHOLE marker and read no
|
|
334
|
+
* field of it — which pane, which kind, whether it is this transport's to read are all
|
|
335
|
+
* decided in here (ADR-023 d1). A caller that needed `target` to use this would be the
|
|
336
|
+
* forty-eighth marker-internal read qa counted outside src/transports/.
|
|
337
|
+
*/
|
|
338
|
+
readPane?(marker: TransportMarker, opts?: { lines?: number; format?: "text" | "ansi" }): Promise<PaneRead>;
|
|
339
|
+
|
|
316
340
|
/**
|
|
317
341
|
* ⟨q-ec020f6a⟩(A) — OPTIONAL, and its absence is the honest answer for tmux (a pane's live
|
|
318
342
|
* "session" concept is a herdr feature; tmux has no equivalent to re-check). Declaring the
|