feat(plugin): SessionStart hook discovers and announces active peers
session-announce now calls /v1/list with the session's X-Mailbox header, which both registers the session with the daemon and returns all known mailboxes in one round-trip. The output appends an "Active peers" block listing mailboxes seen within the last hour (configurable via --peer-window-minutes), capped at 10 entries by default. Self is filtered out; the list is sorted most-recent-first. So when the user says "I started a second session, coordinate with it", Claude already has the peer's mailbox name in context — no manual list_mailboxes call needed. The peer-formatting logic is extracted into formatActivePeerList for unit testing; CLI tests now pin --url to an unreachable port to keep assertions stable on machines that have a real daemon running.
This commit is contained in:
@@ -41,6 +41,41 @@ export function deriveSessionName(sessionId: string, base?: string | null): stri
|
||||
return `claude-${short}`;
|
||||
}
|
||||
|
||||
export interface PeerEntry {
|
||||
name: string;
|
||||
lastSeenAt: string;
|
||||
}
|
||||
|
||||
export function formatActivePeerList(
|
||||
peers: PeerEntry[],
|
||||
selfName: string,
|
||||
options: { windowMinutes: number; maxPeers: number; now?: number },
|
||||
): string[] {
|
||||
const others = peers.filter((p) => p.name !== selfName);
|
||||
const cutoff = (options.now ?? Date.now()) - options.windowMinutes * 60_000;
|
||||
const active = others
|
||||
.filter((p) => {
|
||||
const t = new Date(p.lastSeenAt).getTime();
|
||||
return Number.isFinite(t) && t >= cutoff;
|
||||
})
|
||||
.sort((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt))
|
||||
.slice(0, options.maxPeers);
|
||||
|
||||
if (active.length === 0) {
|
||||
return [
|
||||
`No other mailboxes seen within the last ${options.windowMinutes} minutes (${others.length} total registered).`,
|
||||
];
|
||||
}
|
||||
|
||||
const lines = [
|
||||
`Active peers (seen within last ${options.windowMinutes} min, ${active.length} of ${others.length} total):`,
|
||||
];
|
||||
for (const p of active) {
|
||||
lines.push(` - ${p.name} (last seen ${p.lastSeenAt})`);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
export interface HookMessage {
|
||||
id: number;
|
||||
from: string;
|
||||
|
||||
Reference in New Issue
Block a user