agent-coord-mcp 0.4.9 → 0.5.1
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 +32 -13
- package/dist/server.js +11 -5
- package/dist/server.js.map +1 -1
- package/dist/store.js +79 -1
- package/dist/store.js.map +1 -1
- package/dist/tools.js +241 -33
- package/dist/tools.js.map +1 -1
- package/hooks/peek-coord.mjs +66 -8
- package/hooks/tmux-pusher.mjs +81 -7
- package/package.json +1 -1
- package/scripts/coord-chat.mjs +457 -61
- package/src/server.ts +58 -4
- package/src/store.ts +90 -1
- package/src/tools.ts +269 -32
package/hooks/tmux-pusher.mjs
CHANGED
|
@@ -59,6 +59,10 @@ if (!TMUX_TARGET) die("AGENT_COORD_TMUX_TARGET is required");
|
|
|
59
59
|
|
|
60
60
|
const ROOT = process.env.AGENT_COORD_DIR || path.join(homedir(), "agent-coord");
|
|
61
61
|
const INCLUDE_ROOM = process.env.AGENT_COORD_INCLUDE_ROOM === "1";
|
|
62
|
+
// Default channel + watch registry — declared up here so the hoisted channel
|
|
63
|
+
// helpers, called from the top-level checkOnce(), don't trip the const TDZ.
|
|
64
|
+
const DEFAULT_ROOM = "general";
|
|
65
|
+
const watchedRooms = new Set();
|
|
62
66
|
const ALLOWLIST = (process.env.AGENT_COORD_ALLOWLIST || "")
|
|
63
67
|
.split(",")
|
|
64
68
|
.map((s) => s.trim())
|
|
@@ -68,7 +72,6 @@ const POLL_MS = parseInt(process.env.AGENT_COORD_POLL_MS || "1000", 10);
|
|
|
68
72
|
|
|
69
73
|
const SAFE_ID = AGENT_ID.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
70
74
|
const INBOX_FILE = path.join(ROOT, "inbox", `${SAFE_ID}.jsonl`);
|
|
71
|
-
const ROOM_FILE = path.join(ROOT, "room.jsonl");
|
|
72
75
|
const CURSOR_FILE = path.join(ROOT, "cursors", `${SAFE_ID}.json`);
|
|
73
76
|
const TRANSPORT_FILE = path.join(ROOT, "transports", `${SAFE_ID}.json`);
|
|
74
77
|
const BUFFER_NAME = `coord-${SAFE_ID}`;
|
|
@@ -135,11 +138,33 @@ function drainSource(label, file, cursorKey, cur) {
|
|
|
135
138
|
return true;
|
|
136
139
|
}
|
|
137
140
|
|
|
141
|
+
// Drain one channel against its per-channel offset (general → roomOffset,
|
|
142
|
+
// others → roomOffsets[chan]); tag injected lines with the channel name.
|
|
143
|
+
function drainRoomChannel(chan, cur) {
|
|
144
|
+
const c = normalizeRoom(chan);
|
|
145
|
+
const all = readJsonl(roomFile(c));
|
|
146
|
+
const off = getRoomOffset(cur, c);
|
|
147
|
+
const fresh = all.slice(off);
|
|
148
|
+
if (fresh.length === 0) return false;
|
|
149
|
+
for (const m of fresh) {
|
|
150
|
+
if (shouldInject(m)) pending.push({ kind: `room #${c}`, ...m });
|
|
151
|
+
}
|
|
152
|
+
setRoomOffset(cur, c, off + fresh.length);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
138
156
|
function checkOnce() {
|
|
139
157
|
const cur = readCursor();
|
|
140
158
|
let changed = false;
|
|
141
159
|
if (drainSource("DM", INBOX_FILE, "inboxOffset", cur)) changed = true;
|
|
142
|
-
if (INCLUDE_ROOM
|
|
160
|
+
if (INCLUDE_ROOM) {
|
|
161
|
+
// Tail every channel the agent has joined. checkOnce runs on each poll, so
|
|
162
|
+
// channels joined after startup are picked up automatically.
|
|
163
|
+
for (const chan of joinedRooms()) {
|
|
164
|
+
if (drainRoomChannel(chan, cur)) changed = true;
|
|
165
|
+
watchRoom(chan);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
143
168
|
if (changed) writeCursor(cur);
|
|
144
169
|
if (pending.length > 0) scheduleFlush();
|
|
145
170
|
}
|
|
@@ -255,11 +280,7 @@ try {
|
|
|
255
280
|
// file may not exist yet; polling covers it
|
|
256
281
|
}
|
|
257
282
|
if (INCLUDE_ROOM) {
|
|
258
|
-
|
|
259
|
-
if (existsSync(ROOM_FILE)) watch(ROOM_FILE, () => checkOnce());
|
|
260
|
-
} catch {
|
|
261
|
-
// ignore
|
|
262
|
-
}
|
|
283
|
+
for (const chan of joinedRooms()) watchRoom(chan);
|
|
263
284
|
}
|
|
264
285
|
setInterval(checkOnce, POLL_MS);
|
|
265
286
|
|
|
@@ -271,3 +292,56 @@ function die(msg) {
|
|
|
271
292
|
process.stderr.write(`[tmux-pusher] ${msg}\n`);
|
|
272
293
|
process.exit(1);
|
|
273
294
|
}
|
|
295
|
+
|
|
296
|
+
// ---------- channel helpers (mirror src/store.ts) ----------
|
|
297
|
+
|
|
298
|
+
function normalizeRoom(name) {
|
|
299
|
+
if (!name) return DEFAULT_ROOM;
|
|
300
|
+
const n = String(name).trim().replace(/^#+/, "").toLowerCase().replace(/[^a-z0-9._-]/g, "");
|
|
301
|
+
return n || DEFAULT_ROOM;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function roomFile(chan) {
|
|
305
|
+
const c = normalizeRoom(chan);
|
|
306
|
+
// c is already normalized to a filesystem-safe charset.
|
|
307
|
+
return c === DEFAULT_ROOM ? path.join(ROOT, "room.jsonl") : path.join(ROOT, "rooms", `${c}.jsonl`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function getRoomOffset(cursor, chan) {
|
|
311
|
+
const c = normalizeRoom(chan);
|
|
312
|
+
return c === DEFAULT_ROOM ? cursor.roomOffset ?? 0 : cursor.roomOffsets?.[c] ?? 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function setRoomOffset(cursor, chan, n) {
|
|
316
|
+
const c = normalizeRoom(chan);
|
|
317
|
+
if (c === DEFAULT_ROOM) cursor.roomOffset = n;
|
|
318
|
+
else (cursor.roomOffsets ??= {})[c] = n;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function joinedRooms() {
|
|
322
|
+
let reg = {};
|
|
323
|
+
try {
|
|
324
|
+
const raw = readFileSync(path.join(ROOT, "rooms.json"), "utf8");
|
|
325
|
+
if (raw.trim()) reg = JSON.parse(raw);
|
|
326
|
+
} catch {
|
|
327
|
+
// no registry yet → just the default channel
|
|
328
|
+
}
|
|
329
|
+
const out = new Set([DEFAULT_ROOM]);
|
|
330
|
+
for (const [chan, e] of Object.entries(reg)) {
|
|
331
|
+
if (e && Array.isArray(e.members) && e.members.includes(AGENT_ID)) out.add(chan);
|
|
332
|
+
}
|
|
333
|
+
return [...out];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function watchRoom(chan) {
|
|
337
|
+
const f = roomFile(chan);
|
|
338
|
+
if (watchedRooms.has(f)) return;
|
|
339
|
+
try {
|
|
340
|
+
if (existsSync(f)) {
|
|
341
|
+
watch(f, () => checkOnce());
|
|
342
|
+
watchedRooms.add(f);
|
|
343
|
+
}
|
|
344
|
+
} catch {
|
|
345
|
+
// polling covers it until the file exists
|
|
346
|
+
}
|
|
347
|
+
}
|
package/package.json
CHANGED