@openvole/volenet-mcp 0.2.0 → 0.2.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 CHANGED
@@ -32,6 +32,10 @@ your behalf, not commands you type. Ask for them in your own words.
32
32
  > *"pair with the agent at http://10.0.0.5:9700"* → `volenet_connect`, which reports the
33
33
  > fingerprint first and pairs once you confirm it
34
34
 
35
+ Running it again is safe, and is how you move an existing registration: an identical one is left
36
+ alone, and one pointing somewhere else — a working-tree build, say, when you have since installed
37
+ the published package — is replaced rather than reported as already done.
38
+
35
39
  It registers for **every project**, because the identity it installs is per machine — one keypair
36
40
  in your home directory, shared by every session. Add `--local` to limit it to the current project.
37
41
  If the `claude` CLI is not on PATH, the installer prints the one line to paste instead of guessing
package/dist/index.js CHANGED
@@ -718,15 +718,16 @@ function addArgs(scope, command) {
718
718
  return ["mcp", "add", SERVER_NAME, "-s", scope, "--", ...command];
719
719
  }
720
720
  var NEXT_STEPS = '\nRestart Claude Code \u2014 MCP servers load at startup \u2014 then:\n\n volenet_whoami who you are on the mesh (an identity is made on first run)\n volenet_hub url:"..." join a hub, to be reachable from anywhere\n volenet_connect url:"..." or pair directly with an agent you can dial\n\nNothing else needs configuring.\n';
721
- function install(argv, out = process.stdout) {
721
+ var spawnClaude = (args) => spawnSync("claude", args, {
722
+ stdio: ["ignore", "pipe", "pipe"],
723
+ encoding: "utf-8",
724
+ timeout: 3e4
725
+ });
726
+ function install(argv, out = process.stdout, exec = spawnClaude) {
722
727
  const scope = argv.includes("--local") ? "local" : "user";
723
728
  const command = launchCommand();
724
729
  const paste = `claude mcp add ${SERVER_NAME} -s ${scope} -- ${command.join(" ")}`;
725
- const run3 = (args) => spawnSync("claude", args, {
726
- stdio: ["ignore", "pipe", "pipe"],
727
- encoding: "utf-8",
728
- timeout: 3e4
729
- });
730
+ const run3 = exec;
730
731
  const listed = run3(["mcp", "list"]);
731
732
  if (listed.error) {
732
733
  out.write(
@@ -738,10 +739,18 @@ function install(argv, out = process.stdout) {
738
739
  );
739
740
  return 1;
740
741
  }
741
- if (listed.stdout?.includes(`${SERVER_NAME}:`)) {
742
- out.write(`${SERVER_NAME} is already registered \u2014 nothing to do.
742
+ const existing = listed.stdout?.split("\n").find((l) => l.trimStart().startsWith(`${SERVER_NAME}:`));
743
+ if (existing) {
744
+ if (existing.includes(command.join(" "))) {
745
+ out.write(`${SERVER_NAME} is already registered, unchanged.
743
746
  ${NEXT_STEPS}`);
744
- return 0;
747
+ return 0;
748
+ }
749
+ out.write(`Replacing the existing ${SERVER_NAME} registration:
750
+ was: ${existing.trim()}
751
+ `);
752
+ run3(["mcp", "remove", SERVER_NAME, "-s", scope]);
753
+ run3(["mcp", "remove", SERVER_NAME, "-s", scope === "user" ? "local" : "user"]);
745
754
  }
746
755
  const added = run3(addArgs(scope, command));
747
756
  if (added.status !== 0) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/inbox.ts","../src/net-api.ts","../src/daemon.ts","../src/notify.ts","../src/node.ts","../src/index.ts","../src/cli.ts","../src/install.ts","../src/prompts.ts","../src/tools.ts"],"sourcesContent":["/**\n * Settings that survive a restart, so nothing has to be configured at install time.\n *\n * The first version of this took its name, hub and port from environment variables, which meant\n * the install line carried three flags a new user could not yet know the values of — and changing\n * one meant re-registering the server. Settings belong to the node, not to the command that\n * launches it: they live in its data directory, next to the identity they describe, and are\n * changed from inside a session with `volenet_hub`.\n *\n * Environment still wins where it is set, for scripted setups and CI. Nothing is required.\n */\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\n\nexport interface StoredConfig {\n\tname?: string\n\thub?: string\n\tport?: number\n}\n\nexport interface Settings {\n\tname: string\n\thub?: string\n\tdir: string\n\tport: number\n\t/** Which read state in the shared inbox is this session's. See {@link sessionKey}. */\n\tsession: string\n}\n\n/**\n * Which reader of the shared inbox this session is.\n *\n * The identity is per machine, deliberately: pairing once is the point of having one. Being\n * *caught up* is not — several editor sessions run at once, and one opening its inbox must not\n * mark the messages seen for the others.\n *\n * Keyed by the directory the client started the server in, so it is stable across a restart (the\n * same project reopened is the same reader, and does not replay what it has already seen) and\n * distinct between projects open at the same time. The hash disambiguates two projects that share\n * a basename; the basename is kept in front so the file is recognisable.\n */\nexport function sessionKey(cwd = process.cwd()): string {\n\tconst hash = crypto.createHash('sha256').update(cwd).digest('hex').slice(0, 8)\n\tconst base = (cwd.split('/').filter(Boolean).pop() ?? 'session')\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9._-]+/g, '-')\n\t\t.slice(0, 40)\n\treturn `${base}-${hash}`\n}\n\nexport function defaultDir(): string {\n\treturn process.env.VOLENET_MCP_DIR?.trim() || path.join(os.homedir(), '.openvole', 'volenet-mcp')\n}\n\n/** A name that says what this is without needing to be chosen. Identity is the key, not this. */\nexport function defaultName(): string {\n\treturn `claude-${os.hostname().split('.')[0].toLowerCase()}`\n}\n\nconst file = (dir: string) => path.join(dir, 'config.json')\n\nexport async function loadStored(dir: string): Promise<StoredConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await fs.readFile(file(dir), 'utf-8')) as StoredConfig\n\t\treturn raw && typeof raw === 'object' ? raw : {}\n\t} catch {\n\t\treturn {}\n\t}\n}\n\nexport async function saveStored(dir: string, patch: StoredConfig): Promise<StoredConfig> {\n\tconst next = { ...(await loadStored(dir)), ...patch }\n\tfor (const k of Object.keys(next) as Array<keyof StoredConfig>) {\n\t\tif (next[k] === undefined) delete next[k]\n\t}\n\tawait fs.mkdir(dir, { recursive: true })\n\tawait fs.writeFile(file(dir), `${JSON.stringify(next, null, 2)}\\n`, 'utf-8')\n\treturn next\n}\n\n/** Environment over stored settings over defaults. Every layer is optional. */\nexport async function resolveSettings(): Promise<Settings> {\n\tconst dir = defaultDir()\n\tconst stored = await loadStored(dir)\n\tconst envPort = Number(process.env.VOLENET_MCP_PORT)\n\treturn {\n\t\tname: process.env.VOLENET_MCP_NAME?.trim() || stored.name || defaultName(),\n\t\thub: process.env.VOLENET_MCP_HUB?.trim() || stored.hub || undefined,\n\t\tdir,\n\t\tport: (Number.isFinite(envPort) && envPort > 0 ? envPort : stored.port) || 9750,\n\t\tsession: process.env.VOLENET_MCP_SESSION?.trim() || sessionKey(),\n\t}\n}\n","/**\n * What was said: one log, and a read cursor for each session reading it.\n *\n * An MCP server lives and dies with the editor session that spawned it, but a conversation does\n * not. VoleNet already makes an intermittent peer work — a sender holds what it could not deliver\n * and flushes when you reappear — so what was missing is somewhere to put what arrives that is\n * still there next time.\n *\n * The subtlety is that several editor sessions run at once, sharing one identity because pairing\n * once is the whole point of an identity. They must not share a *read* state: one session opening\n * its inbox would mark the messages seen and the next session would never hear about them. So the\n * messages are one append-only log, and being caught up is per session.\n *\n * Append-only also makes concurrency cheap. Two processes rewriting one JSON file lose each\n * other's writes; two processes appending a line each do not, and a cursor file has exactly one\n * writer. No locking, no daemon.\n */\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\n\nexport interface Message {\n\t/** The peer this is with — the sender for 'in', the recipient for 'out'. */\n\tpeerId: string\n\tpeerName: string\n\tdir: 'in' | 'out'\n\ttext: string\n\t/** Milliseconds since the epoch, from the signed message for 'in'. */\n\tts: number\n\t/** The signed message id, so a replay or a double-flush cannot duplicate a line. */\n\tid: string\n}\n\n/** How many lines to keep. The oldest go when the log is next compacted. */\nexport const MAX_MESSAGES = 2000\n\nexport class Inbox {\n\tprivate messages: Message[] = []\n\t/** Per peer, the timestamp up to which *this* session has been shown its messages. */\n\tprivate readAt = new Map<string, number>()\n\tprivate writing: Promise<void> = Promise.resolve()\n\n\t/**\n\t * @param dir where the shared log and the cursors live\n\t * @param session which read state is ours. Sessions in different projects are different\n\t * readers; the same project reopened is the same reader, so restarting does\n\t * not replay everything already seen.\n\t */\n\tconstructor(\n\t\tprivate readonly dir: string,\n\t\tprivate readonly session = 'default',\n\t) {}\n\n\tprivate get log(): string {\n\t\treturn path.join(this.dir, 'messages.jsonl')\n\t}\n\n\tprivate get cursor(): string {\n\t\treturn path.join(this.dir, 'cursors', `${this.session}.json`)\n\t}\n\n\tasync load(): Promise<void> {\n\t\tawait this.adoptLegacy()\n\t\tthis.messages = await readLog(this.log)\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(this.cursor, 'utf-8')) as Record<string, number>\n\t\t\tthis.readAt = new Map(Object.entries(raw ?? {}))\n\t\t} catch {\n\t\t\tthis.readAt = new Map()\n\t\t}\n\t}\n\n\t/** Re-read what other sessions have appended since we loaded. */\n\tasync refresh(): Promise<void> {\n\t\tthis.messages = await readLog(this.log)\n\t}\n\n\t/** Record a message. Returns false when this id was already recorded. */\n\tasync add(m: Message): Promise<boolean> {\n\t\tawait this.refresh()\n\t\tif (this.messages.some((x) => x.id === m.id)) return false\n\t\tthis.messages.push(m)\n\t\tawait this.append(m)\n\t\treturn true\n\t}\n\n\t/** Everything with one peer, oldest first. */\n\thistory(peerId: string, limit = 50): Message[] {\n\t\treturn this.messages.filter((m) => m.peerId === peerId).slice(-limit)\n\t}\n\n\t/** Inbound messages this session has not been shown yet, oldest first. */\n\tunread(): Message[] {\n\t\treturn this.messages.filter((m) => m.dir === 'in' && m.ts > (this.readAt.get(m.peerId) ?? 0))\n\t}\n\n\t/** Mark everything currently unread as seen — for this session, and nobody else. */\n\tasync markRead(): Promise<void> {\n\t\tfor (const m of this.unread()) {\n\t\t\tconst at = this.readAt.get(m.peerId) ?? 0\n\t\t\tif (m.ts > at) this.readAt.set(m.peerId, m.ts)\n\t\t}\n\t\tawait this.persistCursor()\n\t}\n\n\t/** Every peer we have said anything to or heard anything from, most recent first. */\n\tpeers(): Array<{ peerId: string; peerName: string; last: number; unread: number }> {\n\t\tconst by = new Map<string, { peerId: string; peerName: string; last: number; unread: number }>()\n\t\tfor (const m of this.messages) {\n\t\t\tconst e = by.get(m.peerId) ?? { peerId: m.peerId, peerName: m.peerName, last: 0, unread: 0 }\n\t\t\tif (m.peerName) e.peerName = m.peerName\n\t\t\te.last = Math.max(e.last, m.ts)\n\t\t\tif (m.dir === 'in' && m.ts > (this.readAt.get(m.peerId) ?? 0)) e.unread++\n\t\t\tby.set(m.peerId, e)\n\t\t}\n\t\treturn [...by.values()].sort((a, b) => b.last - a.last)\n\t}\n\n\tget size(): number {\n\t\treturn this.messages.length\n\t}\n\n\t/** One line, one write — an append no other session can lose. */\n\tprivate append(m: Message): Promise<void> {\n\t\tthis.writing = this.writing.then(async () => {\n\t\t\tawait fs.mkdir(this.dir, { recursive: true })\n\t\t\tawait fs.appendFile(this.log, `${JSON.stringify(m)}\\n`, 'utf-8')\n\t\t\tif (this.messages.length > MAX_MESSAGES) await this.compact()\n\t\t})\n\t\treturn this.writing\n\t}\n\n\t/** Rewrite the log with the newest MAX_MESSAGES. Rare, and atomic via rename. */\n\tprivate async compact(): Promise<void> {\n\t\tconst keep = this.messages.slice(-MAX_MESSAGES)\n\t\tconst tmp = `${this.log}.${process.pid}.tmp`\n\t\tawait fs.writeFile(tmp, keep.map((m) => `${JSON.stringify(m)}\\n`).join(''), 'utf-8')\n\t\tawait fs.rename(tmp, this.log)\n\t\tthis.messages = keep\n\t}\n\n\tprivate async persistCursor(): Promise<void> {\n\t\tawait fs.mkdir(path.dirname(this.cursor), { recursive: true })\n\t\tconst tmp = `${this.cursor}.tmp`\n\t\tawait fs.writeFile(tmp, JSON.stringify(Object.fromEntries(this.readAt), null, 2), 'utf-8')\n\t\tawait fs.rename(tmp, this.cursor)\n\t}\n\n\t/**\n\t * Carry over messages written before the log existed.\n\t *\n\t * Earlier versions kept one `inbox.json` holding both the messages and a single read state. The\n\t * messages are still someone's; dropping them on upgrade would lose real conversations. The old\n\t * read state is deliberately *not* carried over — it was one cursor for every session, so honouring\n\t * it would mark messages seen for sessions that never saw them. Unread is the safe direction.\n\t */\n\tprivate async adoptLegacy(): Promise<void> {\n\t\tconst legacy = path.join(this.dir, 'inbox.json')\n\t\ttry {\n\t\t\tawait fs.access(this.log)\n\t\t\treturn // the log exists; nothing to carry over\n\t\t} catch {\n\t\t\t// no log yet\n\t\t}\n\t\tlet raw: { messages?: Message[] }\n\t\ttry {\n\t\t\traw = JSON.parse(await fs.readFile(legacy, 'utf-8')) as { messages?: Message[] }\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\tconst messages = (raw.messages ?? []).filter(\n\t\t\t(m) => m && typeof m.peerId === 'string' && typeof m.text === 'string',\n\t\t)\n\t\tif (messages.length === 0) return\n\t\tawait fs.mkdir(this.dir, { recursive: true })\n\t\tawait fs.writeFile(this.log, messages.map((m) => `${JSON.stringify(m)}\\n`).join(''), 'utf-8')\n\t\tawait fs.rename(legacy, `${legacy}.migrated`)\n\t}\n}\n\nasync function readLog(file: string): Promise<Message[]> {\n\tlet body: string\n\ttry {\n\t\tbody = await fs.readFile(file, 'utf-8')\n\t} catch {\n\t\treturn []\n\t}\n\tconst out: Message[] = []\n\tfor (const line of body.split('\\n')) {\n\t\tif (!line.trim()) continue\n\t\ttry {\n\t\t\tconst m = JSON.parse(line) as Message\n\t\t\tif (m && typeof m.peerId === 'string' && typeof m.text === 'string') out.push(m)\n\t\t} catch {\n\t\t\t// A torn last line from a concurrent append: skip it, it will be read next time.\n\t\t}\n\t}\n\treturn out\n}\n","/**\n * Everything the tools need from a node, as a flat surface.\n *\n * The tools used to reach into `VoleNetManager` directly, including through the objects it hands\n * back — `getTransport()?.getPeers()`, `getRemoteTaskManager().delegateTask()`. That is fine while\n * the node lives in the same process, and impossible once it does not: a socket cannot return a\n * transport. Flattening it to plain calls with plain values is what lets the same tools run\n * against a node here or a node in a daemon, which is what being *present* while no session is\n * open requires.\n */\nimport type { VoleNetManager } from '@openvole/volenet'\n\nexport interface PeerInfo {\n\tid: string\n\tname: string\n\tconnected: boolean\n}\n\nexport interface RelayMemberInfo {\n\tid: string\n\tname: string\n\tviaHubName: string\n\tconnected: boolean\n\taccepted: boolean\n\tincoming: boolean\n\tawaiting: boolean\n}\n\nexport interface PairRequestInfo {\n\tid: string\n\tname: string\n\tnote?: string\n\twants?: string[]\n}\n\nexport interface PairGrantInput {\n\ttrust?: 'full' | 'tool' | 'read'\n\tallowBrain?: boolean\n}\n\nexport interface SendResult {\n\tok: boolean\n\tdelivered?: boolean\n\trelayed?: boolean\n\terror?: string\n}\n\nexport interface AskResult {\n\tstatus: string\n\tresult?: string\n\terror?: string\n}\n\nexport interface Identity {\n\tinstanceId: string\n\tpublicKeyString: string\n}\n\nexport interface RoomView {\n\troom: string\n\tname: string\n\ttopic?: string\n\tmembers: Array<{ instanceId: string; name: string }>\n}\n\n/** A node, wherever it happens to be running. */\nexport interface NetLike {\n\tidentity(): Promise<Identity | null>\n\t/** Peers this node holds a direct link with, and whether the socket is live. */\n\tinstances(): Promise<PeerInfo[]>\n\trelayMembers(): Promise<RelayMemberInfo[]>\n\tsendChat(to: string, text: string): Promise<SendResult>\n\taskBrain(to: string, input: string, fromName: string, timeoutMs: number): Promise<AskResult>\n\tjoinHub(\n\t\turl: string,\n\t): Promise<{ ok: boolean; pending?: boolean; hubName?: string; error?: string }>\n\taddPeer(url: string): Promise<void>\n\tforgetPeer(url: string): Promise<boolean>\n\tprobePair(url: string): Promise<{\n\t\tok: boolean\n\t\tname?: string\n\t\tfingerprint?: string\n\t\tpublicKey?: string\n\t\talreadyTrusted?: boolean\n\t\terror?: string\n\t}>\n\tinitiatePair(\n\t\turl: string,\n\t\tpublicKey: string,\n\t\tnote?: string,\n\t\twants?: string[],\n\t): Promise<{ ok: boolean; pending?: boolean; error?: string }>\n\trequestRelayConnect(\n\t\tref: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; queued?: boolean; error?: string }>\n\tapproveRelayConnect(ref: string): Promise<{ ok: boolean; error?: string }>\n\tdenyRelayConnect(ref: string): Promise<{ ok: boolean; error?: string }>\n\t/** Rooms this node is in, as its hub last described them (PROTOCOL.md §7c). */\n\trooms(): Promise<RoomView[]>\n\troomCommand(\n\t\thub: string,\n\t\ttype: 'room:create' | 'room:join' | 'room:leave' | 'room:invite' | 'room:list',\n\t\tpayload: Record<string, unknown>,\n\t): Promise<{ ok: boolean; error?: string }>\n\t/** Post to a room: one sealed copy per member, so there is no key and no rotation. */\n\tpostToRoom(\n\t\troom: string,\n\t\ttext: string,\n\t): Promise<{ ok: boolean; sent: number; held: number; skipped: number; error?: string }>\n\tlistPairRequests(): Promise<PairRequestInfo[]>\n\tacceptPair(ref: string, grant?: PairGrantInput): Promise<{ ok: boolean; error?: string }>\n\tdenyPair(ref: string): Promise<{ ok: boolean }>\n}\n\n/** The same surface, backed by a manager in this process. */\nexport function localNet(m: VoleNetManager): NetLike {\n\treturn {\n\t\tasync identity() {\n\t\t\tconst k = m.getKeyPair()\n\t\t\treturn k ? { instanceId: k.instanceId, publicKeyString: k.publicKeyString } : null\n\t\t},\n\t\tasync instances() {\n\t\t\t// Whether a direct link is live is the transport's business — an instance record\n\t\t\t// outlives the socket, so `lastSeen` alone would report a dead link as online.\n\t\t\tconst live = new Set(\n\t\t\t\t(m.getTransport()?.getPeers() ?? []).filter((p) => p.connected).map((p) => p.peerId),\n\t\t\t)\n\t\t\treturn m.getInstances().map((i) => ({ id: i.id, name: i.name, connected: live.has(i.id) }))\n\t\t},\n\t\tasync relayMembers() {\n\t\t\treturn m.getRelayMembers().map((r) => ({\n\t\t\t\tid: r.id,\n\t\t\t\tname: r.name,\n\t\t\t\tviaHubName: r.viaHubName,\n\t\t\t\tconnected: r.connected,\n\t\t\t\taccepted: r.accepted,\n\t\t\t\tincoming: r.incoming,\n\t\t\t\tawaiting: r.awaiting,\n\t\t\t}))\n\t\t},\n\t\tsendChat: (to, text) => m.sendChat(to, text),\n\t\tasync askBrain(to, input, fromName, timeoutMs) {\n\t\t\tconst mgr = m.getRemoteTaskManager()\n\t\t\tif (!mgr) return { status: 'failed', error: 'remote task manager not available' }\n\t\t\tconst r = await mgr.delegateTask(to, { taskId: '', input, fromName }, timeoutMs)\n\t\t\treturn { status: r.status, result: r.result, error: r.error }\n\t\t},\n\t\tjoinHub: (url) => m.initiateJoin(url),\n\t\taddPeer: (url) => m.addPeer(url),\n\t\tasync forgetPeer(url) {\n\t\t\treturn m.forgetPeer(url)\n\t\t},\n\t\tprobePair: (url) => m.probePair(url),\n\t\tinitiatePair: (url, publicKey, note, wants) =>\n\t\t\tm.initiatePair(url, publicKey, note, wants as 'brain'[] | undefined),\n\t\trequestRelayConnect: (ref, note) => m.requestRelayConnect(ref, note),\n\t\tapproveRelayConnect: (ref) => m.approveRelayConnect(ref),\n\t\tdenyRelayConnect: (ref) => m.denyRelayConnect(ref),\n\t\tasync listPairRequests() {\n\t\t\treturn m.listPairRequests().map((r) => ({\n\t\t\t\tid: r.id,\n\t\t\t\tname: r.name,\n\t\t\t\tnote: r.note,\n\t\t\t\twants: r.wants,\n\t\t\t}))\n\t\t},\n\t\tasync rooms() {\n\t\t\treturn m.getRooms().map((r) => ({\n\t\t\t\troom: r.room,\n\t\t\t\tname: r.name,\n\t\t\t\ttopic: r.topic,\n\t\t\t\tmembers: r.members.map((x) => ({ instanceId: x.instanceId, name: x.name })),\n\t\t\t}))\n\t\t},\n\t\troomCommand: (hub, type, payload) => m.roomCommand(hub, type, payload),\n\t\tpostToRoom: (room, text) => m.postToRoom(room, text),\n\t\tacceptPair: (ref, grant) => m.acceptPair(ref, grant),\n\t\tdenyPair: (ref) => m.denyPair(ref),\n\t}\n}\n\n/** The method names a remote node must answer — kept beside the interface so they cannot drift. */\nexport const NET_METHODS = [\n\t'identity',\n\t'instances',\n\t'relayMembers',\n\t'sendChat',\n\t'askBrain',\n\t'joinHub',\n\t'addPeer',\n\t'forgetPeer',\n\t'probePair',\n\t'initiatePair',\n\t'requestRelayConnect',\n\t'approveRelayConnect',\n\t'denyRelayConnect',\n\t'rooms',\n\t'roomCommand',\n\t'postToRoom',\n\t'listPairRequests',\n\t'acceptPair',\n\t'denyPair',\n] as const satisfies ReadonlyArray<keyof NetLike>\n","/**\n * One node per machine, alive between sessions.\n *\n * A node that lives and dies with an editor session is *offline* whenever nothing is open: senders\n * hold what they cannot deliver, hubs record that somebody tried, and nothing arrives until you\n * reopen. That is a mailbox, not a channel. It also means two sessions run two nodes on one\n * identity, and a hub binds one socket per identity — so the second connection leaves the first\n * deaf.\n *\n * Both go away with a single long-lived process that owns the identity, the connections and the\n * writing of arrivals. Sessions attach to it over a unix socket and ask it to act. They do *not*\n * ask it what has arrived: the message log is a file, so reading stays local and needs no protocol.\n *\n * The socket is per identity directory, so a second daemon cannot start on the same identity, and\n * the first session to want one starts it.\n */\nimport { spawn } from 'node:child_process'\nimport * as fs from 'node:fs/promises'\nimport * as net from 'node:net'\nimport * as path from 'node:path'\nimport { NET_METHODS, type NetLike } from './net-api.js'\n\nexport const socketPath = (dir: string) => path.join(dir, 'daemon.sock')\n\ninterface Request {\n\tid: number\n\tmethod: string\n\targs: unknown[]\n}\n\ninterface Response {\n\tid: number\n\tok: boolean\n\tresult?: unknown\n\terror?: string\n}\n\n/** Serve a node over a unix socket until the process is stopped. */\nexport async function serve(dir: string, node: NetLike): Promise<net.Server> {\n\tconst sock = socketPath(dir)\n\tawait fs.mkdir(dir, { recursive: true })\n\t// A socket file outlives the process that made it. If nothing answers, it is stale.\n\tawait fs.rm(sock, { force: true })\n\n\tconst server = net.createServer((conn) => {\n\t\tlet buffer = ''\n\t\tconn.setEncoding('utf-8')\n\t\tconn.on('data', (chunk) => {\n\t\t\tbuffer += chunk\n\t\t\tfor (let nl = buffer.indexOf('\\n'); nl >= 0; nl = buffer.indexOf('\\n')) {\n\t\t\t\tconst line = buffer.slice(0, nl)\n\t\t\t\tbuffer = buffer.slice(nl + 1)\n\t\t\t\tif (line.trim()) void handle(line, conn, node)\n\t\t\t}\n\t\t})\n\t\t// A session going away is ordinary; it must never take the daemon with it.\n\t\tconn.on('error', () => undefined)\n\t})\n\tserver.on('error', () => undefined)\n\tawait new Promise<void>((done) => server.listen(sock, done))\n\treturn server\n}\n\nasync function handle(line: string, conn: net.Socket, node: NetLike): Promise<void> {\n\tlet req: Request\n\ttry {\n\t\treq = JSON.parse(line) as Request\n\t} catch {\n\t\treturn\n\t}\n\tconst reply = (r: Omit<Response, 'id'>) => {\n\t\ttry {\n\t\t\tconn.write(`${JSON.stringify({ id: req.id, ...r })}\\n`)\n\t\t} catch {\n\t\t\t// the session went away mid-call\n\t\t}\n\t}\n\tif (!(NET_METHODS as readonly string[]).includes(req.method)) {\n\t\treply({ ok: false, error: `unknown method: ${req.method}` })\n\t\treturn\n\t}\n\ttry {\n\t\tconst fn = node[req.method as keyof NetLike] as (...a: unknown[]) => Promise<unknown>\n\t\treply({ ok: true, result: await fn(...(req.args ?? [])) })\n\t} catch (err) {\n\t\treply({ ok: false, error: err instanceof Error ? err.message : String(err) })\n\t}\n}\n\n/** Talk to a daemon over its socket, as if the node were here. */\nexport function remoteNet(conn: net.Socket): NetLike {\n\tlet next = 1\n\tconst pending = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>()\n\tlet buffer = ''\n\tconn.setEncoding('utf-8')\n\tconn.on('data', (chunk) => {\n\t\tbuffer += chunk\n\t\tfor (let nl = buffer.indexOf('\\n'); nl >= 0; nl = buffer.indexOf('\\n')) {\n\t\t\tconst line = buffer.slice(0, nl)\n\t\t\tbuffer = buffer.slice(nl + 1)\n\t\t\tif (!line.trim()) continue\n\t\t\ttry {\n\t\t\t\tconst res = JSON.parse(line) as Response\n\t\t\t\tconst waiter = pending.get(res.id)\n\t\t\t\tif (!waiter) continue\n\t\t\t\tpending.delete(res.id)\n\t\t\t\tif (res.ok) waiter.resolve(res.result)\n\t\t\t\telse waiter.reject(new Error(res.error ?? 'daemon error'))\n\t\t\t} catch {\n\t\t\t\t// not ours\n\t\t\t}\n\t\t}\n\t})\n\tconst fail = (why: string) => {\n\t\tfor (const [, w] of pending) w.reject(new Error(why))\n\t\tpending.clear()\n\t}\n\tconn.on('close', () => fail('the volenet daemon closed the connection'))\n\tconn.on('error', (e) => fail(e.message))\n\n\tconst call = (method: string, ...args: unknown[]) =>\n\t\tnew Promise<unknown>((resolve, reject) => {\n\t\t\tconst id = next++\n\t\t\tpending.set(id, { resolve, reject })\n\t\t\tconn.write(`${JSON.stringify({ id, method, args })}\\n`)\n\t\t})\n\n\treturn Object.fromEntries(\n\t\tNET_METHODS.map((m) => [m, (...args: unknown[]) => call(m, ...args)]),\n\t) as unknown as NetLike\n}\n\n/** Connect to a daemon already listening, or null when none is. */\nexport async function connect(dir: string): Promise<net.Socket | null> {\n\treturn new Promise((resolve) => {\n\t\tconst conn = net.createConnection(socketPath(dir))\n\t\tconst give = (ok: boolean) => {\n\t\t\tconn.removeAllListeners('connect')\n\t\t\tconn.removeAllListeners('error')\n\t\t\tif (ok) resolve(conn)\n\t\t\telse {\n\t\t\t\tconn.destroy()\n\t\t\t\tresolve(null)\n\t\t\t}\n\t\t}\n\t\tconn.once('connect', () => give(true))\n\t\tconn.once('error', () => give(false))\n\t})\n}\n\n/**\n * Start a daemon for this identity and wait for it to answer.\n *\n * Detached and with its streams released, so it outlives the session that happened to start it —\n * which is the entire point: being reachable is not supposed to depend on an editor being open.\n */\nexport async function spawnDaemon(\n\tdir: string,\n\tenv: NodeJS.ProcessEnv = {},\n): Promise<net.Socket | null> {\n\tconst entry = process.argv[1]\n\tif (!entry) return null\n\tconst child = spawn(process.execPath, [entry, 'daemon'], {\n\t\tdetached: true,\n\t\tstdio: 'ignore',\n\t\tenv: { ...process.env, ...env, VOLENET_MCP_DIR: dir },\n\t})\n\tchild.unref()\n\n\t// Poll briefly rather than guess a fixed delay: it is listening when it answers.\n\tfor (let i = 0; i < 40; i++) {\n\t\tconst conn = await connect(dir)\n\t\tif (conn) return conn\n\t\tawait new Promise((r) => setTimeout(r, 100))\n\t}\n\treturn null\n}\n","/**\n * Telling the person, when nothing can tell the session.\n *\n * MCP has no way for a server to wake its client — Claude Code advertises no `sampling`, so a\n * message cannot prompt a reply on its own. What is left is telling the *human*, which is what a\n * chat client actually does: the notification is the point, and reading it is their move.\n *\n * Only the daemon does this. It is the one thing always running, and it is where arrivals land.\n *\n * Best effort throughout: a machine with no notifier, a headless box, a locked-down desktop — none\n * of that is worth failing a delivery over, so every path here swallows its errors.\n */\nimport { spawn } from 'node:child_process'\n\nexport type Notifier = (title: string, body: string) => void\n\n/** Escape for AppleScript, which is the one path here that interpolates into a script. */\nconst applescript = (s: string) => s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n\nfunction run(command: string, args: string[]): void {\n\ttry {\n\t\tconst child = spawn(command, args, { stdio: 'ignore', detached: true })\n\t\tchild.on('error', () => undefined)\n\t\tchild.unref()\n\t} catch {\n\t\t// no such binary, or spawning is not allowed here\n\t}\n}\n\n/**\n * A notifier for this platform, or one that does nothing.\n *\n * `VOLENET_MCP_NOTIFY=off` turns it off; anything else names a command to run instead, which is\n * given the title and body as its two arguments.\n */\nexport function notifier(platform = process.platform): Notifier {\n\tconst setting = process.env.VOLENET_MCP_NOTIFY?.trim()\n\tif (setting === 'off') return () => undefined\n\tif (setting) return (title, body) => run(setting, [title, body])\n\n\tif (platform === 'darwin') {\n\t\treturn (title, body) =>\n\t\t\trun('osascript', [\n\t\t\t\t'-e',\n\t\t\t\t`display notification \"${applescript(body)}\" with title \"${applescript(title)}\"`,\n\t\t\t])\n\t}\n\tif (platform === 'linux') return (title, body) => run('notify-send', [title, body])\n\tif (platform === 'win32') {\n\t\treturn (title, body) =>\n\t\t\trun('powershell', [\n\t\t\t\t'-NoProfile',\n\t\t\t\t'-Command',\n\t\t\t\t`[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');` +\n\t\t\t\t\t`$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;` +\n\t\t\t\t\t`$n.Visible=$true;$n.ShowBalloonTip(5000,'${title.replace(/'/g, \"''\")}','${body.replace(/'/g, \"''\")}',0)`,\n\t\t\t])\n\t}\n\treturn () => undefined\n}\n\n/** One line of a message, short enough for a notification and with newlines flattened. */\nexport function preview(text: string, limit = 140): string {\n\tconst flat = text.replace(/\\s+/g, ' ').trim()\n\treturn flat.length > limit ? `${flat.slice(0, limit - 1)}…` : flat\n}\n","/**\n * The node this server talks to — usually not in this process.\n *\n * A Claude Code session gets an identity on the mesh rather than borrowing an agent's: its own\n * keypair, its own consent decisions. But an identity that only exists while an editor is open is\n * offline most of the time, and two open editors would run two nodes on one identity and fight over\n * the hub socket. So the node lives in a daemon — one per identity, started on demand, outliving\n * every session — and sessions attach to it.\n *\n * Reading does not go through the daemon. Messages are an append-only file, so a session reads them\n * directly and keeps its own cursor; only *acting* needs the node. That keeps the protocol small\n * and means a session can still show you your history if the daemon is somehow gone.\n */\nimport * as fsSync from 'node:fs'\nimport * as path from 'node:path'\nimport {\n\tVoleNetManager,\n\tcreateEventBus,\n\tloadAuthorizedVoles,\n\tparsePublicKey,\n} from '@openvole/volenet'\nimport { type Settings, resolveSettings } from './config.js'\nimport { connect, remoteNet, serve, spawnDaemon } from './daemon.js'\nimport { Inbox, type Message } from './inbox.js'\nimport { type NetLike, localNet } from './net-api.js'\nimport { type Notifier, notifier, preview } from './notify.js'\n\n/** What a node needs to start. Resolved from stored settings, env and defaults. */\nexport type NodeOptions = Settings\n\nexport { resolveSettings }\n\nexport interface PendingRequest {\n\tkind: 'pair' | 'relay'\n\tfrom: string\n\tfromName: string\n\tnote?: string\n\tat: number\n}\n\nexport interface Notice {\n\tfrom: string\n\tfromName: string\n\tcount: number\n\tlast: number\n}\n\nexport interface Node {\n\tnet: NetLike\n\tinbox: Inbox\n\t/** Trust decisions waiting on the person, newest last. */\n\trequests: PendingRequest[]\n\t/** Who tried to reach us while we were away, as the hub reports on reconnect. */\n\tnotices: Notice[]\n\toptions: NodeOptions\n\t/** What happened when the node last tried to join the configured hub. */\n\thubStatus: string\n\t/**\n\t * Whether the client will run a model when the server asks — MCP's `sampling` capability, and\n\t * the only way an arriving message could ever answer itself. Set once the client has connected.\n\t */\n\tcanSample: boolean\n\t/** Where this node is running, which decides whether it is there when nothing is open. */\n\twhere: 'daemon' | 'in-process'\n\t/** Be told when a message lands, so a session can wait for a reply rather than poll for one. */\n\tonMessage: (fn: (m: Message) => void) => () => void\n\tstop: () => Promise<void>\n}\n\n/**\n * Attach to this identity's daemon, starting one if none is running.\n *\n * Falls back to a node in this process when a daemon cannot be had — a sandbox that forbids\n * spawning, say. Everything still works; it is simply only present while this session is.\n */\nexport async function startNode(options: NodeOptions): Promise<Node> {\n\tconst inbox = new Inbox(options.dir, options.session)\n\tawait inbox.load()\n\n\tif (process.env.VOLENET_MCP_NO_DAEMON !== '1') {\n\t\tconst conn = (await connect(options.dir)) ?? (await spawnDaemon(options.dir))\n\t\tif (conn) {\n\t\t\treturn {\n\t\t\t\tnet: remoteNet(conn),\n\t\t\t\tinbox,\n\t\t\t\trequests: [],\n\t\t\t\tnotices: [],\n\t\t\t\toptions,\n\t\t\t\thubStatus: options.hub ? `joined ${options.hub}` : 'no hub configured',\n\t\t\t\tcanSample: false,\n\t\t\t\twhere: 'daemon',\n\t\t\t\tonMessage: watchLog(options.dir, inbox),\n\t\t\t\tstop: async () => {\n\t\t\t\t\t// The daemon is shared and stays; only this connection to it goes.\n\t\t\t\t\tconn.destroy()\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\tconst local = await startLocal(options, inbox)\n\treturn { ...local, where: 'in-process' }\n}\n\n/** A node in this process — what the daemon itself runs, and the fallback when it cannot. */\nexport async function startLocal(\n\toptions: NodeOptions,\n\tinbox: Inbox,\n\t/** Told when a message lands. Only the daemon passes one — it is the thing always running. */\n\tnotify?: Notifier,\n): Promise<Omit<Node, 'where'>> {\n\tconst bus = createEventBus()\n\tconst requests: PendingRequest[] = []\n\tconst notices: Notice[] = []\n\tconst listeners = new Set<(m: Message) => void>()\n\n\tconst port = (await isFree(options.port)) ? options.port : 0\n\tconst manager = new VoleNetManager(\n\t\t{\n\t\t\tenabled: true,\n\t\t\tinstanceName: options.name,\n\t\t\trole: 'peer',\n\t\t\tport,\n\t\t\tkeyPath: path.join(options.dir, 'net', 'vole_key'),\n\t\t\t// A hub is a peer we dial. 'read' rather than 'full': a hub carries our sealed traffic,\n\t\t\t// it has no business acting on this node.\n\t\t\tpeers: options.hub ? [{ url: options.hub, trust: 'read' }] : [],\n\t\t},\n\t\toptions.dir,\n\t)\n\n\tbus.on('volenet:chat', (d) => {\n\t\tconst m = d as {\n\t\t\tfrom: string\n\t\t\tfromName: string\n\t\t\ttext: string\n\t\t\tmessageId: string\n\t\t\ttimestamp: number\n\t\t}\n\t\tconst message: Message = {\n\t\t\tpeerId: m.from,\n\t\t\tpeerName: m.fromName,\n\t\t\tdir: 'in',\n\t\t\ttext: m.text,\n\t\t\tts: m.timestamp,\n\t\t\tid: m.messageId,\n\t\t}\n\t\t// Only a message we had not already recorded wakes a waiter, so a replay cannot.\n\t\tvoid inbox.add(message).then((added) => {\n\t\t\tif (!added) return\n\t\t\tfor (const fn of listeners) fn(message)\n\t\t\t// Nothing can wake a session, so tell the person instead. Reading it is their move.\n\t\t\tnotify?.(`${message.peerName} on VoleNet`, preview(message.text))\n\t\t})\n\t})\n\n\tbus.on('volenet:chat:pending', (d) => {\n\t\tconst p = d as { from: Array<{ from: string; fromName: string; count: number; last: number }> }\n\t\tfor (const n of p.from ?? []) {\n\t\t\tconst at = notices.findIndex((x) => x.from === n.from)\n\t\t\tif (at >= 0) notices[at] = n\n\t\t\telse notices.push(n)\n\t\t}\n\t})\n\n\tconst remember = (kind: 'pair' | 'relay') => (d: unknown) => {\n\t\tconst r = d as { from: string; fromName: string; note?: string }\n\t\tif (requests.some((x) => x.from === r.from && x.kind === kind)) return\n\t\trequests.push({ kind, from: r.from, fromName: r.fromName, note: r.note, at: Date.now() })\n\t}\n\tbus.on('volenet:pair:request', remember('pair'))\n\tbus.on('volenet:relay:request', remember('relay'))\n\n\tawait manager.start(undefined, bus)\n\tconst bound = manager.getTransport()?.getPort?.() ?? port\n\tconst settings: NodeOptions = { ...options, port: bound || options.port }\n\n\t// Dialling a hub we have never met gets a 401: it has no reason to trust this key yet. The\n\t// join flow is the introduction, and it hands back the hub's own key to pin.\n\tlet hubStatus = 'no hub configured'\n\tif (options.hub) {\n\t\thubStatus = (await alreadyTrusts(options.dir, options.hub))\n\t\t\t? `joined ${options.hub}`\n\t\t\t: await join(manager, options.hub)\n\t}\n\n\treturn {\n\t\tnet: localNet(manager),\n\t\tinbox,\n\t\trequests,\n\t\tnotices,\n\t\toptions: settings,\n\t\thubStatus,\n\t\tcanSample: false,\n\t\tonMessage: (fn) => {\n\t\t\tlisteners.add(fn)\n\t\t\treturn () => listeners.delete(fn)\n\t\t},\n\t\tstop: () => manager.stop(),\n\t}\n}\n\n/** Run as the daemon: a node in this process, served over the socket, until stopped. */\nexport async function runDaemon(options: NodeOptions): Promise<void> {\n\tconst inbox = new Inbox(options.dir, 'daemon')\n\tawait inbox.load()\n\tconst node = await startLocal(options, inbox, notifier())\n\tawait serve(options.dir, node.net)\n\t// Nothing else to do: the node is running and the socket is answering.\n\tawait new Promise(() => undefined)\n}\n\n/**\n * Notice messages the daemon appended.\n *\n * The daemon receives them, so a session cannot be told directly — but the log is a file, and a\n * file can be watched. Cheap, and it works no matter which process did the writing.\n */\nfunction watchLog(dir: string, inbox: Inbox): (fn: (m: Message) => void) => () => void {\n\treturn (fn) => {\n\t\tconst file = path.join(dir, 'messages.jsonl')\n\t\tconst seen = new Set(inbox.history('', 0).map((m) => m.id))\n\t\tlet closed = false\n\t\tconst check = async () => {\n\t\t\tif (closed) return\n\t\t\tconst before = new Set(inbox.unread().map((m) => m.id))\n\t\t\tawait inbox.refresh()\n\t\t\tfor (const m of inbox.unread()) {\n\t\t\t\tif (!before.has(m.id) && !seen.has(m.id)) {\n\t\t\t\t\tseen.add(m.id)\n\t\t\t\t\tfn(m)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet watcher: fsSync.FSWatcher | undefined\n\t\ttry {\n\t\t\twatcher = fsSync.watch(path.dirname(file), (_e, name) => {\n\t\t\t\tif (name === 'messages.jsonl') void check()\n\t\t\t})\n\t\t} catch {\n\t\t\t// no watcher available; the poll below still gets there\n\t\t}\n\t\tconst timer = setInterval(() => void check(), 1000)\n\t\treturn () => {\n\t\t\tclosed = true\n\t\t\tclearInterval(timer)\n\t\t\twatcher?.close()\n\t\t}\n\t}\n}\n\nasync function join(node: VoleNetManager, hub: string): Promise<string> {\n\tconst res = await node.initiateJoin(hub)\n\tif (!res.ok) return `could not join ${hub}: ${res.error}`\n\tif (res.pending) return `waiting for approval at ${hub}`\n\treturn `joined ${res.hubName ?? hub}`\n}\n\n/**\n * Whether the hub at this URL is already trusted, so a restart does not re-join every time.\n * It asks who the hub says it is, then looks that id up in what we already trust.\n */\nexport async function alreadyTrusts(dir: string, hub: string): Promise<boolean> {\n\ttry {\n\t\tconst r = await fetch(`${hub.replace(/\\/$/, '')}/volenet/info`, {\n\t\t\tsignal: AbortSignal.timeout(8000),\n\t\t})\n\t\tconst info = (await r.json()) as { publicKey?: string }\n\t\tconst parsed = info.publicKey ? parsePublicKey(info.publicKey) : null\n\t\tif (!parsed) return false\n\t\treturn (await loadAuthorizedVoles(path.join(dir, 'net'))).has(parsed.instanceId)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether anything is already listening on a port.\n *\n * A node serves the VoleNet endpoints so peers can dial *in*, which for a session behind NAT\n * essentially never happens — it dials out, to a hub or an agent. So a taken port is not worth\n * failing over.\n */\nasync function isFree(port: number): Promise<boolean> {\n\tconst netmod = await import('node:net')\n\treturn new Promise((resolve) => {\n\t\tconst probe = netmod\n\t\t\t.createServer()\n\t\t\t.once('error', () => resolve(false))\n\t\t\t.once('listening', () => probe.close(() => resolve(true)))\n\t\t\t.listen(port, '0.0.0.0')\n\t})\n}\n","/**\n * VoleNet as an MCP server.\n *\n * Claude Code is very good inside one machine and one session. It has no way to reach a person on\n * their phone, no way to talk to an agent someone else owns, and no identity that outlives the\n * session. VoleNet has all three and none of it is coding-assistant work — signed identity, hybrid\n * post-quantum sealing, a hub that carries ciphertext it cannot read, consent, and hold-and-forward\n * for a peer that is not there right now.\n *\n * So this is not another agent. It is the network, handed to an agent that already exists.\n *\n * stdio transport, which means **stdout belongs to the protocol**: anything printed there that is\n * not a JSON-RPC frame breaks the client. The core logger is silent by default and writes to a file\n * when VOLE_LOG_FILE is set; diagnostics here go to stderr.\n */\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n\tCallToolRequestSchema,\n\tGetPromptRequestSchema,\n\tListPromptsRequestSchema,\n\tListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport { run as runCli } from './cli.js'\nimport { type Node, resolveSettings, startNode } from './node.js'\nimport { PROMPTS } from './prompts.js'\nimport { TOOLS } from './tools.js'\n\nexport { Inbox } from './inbox.js'\nexport { run as runCli } from './cli.js'\nexport { type Settings, defaultDir, defaultName, loadStored, saveStored } from './config.js'\nexport { type Node, type NodeOptions, resolveSettings, startNode } from './node.js'\nexport { PROMPTS, type PromptDef } from './prompts.js'\nexport { TOOLS, type ToolDef } from './tools.js'\n\n/**\n * What is waiting, appended to every tool's result.\n *\n * MCP has no way for a server to push, so an arrived message would otherwise sit unseen until\n * somebody thought to look. Saying so on every result means any use of any tool surfaces it —\n * ambient awareness in place of the notification the protocol cannot send. The tools that just\n * showed you the messages are excluded, since they leave nothing unread.\n */\nexport function unreadFooter(node: Node, toolName: string): string {\n\tif (toolName === 'volenet_inbox' || toolName === 'volenet_wait') return ''\n\tconst unread = node.inbox.unread()\n\tif (unread.length === 0) return ''\n\tconst who = [...new Set(unread.map((m) => m.peerName))].join(', ')\n\treturn `\\n\\n— ${unread.length} unread message${unread.length === 1 ? '' : 's'} from ${who}. Read them with volenet_inbox.`\n}\n\n/** Wire the tools to an MCP server. Separated so a test can drive it without a transport. */\nexport function createServer(node: Node): Server {\n\tconst server = new Server(\n\t\t{ name: 'volenet', version: '0.1.0' },\n\t\t{ capabilities: { tools: {}, prompts: {} } },\n\t)\n\n\t// Flows, so a fresh session does not have to infer the order of things from a tool list.\n\tserver.setRequestHandler(ListPromptsRequestSchema, async () => ({\n\t\tprompts: PROMPTS.map((p) => ({\n\t\t\tname: p.name,\n\t\t\tdescription: p.description,\n\t\t\t...(p.arguments ? { arguments: p.arguments } : {}),\n\t\t})),\n\t}))\n\n\tserver.setRequestHandler(GetPromptRequestSchema, async (request) => {\n\t\tconst prompt = PROMPTS.find((p) => p.name === request.params.name)\n\t\tif (!prompt) throw new Error(`No such prompt: ${request.params.name}`)\n\t\treturn {\n\t\t\tdescription: prompt.description,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\trole: 'user' as const,\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: prompt.render((request.params.arguments ?? {}) as Record<string, string>),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t})\n\n\tserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n\t\ttools: TOOLS.map((t) => ({\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tinputSchema: t.inputSchema as { type: 'object' },\n\t\t})),\n\t}))\n\n\tserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n\t\tconst tool = TOOLS.find((t) => t.name === request.params.name)\n\t\tif (!tool) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text' as const, text: `No such tool: ${request.params.name}` }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst text = await tool.run(node, (request.params.arguments ?? {}) as Record<string, unknown>)\n\t\t\treturn { content: [{ type: 'text' as const, text: text + unreadFooter(node, tool.name) }] }\n\t\t} catch (err) {\n\t\t\t// A failed tool is a result, not a crash: the session should see why and carry on.\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{ type: 'text' as const, text: err instanceof Error ? err.message : String(err) },\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\t})\n\n\treturn server\n}\n\n/** Remember what the client can do, so a later session can say so without asking again. */\nexport async function recordClientCapabilities(dir: string, caps: unknown): Promise<void> {\n\ttry {\n\t\tconst fs = await import('node:fs/promises')\n\t\tconst path = await import('node:path')\n\t\tawait fs.mkdir(dir, { recursive: true })\n\t\tawait fs.writeFile(\n\t\t\tpath.join(dir, 'client.json'),\n\t\t\t`${JSON.stringify(caps ?? {}, null, 2)}\\n`,\n\t\t\t'utf-8',\n\t\t)\n\t} catch {\n\t\t// Diagnostics only; never worth failing a startup over.\n\t}\n}\n\nasync function main(): Promise<void> {\n\tconst options = await resolveSettings()\n\tconst node = await startNode(options)\n\tconst server = createServer(node)\n\tawait server.connect(new StdioServerTransport())\n\t// What the client offers back decides what is possible here. `sampling` is the only route to\n\t// an unprompted reply — it lets a server ask the client to run a model — so record it rather\n\t// than guess, and let whoami report it honestly.\n\tconst caps = server.getClientCapabilities()\n\tnode.canSample = Boolean(caps && typeof caps === 'object' && 'sampling' in caps)\n\tawait recordClientCapabilities(options.dir, caps)\n\tconst me = await node.net.identity().catch(() => null)\n\tprocess.stderr.write(\n\t\t`volenet-mcp: ${options.name} (${me?.instanceId.substring(0, 8) ?? '?'}) ready — node ${node.where}` +\n\t\t\t`${options.hub ? `, hub ${options.hub}` : ', no hub configured'}\\n`,\n\t)\n\n\tlet stopping = false\n\tconst shutdown = async () => {\n\t\tif (stopping) return\n\t\tstopping = true\n\t\tawait node.stop().catch(() => undefined)\n\t\tprocess.exit(0)\n\t}\n\tprocess.on('SIGINT', shutdown)\n\tprocess.on('SIGTERM', shutdown)\n\t// The client closing its end is the ordinary way this ends.\n\tprocess.stdin.on('close', shutdown)\n}\n\n// Only when run as the binary, so importing this module in a test starts nothing.\nif (process.argv[1]?.includes('volenet-mcp') || process.env.VOLENET_MCP_RUN === '1') {\n\t// A bare invocation is the MCP server over stdio, which is how a client starts it — but a\n\t// person who runs it in a terminal means the opposite, and would otherwise get a process that\n\t// looks hung, or a port conflict from a server they did not know they had started. A client\n\t// attaches a pipe, never a TTY, so that is the honest way to tell them apart.\n\tif (process.argv[2] || process.stdin.isTTY) {\n\t\trunCli(process.argv.slice(2))\n\t\t\t.then((code) => process.exit(code))\n\t\t\t.catch((err) => {\n\t\t\t\tprocess.stderr.write(`volenet-mcp: ${err instanceof Error ? err.message : String(err)}\\n`)\n\t\t\t\tprocess.exit(1)\n\t\t\t})\n\t} else {\n\t\tmain().catch((err) => {\n\t\t\tprocess.stderr.write(`volenet-mcp: ${err instanceof Error ? err.message : String(err)}\\n`)\n\t\t\tprocess.exit(1)\n\t\t})\n\t}\n}\n","import * as path from 'node:path'\n/**\n * The command line, for the things you want before a session exists — or without one.\n *\n * Everything here works on files alone: no node is started, no port is bound, nothing dials out.\n * That is deliberate. The MCP server may well be running while you type these, and a second node\n * on the same identity would fight it for the port; and a `SessionStart` hook has to be safe to\n * run every single time a session opens.\n *\n * So `hub` records a choice that the next node start acts on, and `inbox` reads what has already\n * been written. Anything that genuinely needs the network — the roster, pairing, asking a brain —\n * is a tool, because it needs a live node and a conversation to happen in.\n */\nimport { loadKeyPair } from '@openvole/volenet'\nimport { defaultDir, defaultName, loadStored, saveStored, sessionKey } from './config.js'\nimport { Inbox } from './inbox.js'\nimport { install } from './install.js'\n\nconst USAGE = `volenet-mcp — VoleNet as an MCP server\n\n volenet-mcp install [--local] register with Claude Code (default: every project)\n volenet-mcp whoami this machine's identity on the mesh\n volenet-mcp daemon run the node in the foreground (normally started for you)\n volenet-mcp hub [url|--leave] which hub to use; takes effect on the next session\n volenet-mcp inbox [--read] [--quiet]\n messages waiting. --read marks them seen, --quiet says\n nothing when there are none (for hooks)\n\nWith no command it runs as the MCP server itself, over stdio, which is how Claude Code starts it.\nAnything needing a live node — peers, pairing, asking an agent's brain — is a tool you ask for in\na session, not a command here.\n`\n\nconst when = (ts: number) => new Date(ts).toISOString().replace('T', ' ').slice(0, 16)\n\nexport async function run(argv: string[], out = process.stdout): Promise<number> {\n\tconst [command, ...rest] = argv\n\tconst dir = defaultDir()\n\n\tif (!command || command === 'help' || command === '--help' || command === '-h') {\n\t\tout.write(USAGE)\n\t\treturn 0\n\t}\n\n\tif (command === 'install') return install(rest, out)\n\n\tif (command === 'daemon') {\n\t\t// The node itself: one per identity, outliving every session. Sessions start this for\n\t\t// themselves, so running it by hand is for looking at what it does.\n\t\tconst { runDaemon } = await import('./node.js')\n\t\tconst { resolveSettings } = await import('./config.js')\n\t\tawait runDaemon(await resolveSettings())\n\t\treturn 0\n\t}\n\n\tif (command === 'whoami') {\n\t\tconst stored = await loadStored(dir)\n\t\tconst keys = await loadKeyPair(path.join(dir, 'net')).catch(() => null)\n\t\tif (!keys) {\n\t\t\tout.write(\n\t\t\t\t`No identity yet at ${dir}.\\nOne is generated the first time the server runs — start a session, or ask for volenet_whoami.\\n`,\n\t\t\t)\n\t\t\treturn 0\n\t\t}\n\t\tout.write(\n\t\t\t[\n\t\t\t\t`name ${stored.name ?? defaultName()}`,\n\t\t\t\t`instanceId ${keys.instanceId}`,\n\t\t\t\t`hub ${stored.hub ?? '(none — set one with: volenet-mcp hub <url>)'}`,\n\t\t\t\t`store ${dir}`,\n\t\t\t\t'',\n\t\t\t\t'That directory is your identity. Back it up; anyone who has it is you.',\n\t\t\t\t'',\n\t\t\t].join('\\n'),\n\t\t)\n\t\treturn 0\n\t}\n\n\tif (command === 'hub') {\n\t\tconst stored = await loadStored(dir)\n\t\tif (rest.includes('--leave')) {\n\t\t\tif (!stored.hub) {\n\t\t\t\tout.write('Not on a hub.\\n')\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tawait saveStored(dir, { hub: undefined })\n\t\t\tout.write(`Left ${stored.hub}. Your identity and direct pairings are untouched.\\n`)\n\t\t\treturn 0\n\t\t}\n\t\tconst url = rest.find((a) => !a.startsWith('-'))\n\t\tif (!url) {\n\t\t\tout.write(stored.hub ? `${stored.hub}\\n` : 'No hub set. Give one: volenet-mcp hub <url>\\n')\n\t\t\treturn 0\n\t\t}\n\t\tawait saveStored(dir, { hub: url.replace(/\\/$/, '') })\n\t\tout.write(\n\t\t\t`Hub set to ${url}.\\nIt is joined the next time the server starts — restart Claude Code, or ask for volenet_hub to do it now.\\n`,\n\t\t)\n\t\treturn 0\n\t}\n\n\tif (command === 'inbox') {\n\t\t// The same reader the session in this directory uses, so a hook and its session agree\n\t\t// about what has been seen.\n\t\tconst inbox = new Inbox(dir, process.env.VOLENET_MCP_SESSION?.trim() || sessionKey())\n\t\tawait inbox.load()\n\t\tconst unread = inbox.unread()\n\t\tif (unread.length === 0) {\n\t\t\t// A hook runs on every turn. Saying \"nothing\" every time is noise in the context it\n\t\t\t// feeds, so --quiet says nothing at all when there is nothing to say.\n\t\t\tif (!rest.includes('--quiet')) out.write('No new messages.\\n')\n\t\t\treturn 0\n\t\t}\n\t\tout.write(`${unread.length} new VoleNet message${unread.length === 1 ? '' : 's'}:\\n\\n`)\n\t\tfor (const m of unread) {\n\t\t\tout.write(` [${when(m.ts)}] ${m.peerName}: ${m.text}\\n`)\n\t\t}\n\t\t// Marking is opt-in: a hook that puts these in context should mark them, an operator\n\t\t// glancing at the inbox should not silently hide them from the next session.\n\t\tif (rest.includes('--read')) await inbox.markRead()\n\t\tout.write('\\n')\n\t\treturn 0\n\t}\n\n\tout.write(`Unknown command: ${command}\\n\\n${USAGE}`)\n\treturn 1\n}\n","/**\n * `npx @openvole/volenet-mcp install` — register this server with Claude Code.\n *\n * Setup used to be a line carrying three environment flags whose values a new user had no way to\n * know yet: their name on the mesh, a hub, a port. None of that belongs in an install command. The\n * name has a sensible default, the port only matters to peers that can dial you, and the hub is a\n * decision better made from inside a session, where `volenet_hub` joins one and remembers it.\n *\n * So this takes no configuration. It shells out to the `claude` CLI when that is on PATH, and\n * otherwise prints the line to paste — guessing at another tool's config file is how you corrupt\n * one. It never inherits stdin: an installer that can block on a prompt is not a one-shot command.\n */\nimport { spawnSync } from 'node:child_process'\nimport * as path from 'node:path'\n\nexport const SERVER_NAME = 'volenet'\nconst PACKAGE = '@openvole/volenet-mcp'\n\n/**\n * How this server should be launched, from wherever `install` was itself run.\n *\n * Not simply \"is this a .js file\": run through `npx`, the entry *is* a .js file, but one living\n * in a transient cache that npm is free to evict — registering that path would work until it\n * suddenly did not. What distinguishes the cases is `node_modules`: an installed copy is always\n * under one and should be launched by package name, while a build in a working tree is not and\n * has to be launched by path, since there is nothing published to resolve.\n */\nexport function launchCommand(entry = process.argv[1]): string[] {\n\tconst installed =\n\t\t!entry || !entry.endsWith('.js') || entry.includes(`${path.sep}node_modules${path.sep}`)\n\treturn installed ? ['npx', '-y', PACKAGE] : ['node', entry]\n}\n\n/** The `claude mcp add` arguments, as a pure value so a test can check them without running one. */\nexport function addArgs(scope: 'user' | 'local', command: string[]): string[] {\n\treturn ['mcp', 'add', SERVER_NAME, '-s', scope, '--', ...command]\n}\n\nconst NEXT_STEPS =\n\t'\\nRestart Claude Code — MCP servers load at startup — then:\\n\\n' +\n\t' volenet_whoami who you are on the mesh (an identity is made on first run)\\n' +\n\t' volenet_hub url:\"...\" join a hub, to be reachable from anywhere\\n' +\n\t' volenet_connect url:\"...\" or pair directly with an agent you can dial\\n\\n' +\n\t'Nothing else needs configuring.\\n'\n\nexport function install(argv: string[], out = process.stdout): number {\n\t// User scope by default, because the identity is: one keypair per machine, in the home\n\t// directory, shared by every session. Registering per project meant installing once and then\n\t// finding no tools in the next directory you opened — the identity was global, the\n\t// registration was not. `--local` is there for anyone who wants it in one project only.\n\tconst scope = argv.includes('--local') ? 'local' : 'user'\n\tconst command = launchCommand()\n\tconst paste = `claude mcp add ${SERVER_NAME} -s ${scope} -- ${command.join(' ')}`\n\n\t// Never inherit stdin: if the CLI asks something, this would hang instead of installing.\n\tconst run = (args: string[]) =>\n\t\tspawnSync('claude', args, {\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\tencoding: 'utf-8',\n\t\t\ttimeout: 30_000,\n\t\t})\n\n\tconst listed = run(['mcp', 'list'])\n\tif (listed.error) {\n\t\tout.write(\n\t\t\t`The \\`claude\\` CLI is not on PATH. Run this once, in the project you want it in:\\n\\n ${paste}\\n\\n`,\n\t\t)\n\t\treturn 1\n\t}\n\tif (listed.stdout?.includes(`${SERVER_NAME}:`)) {\n\t\tout.write(`${SERVER_NAME} is already registered — nothing to do.\\n${NEXT_STEPS}`)\n\t\treturn 0\n\t}\n\n\tconst added = run(addArgs(scope, command))\n\tif (added.status !== 0) {\n\t\tout.write(\n\t\t\t`Could not register it automatically${added.stderr ? `: ${added.stderr.trim()}` : ''}\\n\\n` +\n\t\t\t\t`Run this once instead:\\n\\n ${paste}\\n\\n`,\n\t\t)\n\t\treturn added.status ?? 1\n\t}\n\tout.write(\n\t\t`Registered ${SERVER_NAME} (${scope} scope${scope === 'user' ? ' — available in every project' : ', this project only'}).\\n${NEXT_STEPS}`,\n\t)\n\treturn 0\n}\n","/**\n * Guided flows, shipped with the server.\n *\n * A tool list tells a session what it *can* do, not what to do first, in what order, or what the\n * words mean. In a fresh session \"pair with my agent at <url>\" only works if the model happens to\n * match the sentence to `volenet_connect` — which it usually will, and shouldn't have to. Prompts\n * surface in the client as commands, so the flow is chosen rather than guessed.\n *\n * They ship in this package rather than as files written into someone's editor config, so\n * installing is all it takes and they cannot drift from the tools they describe.\n */\n\nexport interface PromptArgument {\n\tname: string\n\tdescription: string\n\trequired?: boolean\n}\n\nexport interface PromptDef {\n\tname: string\n\tdescription: string\n\targuments?: PromptArgument[]\n\t/** The instruction handed to the session, with any arguments already filled in. */\n\trender: (args: Record<string, string>) => string\n}\n\nexport const PROMPTS: PromptDef[] = [\n\t{\n\t\tname: 'whoami',\n\t\tdescription:\n\t\t\t'This session\\u2019s identity on the VoleNet mesh, and whether it can reach anything.',\n\t\trender: () => `Report this session's VoleNet identity.\n\nCall \\`volenet_whoami\\`. Give back the name, the instance id and where it is listening, and say in\none line whether it is actually reachable — a hub joined, or peers paired — rather than leaving an\nempty roster to be read as a failure.\n\nThe instance id is what someone else needs to grant this session anything: an agent's operator names\nit in \\`net.peers\\`. Offer it if they look like they need it. The public key is several kilobytes of\npost-quantum key material, so ask for it with \\`key: true\\` only when a peer actually wants it.\n\nIf nothing is connected, say so and offer \\`setup\\`.`,\n\t},\n\t{\n\t\tname: 'peers',\n\t\tdescription: 'Who this session can reach right now, and by which route.',\n\t\trender: () => `List who this session can reach on VoleNet.\n\nCall \\`volenet_peers\\`. For each one say whether it is online, and whether the link is direct or\nthrough a hub — the difference matters: a hub carries chat and consent, a direct link is the only\nroute that can ask an agent's brain.\n\nFlag anything that needs an action rather than only listing state: a hub member with no consent yet\ncannot be messaged until one side asks (\\`volenet_connect\\`), and someone offline will receive what\nis sent whenever they return. If the list is empty, say why — no hub, no pairings — and offer\n\\`setup\\`.`,\n\t},\n\t{\n\t\tname: 'rooms',\n\t\tdescription: 'Rooms this session is in, and how to say something to one.',\n\t\trender: () => `Show the VoleNet rooms this session is in.\n\nCall \\`volenet_room\\` with no arguments. For each, say who is in it — a room is several people and\nagents in one conversation, so who else is there is the useful part.\n\nTo say something, \\`volenet_room\\` with \\`post\\` and \\`room\\`. Every member gets their own sealed copy;\nthere is no shared key, which is why removing somebody stops them reading immediately. Report what\ncame back honestly: some copies may be waiting for members who are away, and some may not have been\nsent at all because that member has not accepted this session — **a room does not create consent**,\nso say that rather than let it read as a failure.\n\nIf there are no rooms, offer to make one (\\`create\\`) or to join one by id (\\`join\\`). A room lives on\na hub, so one has to be joined first.`,\n\t},\n\t{\n\t\tname: 'setup',\n\t\tdescription: 'Get this session onto the VoleNet mesh — join a hub, or pair with an agent.',\n\t\trender: () => `Get this session onto the VoleNet mesh.\n\n1. Call \\`volenet_whoami\\` first. It reports the identity, whether a hub is set, and how many peers\n are reachable. An identity is generated on first run; there is nothing to create.\n2. If nothing is connected, explain the two routes and ask which is wanted — do not pick silently:\n - **A hub** (\\`volenet_hub\\`) makes this session reachable from anywhere, including from a phone,\n and works when neither side can dial the other. A hub carries sealed traffic it cannot read and\n stores no message. It will **not** relay a question to an agent's brain.\n - **A direct pair** (\\`volenet_connect\\`) with an agent whose address is reachable from here. This\n is the only route that can ask an agent's brain.\n Both can be used at once, and either can be added later.\n3. Carry out whichever they choose. For a hub, the URL is enough. For a pair, follow the two-step\n fingerprint check — the \\`pair\\` command covers it.\n4. Finish by calling \\`volenet_peers\\` and saying plainly who is now reachable, and by which route.\n\nReaching someone also needs consent, which is separate from being connected: on a hub, either side\nasks and the other accepts. Say so, rather than letting an empty roster look like a failure.`,\n\t},\n\t{\n\t\tname: 'catch-up',\n\t\tdescription: 'Read what arrived while this session was away, and say what needs answering.',\n\t\trender: () => `Catch up on VoleNet.\n\n1. Call \\`volenet_inbox\\`. It returns messages that arrived — including while no session was running,\n since senders hold what they could not deliver and flush on reconnect — and who tried to reach\n this session while it was away. Reading marks them seen.\n2. Call \\`volenet_peers\\` if anything needs context about who a sender is.\n3. Summarise for the person: who wrote, what they want, and what is worth answering. Do not reply on\n their behalf without asking.\n4. If a reply is wanted, \\`volenet_send\\` says it and \\`volenet_wait\\` waits for what comes back, so\n an exchange happens in one turn rather than by checking again later.\n\nIf nothing arrived, say so in one line. This is worth running at the start of a session.`,\n\t},\n\t{\n\t\tname: 'pair',\n\t\tdescription: 'Pair with an agent at a URL, checking the fingerprint before trusting it.',\n\t\targuments: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\tdescription: 'The agent to pair with, e.g. http://10.0.0.5:9700',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trender: (a) => `Pair this session with the VoleNet node at ${a.url ?? '<url>'}.\n\nPairing is deliberately two calls, because trusting a URL blind is trusting whoever holds it.\n\n1. Call \\`volenet_connect\\` with \\`url: \"${a.url ?? '<url>'}\"\\`. It reaches the node and reports the\n fingerprint of whoever answered. It trusts nothing yet.\n2. Show that fingerprint to the person and ask them to check it against what the other side reports\n — \\`vole net show-key\\` on an OpenVole agent. **Wait for them.** Do not confirm on their behalf:\n this step exists precisely so a human compares two values.\n3. Ask whether this session should also be able to use that agent's **brain** — running its model\n to answer questions — or only chat with whoever runs it.\n4. Once they confirm the fingerprint, call \\`volenet_connect\\` again with the same \\`url\\`,\n \\`confirm:\\` set to that fingerprint, and \\`brain: true\\` if they said yes. This trusts the node\n and sends a pair request carrying the ask.\n5. Tell them the request now waits for the operator of that node to accept it, and that nothing\n arrives until they do.\n\nBeing trusted is not the same as being allowed to do anything: the keystore says who may connect,\n\\`net.peers\\` says what they may then do. Sending the ask with the request is what lets the operator\nsettle both while accepting, instead of editing a config file afterwards.`,\n\t},\n\t{\n\t\tname: 'reach',\n\t\tdescription: 'Message a peer and wait for the reply, rather than checking back later.',\n\t\targuments: [\n\t\t\t{ name: 'peer', description: 'Who to reach — a name or instance id', required: true },\n\t\t\t{ name: 'message', description: 'What to say', required: false },\n\t\t],\n\t\trender: (\n\t\t\ta,\n\t\t) => `Reach ${a.peer ?? 'a peer'} over VoleNet${a.message ? ` and say: ${a.message}` : ''}.\n\n1. \\`volenet_peers\\` first if unsure the name resolves, or by which route they are reachable.\n2. \\`volenet_send\\` to say it. This is chat: it reaches whoever is there and does **not** run their\n brain. To ask an agent's model instead, use \\`volenet_ask\\` — direct links only, and its operator\n must have granted brain access.\n3. \\`volenet_wait\\` for the answer, so the exchange completes in this turn. If nothing comes back in\n time, say so plainly: the message is not lost, and a reply lands in the inbox whenever it comes.\n\nIf the peer is offline the message waits here and goes out when they return — report that rather\nthan treating it as a failure.`,\n\t},\n]\n","/**\n * The tools, and what they are for.\n *\n * Deliberately few. An MCP server's tool list is spent from the client's context window on every\n * turn, and the agent's whole tool registry — which is what `/mcp/<agent>` already exposes — is the\n * wrong shape here: this server is about the *network*, not about one agent's abilities. Eight\n * verbs cover it: who am I, who is out there, what did I miss, say something, read a thread, ask\n * an agent to think, and the two halves of deciding whom to trust.\n */\nimport { saveStored } from './config.js'\nimport { type Node, alreadyTrusts } from './node.js'\n\nexport interface ToolDef {\n\tname: string\n\tdescription: string\n\tinputSchema: Record<string, unknown>\n\trun: (node: Node, args: Record<string, unknown>) => Promise<string>\n}\n\nconst obj = (properties: Record<string, unknown>, required: string[] = []) => ({\n\ttype: 'object' as const,\n\tproperties,\n\t...(required.length ? { required } : {}),\n})\nconst str = (description: string) => ({ type: 'string' as const, description })\nconst num = (description: string) => ({ type: 'number' as const, description })\n\nconst when = (ts: number) => (ts ? new Date(ts).toISOString().replace('T', ' ').slice(0, 19) : '-')\n\n/** One peer, however we can reach it. */\ninterface Peer {\n\tid: string\n\tname: string\n\troute: 'direct' | 'hub'\n\tconnected: boolean\n\t/** Hub peers only: whether the consent handshake has completed both ways. */\n\tconsented?: boolean\n\tviaHub?: string\n}\n\nasync function peers(node: Node): Promise<Peer[]> {\n\tconst out: Peer[] = (await node.net.instances()).map((i) => ({\n\t\tid: i.id,\n\t\tname: i.name,\n\t\troute: 'direct' as const,\n\t\tconnected: i.connected,\n\t}))\n\tconst direct = new Set(out.map((p) => p.id))\n\tfor (const m of await node.net.relayMembers()) {\n\t\tif (direct.has(m.id)) continue\n\t\tout.push({\n\t\t\tid: m.id,\n\t\t\tname: m.name,\n\t\t\troute: 'hub',\n\t\t\tconnected: m.connected,\n\t\t\tconsented: m.accepted,\n\t\t\tviaHub: m.viaHubName,\n\t\t})\n\t}\n\treturn out\n}\n\nasync function resolve(node: Node, ref: string): Promise<Peer | undefined> {\n\tconst all = await peers(node)\n\treturn (\n\t\tall.find((p) => p.id === ref) ??\n\t\tall.find((p) => p.name === ref) ??\n\t\tall.find((p) => p.id.startsWith(ref)) ??\n\t\tall.find((p) => p.name.toLowerCase() === ref.toLowerCase())\n\t)\n}\n\nexport const TOOLS: ToolDef[] = [\n\t{\n\t\tname: 'volenet_whoami',\n\t\tdescription:\n\t\t\t\"This session's own identity on the VoleNet mesh — name, instance id, hub, and whether the mesh is reachable. Call this first if you are unsure. Pass key:true only when someone actually needs the full public key; it is several kilobytes of post-quantum key material.\",\n\t\tinputSchema: obj({\n\t\t\tkey: { type: 'boolean' as const, description: 'Include the full public key string' },\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst key = await node.net.identity()\n\t\t\tconst online = (await peers(node)).filter((p) => p.connected).length\n\t\t\tconst lines = [\n\t\t\t\t`name ${node.options.name}`,\n\t\t\t\t`instanceId ${key?.instanceId ?? '(not started)'}`,\n\t\t\t\t`hub ${node.hubStatus}`,\n\t\t\t\t`connected ${online} peer(s) online`,\n\t\t\t\t`listening port ${node.options.port} (reachable only from networks that can dial it)`,\n\t\t\t\t`store ${node.options.dir}`,\n\t\t\t\t`node ${node.where === 'daemon' ? 'a daemon, so this identity stays reachable when no session is open' : 'in this session, so it is only reachable while this session is'}`,\n\t\t\t]\n\t\t\t// The hybrid key string is ~2.5 KB — most of an ML-DSA-65 key — and spending that on\n\t\t\t// every call would be a real cost to the session for something rarely needed.\n\t\t\t// Whether a message can ever prompt a reply on its own is the client's decision, not\n\t\t\t// ours: MCP's only server-initiated model call is `sampling`. Say which it is, so\n\t\t\t// nobody waits for an answer that cannot come.\n\t\t\tlines.push(\n\t\t\t\t`replies ${node.canSample ? 'this client can be asked to answer on its own' : 'only when you ask — this client cannot be woken by a message'}`,\n\t\t\t)\n\t\t\tif (args.key) lines.push('', `publicKey ${key?.publicKeyString ?? '-'}`)\n\t\t\telse\n\t\t\t\tlines.push(\n\t\t\t\t\t'',\n\t\t\t\t\t'Public key withheld (large). Call again with key:true when a peer needs it.',\n\t\t\t\t)\n\t\t\tlines.push(\n\t\t\t\t'',\n\t\t\t\t'To be reachable by someone else: give them that public key to trust, or send them a',\n\t\t\t\t'pair request with volenet_connect and have their operator accept it.',\n\t\t\t)\n\t\t\treturn lines.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_peers',\n\t\tdescription:\n\t\t\t'Everyone this session can reach: agents and people, whether the link is direct or through a hub, and whether they are online right now.',\n\t\tinputSchema: obj({}),\n\t\tasync run(node) {\n\t\t\tconst all = await peers(node)\n\t\t\tif (all.length === 0) {\n\t\t\t\treturn 'No peers. Join a hub (VOLENET_MCP_HUB) or pair with a node directly (volenet_pair).'\n\t\t\t}\n\t\t\treturn all\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst bits = [\n\t\t\t\t\t\tp.connected ? 'online ' : 'away ',\n\t\t\t\t\t\tp.route === 'direct' ? 'direct' : `via ${p.viaHub}`,\n\t\t\t\t\t\tp.name,\n\t\t\t\t\t\tp.id.substring(0, 8),\n\t\t\t\t\t]\n\t\t\t\t\tif (p.route === 'hub' && !p.consented) bits.push('(no consent yet — volenet_connect)')\n\t\t\t\t\treturn ` ${bits.join(' ')}`\n\t\t\t\t})\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_inbox',\n\t\tdescription:\n\t\t\t'Messages that arrived for this session, including while it was not running, plus who tried to reach it while it was away. Reading marks them seen. Check this at the start of a session.',\n\t\tinputSchema: obj({}),\n\t\tasync run(node) {\n\t\t\tconst unread = node.inbox.unread()\n\t\t\tconst lines: string[] = []\n\t\t\tif (unread.length === 0) lines.push('No new messages.')\n\t\t\telse {\n\t\t\t\tlines.push(`${unread.length} new message(s):`, '')\n\t\t\t\tfor (const m of unread) {\n\t\t\t\t\tlines.push(` [${when(m.ts)}] ${m.peerName} (${m.peerId.substring(0, 8)})`)\n\t\t\t\t\tlines.push(` ${m.text.replace(/\\n/g, '\\n ')}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node.notices.length > 0) {\n\t\t\t\tlines.push('', 'Tried to reach you while you were away:')\n\t\t\t\tfor (const n of node.notices) {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t` ${n.fromName} (${n.from.substring(0, 8)}) — ${n.count}x, last ${when(n.last)}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlines.push(\n\t\t\t\t\t' Their messages are held on their own device and arrive when they are next online.',\n\t\t\t\t)\n\t\t\t}\n\t\t\tawait node.inbox.markRead()\n\t\t\treturn lines.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_send',\n\t\tdescription:\n\t\t\t'Send a message to a person or an agent. This is chat: it is delivered and read by whoever is there, and does NOT run their brain or wait for a reply. Use this to reach a person on the phone app. If the peer is offline the message waits here and goes out when they return.',\n\t\tinputSchema: obj(\n\t\t\t{ to: str('Peer name or instance id (see volenet_peers)'), text: str('What to say') },\n\t\t\t['to', 'text'],\n\t\t),\n\t\tasync run(node, args) {\n\t\t\tconst to = String(args.to ?? '')\n\t\t\tconst text = String(args.text ?? '')\n\t\t\tif (!text.trim()) return 'Nothing to send.'\n\t\t\tconst peer = await resolve(node, to)\n\t\t\tconst res = await node.net.sendChat(peer?.id ?? to, text)\n\t\t\tif (!res.ok) return `Not sent: ${res.error ?? 'unknown error'}`\n\t\t\tawait node.inbox.add({\n\t\t\t\tpeerId: peer?.id ?? to,\n\t\t\t\tpeerName: peer?.name ?? to,\n\t\t\t\tdir: 'out',\n\t\t\t\ttext,\n\t\t\t\tts: Date.now(),\n\t\t\t\tid: `out-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n\t\t\t})\n\t\t\tif (res.delivered === false) {\n\t\t\t\treturn `Held for ${peer?.name ?? to}: they are not reachable right now, and it goes out when they are back.`\n\t\t\t}\n\t\t\treturn `Sent to ${peer?.name ?? to}${res.relayed ? ' (through a hub, sealed end to end)' : ''}.`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_history',\n\t\tdescription: 'The conversation with one peer, oldest first.',\n\t\tinputSchema: obj({\n\t\t\tpeer: str('Peer name or instance id'),\n\t\t\tlimit: num('Messages (default 50)'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst peer = await resolve(node, String(args.peer ?? ''))\n\t\t\tconst id = peer?.id ?? String(args.peer ?? '')\n\t\t\tconst msgs = node.inbox.history(id, Number(args.limit ?? 50))\n\t\t\tif (msgs.length === 0) return `Nothing recorded with ${peer?.name ?? id}.`\n\t\t\treturn msgs\n\t\t\t\t.map((m) => `[${when(m.ts)}] ${m.dir === 'out' ? 'you' : m.peerName}: ${m.text}`)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_ask',\n\t\tdescription:\n\t\t\t\"Ask another AGENT's brain a question and wait for its answer. This runs the peer's model, so it takes as long as thinking takes, and the peer's operator must have granted this session brain access. Only works over a direct link — a hub will not carry it — and a person's phone app has no brain to ask; use volenet_send for people.\",\n\t\tinputSchema: obj(\n\t\t\t{\n\t\t\t\tto: str('Agent name or instance id'),\n\t\t\t\tquestion: str('What to ask'),\n\t\t\t\ttimeout_ms: num('How long to wait (default 120000)'),\n\t\t\t},\n\t\t\t['to', 'question'],\n\t\t),\n\t\tasync run(node, args) {\n\t\t\tconst peer = await resolve(node, String(args.to ?? ''))\n\t\t\tif (!peer) return `No peer found: \"${args.to}\". Use volenet_peers.`\n\t\t\tif (peer.route !== 'direct') {\n\t\t\t\treturn `${peer.name} is only reachable through a hub, and a hub will not relay a question to an agent's brain — it carries chat and consent only. Use volenet_send, or pair directly with volenet_pair.`\n\t\t\t}\n\t\t\tconst res = await node.net.askBrain(\n\t\t\t\tpeer.id,\n\t\t\t\tString(args.question ?? ''),\n\t\t\t\tnode.options.name,\n\t\t\t\tNumber(args.timeout_ms ?? 120_000),\n\t\t\t)\n\t\t\tif (res.status === 'completed') return `${peer.name} says:\\n\\n${res.result}`\n\t\t\tconst why = res.error ?? res.status\n\t\t\tif (typeof why === 'string' && why.includes('allowBrain')) {\n\t\t\t\treturn `${peer.name} refused: ${why}\\n\\nIts operator can allow this session by adding { \"id\": \"${(await node.net.identity())?.instanceId}\", \"trust\": \"read\", \"allowBrain\": true } to net.peers and restarting.`\n\t\t\t}\n\t\t\treturn `${peer.name} did not answer: ${why}`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_requests',\n\t\tdescription:\n\t\t\t'Trust decisions waiting on you: nodes asking to be trusted, and hub members asking to chat. Accept or deny one by naming it. Nothing is trusted until you say so.',\n\t\tinputSchema: obj({\n\t\t\taccept: str('Name or id to accept'),\n\t\t\tdeny: str('Name or id to deny'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst accept = args.accept ? String(args.accept) : undefined\n\t\t\tconst deny = args.deny ? String(args.deny) : undefined\n\t\t\tconst act = accept ?? deny\n\t\t\tif (act) {\n\t\t\t\tconst req = node.requests.find((r) => r.fromName === act || r.from.startsWith(act))\n\t\t\t\tif (!req) return `No pending request matching \"${act}\".`\n\t\t\t\tconst ok = accept\n\t\t\t\t\t? req.kind === 'pair'\n\t\t\t\t\t\t? await node.net.acceptPair(req.from)\n\t\t\t\t\t\t: await node.net.approveRelayConnect(req.from)\n\t\t\t\t\t: req.kind === 'pair'\n\t\t\t\t\t\t? await node.net.denyPair(req.from)\n\t\t\t\t\t\t: await node.net.denyRelayConnect(req.from)\n\t\t\t\tnode.requests.splice(node.requests.indexOf(req), 1)\n\t\t\t\treturn ok.ok\n\t\t\t\t\t? `${accept ? 'Accepted' : 'Denied'} ${req.fromName}.`\n\t\t\t\t\t: `Failed: ${'error' in ok ? ok.error : 'unknown'}`\n\t\t\t}\n\t\t\tconst pairs = await node.net.listPairRequests()\n\t\t\tfor (const p of pairs) {\n\t\t\t\tif (!node.requests.some((r) => r.from === p.id)) {\n\t\t\t\t\tnode.requests.push({ kind: 'pair', from: p.id, fromName: p.name, at: Date.now() })\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node.requests.length === 0) return 'Nothing waiting.'\n\t\t\treturn node.requests\n\t\t\t\t.map(\n\t\t\t\t\t(r) =>\n\t\t\t\t\t\t` ${r.kind === 'pair' ? 'wants to be trusted' : 'wants to chat '} ${r.fromName} ${r.from.substring(0, 8)}${r.note ? ` — \"${r.note}\"` : ''}`,\n\t\t\t\t)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_wait',\n\t\tdescription:\n\t\t\t'Wait for the next message to arrive, instead of checking again later. Use it whenever you have said something and a reply is expected — it turns a mailbox into a conversation. Returns as soon as anything lands, or reports that nothing came within the time given. Nothing is lost either way: a message that arrives after you stop waiting is still in the inbox.',\n\t\tinputSchema: obj({\n\t\t\tfrom: str('Only wake for this peer, by name or id. Omit to wake for anyone.'),\n\t\t\ttimeout_ms: num('How long to wait. Default 60000, maximum 300000.'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst limit = Math.min(Math.max(Number(args.timeout_ms ?? 60_000), 1_000), 300_000)\n\t\t\tconst want = args.from ? await resolve(node, String(args.from)) : undefined\n\t\t\tconst wanted = (m: { peerId: string }) => !args.from || m.peerId === (want?.id ?? args.from)\n\n\t\t\t// Anything already unread counts as arrived: waiting for the next one would skip it.\n\t\t\tconst already = node.inbox.unread().filter(wanted)\n\t\t\tif (already.length === 0) {\n\t\t\t\tawait new Promise<void>((done) => {\n\t\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\t\toff()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, limit)\n\t\t\t\t\tconst off = node.onMessage((m) => {\n\t\t\t\t\t\tif (!wanted(m)) return\n\t\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\t\toff()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst arrived = node.inbox.unread().filter(wanted)\n\t\t\tawait node.inbox.markRead()\n\t\t\tif (arrived.length === 0) {\n\t\t\t\treturn `Nothing arrived within ${Math.round(limit / 1000)}s${args.from ? ` from ${want?.name ?? args.from}` : ''}. It is not lost — whatever they send lands in the inbox whenever it comes.`\n\t\t\t}\n\t\t\treturn arrived.map((m) => `[${when(m.ts)}] ${m.peerName}: ${m.text}`).join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_hub',\n\t\tdescription:\n\t\t\t'Join a hub, leave one, or report which hub this session is on. A hub makes you reachable by people and agents that cannot dial your machine — it carries sealed traffic it cannot read, and stores no message. Called with no arguments it just reports. The choice is remembered, so the next session starts where this one left off.',\n\t\tinputSchema: obj({\n\t\t\turl: str('Hub URL to join, e.g. https://hub.example.com/mesh'),\n\t\t\tleave: { type: 'boolean' as const, description: 'Leave the current hub' },\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tif (args.leave) {\n\t\t\t\tconst hub = node.options.hub\n\t\t\t\tif (!hub) return 'Not on a hub.'\n\t\t\t\t// Forget it and drop the socket, so neither this session nor the next dials it.\n\t\t\t\tawait node.net.forgetPeer(hub)\n\t\t\t\tnode.options.hub = undefined\n\t\t\t\tnode.hubStatus = 'no hub configured'\n\t\t\t\tawait saveStored(node.options.dir, { hub: undefined })\n\t\t\t\treturn `Left ${hub}. Your identity and everyone you have paired with directly are untouched.`\n\t\t\t}\n\t\t\tif (!args.url) {\n\t\t\t\treturn node.options.hub\n\t\t\t\t\t? `${node.hubStatus}\\n\\nCall with leave:true to come off it, or url to move to another.`\n\t\t\t\t\t: 'Not on a hub. Give a url to join one — or stay off it and pair directly with volenet_connect.'\n\t\t\t}\n\t\t\tconst url = String(args.url).replace(/\\/$/, '')\n\t\t\t// Already trusted — a hub we have joined before, or paired with — so there is nothing to\n\t\t\t// introduce. Joining again would need its public join still open, which is not a thing\n\t\t\t// coming back should depend on.\n\t\t\tif (await alreadyTrusts(node.options.dir, url)) {\n\t\t\t\tawait node.net.addPeer(url)\n\t\t\t\tnode.options.hub = url\n\t\t\t\tnode.hubStatus = `joined ${url}`\n\t\t\t\tawait saveStored(node.options.dir, { hub: url })\n\t\t\t\treturn `Joined ${url} again — it was already trusted, so no introduction was needed.`\n\t\t\t}\n\t\t\tconst res = await node.net.joinHub(url)\n\t\t\tif (!res.ok) return `Could not join ${url}: ${res.error}`\n\t\t\tnode.options.hub = url\n\t\t\tnode.hubStatus = res.pending\n\t\t\t\t? `waiting for approval at ${url}`\n\t\t\t\t: `joined ${res.hubName ?? url}`\n\t\t\tawait saveStored(node.options.dir, { hub: url })\n\t\t\treturn res.pending\n\t\t\t\t? `Asked to join ${url}. Its operator has to approve before you appear in the roster.`\n\t\t\t\t: `Joined ${res.hubName ?? url}. Remembered, so the next session starts here. Call volenet_peers to see who is around.`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_room',\n\t\tdescription:\n\t\t\t'Rooms: several people and agents in one conversation. With no arguments it lists the rooms this session is in. Give `post` to say something to a room — every member gets their own sealed copy, so there is no shared key and removing someone stops them reading immediately. Give `create`, `join`, `leave` or `invite` to change membership, which the hub keeps. A room does not create consent: a member who has not accepted you will not receive your posts.',\n\t\tinputSchema: obj({\n\t\t\troom: str('Which room, by id or name (see the list)'),\n\t\t\tpost: str('Say this to the room'),\n\t\t\tcreate: str('Make a room with this name'),\n\t\t\tjoin: str('Join a room by id'),\n\t\t\tleave: { type: 'boolean' as const, description: 'Leave the room named in `room`' },\n\t\t\tinvite: str('Bring this peer (name or id) into the room named in `room`'),\n\t\t\thub: str('Which hub holds the room. Only needed with more than one.'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst hubOf = async () => {\n\t\t\t\tif (args.hub) return String(args.hub)\n\t\t\t\tconst hubs = (await node.net.instances()).filter((i) => i.connected)\n\t\t\t\tif (hubs.length === 0) return null\n\t\t\t\treturn hubs[0]!.id\n\t\t\t}\n\t\t\tconst find = async (ref: string) => {\n\t\t\t\tconst all = await node.net.rooms()\n\t\t\t\treturn all.find((r) => r.room === ref) ?? all.find((r) => r.name === ref)\n\t\t\t}\n\n\t\t\tif (args.create || args.join || args.leave || args.invite) {\n\t\t\t\tconst hub = await hubOf()\n\t\t\t\tif (!hub)\n\t\t\t\t\treturn 'No hub connected. A room lives on a hub — join one first with volenet_hub.'\n\t\t\t\tif (args.create) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:create', { name: String(args.create) })\n\t\t\t\t\treturn res.ok\n\t\t\t\t\t\t? `Asked for a room called \"${args.create}\". Call this again in a moment to see it.`\n\t\t\t\t\t\t: `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tif (args.join) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:join', { room: String(args.join) })\n\t\t\t\t\treturn res.ok ? `Asked to join ${args.join}.` : `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tconst room = args.room ? await find(String(args.room)) : undefined\n\t\t\t\tif (!room) return 'Name the room with `room` — see the list.'\n\t\t\t\tif (args.leave) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:leave', { room: room.room })\n\t\t\t\t\treturn res.ok ? `Left ${room.name}.` : `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tconst peer = await resolve(node, String(args.invite))\n\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:invite', {\n\t\t\t\t\troom: room.room,\n\t\t\t\t\tmember: peer?.id ?? String(args.invite),\n\t\t\t\t})\n\t\t\t\treturn res.ok\n\t\t\t\t\t? `Invited ${peer?.name ?? args.invite} to ${room.name}.`\n\t\t\t\t\t: `Could not: ${res.error}`\n\t\t\t}\n\n\t\t\tif (args.post) {\n\t\t\t\tconst room = args.room ? await find(String(args.room)) : (await node.net.rooms())[0]\n\t\t\t\tif (!room) return 'No room to post to. Create or join one first.'\n\t\t\t\tconst res = await node.net.postToRoom(room.room, String(args.post))\n\t\t\t\tif (!res.ok) return `Not posted: ${res.error}`\n\t\t\t\tconst bits = [`Posted to ${room.name}: ${res.sent} delivered`]\n\t\t\t\tif (res.held) bits.push(`${res.held} waiting for members who are away`)\n\t\t\t\tif (res.skipped)\n\t\t\t\t\tbits.push(`${res.skipped} could not be reached — they may not have accepted you`)\n\t\t\t\treturn `${bits.join(', ')}.`\n\t\t\t}\n\n\t\t\tconst all = await node.net.rooms()\n\t\t\tif (all.length === 0) {\n\t\t\t\treturn 'Not in any room. Create one with create:\"name\", or join one you have been given the id for.'\n\t\t\t}\n\t\t\treturn all\n\t\t\t\t.map(\n\t\t\t\t\t(r) =>\n\t\t\t\t\t\t` ${r.name} ${r.room.substring(0, 8)} ${r.members.length} member(s): ${r.members.map((m) => m.name).join(', ')}`,\n\t\t\t\t)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_connect',\n\t\tdescription:\n\t\t\t'Reach out to someone new: pair directly with a node at a URL, or ask a hub member for consent to chat. Pairing is two calls — the first reports the fingerprint of whoever answers, the second confirms it — because trusting a URL blind is trusting whoever holds it. Neither side trusts you until they accept.',\n\t\tinputSchema: obj({\n\t\t\turl: str('Node URL to pair with directly, e.g. http://10.0.0.5:9700'),\n\t\t\tconfirm: str('The fingerprint returned by a first call with url, confirming who answers'),\n\t\t\tbrain: {\n\t\t\t\ttype: 'boolean' as const,\n\t\t\t\tdescription:\n\t\t\t\t\t\"Also ask for permission to use that agent's brain. The operator sees it as part of the same decision and can grant it while accepting; without it, being trusted allows chat only.\",\n\t\t\t},\n\t\t\tmember: str('Hub member name or id to ask for chat consent'),\n\t\t\tnote: str('A line saying who you are'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst note = args.note ? String(args.note) : undefined\n\t\t\tif (args.url) {\n\t\t\t\tconst url = String(args.url)\n\t\t\t\tconst probe = await node.net.probePair(url)\n\t\t\t\tif (!probe.ok || !probe.publicKey) return `Could not reach it: ${probe.error}`\n\t\t\t\t// Trust on first use is a decision, not a side effect: whoever answers that URL is\n\t\t\t\t// whoever answers that URL. Show the fingerprint and require it back before trusting.\n\t\t\t\tconst confirm = args.confirm ? String(args.confirm).trim() : ''\n\t\t\t\tif (!confirm) {\n\t\t\t\t\treturn [\n\t\t\t\t\t\t`${probe.name ?? url} answers with fingerprint:`,\n\t\t\t\t\t\t` ${probe.fingerprint}`,\n\t\t\t\t\t\tprobe.alreadyTrusted ? ' (already trusted by this session)' : '',\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Check that against what the other side reports, then call this again with',\n\t\t\t\t\t\t'confirm set to that fingerprint to trust it and send the pair request.',\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join('\\n')\n\t\t\t\t}\n\t\t\t\tif (!probe.fingerprint?.startsWith(confirm)) {\n\t\t\t\t\treturn `That fingerprint does not match: it answers with ${probe.fingerprint}. Nothing was trusted.`\n\t\t\t\t}\n\t\t\t\tconst res = await node.net.initiatePair(\n\t\t\t\t\turl,\n\t\t\t\t\tprobe.publicKey,\n\t\t\t\t\tnote,\n\t\t\t\t\targs.brain ? ['brain'] : undefined,\n\t\t\t\t)\n\t\t\t\tif (!res.ok) return `Could not ask: ${res.error}`\n\t\t\t\treturn [\n\t\t\t\t\t`Trusted ${probe.name ?? url} and asked it to trust this session.`,\n\t\t\t\t\targs.brain\n\t\t\t\t\t\t? 'The request also asks to use its brain, so its operator can grant that while accepting — no config editing, no restart.'\n\t\t\t\t\t\t: 'It asks for trust only. Pass brain:true to also ask for brain access.',\n\t\t\t\t\t'Nothing arrives until their operator accepts.',\n\t\t\t\t].join(' ')\n\t\t\t}\n\t\t\tif (args.member) {\n\t\t\t\tconst res = await node.net.requestRelayConnect(String(args.member), note)\n\t\t\t\tif (!res.ok) return `Could not ask: ${res.error}`\n\t\t\t\treturn res.queued\n\t\t\t\t\t? `${args.member} is away — the request waits here and goes out when they are back.`\n\t\t\t\t\t: `Asked ${args.member} for consent to chat.`\n\t\t\t}\n\t\t\treturn 'Give either a url (direct pairing) or a member (hub consent).'\n\t\t},\n\t},\n]\n"],"mappings":"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","names":["fs","path","file","fs","path","resolve","spawn","path","join","file","resolve","path","path","run","run","runDaemon","resolveSettings","when","all","res","createServer","fs","path","run"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/inbox.ts","../src/net-api.ts","../src/daemon.ts","../src/notify.ts","../src/node.ts","../src/index.ts","../src/cli.ts","../src/install.ts","../src/prompts.ts","../src/tools.ts"],"sourcesContent":["/**\n * Settings that survive a restart, so nothing has to be configured at install time.\n *\n * The first version of this took its name, hub and port from environment variables, which meant\n * the install line carried three flags a new user could not yet know the values of — and changing\n * one meant re-registering the server. Settings belong to the node, not to the command that\n * launches it: they live in its data directory, next to the identity they describe, and are\n * changed from inside a session with `volenet_hub`.\n *\n * Environment still wins where it is set, for scripted setups and CI. Nothing is required.\n */\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\n\nexport interface StoredConfig {\n\tname?: string\n\thub?: string\n\tport?: number\n}\n\nexport interface Settings {\n\tname: string\n\thub?: string\n\tdir: string\n\tport: number\n\t/** Which read state in the shared inbox is this session's. See {@link sessionKey}. */\n\tsession: string\n}\n\n/**\n * Which reader of the shared inbox this session is.\n *\n * The identity is per machine, deliberately: pairing once is the point of having one. Being\n * *caught up* is not — several editor sessions run at once, and one opening its inbox must not\n * mark the messages seen for the others.\n *\n * Keyed by the directory the client started the server in, so it is stable across a restart (the\n * same project reopened is the same reader, and does not replay what it has already seen) and\n * distinct between projects open at the same time. The hash disambiguates two projects that share\n * a basename; the basename is kept in front so the file is recognisable.\n */\nexport function sessionKey(cwd = process.cwd()): string {\n\tconst hash = crypto.createHash('sha256').update(cwd).digest('hex').slice(0, 8)\n\tconst base = (cwd.split('/').filter(Boolean).pop() ?? 'session')\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9._-]+/g, '-')\n\t\t.slice(0, 40)\n\treturn `${base}-${hash}`\n}\n\nexport function defaultDir(): string {\n\treturn process.env.VOLENET_MCP_DIR?.trim() || path.join(os.homedir(), '.openvole', 'volenet-mcp')\n}\n\n/** A name that says what this is without needing to be chosen. Identity is the key, not this. */\nexport function defaultName(): string {\n\treturn `claude-${os.hostname().split('.')[0].toLowerCase()}`\n}\n\nconst file = (dir: string) => path.join(dir, 'config.json')\n\nexport async function loadStored(dir: string): Promise<StoredConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await fs.readFile(file(dir), 'utf-8')) as StoredConfig\n\t\treturn raw && typeof raw === 'object' ? raw : {}\n\t} catch {\n\t\treturn {}\n\t}\n}\n\nexport async function saveStored(dir: string, patch: StoredConfig): Promise<StoredConfig> {\n\tconst next = { ...(await loadStored(dir)), ...patch }\n\tfor (const k of Object.keys(next) as Array<keyof StoredConfig>) {\n\t\tif (next[k] === undefined) delete next[k]\n\t}\n\tawait fs.mkdir(dir, { recursive: true })\n\tawait fs.writeFile(file(dir), `${JSON.stringify(next, null, 2)}\\n`, 'utf-8')\n\treturn next\n}\n\n/** Environment over stored settings over defaults. Every layer is optional. */\nexport async function resolveSettings(): Promise<Settings> {\n\tconst dir = defaultDir()\n\tconst stored = await loadStored(dir)\n\tconst envPort = Number(process.env.VOLENET_MCP_PORT)\n\treturn {\n\t\tname: process.env.VOLENET_MCP_NAME?.trim() || stored.name || defaultName(),\n\t\thub: process.env.VOLENET_MCP_HUB?.trim() || stored.hub || undefined,\n\t\tdir,\n\t\tport: (Number.isFinite(envPort) && envPort > 0 ? envPort : stored.port) || 9750,\n\t\tsession: process.env.VOLENET_MCP_SESSION?.trim() || sessionKey(),\n\t}\n}\n","/**\n * What was said: one log, and a read cursor for each session reading it.\n *\n * An MCP server lives and dies with the editor session that spawned it, but a conversation does\n * not. VoleNet already makes an intermittent peer work — a sender holds what it could not deliver\n * and flushes when you reappear — so what was missing is somewhere to put what arrives that is\n * still there next time.\n *\n * The subtlety is that several editor sessions run at once, sharing one identity because pairing\n * once is the whole point of an identity. They must not share a *read* state: one session opening\n * its inbox would mark the messages seen and the next session would never hear about them. So the\n * messages are one append-only log, and being caught up is per session.\n *\n * Append-only also makes concurrency cheap. Two processes rewriting one JSON file lose each\n * other's writes; two processes appending a line each do not, and a cursor file has exactly one\n * writer. No locking, no daemon.\n */\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\n\nexport interface Message {\n\t/** The peer this is with — the sender for 'in', the recipient for 'out'. */\n\tpeerId: string\n\tpeerName: string\n\tdir: 'in' | 'out'\n\ttext: string\n\t/** Milliseconds since the epoch, from the signed message for 'in'. */\n\tts: number\n\t/** The signed message id, so a replay or a double-flush cannot duplicate a line. */\n\tid: string\n}\n\n/** How many lines to keep. The oldest go when the log is next compacted. */\nexport const MAX_MESSAGES = 2000\n\nexport class Inbox {\n\tprivate messages: Message[] = []\n\t/** Per peer, the timestamp up to which *this* session has been shown its messages. */\n\tprivate readAt = new Map<string, number>()\n\tprivate writing: Promise<void> = Promise.resolve()\n\n\t/**\n\t * @param dir where the shared log and the cursors live\n\t * @param session which read state is ours. Sessions in different projects are different\n\t * readers; the same project reopened is the same reader, so restarting does\n\t * not replay everything already seen.\n\t */\n\tconstructor(\n\t\tprivate readonly dir: string,\n\t\tprivate readonly session = 'default',\n\t) {}\n\n\tprivate get log(): string {\n\t\treturn path.join(this.dir, 'messages.jsonl')\n\t}\n\n\tprivate get cursor(): string {\n\t\treturn path.join(this.dir, 'cursors', `${this.session}.json`)\n\t}\n\n\tasync load(): Promise<void> {\n\t\tawait this.adoptLegacy()\n\t\tthis.messages = await readLog(this.log)\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(this.cursor, 'utf-8')) as Record<string, number>\n\t\t\tthis.readAt = new Map(Object.entries(raw ?? {}))\n\t\t} catch {\n\t\t\tthis.readAt = new Map()\n\t\t}\n\t}\n\n\t/** Re-read what other sessions have appended since we loaded. */\n\tasync refresh(): Promise<void> {\n\t\tthis.messages = await readLog(this.log)\n\t}\n\n\t/** Record a message. Returns false when this id was already recorded. */\n\tasync add(m: Message): Promise<boolean> {\n\t\tawait this.refresh()\n\t\tif (this.messages.some((x) => x.id === m.id)) return false\n\t\tthis.messages.push(m)\n\t\tawait this.append(m)\n\t\treturn true\n\t}\n\n\t/** Everything with one peer, oldest first. */\n\thistory(peerId: string, limit = 50): Message[] {\n\t\treturn this.messages.filter((m) => m.peerId === peerId).slice(-limit)\n\t}\n\n\t/** Inbound messages this session has not been shown yet, oldest first. */\n\tunread(): Message[] {\n\t\treturn this.messages.filter((m) => m.dir === 'in' && m.ts > (this.readAt.get(m.peerId) ?? 0))\n\t}\n\n\t/** Mark everything currently unread as seen — for this session, and nobody else. */\n\tasync markRead(): Promise<void> {\n\t\tfor (const m of this.unread()) {\n\t\t\tconst at = this.readAt.get(m.peerId) ?? 0\n\t\t\tif (m.ts > at) this.readAt.set(m.peerId, m.ts)\n\t\t}\n\t\tawait this.persistCursor()\n\t}\n\n\t/** Every peer we have said anything to or heard anything from, most recent first. */\n\tpeers(): Array<{ peerId: string; peerName: string; last: number; unread: number }> {\n\t\tconst by = new Map<string, { peerId: string; peerName: string; last: number; unread: number }>()\n\t\tfor (const m of this.messages) {\n\t\t\tconst e = by.get(m.peerId) ?? { peerId: m.peerId, peerName: m.peerName, last: 0, unread: 0 }\n\t\t\tif (m.peerName) e.peerName = m.peerName\n\t\t\te.last = Math.max(e.last, m.ts)\n\t\t\tif (m.dir === 'in' && m.ts > (this.readAt.get(m.peerId) ?? 0)) e.unread++\n\t\t\tby.set(m.peerId, e)\n\t\t}\n\t\treturn [...by.values()].sort((a, b) => b.last - a.last)\n\t}\n\n\tget size(): number {\n\t\treturn this.messages.length\n\t}\n\n\t/** One line, one write — an append no other session can lose. */\n\tprivate append(m: Message): Promise<void> {\n\t\tthis.writing = this.writing.then(async () => {\n\t\t\tawait fs.mkdir(this.dir, { recursive: true })\n\t\t\tawait fs.appendFile(this.log, `${JSON.stringify(m)}\\n`, 'utf-8')\n\t\t\tif (this.messages.length > MAX_MESSAGES) await this.compact()\n\t\t})\n\t\treturn this.writing\n\t}\n\n\t/** Rewrite the log with the newest MAX_MESSAGES. Rare, and atomic via rename. */\n\tprivate async compact(): Promise<void> {\n\t\tconst keep = this.messages.slice(-MAX_MESSAGES)\n\t\tconst tmp = `${this.log}.${process.pid}.tmp`\n\t\tawait fs.writeFile(tmp, keep.map((m) => `${JSON.stringify(m)}\\n`).join(''), 'utf-8')\n\t\tawait fs.rename(tmp, this.log)\n\t\tthis.messages = keep\n\t}\n\n\tprivate async persistCursor(): Promise<void> {\n\t\tawait fs.mkdir(path.dirname(this.cursor), { recursive: true })\n\t\tconst tmp = `${this.cursor}.tmp`\n\t\tawait fs.writeFile(tmp, JSON.stringify(Object.fromEntries(this.readAt), null, 2), 'utf-8')\n\t\tawait fs.rename(tmp, this.cursor)\n\t}\n\n\t/**\n\t * Carry over messages written before the log existed.\n\t *\n\t * Earlier versions kept one `inbox.json` holding both the messages and a single read state. The\n\t * messages are still someone's; dropping them on upgrade would lose real conversations. The old\n\t * read state is deliberately *not* carried over — it was one cursor for every session, so honouring\n\t * it would mark messages seen for sessions that never saw them. Unread is the safe direction.\n\t */\n\tprivate async adoptLegacy(): Promise<void> {\n\t\tconst legacy = path.join(this.dir, 'inbox.json')\n\t\ttry {\n\t\t\tawait fs.access(this.log)\n\t\t\treturn // the log exists; nothing to carry over\n\t\t} catch {\n\t\t\t// no log yet\n\t\t}\n\t\tlet raw: { messages?: Message[] }\n\t\ttry {\n\t\t\traw = JSON.parse(await fs.readFile(legacy, 'utf-8')) as { messages?: Message[] }\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\tconst messages = (raw.messages ?? []).filter(\n\t\t\t(m) => m && typeof m.peerId === 'string' && typeof m.text === 'string',\n\t\t)\n\t\tif (messages.length === 0) return\n\t\tawait fs.mkdir(this.dir, { recursive: true })\n\t\tawait fs.writeFile(this.log, messages.map((m) => `${JSON.stringify(m)}\\n`).join(''), 'utf-8')\n\t\tawait fs.rename(legacy, `${legacy}.migrated`)\n\t}\n}\n\nasync function readLog(file: string): Promise<Message[]> {\n\tlet body: string\n\ttry {\n\t\tbody = await fs.readFile(file, 'utf-8')\n\t} catch {\n\t\treturn []\n\t}\n\tconst out: Message[] = []\n\tfor (const line of body.split('\\n')) {\n\t\tif (!line.trim()) continue\n\t\ttry {\n\t\t\tconst m = JSON.parse(line) as Message\n\t\t\tif (m && typeof m.peerId === 'string' && typeof m.text === 'string') out.push(m)\n\t\t} catch {\n\t\t\t// A torn last line from a concurrent append: skip it, it will be read next time.\n\t\t}\n\t}\n\treturn out\n}\n","/**\n * Everything the tools need from a node, as a flat surface.\n *\n * The tools used to reach into `VoleNetManager` directly, including through the objects it hands\n * back — `getTransport()?.getPeers()`, `getRemoteTaskManager().delegateTask()`. That is fine while\n * the node lives in the same process, and impossible once it does not: a socket cannot return a\n * transport. Flattening it to plain calls with plain values is what lets the same tools run\n * against a node here or a node in a daemon, which is what being *present* while no session is\n * open requires.\n */\nimport type { VoleNetManager } from '@openvole/volenet'\n\nexport interface PeerInfo {\n\tid: string\n\tname: string\n\tconnected: boolean\n}\n\nexport interface RelayMemberInfo {\n\tid: string\n\tname: string\n\tviaHubName: string\n\tconnected: boolean\n\taccepted: boolean\n\tincoming: boolean\n\tawaiting: boolean\n}\n\nexport interface PairRequestInfo {\n\tid: string\n\tname: string\n\tnote?: string\n\twants?: string[]\n}\n\nexport interface PairGrantInput {\n\ttrust?: 'full' | 'tool' | 'read'\n\tallowBrain?: boolean\n}\n\nexport interface SendResult {\n\tok: boolean\n\tdelivered?: boolean\n\trelayed?: boolean\n\terror?: string\n}\n\nexport interface AskResult {\n\tstatus: string\n\tresult?: string\n\terror?: string\n}\n\nexport interface Identity {\n\tinstanceId: string\n\tpublicKeyString: string\n}\n\nexport interface RoomView {\n\troom: string\n\tname: string\n\ttopic?: string\n\tmembers: Array<{ instanceId: string; name: string }>\n}\n\n/** A node, wherever it happens to be running. */\nexport interface NetLike {\n\tidentity(): Promise<Identity | null>\n\t/** Peers this node holds a direct link with, and whether the socket is live. */\n\tinstances(): Promise<PeerInfo[]>\n\trelayMembers(): Promise<RelayMemberInfo[]>\n\tsendChat(to: string, text: string): Promise<SendResult>\n\taskBrain(to: string, input: string, fromName: string, timeoutMs: number): Promise<AskResult>\n\tjoinHub(\n\t\turl: string,\n\t): Promise<{ ok: boolean; pending?: boolean; hubName?: string; error?: string }>\n\taddPeer(url: string): Promise<void>\n\tforgetPeer(url: string): Promise<boolean>\n\tprobePair(url: string): Promise<{\n\t\tok: boolean\n\t\tname?: string\n\t\tfingerprint?: string\n\t\tpublicKey?: string\n\t\talreadyTrusted?: boolean\n\t\terror?: string\n\t}>\n\tinitiatePair(\n\t\turl: string,\n\t\tpublicKey: string,\n\t\tnote?: string,\n\t\twants?: string[],\n\t): Promise<{ ok: boolean; pending?: boolean; error?: string }>\n\trequestRelayConnect(\n\t\tref: string,\n\t\tnote?: string,\n\t): Promise<{ ok: boolean; queued?: boolean; error?: string }>\n\tapproveRelayConnect(ref: string): Promise<{ ok: boolean; error?: string }>\n\tdenyRelayConnect(ref: string): Promise<{ ok: boolean; error?: string }>\n\t/** Rooms this node is in, as its hub last described them (PROTOCOL.md §7c). */\n\trooms(): Promise<RoomView[]>\n\troomCommand(\n\t\thub: string,\n\t\ttype: 'room:create' | 'room:join' | 'room:leave' | 'room:invite' | 'room:list',\n\t\tpayload: Record<string, unknown>,\n\t): Promise<{ ok: boolean; error?: string }>\n\t/** Post to a room: one sealed copy per member, so there is no key and no rotation. */\n\tpostToRoom(\n\t\troom: string,\n\t\ttext: string,\n\t): Promise<{ ok: boolean; sent: number; held: number; skipped: number; error?: string }>\n\tlistPairRequests(): Promise<PairRequestInfo[]>\n\tacceptPair(ref: string, grant?: PairGrantInput): Promise<{ ok: boolean; error?: string }>\n\tdenyPair(ref: string): Promise<{ ok: boolean }>\n}\n\n/** The same surface, backed by a manager in this process. */\nexport function localNet(m: VoleNetManager): NetLike {\n\treturn {\n\t\tasync identity() {\n\t\t\tconst k = m.getKeyPair()\n\t\t\treturn k ? { instanceId: k.instanceId, publicKeyString: k.publicKeyString } : null\n\t\t},\n\t\tasync instances() {\n\t\t\t// Whether a direct link is live is the transport's business — an instance record\n\t\t\t// outlives the socket, so `lastSeen` alone would report a dead link as online.\n\t\t\tconst live = new Set(\n\t\t\t\t(m.getTransport()?.getPeers() ?? []).filter((p) => p.connected).map((p) => p.peerId),\n\t\t\t)\n\t\t\treturn m.getInstances().map((i) => ({ id: i.id, name: i.name, connected: live.has(i.id) }))\n\t\t},\n\t\tasync relayMembers() {\n\t\t\treturn m.getRelayMembers().map((r) => ({\n\t\t\t\tid: r.id,\n\t\t\t\tname: r.name,\n\t\t\t\tviaHubName: r.viaHubName,\n\t\t\t\tconnected: r.connected,\n\t\t\t\taccepted: r.accepted,\n\t\t\t\tincoming: r.incoming,\n\t\t\t\tawaiting: r.awaiting,\n\t\t\t}))\n\t\t},\n\t\tsendChat: (to, text) => m.sendChat(to, text),\n\t\tasync askBrain(to, input, fromName, timeoutMs) {\n\t\t\tconst mgr = m.getRemoteTaskManager()\n\t\t\tif (!mgr) return { status: 'failed', error: 'remote task manager not available' }\n\t\t\tconst r = await mgr.delegateTask(to, { taskId: '', input, fromName }, timeoutMs)\n\t\t\treturn { status: r.status, result: r.result, error: r.error }\n\t\t},\n\t\tjoinHub: (url) => m.initiateJoin(url),\n\t\taddPeer: (url) => m.addPeer(url),\n\t\tasync forgetPeer(url) {\n\t\t\treturn m.forgetPeer(url)\n\t\t},\n\t\tprobePair: (url) => m.probePair(url),\n\t\tinitiatePair: (url, publicKey, note, wants) =>\n\t\t\tm.initiatePair(url, publicKey, note, wants as 'brain'[] | undefined),\n\t\trequestRelayConnect: (ref, note) => m.requestRelayConnect(ref, note),\n\t\tapproveRelayConnect: (ref) => m.approveRelayConnect(ref),\n\t\tdenyRelayConnect: (ref) => m.denyRelayConnect(ref),\n\t\tasync listPairRequests() {\n\t\t\treturn m.listPairRequests().map((r) => ({\n\t\t\t\tid: r.id,\n\t\t\t\tname: r.name,\n\t\t\t\tnote: r.note,\n\t\t\t\twants: r.wants,\n\t\t\t}))\n\t\t},\n\t\tasync rooms() {\n\t\t\treturn m.getRooms().map((r) => ({\n\t\t\t\troom: r.room,\n\t\t\t\tname: r.name,\n\t\t\t\ttopic: r.topic,\n\t\t\t\tmembers: r.members.map((x) => ({ instanceId: x.instanceId, name: x.name })),\n\t\t\t}))\n\t\t},\n\t\troomCommand: (hub, type, payload) => m.roomCommand(hub, type, payload),\n\t\tpostToRoom: (room, text) => m.postToRoom(room, text),\n\t\tacceptPair: (ref, grant) => m.acceptPair(ref, grant),\n\t\tdenyPair: (ref) => m.denyPair(ref),\n\t}\n}\n\n/** The method names a remote node must answer — kept beside the interface so they cannot drift. */\nexport const NET_METHODS = [\n\t'identity',\n\t'instances',\n\t'relayMembers',\n\t'sendChat',\n\t'askBrain',\n\t'joinHub',\n\t'addPeer',\n\t'forgetPeer',\n\t'probePair',\n\t'initiatePair',\n\t'requestRelayConnect',\n\t'approveRelayConnect',\n\t'denyRelayConnect',\n\t'rooms',\n\t'roomCommand',\n\t'postToRoom',\n\t'listPairRequests',\n\t'acceptPair',\n\t'denyPair',\n] as const satisfies ReadonlyArray<keyof NetLike>\n","/**\n * One node per machine, alive between sessions.\n *\n * A node that lives and dies with an editor session is *offline* whenever nothing is open: senders\n * hold what they cannot deliver, hubs record that somebody tried, and nothing arrives until you\n * reopen. That is a mailbox, not a channel. It also means two sessions run two nodes on one\n * identity, and a hub binds one socket per identity — so the second connection leaves the first\n * deaf.\n *\n * Both go away with a single long-lived process that owns the identity, the connections and the\n * writing of arrivals. Sessions attach to it over a unix socket and ask it to act. They do *not*\n * ask it what has arrived: the message log is a file, so reading stays local and needs no protocol.\n *\n * The socket is per identity directory, so a second daemon cannot start on the same identity, and\n * the first session to want one starts it.\n */\nimport { spawn } from 'node:child_process'\nimport * as fs from 'node:fs/promises'\nimport * as net from 'node:net'\nimport * as path from 'node:path'\nimport { NET_METHODS, type NetLike } from './net-api.js'\n\nexport const socketPath = (dir: string) => path.join(dir, 'daemon.sock')\n\ninterface Request {\n\tid: number\n\tmethod: string\n\targs: unknown[]\n}\n\ninterface Response {\n\tid: number\n\tok: boolean\n\tresult?: unknown\n\terror?: string\n}\n\n/** Serve a node over a unix socket until the process is stopped. */\nexport async function serve(dir: string, node: NetLike): Promise<net.Server> {\n\tconst sock = socketPath(dir)\n\tawait fs.mkdir(dir, { recursive: true })\n\t// A socket file outlives the process that made it. If nothing answers, it is stale.\n\tawait fs.rm(sock, { force: true })\n\n\tconst server = net.createServer((conn) => {\n\t\tlet buffer = ''\n\t\tconn.setEncoding('utf-8')\n\t\tconn.on('data', (chunk) => {\n\t\t\tbuffer += chunk\n\t\t\tfor (let nl = buffer.indexOf('\\n'); nl >= 0; nl = buffer.indexOf('\\n')) {\n\t\t\t\tconst line = buffer.slice(0, nl)\n\t\t\t\tbuffer = buffer.slice(nl + 1)\n\t\t\t\tif (line.trim()) void handle(line, conn, node)\n\t\t\t}\n\t\t})\n\t\t// A session going away is ordinary; it must never take the daemon with it.\n\t\tconn.on('error', () => undefined)\n\t})\n\tserver.on('error', () => undefined)\n\tawait new Promise<void>((done) => server.listen(sock, done))\n\treturn server\n}\n\nasync function handle(line: string, conn: net.Socket, node: NetLike): Promise<void> {\n\tlet req: Request\n\ttry {\n\t\treq = JSON.parse(line) as Request\n\t} catch {\n\t\treturn\n\t}\n\tconst reply = (r: Omit<Response, 'id'>) => {\n\t\ttry {\n\t\t\tconn.write(`${JSON.stringify({ id: req.id, ...r })}\\n`)\n\t\t} catch {\n\t\t\t// the session went away mid-call\n\t\t}\n\t}\n\tif (!(NET_METHODS as readonly string[]).includes(req.method)) {\n\t\treply({ ok: false, error: `unknown method: ${req.method}` })\n\t\treturn\n\t}\n\ttry {\n\t\tconst fn = node[req.method as keyof NetLike] as (...a: unknown[]) => Promise<unknown>\n\t\treply({ ok: true, result: await fn(...(req.args ?? [])) })\n\t} catch (err) {\n\t\treply({ ok: false, error: err instanceof Error ? err.message : String(err) })\n\t}\n}\n\n/** Talk to a daemon over its socket, as if the node were here. */\nexport function remoteNet(conn: net.Socket): NetLike {\n\tlet next = 1\n\tconst pending = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>()\n\tlet buffer = ''\n\tconn.setEncoding('utf-8')\n\tconn.on('data', (chunk) => {\n\t\tbuffer += chunk\n\t\tfor (let nl = buffer.indexOf('\\n'); nl >= 0; nl = buffer.indexOf('\\n')) {\n\t\t\tconst line = buffer.slice(0, nl)\n\t\t\tbuffer = buffer.slice(nl + 1)\n\t\t\tif (!line.trim()) continue\n\t\t\ttry {\n\t\t\t\tconst res = JSON.parse(line) as Response\n\t\t\t\tconst waiter = pending.get(res.id)\n\t\t\t\tif (!waiter) continue\n\t\t\t\tpending.delete(res.id)\n\t\t\t\tif (res.ok) waiter.resolve(res.result)\n\t\t\t\telse waiter.reject(new Error(res.error ?? 'daemon error'))\n\t\t\t} catch {\n\t\t\t\t// not ours\n\t\t\t}\n\t\t}\n\t})\n\tconst fail = (why: string) => {\n\t\tfor (const [, w] of pending) w.reject(new Error(why))\n\t\tpending.clear()\n\t}\n\tconn.on('close', () => fail('the volenet daemon closed the connection'))\n\tconn.on('error', (e) => fail(e.message))\n\n\tconst call = (method: string, ...args: unknown[]) =>\n\t\tnew Promise<unknown>((resolve, reject) => {\n\t\t\tconst id = next++\n\t\t\tpending.set(id, { resolve, reject })\n\t\t\tconn.write(`${JSON.stringify({ id, method, args })}\\n`)\n\t\t})\n\n\treturn Object.fromEntries(\n\t\tNET_METHODS.map((m) => [m, (...args: unknown[]) => call(m, ...args)]),\n\t) as unknown as NetLike\n}\n\n/** Connect to a daemon already listening, or null when none is. */\nexport async function connect(dir: string): Promise<net.Socket | null> {\n\treturn new Promise((resolve) => {\n\t\tconst conn = net.createConnection(socketPath(dir))\n\t\tconst give = (ok: boolean) => {\n\t\t\tconn.removeAllListeners('connect')\n\t\t\tconn.removeAllListeners('error')\n\t\t\tif (ok) resolve(conn)\n\t\t\telse {\n\t\t\t\tconn.destroy()\n\t\t\t\tresolve(null)\n\t\t\t}\n\t\t}\n\t\tconn.once('connect', () => give(true))\n\t\tconn.once('error', () => give(false))\n\t})\n}\n\n/**\n * Start a daemon for this identity and wait for it to answer.\n *\n * Detached and with its streams released, so it outlives the session that happened to start it —\n * which is the entire point: being reachable is not supposed to depend on an editor being open.\n */\nexport async function spawnDaemon(\n\tdir: string,\n\tenv: NodeJS.ProcessEnv = {},\n): Promise<net.Socket | null> {\n\tconst entry = process.argv[1]\n\tif (!entry) return null\n\tconst child = spawn(process.execPath, [entry, 'daemon'], {\n\t\tdetached: true,\n\t\tstdio: 'ignore',\n\t\tenv: { ...process.env, ...env, VOLENET_MCP_DIR: dir },\n\t})\n\tchild.unref()\n\n\t// Poll briefly rather than guess a fixed delay: it is listening when it answers.\n\tfor (let i = 0; i < 40; i++) {\n\t\tconst conn = await connect(dir)\n\t\tif (conn) return conn\n\t\tawait new Promise((r) => setTimeout(r, 100))\n\t}\n\treturn null\n}\n","/**\n * Telling the person, when nothing can tell the session.\n *\n * MCP has no way for a server to wake its client — Claude Code advertises no `sampling`, so a\n * message cannot prompt a reply on its own. What is left is telling the *human*, which is what a\n * chat client actually does: the notification is the point, and reading it is their move.\n *\n * Only the daemon does this. It is the one thing always running, and it is where arrivals land.\n *\n * Best effort throughout: a machine with no notifier, a headless box, a locked-down desktop — none\n * of that is worth failing a delivery over, so every path here swallows its errors.\n */\nimport { spawn } from 'node:child_process'\n\nexport type Notifier = (title: string, body: string) => void\n\n/** Escape for AppleScript, which is the one path here that interpolates into a script. */\nconst applescript = (s: string) => s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n\nfunction run(command: string, args: string[]): void {\n\ttry {\n\t\tconst child = spawn(command, args, { stdio: 'ignore', detached: true })\n\t\tchild.on('error', () => undefined)\n\t\tchild.unref()\n\t} catch {\n\t\t// no such binary, or spawning is not allowed here\n\t}\n}\n\n/**\n * A notifier for this platform, or one that does nothing.\n *\n * `VOLENET_MCP_NOTIFY=off` turns it off; anything else names a command to run instead, which is\n * given the title and body as its two arguments.\n */\nexport function notifier(platform = process.platform): Notifier {\n\tconst setting = process.env.VOLENET_MCP_NOTIFY?.trim()\n\tif (setting === 'off') return () => undefined\n\tif (setting) return (title, body) => run(setting, [title, body])\n\n\tif (platform === 'darwin') {\n\t\treturn (title, body) =>\n\t\t\trun('osascript', [\n\t\t\t\t'-e',\n\t\t\t\t`display notification \"${applescript(body)}\" with title \"${applescript(title)}\"`,\n\t\t\t])\n\t}\n\tif (platform === 'linux') return (title, body) => run('notify-send', [title, body])\n\tif (platform === 'win32') {\n\t\treturn (title, body) =>\n\t\t\trun('powershell', [\n\t\t\t\t'-NoProfile',\n\t\t\t\t'-Command',\n\t\t\t\t`[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');` +\n\t\t\t\t\t`$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;` +\n\t\t\t\t\t`$n.Visible=$true;$n.ShowBalloonTip(5000,'${title.replace(/'/g, \"''\")}','${body.replace(/'/g, \"''\")}',0)`,\n\t\t\t])\n\t}\n\treturn () => undefined\n}\n\n/** One line of a message, short enough for a notification and with newlines flattened. */\nexport function preview(text: string, limit = 140): string {\n\tconst flat = text.replace(/\\s+/g, ' ').trim()\n\treturn flat.length > limit ? `${flat.slice(0, limit - 1)}…` : flat\n}\n","/**\n * The node this server talks to — usually not in this process.\n *\n * A Claude Code session gets an identity on the mesh rather than borrowing an agent's: its own\n * keypair, its own consent decisions. But an identity that only exists while an editor is open is\n * offline most of the time, and two open editors would run two nodes on one identity and fight over\n * the hub socket. So the node lives in a daemon — one per identity, started on demand, outliving\n * every session — and sessions attach to it.\n *\n * Reading does not go through the daemon. Messages are an append-only file, so a session reads them\n * directly and keeps its own cursor; only *acting* needs the node. That keeps the protocol small\n * and means a session can still show you your history if the daemon is somehow gone.\n */\nimport * as fsSync from 'node:fs'\nimport * as path from 'node:path'\nimport {\n\tVoleNetManager,\n\tcreateEventBus,\n\tloadAuthorizedVoles,\n\tparsePublicKey,\n} from '@openvole/volenet'\nimport { type Settings, resolveSettings } from './config.js'\nimport { connect, remoteNet, serve, spawnDaemon } from './daemon.js'\nimport { Inbox, type Message } from './inbox.js'\nimport { type NetLike, localNet } from './net-api.js'\nimport { type Notifier, notifier, preview } from './notify.js'\n\n/** What a node needs to start. Resolved from stored settings, env and defaults. */\nexport type NodeOptions = Settings\n\nexport { resolveSettings }\n\nexport interface PendingRequest {\n\tkind: 'pair' | 'relay'\n\tfrom: string\n\tfromName: string\n\tnote?: string\n\tat: number\n}\n\nexport interface Notice {\n\tfrom: string\n\tfromName: string\n\tcount: number\n\tlast: number\n}\n\nexport interface Node {\n\tnet: NetLike\n\tinbox: Inbox\n\t/** Trust decisions waiting on the person, newest last. */\n\trequests: PendingRequest[]\n\t/** Who tried to reach us while we were away, as the hub reports on reconnect. */\n\tnotices: Notice[]\n\toptions: NodeOptions\n\t/** What happened when the node last tried to join the configured hub. */\n\thubStatus: string\n\t/**\n\t * Whether the client will run a model when the server asks — MCP's `sampling` capability, and\n\t * the only way an arriving message could ever answer itself. Set once the client has connected.\n\t */\n\tcanSample: boolean\n\t/** Where this node is running, which decides whether it is there when nothing is open. */\n\twhere: 'daemon' | 'in-process'\n\t/** Be told when a message lands, so a session can wait for a reply rather than poll for one. */\n\tonMessage: (fn: (m: Message) => void) => () => void\n\tstop: () => Promise<void>\n}\n\n/**\n * Attach to this identity's daemon, starting one if none is running.\n *\n * Falls back to a node in this process when a daemon cannot be had — a sandbox that forbids\n * spawning, say. Everything still works; it is simply only present while this session is.\n */\nexport async function startNode(options: NodeOptions): Promise<Node> {\n\tconst inbox = new Inbox(options.dir, options.session)\n\tawait inbox.load()\n\n\tif (process.env.VOLENET_MCP_NO_DAEMON !== '1') {\n\t\tconst conn = (await connect(options.dir)) ?? (await spawnDaemon(options.dir))\n\t\tif (conn) {\n\t\t\treturn {\n\t\t\t\tnet: remoteNet(conn),\n\t\t\t\tinbox,\n\t\t\t\trequests: [],\n\t\t\t\tnotices: [],\n\t\t\t\toptions,\n\t\t\t\thubStatus: options.hub ? `joined ${options.hub}` : 'no hub configured',\n\t\t\t\tcanSample: false,\n\t\t\t\twhere: 'daemon',\n\t\t\t\tonMessage: watchLog(options.dir, inbox),\n\t\t\t\tstop: async () => {\n\t\t\t\t\t// The daemon is shared and stays; only this connection to it goes.\n\t\t\t\t\tconn.destroy()\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\tconst local = await startLocal(options, inbox)\n\treturn { ...local, where: 'in-process' }\n}\n\n/** A node in this process — what the daemon itself runs, and the fallback when it cannot. */\nexport async function startLocal(\n\toptions: NodeOptions,\n\tinbox: Inbox,\n\t/** Told when a message lands. Only the daemon passes one — it is the thing always running. */\n\tnotify?: Notifier,\n): Promise<Omit<Node, 'where'>> {\n\tconst bus = createEventBus()\n\tconst requests: PendingRequest[] = []\n\tconst notices: Notice[] = []\n\tconst listeners = new Set<(m: Message) => void>()\n\n\tconst port = (await isFree(options.port)) ? options.port : 0\n\tconst manager = new VoleNetManager(\n\t\t{\n\t\t\tenabled: true,\n\t\t\tinstanceName: options.name,\n\t\t\trole: 'peer',\n\t\t\tport,\n\t\t\tkeyPath: path.join(options.dir, 'net', 'vole_key'),\n\t\t\t// A hub is a peer we dial. 'read' rather than 'full': a hub carries our sealed traffic,\n\t\t\t// it has no business acting on this node.\n\t\t\tpeers: options.hub ? [{ url: options.hub, trust: 'read' }] : [],\n\t\t},\n\t\toptions.dir,\n\t)\n\n\tbus.on('volenet:chat', (d) => {\n\t\tconst m = d as {\n\t\t\tfrom: string\n\t\t\tfromName: string\n\t\t\ttext: string\n\t\t\tmessageId: string\n\t\t\ttimestamp: number\n\t\t}\n\t\tconst message: Message = {\n\t\t\tpeerId: m.from,\n\t\t\tpeerName: m.fromName,\n\t\t\tdir: 'in',\n\t\t\ttext: m.text,\n\t\t\tts: m.timestamp,\n\t\t\tid: m.messageId,\n\t\t}\n\t\t// Only a message we had not already recorded wakes a waiter, so a replay cannot.\n\t\tvoid inbox.add(message).then((added) => {\n\t\t\tif (!added) return\n\t\t\tfor (const fn of listeners) fn(message)\n\t\t\t// Nothing can wake a session, so tell the person instead. Reading it is their move.\n\t\t\tnotify?.(`${message.peerName} on VoleNet`, preview(message.text))\n\t\t})\n\t})\n\n\tbus.on('volenet:chat:pending', (d) => {\n\t\tconst p = d as { from: Array<{ from: string; fromName: string; count: number; last: number }> }\n\t\tfor (const n of p.from ?? []) {\n\t\t\tconst at = notices.findIndex((x) => x.from === n.from)\n\t\t\tif (at >= 0) notices[at] = n\n\t\t\telse notices.push(n)\n\t\t}\n\t})\n\n\tconst remember = (kind: 'pair' | 'relay') => (d: unknown) => {\n\t\tconst r = d as { from: string; fromName: string; note?: string }\n\t\tif (requests.some((x) => x.from === r.from && x.kind === kind)) return\n\t\trequests.push({ kind, from: r.from, fromName: r.fromName, note: r.note, at: Date.now() })\n\t}\n\tbus.on('volenet:pair:request', remember('pair'))\n\tbus.on('volenet:relay:request', remember('relay'))\n\n\tawait manager.start(undefined, bus)\n\tconst bound = manager.getTransport()?.getPort?.() ?? port\n\tconst settings: NodeOptions = { ...options, port: bound || options.port }\n\n\t// Dialling a hub we have never met gets a 401: it has no reason to trust this key yet. The\n\t// join flow is the introduction, and it hands back the hub's own key to pin.\n\tlet hubStatus = 'no hub configured'\n\tif (options.hub) {\n\t\thubStatus = (await alreadyTrusts(options.dir, options.hub))\n\t\t\t? `joined ${options.hub}`\n\t\t\t: await join(manager, options.hub)\n\t}\n\n\treturn {\n\t\tnet: localNet(manager),\n\t\tinbox,\n\t\trequests,\n\t\tnotices,\n\t\toptions: settings,\n\t\thubStatus,\n\t\tcanSample: false,\n\t\tonMessage: (fn) => {\n\t\t\tlisteners.add(fn)\n\t\t\treturn () => listeners.delete(fn)\n\t\t},\n\t\tstop: () => manager.stop(),\n\t}\n}\n\n/** Run as the daemon: a node in this process, served over the socket, until stopped. */\nexport async function runDaemon(options: NodeOptions): Promise<void> {\n\tconst inbox = new Inbox(options.dir, 'daemon')\n\tawait inbox.load()\n\tconst node = await startLocal(options, inbox, notifier())\n\tawait serve(options.dir, node.net)\n\t// Nothing else to do: the node is running and the socket is answering.\n\tawait new Promise(() => undefined)\n}\n\n/**\n * Notice messages the daemon appended.\n *\n * The daemon receives them, so a session cannot be told directly — but the log is a file, and a\n * file can be watched. Cheap, and it works no matter which process did the writing.\n */\nfunction watchLog(dir: string, inbox: Inbox): (fn: (m: Message) => void) => () => void {\n\treturn (fn) => {\n\t\tconst file = path.join(dir, 'messages.jsonl')\n\t\tconst seen = new Set(inbox.history('', 0).map((m) => m.id))\n\t\tlet closed = false\n\t\tconst check = async () => {\n\t\t\tif (closed) return\n\t\t\tconst before = new Set(inbox.unread().map((m) => m.id))\n\t\t\tawait inbox.refresh()\n\t\t\tfor (const m of inbox.unread()) {\n\t\t\t\tif (!before.has(m.id) && !seen.has(m.id)) {\n\t\t\t\t\tseen.add(m.id)\n\t\t\t\t\tfn(m)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet watcher: fsSync.FSWatcher | undefined\n\t\ttry {\n\t\t\twatcher = fsSync.watch(path.dirname(file), (_e, name) => {\n\t\t\t\tif (name === 'messages.jsonl') void check()\n\t\t\t})\n\t\t} catch {\n\t\t\t// no watcher available; the poll below still gets there\n\t\t}\n\t\tconst timer = setInterval(() => void check(), 1000)\n\t\treturn () => {\n\t\t\tclosed = true\n\t\t\tclearInterval(timer)\n\t\t\twatcher?.close()\n\t\t}\n\t}\n}\n\nasync function join(node: VoleNetManager, hub: string): Promise<string> {\n\tconst res = await node.initiateJoin(hub)\n\tif (!res.ok) return `could not join ${hub}: ${res.error}`\n\tif (res.pending) return `waiting for approval at ${hub}`\n\treturn `joined ${res.hubName ?? hub}`\n}\n\n/**\n * Whether the hub at this URL is already trusted, so a restart does not re-join every time.\n * It asks who the hub says it is, then looks that id up in what we already trust.\n */\nexport async function alreadyTrusts(dir: string, hub: string): Promise<boolean> {\n\ttry {\n\t\tconst r = await fetch(`${hub.replace(/\\/$/, '')}/volenet/info`, {\n\t\t\tsignal: AbortSignal.timeout(8000),\n\t\t})\n\t\tconst info = (await r.json()) as { publicKey?: string }\n\t\tconst parsed = info.publicKey ? parsePublicKey(info.publicKey) : null\n\t\tif (!parsed) return false\n\t\treturn (await loadAuthorizedVoles(path.join(dir, 'net'))).has(parsed.instanceId)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether anything is already listening on a port.\n *\n * A node serves the VoleNet endpoints so peers can dial *in*, which for a session behind NAT\n * essentially never happens — it dials out, to a hub or an agent. So a taken port is not worth\n * failing over.\n */\nasync function isFree(port: number): Promise<boolean> {\n\tconst netmod = await import('node:net')\n\treturn new Promise((resolve) => {\n\t\tconst probe = netmod\n\t\t\t.createServer()\n\t\t\t.once('error', () => resolve(false))\n\t\t\t.once('listening', () => probe.close(() => resolve(true)))\n\t\t\t.listen(port, '0.0.0.0')\n\t})\n}\n","/**\n * VoleNet as an MCP server.\n *\n * Claude Code is very good inside one machine and one session. It has no way to reach a person on\n * their phone, no way to talk to an agent someone else owns, and no identity that outlives the\n * session. VoleNet has all three and none of it is coding-assistant work — signed identity, hybrid\n * post-quantum sealing, a hub that carries ciphertext it cannot read, consent, and hold-and-forward\n * for a peer that is not there right now.\n *\n * So this is not another agent. It is the network, handed to an agent that already exists.\n *\n * stdio transport, which means **stdout belongs to the protocol**: anything printed there that is\n * not a JSON-RPC frame breaks the client. The core logger is silent by default and writes to a file\n * when VOLE_LOG_FILE is set; diagnostics here go to stderr.\n */\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n\tCallToolRequestSchema,\n\tGetPromptRequestSchema,\n\tListPromptsRequestSchema,\n\tListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport { run as runCli } from './cli.js'\nimport { type Node, resolveSettings, startNode } from './node.js'\nimport { PROMPTS } from './prompts.js'\nimport { TOOLS } from './tools.js'\n\nexport { Inbox } from './inbox.js'\nexport { run as runCli } from './cli.js'\nexport { type Settings, defaultDir, defaultName, loadStored, saveStored } from './config.js'\nexport { type Node, type NodeOptions, resolveSettings, startNode } from './node.js'\nexport { PROMPTS, type PromptDef } from './prompts.js'\nexport { TOOLS, type ToolDef } from './tools.js'\n\n/**\n * What is waiting, appended to every tool's result.\n *\n * MCP has no way for a server to push, so an arrived message would otherwise sit unseen until\n * somebody thought to look. Saying so on every result means any use of any tool surfaces it —\n * ambient awareness in place of the notification the protocol cannot send. The tools that just\n * showed you the messages are excluded, since they leave nothing unread.\n */\nexport function unreadFooter(node: Node, toolName: string): string {\n\tif (toolName === 'volenet_inbox' || toolName === 'volenet_wait') return ''\n\tconst unread = node.inbox.unread()\n\tif (unread.length === 0) return ''\n\tconst who = [...new Set(unread.map((m) => m.peerName))].join(', ')\n\treturn `\\n\\n— ${unread.length} unread message${unread.length === 1 ? '' : 's'} from ${who}. Read them with volenet_inbox.`\n}\n\n/** Wire the tools to an MCP server. Separated so a test can drive it without a transport. */\nexport function createServer(node: Node): Server {\n\tconst server = new Server(\n\t\t{ name: 'volenet', version: '0.1.0' },\n\t\t{ capabilities: { tools: {}, prompts: {} } },\n\t)\n\n\t// Flows, so a fresh session does not have to infer the order of things from a tool list.\n\tserver.setRequestHandler(ListPromptsRequestSchema, async () => ({\n\t\tprompts: PROMPTS.map((p) => ({\n\t\t\tname: p.name,\n\t\t\tdescription: p.description,\n\t\t\t...(p.arguments ? { arguments: p.arguments } : {}),\n\t\t})),\n\t}))\n\n\tserver.setRequestHandler(GetPromptRequestSchema, async (request) => {\n\t\tconst prompt = PROMPTS.find((p) => p.name === request.params.name)\n\t\tif (!prompt) throw new Error(`No such prompt: ${request.params.name}`)\n\t\treturn {\n\t\t\tdescription: prompt.description,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\trole: 'user' as const,\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: prompt.render((request.params.arguments ?? {}) as Record<string, string>),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t})\n\n\tserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n\t\ttools: TOOLS.map((t) => ({\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tinputSchema: t.inputSchema as { type: 'object' },\n\t\t})),\n\t}))\n\n\tserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n\t\tconst tool = TOOLS.find((t) => t.name === request.params.name)\n\t\tif (!tool) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text' as const, text: `No such tool: ${request.params.name}` }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst text = await tool.run(node, (request.params.arguments ?? {}) as Record<string, unknown>)\n\t\t\treturn { content: [{ type: 'text' as const, text: text + unreadFooter(node, tool.name) }] }\n\t\t} catch (err) {\n\t\t\t// A failed tool is a result, not a crash: the session should see why and carry on.\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{ type: 'text' as const, text: err instanceof Error ? err.message : String(err) },\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\t})\n\n\treturn server\n}\n\n/** Remember what the client can do, so a later session can say so without asking again. */\nexport async function recordClientCapabilities(dir: string, caps: unknown): Promise<void> {\n\ttry {\n\t\tconst fs = await import('node:fs/promises')\n\t\tconst path = await import('node:path')\n\t\tawait fs.mkdir(dir, { recursive: true })\n\t\tawait fs.writeFile(\n\t\t\tpath.join(dir, 'client.json'),\n\t\t\t`${JSON.stringify(caps ?? {}, null, 2)}\\n`,\n\t\t\t'utf-8',\n\t\t)\n\t} catch {\n\t\t// Diagnostics only; never worth failing a startup over.\n\t}\n}\n\nasync function main(): Promise<void> {\n\tconst options = await resolveSettings()\n\tconst node = await startNode(options)\n\tconst server = createServer(node)\n\tawait server.connect(new StdioServerTransport())\n\t// What the client offers back decides what is possible here. `sampling` is the only route to\n\t// an unprompted reply — it lets a server ask the client to run a model — so record it rather\n\t// than guess, and let whoami report it honestly.\n\tconst caps = server.getClientCapabilities()\n\tnode.canSample = Boolean(caps && typeof caps === 'object' && 'sampling' in caps)\n\tawait recordClientCapabilities(options.dir, caps)\n\tconst me = await node.net.identity().catch(() => null)\n\tprocess.stderr.write(\n\t\t`volenet-mcp: ${options.name} (${me?.instanceId.substring(0, 8) ?? '?'}) ready — node ${node.where}` +\n\t\t\t`${options.hub ? `, hub ${options.hub}` : ', no hub configured'}\\n`,\n\t)\n\n\tlet stopping = false\n\tconst shutdown = async () => {\n\t\tif (stopping) return\n\t\tstopping = true\n\t\tawait node.stop().catch(() => undefined)\n\t\tprocess.exit(0)\n\t}\n\tprocess.on('SIGINT', shutdown)\n\tprocess.on('SIGTERM', shutdown)\n\t// The client closing its end is the ordinary way this ends.\n\tprocess.stdin.on('close', shutdown)\n}\n\n// Only when run as the binary, so importing this module in a test starts nothing.\nif (process.argv[1]?.includes('volenet-mcp') || process.env.VOLENET_MCP_RUN === '1') {\n\t// A bare invocation is the MCP server over stdio, which is how a client starts it — but a\n\t// person who runs it in a terminal means the opposite, and would otherwise get a process that\n\t// looks hung, or a port conflict from a server they did not know they had started. A client\n\t// attaches a pipe, never a TTY, so that is the honest way to tell them apart.\n\tif (process.argv[2] || process.stdin.isTTY) {\n\t\trunCli(process.argv.slice(2))\n\t\t\t.then((code) => process.exit(code))\n\t\t\t.catch((err) => {\n\t\t\t\tprocess.stderr.write(`volenet-mcp: ${err instanceof Error ? err.message : String(err)}\\n`)\n\t\t\t\tprocess.exit(1)\n\t\t\t})\n\t} else {\n\t\tmain().catch((err) => {\n\t\t\tprocess.stderr.write(`volenet-mcp: ${err instanceof Error ? err.message : String(err)}\\n`)\n\t\t\tprocess.exit(1)\n\t\t})\n\t}\n}\n","import * as path from 'node:path'\n/**\n * The command line, for the things you want before a session exists — or without one.\n *\n * Everything here works on files alone: no node is started, no port is bound, nothing dials out.\n * That is deliberate. The MCP server may well be running while you type these, and a second node\n * on the same identity would fight it for the port; and a `SessionStart` hook has to be safe to\n * run every single time a session opens.\n *\n * So `hub` records a choice that the next node start acts on, and `inbox` reads what has already\n * been written. Anything that genuinely needs the network — the roster, pairing, asking a brain —\n * is a tool, because it needs a live node and a conversation to happen in.\n */\nimport { loadKeyPair } from '@openvole/volenet'\nimport { defaultDir, defaultName, loadStored, saveStored, sessionKey } from './config.js'\nimport { Inbox } from './inbox.js'\nimport { install } from './install.js'\n\nconst USAGE = `volenet-mcp — VoleNet as an MCP server\n\n volenet-mcp install [--local] register with Claude Code (default: every project)\n volenet-mcp whoami this machine's identity on the mesh\n volenet-mcp daemon run the node in the foreground (normally started for you)\n volenet-mcp hub [url|--leave] which hub to use; takes effect on the next session\n volenet-mcp inbox [--read] [--quiet]\n messages waiting. --read marks them seen, --quiet says\n nothing when there are none (for hooks)\n\nWith no command it runs as the MCP server itself, over stdio, which is how Claude Code starts it.\nAnything needing a live node — peers, pairing, asking an agent's brain — is a tool you ask for in\na session, not a command here.\n`\n\nconst when = (ts: number) => new Date(ts).toISOString().replace('T', ' ').slice(0, 16)\n\nexport async function run(argv: string[], out = process.stdout): Promise<number> {\n\tconst [command, ...rest] = argv\n\tconst dir = defaultDir()\n\n\tif (!command || command === 'help' || command === '--help' || command === '-h') {\n\t\tout.write(USAGE)\n\t\treturn 0\n\t}\n\n\tif (command === 'install') return install(rest, out)\n\n\tif (command === 'daemon') {\n\t\t// The node itself: one per identity, outliving every session. Sessions start this for\n\t\t// themselves, so running it by hand is for looking at what it does.\n\t\tconst { runDaemon } = await import('./node.js')\n\t\tconst { resolveSettings } = await import('./config.js')\n\t\tawait runDaemon(await resolveSettings())\n\t\treturn 0\n\t}\n\n\tif (command === 'whoami') {\n\t\tconst stored = await loadStored(dir)\n\t\tconst keys = await loadKeyPair(path.join(dir, 'net')).catch(() => null)\n\t\tif (!keys) {\n\t\t\tout.write(\n\t\t\t\t`No identity yet at ${dir}.\\nOne is generated the first time the server runs — start a session, or ask for volenet_whoami.\\n`,\n\t\t\t)\n\t\t\treturn 0\n\t\t}\n\t\tout.write(\n\t\t\t[\n\t\t\t\t`name ${stored.name ?? defaultName()}`,\n\t\t\t\t`instanceId ${keys.instanceId}`,\n\t\t\t\t`hub ${stored.hub ?? '(none — set one with: volenet-mcp hub <url>)'}`,\n\t\t\t\t`store ${dir}`,\n\t\t\t\t'',\n\t\t\t\t'That directory is your identity. Back it up; anyone who has it is you.',\n\t\t\t\t'',\n\t\t\t].join('\\n'),\n\t\t)\n\t\treturn 0\n\t}\n\n\tif (command === 'hub') {\n\t\tconst stored = await loadStored(dir)\n\t\tif (rest.includes('--leave')) {\n\t\t\tif (!stored.hub) {\n\t\t\t\tout.write('Not on a hub.\\n')\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tawait saveStored(dir, { hub: undefined })\n\t\t\tout.write(`Left ${stored.hub}. Your identity and direct pairings are untouched.\\n`)\n\t\t\treturn 0\n\t\t}\n\t\tconst url = rest.find((a) => !a.startsWith('-'))\n\t\tif (!url) {\n\t\t\tout.write(stored.hub ? `${stored.hub}\\n` : 'No hub set. Give one: volenet-mcp hub <url>\\n')\n\t\t\treturn 0\n\t\t}\n\t\tawait saveStored(dir, { hub: url.replace(/\\/$/, '') })\n\t\tout.write(\n\t\t\t`Hub set to ${url}.\\nIt is joined the next time the server starts — restart Claude Code, or ask for volenet_hub to do it now.\\n`,\n\t\t)\n\t\treturn 0\n\t}\n\n\tif (command === 'inbox') {\n\t\t// The same reader the session in this directory uses, so a hook and its session agree\n\t\t// about what has been seen.\n\t\tconst inbox = new Inbox(dir, process.env.VOLENET_MCP_SESSION?.trim() || sessionKey())\n\t\tawait inbox.load()\n\t\tconst unread = inbox.unread()\n\t\tif (unread.length === 0) {\n\t\t\t// A hook runs on every turn. Saying \"nothing\" every time is noise in the context it\n\t\t\t// feeds, so --quiet says nothing at all when there is nothing to say.\n\t\t\tif (!rest.includes('--quiet')) out.write('No new messages.\\n')\n\t\t\treturn 0\n\t\t}\n\t\tout.write(`${unread.length} new VoleNet message${unread.length === 1 ? '' : 's'}:\\n\\n`)\n\t\tfor (const m of unread) {\n\t\t\tout.write(` [${when(m.ts)}] ${m.peerName}: ${m.text}\\n`)\n\t\t}\n\t\t// Marking is opt-in: a hook that puts these in context should mark them, an operator\n\t\t// glancing at the inbox should not silently hide them from the next session.\n\t\tif (rest.includes('--read')) await inbox.markRead()\n\t\tout.write('\\n')\n\t\treturn 0\n\t}\n\n\tout.write(`Unknown command: ${command}\\n\\n${USAGE}`)\n\treturn 1\n}\n","/**\n * `npx @openvole/volenet-mcp install` — register this server with Claude Code.\n *\n * Setup used to be a line carrying three environment flags whose values a new user had no way to\n * know yet: their name on the mesh, a hub, a port. None of that belongs in an install command. The\n * name has a sensible default, the port only matters to peers that can dial you, and the hub is a\n * decision better made from inside a session, where `volenet_hub` joins one and remembers it.\n *\n * So this takes no configuration. It shells out to the `claude` CLI when that is on PATH, and\n * otherwise prints the line to paste — guessing at another tool's config file is how you corrupt\n * one. It never inherits stdin: an installer that can block on a prompt is not a one-shot command.\n */\nimport { spawnSync } from 'node:child_process'\nimport * as path from 'node:path'\n\nexport const SERVER_NAME = 'volenet'\nconst PACKAGE = '@openvole/volenet-mcp'\n\n/**\n * How this server should be launched, from wherever `install` was itself run.\n *\n * Not simply \"is this a .js file\": run through `npx`, the entry *is* a .js file, but one living\n * in a transient cache that npm is free to evict — registering that path would work until it\n * suddenly did not. What distinguishes the cases is `node_modules`: an installed copy is always\n * under one and should be launched by package name, while a build in a working tree is not and\n * has to be launched by path, since there is nothing published to resolve.\n */\nexport function launchCommand(entry = process.argv[1]): string[] {\n\tconst installed =\n\t\t!entry || !entry.endsWith('.js') || entry.includes(`${path.sep}node_modules${path.sep}`)\n\treturn installed ? ['npx', '-y', PACKAGE] : ['node', entry]\n}\n\n/** The `claude mcp add` arguments, as a pure value so a test can check them without running one. */\nexport function addArgs(scope: 'user' | 'local', command: string[]): string[] {\n\treturn ['mcp', 'add', SERVER_NAME, '-s', scope, '--', ...command]\n}\n\nconst NEXT_STEPS =\n\t'\\nRestart Claude Code — MCP servers load at startup — then:\\n\\n' +\n\t' volenet_whoami who you are on the mesh (an identity is made on first run)\\n' +\n\t' volenet_hub url:\"...\" join a hub, to be reachable from anywhere\\n' +\n\t' volenet_connect url:\"...\" or pair directly with an agent you can dial\\n\\n' +\n\t'Nothing else needs configuring.\\n'\n\n/** One run of the `claude` CLI. Injectable so a test can check what would be done, not do it. */\nexport interface ClaudeRun {\n\tstdout?: string\n\tstderr?: string\n\tstatus?: number | null\n\terror?: Error\n}\nexport type ClaudeExec = (args: string[]) => ClaudeRun\n\n/** Never inherits stdin: an installer that can block on a prompt is not a one-shot command. */\nconst spawnClaude: ClaudeExec = (args) =>\n\tspawnSync('claude', args, {\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tencoding: 'utf-8',\n\t\ttimeout: 30_000,\n\t})\n\nexport function install(\n\targv: string[],\n\tout = process.stdout,\n\texec: ClaudeExec = spawnClaude,\n): number {\n\t// User scope by default, because the identity is: one keypair per machine, in the home\n\t// directory, shared by every session. Registering per project meant installing once and then\n\t// finding no tools in the next directory you opened — the identity was global, the\n\t// registration was not. `--local` is there for anyone who wants it in one project only.\n\tconst scope = argv.includes('--local') ? 'local' : 'user'\n\tconst command = launchCommand()\n\tconst paste = `claude mcp add ${SERVER_NAME} -s ${scope} -- ${command.join(' ')}`\n\n\tconst run = exec\n\n\tconst listed = run(['mcp', 'list'])\n\tif (listed.error) {\n\t\tout.write(\n\t\t\t`The \\`claude\\` CLI is not on PATH. Run this once, in the project you want it in:\\n\\n ${paste}\\n\\n`,\n\t\t)\n\t\treturn 1\n\t}\n\t// Already there — but registered to *what*? Someone moving from a working-tree build to the\n\t// published package runs exactly this command, and reporting \"nothing to do\" would leave them\n\t// pointed at a path that may not survive. Same command: leave it. Different: replace it.\n\tconst existing = listed.stdout\n\t\t?.split('\\n')\n\t\t.find((l) => l.trimStart().startsWith(`${SERVER_NAME}:`))\n\tif (existing) {\n\t\tif (existing.includes(command.join(' '))) {\n\t\t\tout.write(`${SERVER_NAME} is already registered, unchanged.\\n${NEXT_STEPS}`)\n\t\t\treturn 0\n\t\t}\n\t\tout.write(`Replacing the existing ${SERVER_NAME} registration:\\n was: ${existing.trim()}\\n`)\n\t\trun(['mcp', 'remove', SERVER_NAME, '-s', scope])\n\t\t// The old one may have been in the other scope; clear that too so one is left, not two.\n\t\trun(['mcp', 'remove', SERVER_NAME, '-s', scope === 'user' ? 'local' : 'user'])\n\t}\n\n\tconst added = run(addArgs(scope, command))\n\tif (added.status !== 0) {\n\t\tout.write(\n\t\t\t`Could not register it automatically${added.stderr ? `: ${added.stderr.trim()}` : ''}\\n\\n` +\n\t\t\t\t`Run this once instead:\\n\\n ${paste}\\n\\n`,\n\t\t)\n\t\treturn added.status ?? 1\n\t}\n\tout.write(\n\t\t`Registered ${SERVER_NAME} (${scope} scope${scope === 'user' ? ' — available in every project' : ', this project only'}).\\n${NEXT_STEPS}`,\n\t)\n\treturn 0\n}\n","/**\n * Guided flows, shipped with the server.\n *\n * A tool list tells a session what it *can* do, not what to do first, in what order, or what the\n * words mean. In a fresh session \"pair with my agent at <url>\" only works if the model happens to\n * match the sentence to `volenet_connect` — which it usually will, and shouldn't have to. Prompts\n * surface in the client as commands, so the flow is chosen rather than guessed.\n *\n * They ship in this package rather than as files written into someone's editor config, so\n * installing is all it takes and they cannot drift from the tools they describe.\n */\n\nexport interface PromptArgument {\n\tname: string\n\tdescription: string\n\trequired?: boolean\n}\n\nexport interface PromptDef {\n\tname: string\n\tdescription: string\n\targuments?: PromptArgument[]\n\t/** The instruction handed to the session, with any arguments already filled in. */\n\trender: (args: Record<string, string>) => string\n}\n\nexport const PROMPTS: PromptDef[] = [\n\t{\n\t\tname: 'whoami',\n\t\tdescription:\n\t\t\t'This session\\u2019s identity on the VoleNet mesh, and whether it can reach anything.',\n\t\trender: () => `Report this session's VoleNet identity.\n\nCall \\`volenet_whoami\\`. Give back the name, the instance id and where it is listening, and say in\none line whether it is actually reachable — a hub joined, or peers paired — rather than leaving an\nempty roster to be read as a failure.\n\nThe instance id is what someone else needs to grant this session anything: an agent's operator names\nit in \\`net.peers\\`. Offer it if they look like they need it. The public key is several kilobytes of\npost-quantum key material, so ask for it with \\`key: true\\` only when a peer actually wants it.\n\nIf nothing is connected, say so and offer \\`setup\\`.`,\n\t},\n\t{\n\t\tname: 'peers',\n\t\tdescription: 'Who this session can reach right now, and by which route.',\n\t\trender: () => `List who this session can reach on VoleNet.\n\nCall \\`volenet_peers\\`. For each one say whether it is online, and whether the link is direct or\nthrough a hub — the difference matters: a hub carries chat and consent, a direct link is the only\nroute that can ask an agent's brain.\n\nFlag anything that needs an action rather than only listing state: a hub member with no consent yet\ncannot be messaged until one side asks (\\`volenet_connect\\`), and someone offline will receive what\nis sent whenever they return. If the list is empty, say why — no hub, no pairings — and offer\n\\`setup\\`.`,\n\t},\n\t{\n\t\tname: 'rooms',\n\t\tdescription: 'Rooms this session is in, and how to say something to one.',\n\t\trender: () => `Show the VoleNet rooms this session is in.\n\nCall \\`volenet_room\\` with no arguments. For each, say who is in it — a room is several people and\nagents in one conversation, so who else is there is the useful part.\n\nTo say something, \\`volenet_room\\` with \\`post\\` and \\`room\\`. Every member gets their own sealed copy;\nthere is no shared key, which is why removing somebody stops them reading immediately. Report what\ncame back honestly: some copies may be waiting for members who are away, and some may not have been\nsent at all because that member has not accepted this session — **a room does not create consent**,\nso say that rather than let it read as a failure.\n\nIf there are no rooms, offer to make one (\\`create\\`) or to join one by id (\\`join\\`). A room lives on\na hub, so one has to be joined first.`,\n\t},\n\t{\n\t\tname: 'setup',\n\t\tdescription: 'Get this session onto the VoleNet mesh — join a hub, or pair with an agent.',\n\t\trender: () => `Get this session onto the VoleNet mesh.\n\n1. Call \\`volenet_whoami\\` first. It reports the identity, whether a hub is set, and how many peers\n are reachable. An identity is generated on first run; there is nothing to create.\n2. If nothing is connected, explain the two routes and ask which is wanted — do not pick silently:\n - **A hub** (\\`volenet_hub\\`) makes this session reachable from anywhere, including from a phone,\n and works when neither side can dial the other. A hub carries sealed traffic it cannot read and\n stores no message. It will **not** relay a question to an agent's brain.\n - **A direct pair** (\\`volenet_connect\\`) with an agent whose address is reachable from here. This\n is the only route that can ask an agent's brain.\n Both can be used at once, and either can be added later.\n3. Carry out whichever they choose. For a hub, the URL is enough. For a pair, follow the two-step\n fingerprint check — the \\`pair\\` command covers it.\n4. Finish by calling \\`volenet_peers\\` and saying plainly who is now reachable, and by which route.\n\nReaching someone also needs consent, which is separate from being connected: on a hub, either side\nasks and the other accepts. Say so, rather than letting an empty roster look like a failure.`,\n\t},\n\t{\n\t\tname: 'catch-up',\n\t\tdescription: 'Read what arrived while this session was away, and say what needs answering.',\n\t\trender: () => `Catch up on VoleNet.\n\n1. Call \\`volenet_inbox\\`. It returns messages that arrived — including while no session was running,\n since senders hold what they could not deliver and flush on reconnect — and who tried to reach\n this session while it was away. Reading marks them seen.\n2. Call \\`volenet_peers\\` if anything needs context about who a sender is.\n3. Summarise for the person: who wrote, what they want, and what is worth answering. Do not reply on\n their behalf without asking.\n4. If a reply is wanted, \\`volenet_send\\` says it and \\`volenet_wait\\` waits for what comes back, so\n an exchange happens in one turn rather than by checking again later.\n\nIf nothing arrived, say so in one line. This is worth running at the start of a session.`,\n\t},\n\t{\n\t\tname: 'pair',\n\t\tdescription: 'Pair with an agent at a URL, checking the fingerprint before trusting it.',\n\t\targuments: [\n\t\t\t{\n\t\t\t\tname: 'url',\n\t\t\t\tdescription: 'The agent to pair with, e.g. http://10.0.0.5:9700',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trender: (a) => `Pair this session with the VoleNet node at ${a.url ?? '<url>'}.\n\nPairing is deliberately two calls, because trusting a URL blind is trusting whoever holds it.\n\n1. Call \\`volenet_connect\\` with \\`url: \"${a.url ?? '<url>'}\"\\`. It reaches the node and reports the\n fingerprint of whoever answered. It trusts nothing yet.\n2. Show that fingerprint to the person and ask them to check it against what the other side reports\n — \\`vole net show-key\\` on an OpenVole agent. **Wait for them.** Do not confirm on their behalf:\n this step exists precisely so a human compares two values.\n3. Ask whether this session should also be able to use that agent's **brain** — running its model\n to answer questions — or only chat with whoever runs it.\n4. Once they confirm the fingerprint, call \\`volenet_connect\\` again with the same \\`url\\`,\n \\`confirm:\\` set to that fingerprint, and \\`brain: true\\` if they said yes. This trusts the node\n and sends a pair request carrying the ask.\n5. Tell them the request now waits for the operator of that node to accept it, and that nothing\n arrives until they do.\n\nBeing trusted is not the same as being allowed to do anything: the keystore says who may connect,\n\\`net.peers\\` says what they may then do. Sending the ask with the request is what lets the operator\nsettle both while accepting, instead of editing a config file afterwards.`,\n\t},\n\t{\n\t\tname: 'reach',\n\t\tdescription: 'Message a peer and wait for the reply, rather than checking back later.',\n\t\targuments: [\n\t\t\t{ name: 'peer', description: 'Who to reach — a name or instance id', required: true },\n\t\t\t{ name: 'message', description: 'What to say', required: false },\n\t\t],\n\t\trender: (\n\t\t\ta,\n\t\t) => `Reach ${a.peer ?? 'a peer'} over VoleNet${a.message ? ` and say: ${a.message}` : ''}.\n\n1. \\`volenet_peers\\` first if unsure the name resolves, or by which route they are reachable.\n2. \\`volenet_send\\` to say it. This is chat: it reaches whoever is there and does **not** run their\n brain. To ask an agent's model instead, use \\`volenet_ask\\` — direct links only, and its operator\n must have granted brain access.\n3. \\`volenet_wait\\` for the answer, so the exchange completes in this turn. If nothing comes back in\n time, say so plainly: the message is not lost, and a reply lands in the inbox whenever it comes.\n\nIf the peer is offline the message waits here and goes out when they return — report that rather\nthan treating it as a failure.`,\n\t},\n]\n","/**\n * The tools, and what they are for.\n *\n * Deliberately few. An MCP server's tool list is spent from the client's context window on every\n * turn, and the agent's whole tool registry — which is what `/mcp/<agent>` already exposes — is the\n * wrong shape here: this server is about the *network*, not about one agent's abilities. Eight\n * verbs cover it: who am I, who is out there, what did I miss, say something, read a thread, ask\n * an agent to think, and the two halves of deciding whom to trust.\n */\nimport { saveStored } from './config.js'\nimport { type Node, alreadyTrusts } from './node.js'\n\nexport interface ToolDef {\n\tname: string\n\tdescription: string\n\tinputSchema: Record<string, unknown>\n\trun: (node: Node, args: Record<string, unknown>) => Promise<string>\n}\n\nconst obj = (properties: Record<string, unknown>, required: string[] = []) => ({\n\ttype: 'object' as const,\n\tproperties,\n\t...(required.length ? { required } : {}),\n})\nconst str = (description: string) => ({ type: 'string' as const, description })\nconst num = (description: string) => ({ type: 'number' as const, description })\n\nconst when = (ts: number) => (ts ? new Date(ts).toISOString().replace('T', ' ').slice(0, 19) : '-')\n\n/** One peer, however we can reach it. */\ninterface Peer {\n\tid: string\n\tname: string\n\troute: 'direct' | 'hub'\n\tconnected: boolean\n\t/** Hub peers only: whether the consent handshake has completed both ways. */\n\tconsented?: boolean\n\tviaHub?: string\n}\n\nasync function peers(node: Node): Promise<Peer[]> {\n\tconst out: Peer[] = (await node.net.instances()).map((i) => ({\n\t\tid: i.id,\n\t\tname: i.name,\n\t\troute: 'direct' as const,\n\t\tconnected: i.connected,\n\t}))\n\tconst direct = new Set(out.map((p) => p.id))\n\tfor (const m of await node.net.relayMembers()) {\n\t\tif (direct.has(m.id)) continue\n\t\tout.push({\n\t\t\tid: m.id,\n\t\t\tname: m.name,\n\t\t\troute: 'hub',\n\t\t\tconnected: m.connected,\n\t\t\tconsented: m.accepted,\n\t\t\tviaHub: m.viaHubName,\n\t\t})\n\t}\n\treturn out\n}\n\nasync function resolve(node: Node, ref: string): Promise<Peer | undefined> {\n\tconst all = await peers(node)\n\treturn (\n\t\tall.find((p) => p.id === ref) ??\n\t\tall.find((p) => p.name === ref) ??\n\t\tall.find((p) => p.id.startsWith(ref)) ??\n\t\tall.find((p) => p.name.toLowerCase() === ref.toLowerCase())\n\t)\n}\n\nexport const TOOLS: ToolDef[] = [\n\t{\n\t\tname: 'volenet_whoami',\n\t\tdescription:\n\t\t\t\"This session's own identity on the VoleNet mesh — name, instance id, hub, and whether the mesh is reachable. Call this first if you are unsure. Pass key:true only when someone actually needs the full public key; it is several kilobytes of post-quantum key material.\",\n\t\tinputSchema: obj({\n\t\t\tkey: { type: 'boolean' as const, description: 'Include the full public key string' },\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst key = await node.net.identity()\n\t\t\tconst online = (await peers(node)).filter((p) => p.connected).length\n\t\t\tconst lines = [\n\t\t\t\t`name ${node.options.name}`,\n\t\t\t\t`instanceId ${key?.instanceId ?? '(not started)'}`,\n\t\t\t\t`hub ${node.hubStatus}`,\n\t\t\t\t`connected ${online} peer(s) online`,\n\t\t\t\t`listening port ${node.options.port} (reachable only from networks that can dial it)`,\n\t\t\t\t`store ${node.options.dir}`,\n\t\t\t\t`node ${node.where === 'daemon' ? 'a daemon, so this identity stays reachable when no session is open' : 'in this session, so it is only reachable while this session is'}`,\n\t\t\t]\n\t\t\t// The hybrid key string is ~2.5 KB — most of an ML-DSA-65 key — and spending that on\n\t\t\t// every call would be a real cost to the session for something rarely needed.\n\t\t\t// Whether a message can ever prompt a reply on its own is the client's decision, not\n\t\t\t// ours: MCP's only server-initiated model call is `sampling`. Say which it is, so\n\t\t\t// nobody waits for an answer that cannot come.\n\t\t\tlines.push(\n\t\t\t\t`replies ${node.canSample ? 'this client can be asked to answer on its own' : 'only when you ask — this client cannot be woken by a message'}`,\n\t\t\t)\n\t\t\tif (args.key) lines.push('', `publicKey ${key?.publicKeyString ?? '-'}`)\n\t\t\telse\n\t\t\t\tlines.push(\n\t\t\t\t\t'',\n\t\t\t\t\t'Public key withheld (large). Call again with key:true when a peer needs it.',\n\t\t\t\t)\n\t\t\tlines.push(\n\t\t\t\t'',\n\t\t\t\t'To be reachable by someone else: give them that public key to trust, or send them a',\n\t\t\t\t'pair request with volenet_connect and have their operator accept it.',\n\t\t\t)\n\t\t\treturn lines.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_peers',\n\t\tdescription:\n\t\t\t'Everyone this session can reach: agents and people, whether the link is direct or through a hub, and whether they are online right now.',\n\t\tinputSchema: obj({}),\n\t\tasync run(node) {\n\t\t\tconst all = await peers(node)\n\t\t\tif (all.length === 0) {\n\t\t\t\treturn 'No peers. Join a hub (VOLENET_MCP_HUB) or pair with a node directly (volenet_pair).'\n\t\t\t}\n\t\t\treturn all\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst bits = [\n\t\t\t\t\t\tp.connected ? 'online ' : 'away ',\n\t\t\t\t\t\tp.route === 'direct' ? 'direct' : `via ${p.viaHub}`,\n\t\t\t\t\t\tp.name,\n\t\t\t\t\t\tp.id.substring(0, 8),\n\t\t\t\t\t]\n\t\t\t\t\tif (p.route === 'hub' && !p.consented) bits.push('(no consent yet — volenet_connect)')\n\t\t\t\t\treturn ` ${bits.join(' ')}`\n\t\t\t\t})\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_inbox',\n\t\tdescription:\n\t\t\t'Messages that arrived for this session, including while it was not running, plus who tried to reach it while it was away. Reading marks them seen. Check this at the start of a session.',\n\t\tinputSchema: obj({}),\n\t\tasync run(node) {\n\t\t\tconst unread = node.inbox.unread()\n\t\t\tconst lines: string[] = []\n\t\t\tif (unread.length === 0) lines.push('No new messages.')\n\t\t\telse {\n\t\t\t\tlines.push(`${unread.length} new message(s):`, '')\n\t\t\t\tfor (const m of unread) {\n\t\t\t\t\tlines.push(` [${when(m.ts)}] ${m.peerName} (${m.peerId.substring(0, 8)})`)\n\t\t\t\t\tlines.push(` ${m.text.replace(/\\n/g, '\\n ')}`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node.notices.length > 0) {\n\t\t\t\tlines.push('', 'Tried to reach you while you were away:')\n\t\t\t\tfor (const n of node.notices) {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t` ${n.fromName} (${n.from.substring(0, 8)}) — ${n.count}x, last ${when(n.last)}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlines.push(\n\t\t\t\t\t' Their messages are held on their own device and arrive when they are next online.',\n\t\t\t\t)\n\t\t\t}\n\t\t\tawait node.inbox.markRead()\n\t\t\treturn lines.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_send',\n\t\tdescription:\n\t\t\t'Send a message to a person or an agent. This is chat: it is delivered and read by whoever is there, and does NOT run their brain or wait for a reply. Use this to reach a person on the phone app. If the peer is offline the message waits here and goes out when they return.',\n\t\tinputSchema: obj(\n\t\t\t{ to: str('Peer name or instance id (see volenet_peers)'), text: str('What to say') },\n\t\t\t['to', 'text'],\n\t\t),\n\t\tasync run(node, args) {\n\t\t\tconst to = String(args.to ?? '')\n\t\t\tconst text = String(args.text ?? '')\n\t\t\tif (!text.trim()) return 'Nothing to send.'\n\t\t\tconst peer = await resolve(node, to)\n\t\t\tconst res = await node.net.sendChat(peer?.id ?? to, text)\n\t\t\tif (!res.ok) return `Not sent: ${res.error ?? 'unknown error'}`\n\t\t\tawait node.inbox.add({\n\t\t\t\tpeerId: peer?.id ?? to,\n\t\t\t\tpeerName: peer?.name ?? to,\n\t\t\t\tdir: 'out',\n\t\t\t\ttext,\n\t\t\t\tts: Date.now(),\n\t\t\t\tid: `out-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n\t\t\t})\n\t\t\tif (res.delivered === false) {\n\t\t\t\treturn `Held for ${peer?.name ?? to}: they are not reachable right now, and it goes out when they are back.`\n\t\t\t}\n\t\t\treturn `Sent to ${peer?.name ?? to}${res.relayed ? ' (through a hub, sealed end to end)' : ''}.`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_history',\n\t\tdescription: 'The conversation with one peer, oldest first.',\n\t\tinputSchema: obj({\n\t\t\tpeer: str('Peer name or instance id'),\n\t\t\tlimit: num('Messages (default 50)'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst peer = await resolve(node, String(args.peer ?? ''))\n\t\t\tconst id = peer?.id ?? String(args.peer ?? '')\n\t\t\tconst msgs = node.inbox.history(id, Number(args.limit ?? 50))\n\t\t\tif (msgs.length === 0) return `Nothing recorded with ${peer?.name ?? id}.`\n\t\t\treturn msgs\n\t\t\t\t.map((m) => `[${when(m.ts)}] ${m.dir === 'out' ? 'you' : m.peerName}: ${m.text}`)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_ask',\n\t\tdescription:\n\t\t\t\"Ask another AGENT's brain a question and wait for its answer. This runs the peer's model, so it takes as long as thinking takes, and the peer's operator must have granted this session brain access. Only works over a direct link — a hub will not carry it — and a person's phone app has no brain to ask; use volenet_send for people.\",\n\t\tinputSchema: obj(\n\t\t\t{\n\t\t\t\tto: str('Agent name or instance id'),\n\t\t\t\tquestion: str('What to ask'),\n\t\t\t\ttimeout_ms: num('How long to wait (default 120000)'),\n\t\t\t},\n\t\t\t['to', 'question'],\n\t\t),\n\t\tasync run(node, args) {\n\t\t\tconst peer = await resolve(node, String(args.to ?? ''))\n\t\t\tif (!peer) return `No peer found: \"${args.to}\". Use volenet_peers.`\n\t\t\tif (peer.route !== 'direct') {\n\t\t\t\treturn `${peer.name} is only reachable through a hub, and a hub will not relay a question to an agent's brain — it carries chat and consent only. Use volenet_send, or pair directly with volenet_pair.`\n\t\t\t}\n\t\t\tconst res = await node.net.askBrain(\n\t\t\t\tpeer.id,\n\t\t\t\tString(args.question ?? ''),\n\t\t\t\tnode.options.name,\n\t\t\t\tNumber(args.timeout_ms ?? 120_000),\n\t\t\t)\n\t\t\tif (res.status === 'completed') return `${peer.name} says:\\n\\n${res.result}`\n\t\t\tconst why = res.error ?? res.status\n\t\t\tif (typeof why === 'string' && why.includes('allowBrain')) {\n\t\t\t\treturn `${peer.name} refused: ${why}\\n\\nIts operator can allow this session by adding { \"id\": \"${(await node.net.identity())?.instanceId}\", \"trust\": \"read\", \"allowBrain\": true } to net.peers and restarting.`\n\t\t\t}\n\t\t\treturn `${peer.name} did not answer: ${why}`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_requests',\n\t\tdescription:\n\t\t\t'Trust decisions waiting on you: nodes asking to be trusted, and hub members asking to chat. Accept or deny one by naming it. Nothing is trusted until you say so.',\n\t\tinputSchema: obj({\n\t\t\taccept: str('Name or id to accept'),\n\t\t\tdeny: str('Name or id to deny'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst accept = args.accept ? String(args.accept) : undefined\n\t\t\tconst deny = args.deny ? String(args.deny) : undefined\n\t\t\tconst act = accept ?? deny\n\t\t\tif (act) {\n\t\t\t\tconst req = node.requests.find((r) => r.fromName === act || r.from.startsWith(act))\n\t\t\t\tif (!req) return `No pending request matching \"${act}\".`\n\t\t\t\tconst ok = accept\n\t\t\t\t\t? req.kind === 'pair'\n\t\t\t\t\t\t? await node.net.acceptPair(req.from)\n\t\t\t\t\t\t: await node.net.approveRelayConnect(req.from)\n\t\t\t\t\t: req.kind === 'pair'\n\t\t\t\t\t\t? await node.net.denyPair(req.from)\n\t\t\t\t\t\t: await node.net.denyRelayConnect(req.from)\n\t\t\t\tnode.requests.splice(node.requests.indexOf(req), 1)\n\t\t\t\treturn ok.ok\n\t\t\t\t\t? `${accept ? 'Accepted' : 'Denied'} ${req.fromName}.`\n\t\t\t\t\t: `Failed: ${'error' in ok ? ok.error : 'unknown'}`\n\t\t\t}\n\t\t\tconst pairs = await node.net.listPairRequests()\n\t\t\tfor (const p of pairs) {\n\t\t\t\tif (!node.requests.some((r) => r.from === p.id)) {\n\t\t\t\t\tnode.requests.push({ kind: 'pair', from: p.id, fromName: p.name, at: Date.now() })\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node.requests.length === 0) return 'Nothing waiting.'\n\t\t\treturn node.requests\n\t\t\t\t.map(\n\t\t\t\t\t(r) =>\n\t\t\t\t\t\t` ${r.kind === 'pair' ? 'wants to be trusted' : 'wants to chat '} ${r.fromName} ${r.from.substring(0, 8)}${r.note ? ` — \"${r.note}\"` : ''}`,\n\t\t\t\t)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_wait',\n\t\tdescription:\n\t\t\t'Wait for the next message to arrive, instead of checking again later. Use it whenever you have said something and a reply is expected — it turns a mailbox into a conversation. Returns as soon as anything lands, or reports that nothing came within the time given. Nothing is lost either way: a message that arrives after you stop waiting is still in the inbox.',\n\t\tinputSchema: obj({\n\t\t\tfrom: str('Only wake for this peer, by name or id. Omit to wake for anyone.'),\n\t\t\ttimeout_ms: num('How long to wait. Default 60000, maximum 300000.'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst limit = Math.min(Math.max(Number(args.timeout_ms ?? 60_000), 1_000), 300_000)\n\t\t\tconst want = args.from ? await resolve(node, String(args.from)) : undefined\n\t\t\tconst wanted = (m: { peerId: string }) => !args.from || m.peerId === (want?.id ?? args.from)\n\n\t\t\t// Anything already unread counts as arrived: waiting for the next one would skip it.\n\t\t\tconst already = node.inbox.unread().filter(wanted)\n\t\t\tif (already.length === 0) {\n\t\t\t\tawait new Promise<void>((done) => {\n\t\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\t\toff()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, limit)\n\t\t\t\t\tconst off = node.onMessage((m) => {\n\t\t\t\t\t\tif (!wanted(m)) return\n\t\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\t\toff()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst arrived = node.inbox.unread().filter(wanted)\n\t\t\tawait node.inbox.markRead()\n\t\t\tif (arrived.length === 0) {\n\t\t\t\treturn `Nothing arrived within ${Math.round(limit / 1000)}s${args.from ? ` from ${want?.name ?? args.from}` : ''}. It is not lost — whatever they send lands in the inbox whenever it comes.`\n\t\t\t}\n\t\t\treturn arrived.map((m) => `[${when(m.ts)}] ${m.peerName}: ${m.text}`).join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_hub',\n\t\tdescription:\n\t\t\t'Join a hub, leave one, or report which hub this session is on. A hub makes you reachable by people and agents that cannot dial your machine — it carries sealed traffic it cannot read, and stores no message. Called with no arguments it just reports. The choice is remembered, so the next session starts where this one left off.',\n\t\tinputSchema: obj({\n\t\t\turl: str('Hub URL to join, e.g. https://hub.example.com/mesh'),\n\t\t\tleave: { type: 'boolean' as const, description: 'Leave the current hub' },\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tif (args.leave) {\n\t\t\t\tconst hub = node.options.hub\n\t\t\t\tif (!hub) return 'Not on a hub.'\n\t\t\t\t// Forget it and drop the socket, so neither this session nor the next dials it.\n\t\t\t\tawait node.net.forgetPeer(hub)\n\t\t\t\tnode.options.hub = undefined\n\t\t\t\tnode.hubStatus = 'no hub configured'\n\t\t\t\tawait saveStored(node.options.dir, { hub: undefined })\n\t\t\t\treturn `Left ${hub}. Your identity and everyone you have paired with directly are untouched.`\n\t\t\t}\n\t\t\tif (!args.url) {\n\t\t\t\treturn node.options.hub\n\t\t\t\t\t? `${node.hubStatus}\\n\\nCall with leave:true to come off it, or url to move to another.`\n\t\t\t\t\t: 'Not on a hub. Give a url to join one — or stay off it and pair directly with volenet_connect.'\n\t\t\t}\n\t\t\tconst url = String(args.url).replace(/\\/$/, '')\n\t\t\t// Already trusted — a hub we have joined before, or paired with — so there is nothing to\n\t\t\t// introduce. Joining again would need its public join still open, which is not a thing\n\t\t\t// coming back should depend on.\n\t\t\tif (await alreadyTrusts(node.options.dir, url)) {\n\t\t\t\tawait node.net.addPeer(url)\n\t\t\t\tnode.options.hub = url\n\t\t\t\tnode.hubStatus = `joined ${url}`\n\t\t\t\tawait saveStored(node.options.dir, { hub: url })\n\t\t\t\treturn `Joined ${url} again — it was already trusted, so no introduction was needed.`\n\t\t\t}\n\t\t\tconst res = await node.net.joinHub(url)\n\t\t\tif (!res.ok) return `Could not join ${url}: ${res.error}`\n\t\t\tnode.options.hub = url\n\t\t\tnode.hubStatus = res.pending\n\t\t\t\t? `waiting for approval at ${url}`\n\t\t\t\t: `joined ${res.hubName ?? url}`\n\t\t\tawait saveStored(node.options.dir, { hub: url })\n\t\t\treturn res.pending\n\t\t\t\t? `Asked to join ${url}. Its operator has to approve before you appear in the roster.`\n\t\t\t\t: `Joined ${res.hubName ?? url}. Remembered, so the next session starts here. Call volenet_peers to see who is around.`\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_room',\n\t\tdescription:\n\t\t\t'Rooms: several people and agents in one conversation. With no arguments it lists the rooms this session is in. Give `post` to say something to a room — every member gets their own sealed copy, so there is no shared key and removing someone stops them reading immediately. Give `create`, `join`, `leave` or `invite` to change membership, which the hub keeps. A room does not create consent: a member who has not accepted you will not receive your posts.',\n\t\tinputSchema: obj({\n\t\t\troom: str('Which room, by id or name (see the list)'),\n\t\t\tpost: str('Say this to the room'),\n\t\t\tcreate: str('Make a room with this name'),\n\t\t\tjoin: str('Join a room by id'),\n\t\t\tleave: { type: 'boolean' as const, description: 'Leave the room named in `room`' },\n\t\t\tinvite: str('Bring this peer (name or id) into the room named in `room`'),\n\t\t\thub: str('Which hub holds the room. Only needed with more than one.'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst hubOf = async () => {\n\t\t\t\tif (args.hub) return String(args.hub)\n\t\t\t\tconst hubs = (await node.net.instances()).filter((i) => i.connected)\n\t\t\t\tif (hubs.length === 0) return null\n\t\t\t\treturn hubs[0]!.id\n\t\t\t}\n\t\t\tconst find = async (ref: string) => {\n\t\t\t\tconst all = await node.net.rooms()\n\t\t\t\treturn all.find((r) => r.room === ref) ?? all.find((r) => r.name === ref)\n\t\t\t}\n\n\t\t\tif (args.create || args.join || args.leave || args.invite) {\n\t\t\t\tconst hub = await hubOf()\n\t\t\t\tif (!hub)\n\t\t\t\t\treturn 'No hub connected. A room lives on a hub — join one first with volenet_hub.'\n\t\t\t\tif (args.create) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:create', { name: String(args.create) })\n\t\t\t\t\treturn res.ok\n\t\t\t\t\t\t? `Asked for a room called \"${args.create}\". Call this again in a moment to see it.`\n\t\t\t\t\t\t: `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tif (args.join) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:join', { room: String(args.join) })\n\t\t\t\t\treturn res.ok ? `Asked to join ${args.join}.` : `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tconst room = args.room ? await find(String(args.room)) : undefined\n\t\t\t\tif (!room) return 'Name the room with `room` — see the list.'\n\t\t\t\tif (args.leave) {\n\t\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:leave', { room: room.room })\n\t\t\t\t\treturn res.ok ? `Left ${room.name}.` : `Could not: ${res.error}`\n\t\t\t\t}\n\t\t\t\tconst peer = await resolve(node, String(args.invite))\n\t\t\t\tconst res = await node.net.roomCommand(hub, 'room:invite', {\n\t\t\t\t\troom: room.room,\n\t\t\t\t\tmember: peer?.id ?? String(args.invite),\n\t\t\t\t})\n\t\t\t\treturn res.ok\n\t\t\t\t\t? `Invited ${peer?.name ?? args.invite} to ${room.name}.`\n\t\t\t\t\t: `Could not: ${res.error}`\n\t\t\t}\n\n\t\t\tif (args.post) {\n\t\t\t\tconst room = args.room ? await find(String(args.room)) : (await node.net.rooms())[0]\n\t\t\t\tif (!room) return 'No room to post to. Create or join one first.'\n\t\t\t\tconst res = await node.net.postToRoom(room.room, String(args.post))\n\t\t\t\tif (!res.ok) return `Not posted: ${res.error}`\n\t\t\t\tconst bits = [`Posted to ${room.name}: ${res.sent} delivered`]\n\t\t\t\tif (res.held) bits.push(`${res.held} waiting for members who are away`)\n\t\t\t\tif (res.skipped)\n\t\t\t\t\tbits.push(`${res.skipped} could not be reached — they may not have accepted you`)\n\t\t\t\treturn `${bits.join(', ')}.`\n\t\t\t}\n\n\t\t\tconst all = await node.net.rooms()\n\t\t\tif (all.length === 0) {\n\t\t\t\treturn 'Not in any room. Create one with create:\"name\", or join one you have been given the id for.'\n\t\t\t}\n\t\t\treturn all\n\t\t\t\t.map(\n\t\t\t\t\t(r) =>\n\t\t\t\t\t\t` ${r.name} ${r.room.substring(0, 8)} ${r.members.length} member(s): ${r.members.map((m) => m.name).join(', ')}`,\n\t\t\t\t)\n\t\t\t\t.join('\\n')\n\t\t},\n\t},\n\t{\n\t\tname: 'volenet_connect',\n\t\tdescription:\n\t\t\t'Reach out to someone new: pair directly with a node at a URL, or ask a hub member for consent to chat. Pairing is two calls — the first reports the fingerprint of whoever answers, the second confirms it — because trusting a URL blind is trusting whoever holds it. Neither side trusts you until they accept.',\n\t\tinputSchema: obj({\n\t\t\turl: str('Node URL to pair with directly, e.g. http://10.0.0.5:9700'),\n\t\t\tconfirm: str('The fingerprint returned by a first call with url, confirming who answers'),\n\t\t\tbrain: {\n\t\t\t\ttype: 'boolean' as const,\n\t\t\t\tdescription:\n\t\t\t\t\t\"Also ask for permission to use that agent's brain. The operator sees it as part of the same decision and can grant it while accepting; without it, being trusted allows chat only.\",\n\t\t\t},\n\t\t\tmember: str('Hub member name or id to ask for chat consent'),\n\t\t\tnote: str('A line saying who you are'),\n\t\t}),\n\t\tasync run(node, args) {\n\t\t\tconst note = args.note ? String(args.note) : undefined\n\t\t\tif (args.url) {\n\t\t\t\tconst url = String(args.url)\n\t\t\t\tconst probe = await node.net.probePair(url)\n\t\t\t\tif (!probe.ok || !probe.publicKey) return `Could not reach it: ${probe.error}`\n\t\t\t\t// Trust on first use is a decision, not a side effect: whoever answers that URL is\n\t\t\t\t// whoever answers that URL. Show the fingerprint and require it back before trusting.\n\t\t\t\tconst confirm = args.confirm ? String(args.confirm).trim() : ''\n\t\t\t\tif (!confirm) {\n\t\t\t\t\treturn [\n\t\t\t\t\t\t`${probe.name ?? url} answers with fingerprint:`,\n\t\t\t\t\t\t` ${probe.fingerprint}`,\n\t\t\t\t\t\tprobe.alreadyTrusted ? ' (already trusted by this session)' : '',\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Check that against what the other side reports, then call this again with',\n\t\t\t\t\t\t'confirm set to that fingerprint to trust it and send the pair request.',\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join('\\n')\n\t\t\t\t}\n\t\t\t\tif (!probe.fingerprint?.startsWith(confirm)) {\n\t\t\t\t\treturn `That fingerprint does not match: it answers with ${probe.fingerprint}. Nothing was trusted.`\n\t\t\t\t}\n\t\t\t\tconst res = await node.net.initiatePair(\n\t\t\t\t\turl,\n\t\t\t\t\tprobe.publicKey,\n\t\t\t\t\tnote,\n\t\t\t\t\targs.brain ? ['brain'] : undefined,\n\t\t\t\t)\n\t\t\t\tif (!res.ok) return `Could not ask: ${res.error}`\n\t\t\t\treturn [\n\t\t\t\t\t`Trusted ${probe.name ?? url} and asked it to trust this session.`,\n\t\t\t\t\targs.brain\n\t\t\t\t\t\t? 'The request also asks to use its brain, so its operator can grant that while accepting — no config editing, no restart.'\n\t\t\t\t\t\t: 'It asks for trust only. Pass brain:true to also ask for brain access.',\n\t\t\t\t\t'Nothing arrives until their operator accepts.',\n\t\t\t\t].join(' ')\n\t\t\t}\n\t\t\tif (args.member) {\n\t\t\t\tconst res = await node.net.requestRelayConnect(String(args.member), note)\n\t\t\t\tif (!res.ok) return `Could not ask: ${res.error}`\n\t\t\t\treturn res.queued\n\t\t\t\t\t? `${args.member} is away — the request waits here and goes out when they are back.`\n\t\t\t\t\t: `Asked ${args.member} for consent to chat.`\n\t\t\t}\n\t\t\treturn 'Give either a url (direct pairing) or a member (hub consent).'\n\t\t},\n\t},\n]\n"],"mappings":"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","names":["fs","path","file","fs","path","resolve","spawn","path","join","file","resolve","path","path","run","run","runDaemon","resolveSettings","when","all","res","createServer","fs","path","run"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openvole/volenet-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "VoleNet as an MCP server — give Claude Code an identity on the mesh, so it can reach people and other agents across machines",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@modelcontextprotocol/sdk": "^1.30.0",
19
- "@openvole/volenet": "1.1.0"
19
+ "@openvole/volenet": "1.1.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.0.0",