agent-coord-mcp 0.17.1 → 0.19.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.
Files changed (52) hide show
  1. package/README.md +8 -1
  2. package/dist/build.js +113 -0
  3. package/dist/build.js.map +1 -0
  4. package/dist/roles.js +92 -0
  5. package/dist/roles.js.map +1 -0
  6. package/dist/server.js +30 -14
  7. package/dist/server.js.map +1 -1
  8. package/dist/store.js +54 -1
  9. package/dist/store.js.map +1 -1
  10. package/dist/tools/admin.js +5 -3
  11. package/dist/tools/admin.js.map +1 -1
  12. package/dist/tools/index.js +2 -0
  13. package/dist/tools/index.js.map +1 -1
  14. package/dist/tools/messaging.js +187 -8
  15. package/dist/tools/messaging.js.map +1 -1
  16. package/dist/tools/registry.js +54 -3
  17. package/dist/tools/registry.js.map +1 -1
  18. package/dist/tools/render.js +84 -0
  19. package/dist/tools/render.js.map +1 -0
  20. package/dist/tools/scopes.js +126 -0
  21. package/dist/tools/scopes.js.map +1 -0
  22. package/dist/tools/shared.js +18 -0
  23. package/dist/tools/shared.js.map +1 -1
  24. package/dist/tools/transport.js +393 -40
  25. package/dist/tools/transport.js.map +1 -1
  26. package/dist/tools/work.js +209 -0
  27. package/dist/tools/work.js.map +1 -0
  28. package/dist/work.js +260 -0
  29. package/dist/work.js.map +1 -0
  30. package/hooks/marker.mjs +18 -0
  31. package/hooks/roles.mjs +79 -0
  32. package/hooks/submit.mjs +393 -0
  33. package/hooks/tier.mjs +104 -11
  34. package/hooks/tmux-pusher.mjs +137 -51
  35. package/package.json +5 -4
  36. package/scripts/check-self-dependency.mjs +54 -0
  37. package/scripts/check-test-count.mjs +83 -0
  38. package/scripts/coord-pusher.mjs +150 -33
  39. package/src/build.ts +111 -0
  40. package/src/roles.ts +111 -0
  41. package/src/server.ts +79 -14
  42. package/src/store.ts +60 -1
  43. package/src/tools/admin.ts +10 -4
  44. package/src/tools/index.ts +2 -0
  45. package/src/tools/messaging.ts +206 -7
  46. package/src/tools/registry.ts +63 -4
  47. package/src/tools/render.ts +80 -0
  48. package/src/tools/scopes.ts +177 -0
  49. package/src/tools/shared.ts +120 -0
  50. package/src/tools/transport.ts +413 -40
  51. package/src/tools/work.ts +265 -0
  52. package/src/work.ts +329 -0
package/README.md CHANGED
@@ -84,7 +84,11 @@ If you're building an agent with the official MCP SDKs (`@modelcontextprotocol/s
84
84
  | `report_transport({agentId, transport, host?, tmuxTarget?, since?})` | Publish a transport marker for an agent. Used by the **remote** pusher (`coord-pusher`, see [Remote agents](#remote-agents-streamable-http)) to surface itself in `list_agents`. Local tmux push uses `attach_agent` instead. |
85
85
  | `clear_transport({agentId})` | Idempotent delete of an agent's transport marker — wire-callable counterpart to `detach_agent` for the remote pusher. Removes the marker only; nothing local to kill. |
86
86
  | `prune({olderThanDays?, decisionDays?, room?, targets?, archiveEmptyRooms?, removeOrphanInboxes?, dryRun?})` | Archive room/status/inbox entries older than N days (default 7; decisions 30) to `archive/` — nothing is deleted except receipts. Scope with `room` or `targets`. Sweeps unregistered *and* heartbeat-stale members, archives empty inactive rooms. Pass `dryRun:true` to preview. |
87
- | `doctor({fix?, maxFileBytes?})` | Bus-wide health check inspects the whole state dir for drift/leaks/corruption (orphan transport markers, **stale pusher daemons whose loaded code predates the on-disk script**, orphan memberships, orphan inbox/cursor files, cursor offsets past EOF, malformed JSONL, stale agents, oversized files, stale locks, channel/registry mismatches, environment). Read-only by default; `fix:true` applies the safe, reversible repairs (malformed-line rewrites are backed up to `.bak`). `healthy:true` means the bus is internally consistent. |
87
+ | `list_scopes({path?, agentId?})` | Read the declared write scopes for managed documents (`~/agent-coord/scopes.json`). With `path` (+ your `agentId`) it answers *"may I write this?"* before you edit a shared doc; bare, it lists every declared document and its owning role. **Advisory** the bus does not mediate file writes, so this reports ownership and `doctor` detects drift after the fact; nothing is prevented. Absent `scopes.json` nothing owned, nothing warns (opt-in). |
88
+ | `import_work({project, repo?})` | Read a project's work documents (`docs/QUEUE.md` + `docs/DONE.md`, or the legacy `docs/BACKLOG.md`, plus `docs/WORKSTREAMS.md`) into typed records — queue items `{priority,text,done}`, done entries `{text,ref,date}`, board rows. The markdown stays authoritative; the store is a derived index. |
89
+ | `list_work({project, kind?, priority?, includeDone?, repo?})` | Query work state as records instead of parsing markdown. Falls back to reading the documents directly when nothing has been imported, so it works with no store at all. |
90
+ | `export_work({project, write?, repo?, agentId?})` | Render the documents back out of the store, reproducing the pinned glyph contract exactly (ref after the last ` — `, date after a trailing ` · `). Reports by default; `write:true` rewrites the files. Refuses to export from an empty store rather than blanking a document. A declared write scope is **reported, never enforced**. |
91
+ | `doctor({fix?, maxFileBytes?})` | Bus-wide health check — inspects the whole state dir for drift/leaks/corruption (orphan transport markers, **stale pusher daemons whose loaded code predates the on-disk script**, **a server process running pre-rebuild code and markers stamped by such a server — merging is not deploying**, orphan memberships, orphan inbox/cursor files, cursor offsets past EOF, malformed JSONL, stale agents, oversized files, stale locks, channel/registry mismatches, **documents whose last writer disagrees with their declared scope**, environment). Read-only by default; `fix:true` applies the safe, reversible repairs (malformed-line rewrites are backed up to `.bak`). `healthy:true` means the bus is internally consistent. |
88
92
 
89
93
  ## First session checklist
90
94
 
@@ -311,6 +315,7 @@ Under the hood: [`hooks/tmux-pusher.mjs`](./hooks/tmux-pusher.mjs) is the daemon
311
315
  - The pusher pastes into the pane unconditionally. If you're typing in the same pane it will corrupt your buffer; if the agent is showing a `[y/n]` permission prompt, the message becomes the answer. Run the receiving agent in a pane you don't normally edit in.
312
316
  - Untrusted peer messages become real prompts with full agent privileges. Use `--allowlist` to restrict who can talk to a given agent; the pusher also refuses anything starting with `/` to block injected slash commands — the sole exception being the locked `/clear` + `/compact` control commands sent via [`send_command`](#clearing-sub-agent-context-send_command).
313
317
  - Bursts get coalesced (1s default) into a single paste so 5 rapid DMs become one prompt rather than five.
318
+ - **Never `pkill -f tmux-pusher.mjs`.** The pattern matches **every** pusher on the bus, not just yours — one agent's test-rig cleanup doing exactly that silently detached all four live agents at once (the panes stay alive; delivery just stops until each agent re-attaches). The pusher is spawned with its agentId in argv (`tmux-pusher.mjs --agent <id>`) precisely so a pattern kill can be scoped to one agent: `pkill -f "tmux-pusher.mjs --agent <id>"`. Better still, use `detach_agent({agentId})` — it SIGTERMs the exact pid from the transport marker.
314
319
 
315
320
  ### Clearing sub-agent context (`send_command`)
316
321
 
@@ -344,6 +349,8 @@ The reminder is per-recipient on `room:` broadcasts (each gets their own DM with
344
349
 
345
350
  The receipt lives in a **file the sender polls**, never in any inbox or room — so verification costs **zero added agent context/tokens** (the confirmation rides back in the caller's own tool result, which it was already paying for). Pass `waitForDelivery:false` for the old fire-and-forget behavior, or tune `deliveryTimeoutMs`. Receipts are trimmed by `prune` like any other log.
346
351
 
352
+ **Ghost text vs. real drafts.** The guard that refuses to paste onto unsent input reads the pane **with styling** (`capture-pane -e`): Claude Code renders a session-derived *suggested* next prompt dim (SGR 2) inside the input box while idle, and to a plain capture that chrome is byte-for-byte identical to a draft — for a while every idle worker looked permanently mid-draft and control commands were undeliverable fleet-wide. Dim spans are ignored; any **non-dim** input content still refuses, and the refusal quotes the styled line so a false positive self-diagnoses. The dim assumption is a per-harness rendering detail pinned in one place (`AGENT_COORD_GHOST_TEXT_SGR` overrides it for a TUI that styles suggestions differently); a harness we can't read fails toward refusing, because a false refusal costs one retryable command while a false delivery types into someone's real unsent text.
353
+
347
354
  Works identically over the remote transport (`coord-pusher`).
348
355
 
349
356
  ## Remote agents (Streamable HTTP)
package/dist/build.js ADDED
@@ -0,0 +1,113 @@
1
+ // Build identity of the RUNNING server process.
2
+ //
3
+ // The principle is the #28 pusher-freshness fix carried one layer up: stamp
4
+ // what you LOADED at init, compare against what's on disk NOW, and resolve the
5
+ // measured artifact from the code that is actually executing
6
+ // (import.meta.url), never from configuration — the thing that measures must
7
+ // be the thing that ran. A server process outlives `npm run build`; without a
8
+ // load-time sample there is nothing truthful to compare the on-disk build to,
9
+ // and a server running pre-rebuild code stamps transport markers with logic
10
+ // the rebuild replaced (observed live 2026-07-29: post-#28 attach spawned a
11
+ // pusher with no `--agent` argv and a single-file freshness stamp, agreeing
12
+ // with the new on-disk check only by coincidence).
13
+ import { readdirSync, statSync, readFileSync } from "node:fs";
14
+ import path from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ // Newest mtime (epoch ms) across every file under `dir` (recursive) whose
17
+ // name ends with one of `exts`. Returns undefined when the dir is missing or
18
+ // unreadable — callers skip their check rather than guess. Pure over its
19
+ // arguments so tests exercise it on temp trees instead of touching real
20
+ // sources (a utimes on a shared checkout flips every live pusher's freshness
21
+ // while the suite runs files in parallel).
22
+ export function newestMtimeUnder(dir, exts) {
23
+ try {
24
+ let newest;
25
+ for (const rel of readdirSync(dir, { recursive: true })) {
26
+ const name = String(rel);
27
+ if (!exts.some((e) => name.endsWith(e)))
28
+ continue;
29
+ let m;
30
+ try {
31
+ m = statSync(path.join(dir, name)).mtimeMs;
32
+ }
33
+ catch {
34
+ continue; // deleted mid-scan
35
+ }
36
+ if (newest === undefined || m > newest)
37
+ newest = m;
38
+ }
39
+ return newest;
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ // The dir this module was loaded FROM: dist/ in production, src/ under tsx.
46
+ // Either way it is the code actually running, which is the point.
47
+ export const BUILD_DIR = path.dirname(fileURLToPath(import.meta.url));
48
+ // .js for the compiled build, .ts for dev-mode (tsx src/server.ts) — both
49
+ // sides of every comparison use the same list, so the two modes are each
50
+ // self-consistent and can never be compared across.
51
+ const BUILD_EXTS = [".js", ".ts"];
52
+ // Sampled ONCE at module load: the newest mtime across the build this server
53
+ // process actually imported. A later `npm run build` rewrites dist/ under a
54
+ // still-running server; this value stays behind, which is exactly what
55
+ // doctor's server-build-drift check compares against.
56
+ export const SERVER_BUILD_MTIME = newestMtimeUnder(BUILD_DIR, BUILD_EXTS);
57
+ // The on-disk side of the comparison, statted fresh per call.
58
+ // AGENT_COORD_DIST_DIR is a test seam only: it redirects what doctor
59
+ // MEASURES so tests can stage a newer/older build in a temp dir — it never
60
+ // changes what the server loads.
61
+ export function onDiskBuildMtime() {
62
+ const dir = process.env.AGENT_COORD_DIST_DIR ?? BUILD_DIR;
63
+ return newestMtimeUnder(dir, BUILD_EXTS);
64
+ }
65
+ // The uncompiled side of the dist-behind-source comparison: newest mtime
66
+ // across src/**/*.ts, resolved as BUILD_DIR's sibling. undefined on a
67
+ // packaged install with no src/ (callers report "nothing to compare", never
68
+ // warn). Under tsx dev-mode BUILD_DIR *is* src/, so the comparison degrades
69
+ // to src-vs-src and reads ok — dev-mode has no build to fall behind.
70
+ // AGENT_COORD_SRC_DIR is the same test seam as AGENT_COORD_DIST_DIR:
71
+ // it redirects measurement only.
72
+ export function onDiskSourceMtime() {
73
+ const dir = process.env.AGENT_COORD_SRC_DIR ?? path.resolve(BUILD_DIR, "..", "src");
74
+ return newestMtimeUnder(dir, [".ts"]);
75
+ }
76
+ // Best-effort checkout identity, report-only: lets doctor NAME the build
77
+ // (`branch@sha` would be nicer, but HEAD's sha alone already makes
78
+ // mutable-checkout drift visible, which is all this claims). undefined when
79
+ // not a git checkout (npm install) — never an error.
80
+ export const SERVER_BUILD_SHA = (() => {
81
+ try {
82
+ let gitDir = path.resolve(BUILD_DIR, "..", ".git");
83
+ const st = statSync(gitDir);
84
+ if (st.isFile()) {
85
+ // A worktree's .git is a pointer file: "gitdir: <real dir>".
86
+ const ptr = readFileSync(gitDir, "utf8").trim();
87
+ if (!ptr.startsWith("gitdir:"))
88
+ return undefined;
89
+ gitDir = ptr.slice("gitdir:".length).trim();
90
+ }
91
+ const head = readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
92
+ if (!head.startsWith("ref:"))
93
+ return head.slice(0, 12); // detached
94
+ const ref = head.slice(4).trim();
95
+ try {
96
+ return readFileSync(path.join(gitDir, ref), "utf8").trim().slice(0, 12);
97
+ }
98
+ catch {
99
+ // Ref may be packed. commondir handling is deliberately out of scope —
100
+ // best-effort means undefined beats wrong.
101
+ const packed = readFileSync(path.join(gitDir, "packed-refs"), "utf8");
102
+ for (const line of packed.split("\n")) {
103
+ if (line.endsWith(` ${ref}`))
104
+ return line.slice(0, 12);
105
+ }
106
+ return undefined;
107
+ }
108
+ }
109
+ catch {
110
+ return undefined;
111
+ }
112
+ })();
113
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
package/dist/roles.js ADDED
@@ -0,0 +1,92 @@
1
+ // Canonical role identity, server side (Phase 8 Task 4).
2
+ //
3
+ // MIRROR of hooks/roles.mjs — tsconfig's rootDir is `src`, so this cannot
4
+ // import the hook copy, and the hook copy must stay build-free (the pusher
5
+ // loads it from a bare checkout). test/roles.test.mjs asserts the two agree;
6
+ // change one, change the other.
7
+ //
8
+ // See hooks/roles.mjs for the rationale behind `explicit` and the word-match
9
+ // fallback.
10
+ import { z } from "zod";
11
+ export function slugifyRole(role) {
12
+ return String(role ?? "")
13
+ .trim()
14
+ .toLowerCase()
15
+ .replace(/[^a-z0-9]+/g, "-")
16
+ .replace(/^-+|-+$/g, "");
17
+ }
18
+ export const GATE_RUNNER_ROLE_IDS = new Set(["qa", "quality", "coordinator", "gate"]);
19
+ export const COORDINATOR_ROLE_IDS = new Set(["coordinator"]);
20
+ export function resolveRole(role) {
21
+ if (role === null || role === undefined)
22
+ return undefined;
23
+ if (typeof role === "string") {
24
+ const roleId = slugifyRole(role);
25
+ return roleId ? { roleId, displayName: role, explicit: false } : undefined;
26
+ }
27
+ if (typeof role === "object") {
28
+ const declared = typeof role.roleId === "string" ? slugifyRole(role.roleId) : "";
29
+ const name = typeof role.displayName === "string" && role.displayName
30
+ ? role.displayName
31
+ : typeof role.role === "string" && role.role
32
+ ? role.role
33
+ : undefined;
34
+ if (declared)
35
+ return { roleId: declared, displayName: name ?? declared, explicit: true };
36
+ if (name)
37
+ return resolveRole(name);
38
+ }
39
+ return undefined;
40
+ }
41
+ export function roleMatches(role, allowedIds) {
42
+ const resolved = resolveRole(role);
43
+ if (!resolved)
44
+ return false;
45
+ if (allowedIds.has(resolved.roleId))
46
+ return true;
47
+ if (resolved.explicit)
48
+ return false;
49
+ return resolved.roleId.split("-").some((word) => allowedIds.has(word));
50
+ }
51
+ export function isGateRunner(role) {
52
+ return roleMatches(role, GATE_RUNNER_ROLE_IDS);
53
+ }
54
+ export function isCoordinator(role) {
55
+ return roleMatches(role, COORDINATOR_ROLE_IDS);
56
+ }
57
+ // Which roles may emit which record.type at the send path. Enforcement lives
58
+ // in messaging.ts (checkRecordAuthority); the table lives here so register/join
59
+ // can ECHO the consequence back at onboarding — a role that cannot emit `go`
60
+ // should learn it when it registers, not when it sends its first work order.
61
+ //
62
+ // NOT A TRUST BOUNDARY — roles are self-declared. See checkRecordAuthority.
63
+ export const RECORD_AUTHORITY = {
64
+ verdict: { roles: GATE_RUNNER_ROLE_IDS, label: "gate-runner" },
65
+ go: { roles: COORDINATOR_ROLE_IDS, label: "coordinator" },
66
+ scope: { roles: COORDINATOR_ROLE_IDS, label: "coordinator" },
67
+ };
68
+ // Split the restricted record types into what this role may and may not emit.
69
+ // Unrestricted types are omitted from both lists — they are nobody's business.
70
+ export function recordAuthorityFor(role) {
71
+ const mayEmit = [];
72
+ const mayNotEmit = [];
73
+ for (const [type, rule] of Object.entries(RECORD_AUTHORITY)) {
74
+ (roleMatches(role, rule.roles) ? mayEmit : mayNotEmit).push(type);
75
+ }
76
+ return { mayEmit, mayNotEmit };
77
+ }
78
+ // Wire shape for `role` on register/join. Either free text (v1: `role: "qa
79
+ // lead"`) or a declared identity (`{roleId: "qa", displayName: "QA gate"}`).
80
+ // Both are supported forever — the string form is not deprecated, it just
81
+ // leaves the id derived rather than frozen.
82
+ //
83
+ // Lives here rather than in tools/ because registry.ts and transport.ts import
84
+ // each other; a schema in either would be a temporal-dead-zone hazard.
85
+ export const roleInputSchema = z.union([
86
+ z.string(),
87
+ z.object({
88
+ roleId: z.string().min(1).optional(),
89
+ displayName: z.string().min(1).optional(),
90
+ }),
91
+ ]);
92
+ //# sourceMappingURL=roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.js","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -4,8 +4,8 @@ import { createServer } from "node:http";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
- import { ensureDirs, readTokenMapSync } from "./store.js";
8
- import { attachAgentSchema, attachAgentTool, clearTransportSchema, clearTransportTool, deleteRoomSchema, deleteRoomTool, detachAgentSchema, detachAgentTool, doctorSchema, doctorTool, forceUnregisterSchema, forceUnregisterTool, heartbeatSchema, heartbeatTool, joinRoomSchema, joinRoomTool, joinSchema, joinTool, leaveRoomSchema, leaveRoomTool, listAgentsSchema, listAgentsTool, pingSchema, pingTool, listRoomsSchema, listRoomsTool, postStatusSchema, postStatusTool, pruneSchema, pruneTool, readMessagesSchema, readMessagesTool, retrieveRoomHistorySchema, retrieveRoomHistoryTool, registerSchema, registerTool, renameAgentSchema, renameAgentTool, reportTransportSchema, reportTransportTool, sendCommandSchema, sendCommandTool, sendMessageSchema, sendMessageTool, setRoomMotdSchema, setRoomMotdTool, setRoomTopicSchema, setRoomTopicTool, statusSchema, statusTool, unregisterSchema, unregisterTool, quitSchema, quitTool, waitForMessageSchema, waitForMessageTool, } from "./tools/index.js";
7
+ import { ensureDirs, getTokenMap, reloadTokenMapSync } from "./store.js";
8
+ import { attachAgentSchema, attachAgentTool, clearTransportSchema, clearTransportTool, deleteRoomSchema, deleteRoomTool, detachAgentSchema, detachAgentTool, doctorSchema, doctorTool, forceUnregisterSchema, forceUnregisterTool, heartbeatSchema, heartbeatTool, joinRoomSchema, joinRoomTool, joinSchema, joinTool, leaveRoomSchema, leaveRoomTool, listAgentsSchema, listAgentsTool, pingSchema, pingTool, listRoomsSchema, listRoomsTool, postStatusSchema, postStatusTool, pruneSchema, pruneTool, readMessagesSchema, readMessagesTool, retrieveMessageSchema, retrieveMessageTool, retrieveRoomHistorySchema, retrieveRoomHistoryTool, registerSchema, registerTool, renameAgentSchema, renameAgentTool, reportReceiptSchema, reportReceiptTool, reportTransportSchema, reportTransportTool, sendCommandSchema, sendCommandTool, sendMessageSchema, sendMessageTool, setRoomMotdSchema, setRoomMotdTool, setRoomTopicSchema, setRoomTopicTool, statusSchema, statusTool, unregisterSchema, unregisterTool, quitSchema, quitTool, waitForMessageSchema, waitForMessageTool, listScopesSchema, listScopesTool, importWorkSchema, importWorkTool, listWorkSchema, listWorkTool, exportWorkSchema, exportWorkTool, } from "./tools/index.js";
9
9
  function jsonResult(data) {
10
10
  return {
11
11
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
@@ -28,13 +28,21 @@ function buildServer(initialBound) {
28
28
  let bound = initialBound;
29
29
  // Gate every tool that takes a caller identity. `field: null` (list_agents,
30
30
  // list_rooms, prune) bypasses the check entirely.
31
- function gate(field, handler) {
31
+ //
32
+ // `bindOnClaim: false` (status, ping) means the tool still enforces a
33
+ // mismatch against an *existing* binding, but a fresh (unbound) session
34
+ // never claims one just by naming an agentId — a diagnostic status/ping
35
+ // call must not be able to silently TOFU-bind a session to some other,
36
+ // already-live agent's identity.
37
+ function gate(field, handler, { bindOnClaim = true } = {}) {
32
38
  return async (args) => {
33
39
  if (field) {
34
40
  const claimed = args[field];
35
41
  if (typeof claimed === "string") {
36
42
  if (bound === undefined) {
37
- bound = claimed; // TOFU: first claim wins, then sticky.
43
+ if (bindOnClaim) {
44
+ bound = claimed; // TOFU: first claim wins, then sticky.
45
+ }
38
46
  }
39
47
  else if (bound !== claimed) {
40
48
  throw new Error(`identity bound to '${bound}'; rejected attempt to act as '${claimed}'`);
@@ -66,14 +74,15 @@ function buildServer(initialBound) {
66
74
  server.tool("register", "Register this agent in the shared registry. Lower-level than `join` — does not attach a transport or drain the inbox. Prefer `join` unless you need explicit control.", registerSchema, gate("agentId", registerTool));
67
75
  server.tool("unregister", "Tear down this agent: detach any attached transport (kills the pusher) and remove the registry entry. Clean shutdown counterpart to `join`.", unregisterSchema, gate("agentId", unregisterTool));
68
76
  server.tool("quit", "Clean shutdown: unregister this agent (detach transport, leave rooms, remove registry entry) then exit the MCP process. Only callable by the session's bound identity. Use this to cleanly hand off before a restart with a new name.", quitSchema, gate("agentId", quitTool));
69
- server.tool("status", "Introspect this agent's coord state: registration, attached transport, inbox depth and unread count, and whether this MCP server is running inside tmux. Useful for debugging 'why isn't my DM landing'.", statusSchema, gate("agentId", statusTool));
77
+ server.tool("status", "Introspect this agent's coord state: registration, attached transport, inbox depth and unread count, and whether this MCP server is running inside tmux. Useful for debugging 'why isn't my DM landing'. Read-only — naming an agentId here never binds this session's identity.", statusSchema, gate("agentId", statusTool, { bindOnClaim: false }));
70
78
  server.tool("heartbeat", "Refresh this agent's lastHeartbeat timestamp.", heartbeatSchema, gate("agentId", heartbeatTool));
71
- server.tool("ping", "Liveness probe for another agent, answered entirely from server-side state (registry entry, transport marker, pusher pid, tmux pane) — it never touches the target's session, so a fleet-wide sweep costs zero model tokens on the targets. Returns alive (fresh heartbeat or live transport), reachable (a DM pushed now would land), granular checks, and latencyMs. Distinct from heartbeat, which is an agent refreshing its OWN activity timestamp. Pass echo:true (default off) to additionally drop a PING DM into the target's inbox — that wakes the target's model, so use it sparingly and only when you need an agent-level acknowledgement. 'from' is enforced against the session's bound identity.", pingSchema, gate("from", pingTool));
79
+ server.tool("ping", "Liveness probe for another agent, answered entirely from server-side state (registry entry, transport marker, pusher pid, tmux pane) — it never touches the target's session, so a fleet-wide sweep costs zero model tokens on the targets. Returns alive (fresh heartbeat or live transport), reachable (a DM pushed now would land), granular checks, and latencyMs. Distinct from heartbeat, which is an agent refreshing its OWN activity timestamp. Pass echo:true (default off) to additionally drop a PING DM into the target's inbox — that wakes the target's model, so use it sparingly and only when you need an agent-level acknowledgement. 'from' is enforced against the session's bound identity, but read-only — naming 'from' here never binds this session's identity.", pingSchema, gate("from", pingTool, { bindOnClaim: false }));
72
80
  server.tool("list_agents", "List all known agents and whether they appear online (heartbeat <5min).", listAgentsSchema, gate(null, listAgentsTool));
73
81
  server.tool("send_message", "Send a message. If 'to' is set, goes to that agent's inbox (DM); otherwise to a channel — pass 'room' (e.g. 'seo' or '#seo') to target a specific channel, or omit it for the default 'general' channel. For channel posts, tag 'kind': 'decision' for GOs/verdicts/agreements that must outlive routine cleanup (kept ~30 days, quoted verbatim in digests), 'status' for progress notes, omit for ordinary chatter. The 'from' field is enforced against the session's bound identity when binding is configured.", sendMessageSchema, gate("from", sendMessageTool));
74
82
  server.tool("send_command", "Inject a context-management slash command (/clear or /compact) directly into a sub-agent's live tmux session — delivered RAW with no banner or prefix, so the agent's CLI runs it as a real slash command. Target one agent with 'to' or broadcast to a channel's tmux-attached members with 'room' (never the sender). Hard-gated to tmux: returns ok:false if the target has no live tmux-push(-remote) transport. By default BLOCKS until the receiving pusher confirms it actually typed the command into the pane (out-of-band delivery receipt, no added agent context) and returns delivery:'confirmed' with deliveredAt, or delivery:'pending'+warning if no receipt arrived within deliveryTimeoutMs (default 8000) — a stale/wedged pusher. Pass waitForDelivery:false for fire-and-forget. Intended for a lead agent to clear/compact sub-agent context and save tokens. The command allowlist is locked to /clear and /compact; nothing else is accepted. 'from' is enforced against the session's bound identity.", sendCommandSchema, gate("from", sendCommandTool));
75
83
  server.tool("read_messages", "Read new messages from inbox|room|status. For source='room', pass 'room' to read a specific channel (default 'general'). Room and status reads return the most recent 50 entries per call — pass limit to override (max 500). When the backlog exceeds the window, the older overflow is replaced by a compact `history` digest carrying a retrieval hash; call retrieve_room_history(hash) to expand it. Inbox drains fully by default. Advances the per-channel cursor unless peek=true.", readMessagesSchema, gate("agentId", readMessagesTool));
76
84
  server.tool("retrieve_room_history", "Expand a compressed channel-history digest returned by read_messages. Pass the `hash` from the `history` field; optionally pass `query` to return only matching messages (case-insensitive substring). Entries are scoped to the agent that produced them and expire after 30 minutes — if expired, re-read the channel with a higher limit instead.", retrieveRoomHistorySchema, gate("agentId", retrieveRoomHistoryTool));
85
+ server.tool("retrieve_message", "Expand a `retrieve_message id=<uuid>` handle from a pane digest into the full message and its typed `record`. A record whose text rendering spans multiple lines (a DAVID_DECISION packet) is delivered to a pane as ONE attributed line plus this handle; call it to get the structured record back. Reads the message by id from the channels you can read (your inbox and rooms you belong to), falling through to the append-only archive if compaction moved it — so unlike retrieve_room_history there is no TTL and nothing to expire. A handle for a message never delivered to you is simply not found.", retrieveMessageSchema, gate("agentId", retrieveMessageTool));
77
86
  server.tool("post_status", "Append a status broadcast to the shared status stream.", postStatusSchema, gate("agentId", postStatusTool));
78
87
  server.tool("prune", "Trim room/status/inbox JSONL to entries newer than `olderThanDays` (default 7); kind='decision' posts keep a longer `decisionDays` retention (default 30). Nothing is lost: aged-out entries are archived under archive/ (rooms/<chan>.jsonl, status.jsonl, inbox/<agent>.jsonl) — only receipts are truly deleted. Pass `room` to prune a single channel, or `targets` (rooms|status|inbox|receipts|members) to narrow the sweep. Sweeps room members that are unregistered or haven't heartbeated since the cutoff, and archives+removes non-default rooms left empty and inactive (disable via archiveEmptyRooms=false). Removes inbox files for agents no longer in the registry unless removeOrphanInboxes=false. Pass dryRun=true to preview.", pruneSchema, gate(null, pruneTool));
79
88
  server.tool("wait_for_message", "Block (max 60s) until a new message appears on the given source, then return it. For source='room', pass 'room' to wait on a specific channel (default 'general').", waitForMessageSchema, gate("agentId", waitForMessageTool));
@@ -107,17 +116,23 @@ function buildServer(initialBound) {
107
116
  server.tool("detach_agent", "Stop the tmux-push transport for an agent: kills the pusher process and clears the transport marker.", detachAgentSchema, gate("agentId", detachAgentTool));
108
117
  server.tool("report_transport", "Publish a transport marker for an agent (used by the remote tmux pusher, scripts/coord-pusher.mjs, to surface itself in list_agents). Set transport='tmux-push-remote' and optionally host/tmuxTarget. Liveness for remote markers is heartbeat-based — keep calling heartbeat or this marker gets GC'd after staleness.", reportTransportSchema, gate("agentId", reportTransportTool));
109
118
  server.tool("clear_transport", "Idempotent delete of an agent's transport marker. The wire-callable counterpart to detach_agent for remote pushers: it only removes the marker — there's no local process to kill.", clearTransportSchema, gate("agentId", clearTransportTool));
119
+ server.tool("report_receipt", "Append a delivery receipt for a message this agent's pusher just typed into its pane — the wire-callable counterpart to the local pusher's receipts/<id>.jsonl stamp, for remote pushers (scripts/coord-pusher.mjs) that cannot write this host's filesystem. This is what lets send_command to a tmux-push-remote agent return delivery:'confirmed'. For control commands pass exactly what submit verification observed (submitted/verified/reason); omitting 'submitted' means 'typed but unverified' and is reported as delivery:'pending', never 'confirmed'. 'agentId' (the receiving agent) is enforced against the session's bound identity, so a pusher can only stamp its own agent's receipt file.", reportReceiptSchema, gate("agentId", reportReceiptTool));
110
120
  server.tool("doctor", "Bus-wide health check: inspects the whole state dir and reports drift, leaks, and corruption (orphan transport markers / memberships / inboxes, cursor offsets past EOF, malformed JSONL, stale agents, oversized files, stale locks, channel/registry mismatches, environment). Read-only by default; pass fix=true to apply the safe, reversible repairs (malformed-line rewrites are backed up to .bak first). A clean report (healthy=true) means the bus is internally consistent.", doctorSchema, gate(null, doctorTool));
121
+ server.tool("list_scopes", "Read the declared write scopes for managed documents (~/agent-coord/scopes.json). Call it with 'path' (and your 'agentId') to ask \"may I write this?\" BEFORE editing a shared doc like docs/QUEUE.md; call it bare to list every declared document and its owning role. ADVISORY ONLY: the bus does not mediate file writes, so this answers who owns a document, it does not stop anyone — enforcement arrives when work state moves into the store. Absent scopes.json means nothing is owned and nothing warns (opt-in).", listScopesSchema, gate(null, listScopesTool));
122
+ server.tool("import_work", "Read a project's work documents (docs/QUEUE.md + docs/DONE.md, or the legacy docs/BACKLOG.md, plus docs/WORKSTREAMS.md) into typed records: queue items {priority,text,done}, done entries {text,ref,date} and board rows. The markdown stays authoritative — this store is a derived index, and export_work renders it back byte-identically.", importWorkSchema, gate(null, importWorkTool));
123
+ server.tool("list_work", "Query a project's work state as records instead of parsing markdown: open queue items (filter by priority), done entries with their ref and date as separate fields, and the board's lane rows. Falls back to reading the documents directly when nothing has been imported, so it works with no store at all.", listWorkSchema, gate(null, listWorkTool));
124
+ server.tool("export_work", "Render a project's work documents back out of the store, reproducing the pinned glyph contract exactly (ref after the last ' \u2014 ', date after a trailing ' \u00b7 '). Reports by default; pass write:true to rewrite the files. Refuses to export from an empty store rather than blanking a document. Any declared Task 4 write scope is REPORTED alongside the write, never enforced.", exportWorkSchema, gate(null, exportWorkTool));
111
125
  server.tool("delete_room", "Permanently delete a channel: removes it from the registry, deletes its JSONL file, and clears all agent cursor offsets for that channel. Refuses if agents are still joined unless force=true. Cannot delete the default 'general' channel. Posts a system notice to #general on success.", deleteRoomSchema, gate("agentId", deleteRoomTool));
112
126
  server.tool("force_unregister", "Admin eviction: unregisters any agent by targetAgentId regardless of the caller's identity. Detaches the agent's transport, removes it from all channel memberships, and drops its registry entry. Use after a reboot to clean up stale agents that can no longer unregister themselves.", forceUnregisterSchema, gate(null, forceUnregisterTool));
113
127
  return server;
114
128
  }
115
- // Lazy-loaded token map for HTTP identity binding. Hot-reloaded on SIGHUP so
116
- // operators can rotate / add agents without a server restart.
117
- let tokenMap = null;
129
+ // Token map for HTTP identity binding, held in-process via store.ts's
130
+ // shared cache. Hot-reloaded on SIGHUP so operators can rotate / add agents
131
+ // without a server restart; also refreshed automatically by rename_agent
132
+ // (see rotateAgentToken) so a live rename doesn't need one.
118
133
  function loadTokenMap(initial) {
119
134
  try {
120
- tokenMap = readTokenMapSync();
135
+ reloadTokenMapSync();
121
136
  }
122
137
  catch (e) {
123
138
  // On initial load a bad file is fatal — refuse to start in a known-bad
@@ -130,7 +145,7 @@ function loadTokenMap(initial) {
130
145
  return;
131
146
  }
132
147
  if (!initial) {
133
- console.error(`[agent-coord-mcp] SIGHUP: token map reloaded (${tokenMap?.size ?? 0} agents)`);
148
+ console.error(`[agent-coord-mcp] SIGHUP: token map reloaded (${getTokenMap()?.size ?? 0} agents)`);
134
149
  }
135
150
  }
136
151
  async function main() {
@@ -161,7 +176,7 @@ async function main() {
161
176
  }
162
177
  async function startHttp(port) {
163
178
  const sharedToken = process.env.AGENT_COORD_TOKEN;
164
- const bound = tokenMap !== null;
179
+ const bound = getTokenMap() !== null;
165
180
  if (!bound && !sharedToken) {
166
181
  console.error("[agent-coord-mcp] HTTP mode needs auth: either set AGENT_COORD_TOKEN (legacy " +
167
182
  "shared bearer, advisory identity) or create ~/agent-coord/tokens.json (per-agent " +
@@ -237,6 +252,7 @@ async function startHttp(port) {
237
252
  if (!authHeader || !authHeader.startsWith("Bearer "))
238
253
  return { ok: false };
239
254
  const bearer = authHeader.slice("Bearer ".length);
255
+ const tokenMap = getTokenMap();
240
256
  if (tokenMap) {
241
257
  const agent = tokenMap.get(bearer);
242
258
  return agent ? { ok: true, agent } : { ok: false };
@@ -275,7 +291,7 @@ async function startHttp(port) {
275
291
  // agent than the session was created for — otherwise any valid token plus
276
292
  // a leaked session id could drive that session's identity (session hijack).
277
293
  if (transport &&
278
- tokenMap &&
294
+ getTokenMap() &&
279
295
  typeof sid === "string" &&
280
296
  sessionAgents.get(sid) !== resolved.agent) {
281
297
  res.writeHead(403, { "Content-Type": "text/plain" });
@@ -301,7 +317,7 @@ async function startHttp(port) {
301
317
  }
302
318
  });
303
319
  http.listen(port, bindAddr, () => {
304
- const mode = bound ? `pre-bound (${tokenMap?.size ?? 0} agents)` : "TOFU";
320
+ const mode = bound ? `pre-bound (${getTokenMap()?.size ?? 0} agents)` : "TOFU";
305
321
  console.error(`[agent-coord-mcp] http listening on ${bindAddr}:${port} — identity ${mode}`);
306
322
  if (bindAddr !== "127.0.0.1" && bindAddr !== "localhost") {
307
323
  console.error(`[agent-coord-mcp] WARNING: bound to ${bindAddr} without TLS. Front with a TLS reverse proxy ` +
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/store.js CHANGED
@@ -56,8 +56,24 @@ export const ARCHIVE_STATUS_FILE = path.join(ARCHIVE_DIR, "status.jsonl");
56
56
  // from/agentId field. Absent → advisory mode (legacy behaviour, with a
57
57
  // startup warning). Should be mode 600; operator-managed.
58
58
  export const TOKENS_FILE = path.join(ROOT, "tokens.json");
59
+ // Declared write scopes for managed documents (v0.18.0, Phase 8 Task 4).
60
+ // Opt-in and operator-managed: absent → nothing is owned and nothing warns.
61
+ // ADVISORY. The bus does not mediate writes to docs/QUEUE.md & friends —
62
+ // agents edit them with ordinary file tools, so there is no interception
63
+ // point. This file lets an agent ASK who owns a document (list_scopes) and
64
+ // lets `doctor` DETECT drift after the fact. Pre-emptive enforcement waits
65
+ // for Phase 8 Task 5, when work state moves into the store.
66
+ export const SCOPES_FILE = path.join(ROOT, "scopes.json");
67
+ // Work state as data (v0.18.0, Phase 8 Task 5). One file per project holding
68
+ // the parsed QUEUE/DONE/board documents. DERIVED, not authoritative: the
69
+ // markdown in the repo remains the source of truth, and deleting this
70
+ // directory costs an `import_work`, never data (see src/work.ts).
71
+ export const WORK_DIR = path.join(ROOT, "work");
72
+ export function workFile(project) {
73
+ return path.join(WORK_DIR, `${sanitize(project)}.json`);
74
+ }
59
75
  export function ensureDirs() {
60
- for (const d of [ROOT, INBOX_DIR, CURSOR_DIR, TRANSPORT_DIR, PID_DIR, LOG_DIR, ROOMS_DIR, RECEIPTS_DIR, HISTORY_DIR]) {
76
+ for (const d of [ROOT, INBOX_DIR, CURSOR_DIR, TRANSPORT_DIR, PID_DIR, LOG_DIR, ROOMS_DIR, RECEIPTS_DIR, HISTORY_DIR, WORK_DIR]) {
61
77
  if (!existsSync(d))
62
78
  mkdirSync(d, { recursive: true });
63
79
  }
@@ -94,19 +110,56 @@ export function readTokenMapSync() {
94
110
  }
95
111
  return out;
96
112
  }
113
+ // In-process cache of the token map, shared by the HTTP identity-binding
114
+ // layer (server.ts) and rotateAgentToken below. A module-level cache (rather
115
+ // than each caller re-reading the file) is what lets a rename refresh the
116
+ // live server's view without an operator SIGHUP.
117
+ let tokenMapCache = null;
118
+ let tokenMapInitialized = false;
119
+ // Current in-process token map (bearer -> agentId), or null if unbound (no
120
+ // tokens.json). Lazily loads from disk on first call.
121
+ export function getTokenMap() {
122
+ if (!tokenMapInitialized) {
123
+ tokenMapCache = readTokenMapSync();
124
+ tokenMapInitialized = true;
125
+ }
126
+ return tokenMapCache;
127
+ }
128
+ // Re-read tokens.json from disk and replace the in-process cache. Throws on
129
+ // a malformed file — callers decide whether that's fatal (startup) or
130
+ // recoverable (SIGHUP, post-rotate refresh).
131
+ export function reloadTokenMapSync() {
132
+ tokenMapCache = readTokenMapSync();
133
+ tokenMapInitialized = true;
134
+ return tokenMapCache;
135
+ }
97
136
  // Atomically rotate the token entry for an agent rename (used by
98
137
  // rename_agent so the same bearer continues to authenticate the renamed
99
138
  // identity). No-op if the file is absent or the old id isn't in the map.
100
139
  export async function rotateAgentToken(oldAgentId, newAgentId) {
101
140
  if (!existsSync(TOKENS_FILE))
102
141
  return;
142
+ let rotated = false;
103
143
  await updateJson(TOKENS_FILE, {}, (current) => {
104
144
  if (current[oldAgentId] !== undefined) {
105
145
  current[newAgentId] = current[oldAgentId];
106
146
  delete current[oldAgentId];
147
+ rotated = true;
107
148
  }
108
149
  return current;
109
150
  });
151
+ if (rotated) {
152
+ // Refresh the in-process cache immediately: without this, the renamed
153
+ // agent's bearer keeps resolving to the OLD id in this process until an
154
+ // operator sends SIGHUP, silently misattributing its calls.
155
+ try {
156
+ reloadTokenMapSync();
157
+ }
158
+ catch (e) {
159
+ console.error(`[agent-coord-mcp] rename_agent: token map reload after rotate failed: ${e.message} ` +
160
+ `(keeping previous in-process map; send SIGHUP to retry)`);
161
+ }
162
+ }
110
163
  }
111
164
  // Normalize a channel name: strip leading '#', lowercase, restrict to a safe
112
165
  // charset, empty → the default channel. Display layers re-add the '#'.