From 2c9c8c7b6c009eba2a0573a8e8c0f932e5edd986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:05:46 +0100 Subject: [PATCH] feat: add hostname to hello + local/remote peer locality detection Peers report os.hostname() in the hello handshake. list_peers shows [local] or [remote] tag per peer. MCP instructions teach AI to read local peers' files directly via filesystem instead of relay. Co-Authored-By: Claude Sonnet 4.6 --- apps/broker/src/index.ts | 3 +++ apps/broker/src/types.ts | 3 +++ apps/cli/src/mcp/server.ts | 9 ++++++++- apps/cli/src/ws/client.ts | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/broker/src/index.ts b/apps/broker/src/index.ts index 62a14d4..bb1f607 100644 --- a/apps/broker/src/index.ts +++ b/apps/broker/src/index.ts @@ -101,6 +101,7 @@ interface PeerConn { sessionPubkey: string | null; displayName: string; cwd: string; + hostname?: string; peerType?: "ai" | "human" | "connector"; channel?: string; model?: string; @@ -920,6 +921,7 @@ async function handleHello( sessionPubkey: hello.sessionPubkey ?? null, displayName: effectiveDisplayName, cwd: hello.cwd, + hostname: hello.hostname, peerType: hello.peerType, channel: hello.channel, model: hello.model, @@ -1138,6 +1140,7 @@ function handleConnection(ws: WebSocket): void { sessionId: p.sessionId, connectedAt: p.connectedAt.toISOString(), cwd: pc?.cwd ?? p.cwd, + ...(pc?.hostname ? { hostname: pc.hostname } : {}), ...(pc?.peerType ? { peerType: pc.peerType } : {}), ...(pc?.channel ? { channel: pc.channel } : {}), ...(pc?.model ? { model: pc.model } : {}), diff --git a/apps/broker/src/types.ts b/apps/broker/src/types.ts index bcc8685..16a4e29 100644 --- a/apps/broker/src/types.ts +++ b/apps/broker/src/types.ts @@ -57,6 +57,8 @@ export interface WSHelloMessage { sessionId: string; pid: number; cwd: string; + /** OS hostname — used to detect same-machine peers for direct file access. */ + hostname?: string; /** Peer type: ai session, human user, or external connector. */ peerType?: "ai" | "human" | "connector"; /** Channel the peer connected from (e.g. "claude-code", "telegram", "slack", "web"). */ @@ -226,6 +228,7 @@ export interface WSPeersListMessage { sessionId: string; connectedAt: string; cwd?: string; + hostname?: string; peerType?: "ai" | "human" | "connector"; channel?: string; model?: string; diff --git a/apps/cli/src/mcp/server.ts b/apps/cli/src/mcp/server.ts index 04d33c9..7a6e214 100644 --- a/apps/cli/src/mcp/server.ts +++ b/apps/cli/src/mcp/server.ts @@ -228,6 +228,11 @@ Persistent knowledge that survives across sessions. Use remember(content, tags?) share_file for persistent references, send_message(file:) for ephemeral attachments. Tags on shared files make them searchable. Use list_files to find what peers shared. +## Peer project files +read_peer_file and list_peer_files request files from remote peers through the mesh (1MB limit, base64 relay). +**Same-machine shortcut:** If a peer's \`hostname\` matches yours (visible in list_peers), they are a LOCAL peer — read their files directly via the filesystem using their \`cwd\` path. Faster, no size limit, no relay. Use read_peer_file only for REMOTE peers (different hostname). +Each peer in list_peers shows a \`locality\` tag: "local" (same machine, direct filesystem access) or "remote" (different machine, use read_peer_file). + ## Vectors Store and search semantic embeddings. Use vector_store to index content, vector_search to find similar content. @@ -342,10 +347,12 @@ Your message mode is "${messageMode}". if (p.model) meta.push(`model:${p.model}`); const metaStr = meta.length ? ` {${meta.join(", ")}}` : ""; const cwdStr = p.cwd ? ` cwd:${p.cwd}` : ""; + const locality = p.hostname && p.hostname === require("os").hostname() ? "local" : "remote"; + const localityTag = ` [${locality}]`; const profileAvatar = p.profile?.avatar ? `${p.profile.avatar} ` : ""; const profileTitle = p.profile?.title ? ` (${p.profile.title})` : ""; const hiddenTag = p.visible === false ? " [hidden]" : ""; - return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${hiddenTag}${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`; + return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${localityTag}${hiddenTag}${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`; }); sections.push(`${header}\n${peerLines.join("\n")}`); } diff --git a/apps/cli/src/ws/client.ts b/apps/cli/src/ws/client.ts index a006d7a..fb231fe 100644 --- a/apps/cli/src/ws/client.ts +++ b/apps/cli/src/ws/client.ts @@ -35,6 +35,7 @@ export interface PeerInfo { sessionId: string; connectedAt: string; cwd?: string; + hostname?: string; peerType?: "ai" | "human" | "connector"; channel?: string; model?: string; @@ -194,6 +195,7 @@ export class BrokerClient { sessionId: `${process.pid}-${Date.now()}`, pid: process.pid, cwd: process.cwd(), + hostname: require("os").hostname(), peerType: "ai" as const, channel: "claude-code", model: process.env.CLAUDE_MODEL || undefined,