/** * Exact roster-resolved fleet communications contract (#766). * * The runtime composer and `mosaic fleet` command surface share the canonical * v1 roster resolver. This module never probes tmux, guesses an SSH target, or * mutates an active session. */ import { createHash } from 'node:crypto'; import { existsSync } from 'node:fs'; import { homedir, hostname } from 'node:os'; import { join } from 'node:path'; import { readRegularFileSecure } from './secure-file.js'; import { parseFleetRosterV1, resolveInstalledFleetRosterPath, getRosterAgent, type FleetAgent, type FleetRoster, } from './fleet-roster-v1.js'; export interface FleetCommsOptions { /** Exact current roster member. */ selfName: string; /** Canonically resolved roster. */ roster: FleetRoster; /** Stable fleet-host baseline for members whose roster host is absent. */ localHost: string; /** Absolute helper path in this installation. */ agentSendPath: string; } export interface CommsBlockResult { ok: boolean; output: string; error?: string; } export interface ResolvedFleetIdentity { readonly roster: FleetRoster; readonly member: FleetAgent; readonly requestedName: string; readonly agentSendPath: string; readonly localHost: string; } export interface FleetIdentityResult { ok: boolean; identity?: ResolvedFleetIdentity; error?: string; } export interface PeerCommandResult { ok: boolean; command: string; error?: string; } export const FLEET_COMMS_TOOLS_CONTRACT = 'fleet-comms-contract: 1'; const MAX_TOOLS_CONTRACT_BYTES = 256 * 1024; function shortHostname(): string { return hostname().split('.')[0] || 'localhost'; } function resolvedHost(agent: FleetAgent, fleetHost: string): string { return agent.host ?? fleetHost; } function displaySocket(socket: string): string { return socket || '(default)'; } function knownNames(roster: FleetRoster, except?: string): string { return roster.agents .filter((agent) => agent.name !== except) .map((agent) => agent.name) .join(', '); } function missingMemberError(roster: FleetRoster, selfName: string): string { return `Agent "${selfName}" is not in the fleet roster. Known exact names: ${knownNames(roster)}. Select an exact roster name; do not infer or fuzzy-match a tmux session.`; } /** Render one shell argument without changing already-safe exact values. */ function shellArg(value: string): string { if (/^[A-Za-z0-9_./:@=+-]+$/.test(value)) return value; return `'${value.replaceAll("'", `'"'"'`)}'`; } /** Render the exact command for one peer. Throws rather than guessing. */ export function renderPeerReach( peer: FleetAgent, selfHost: string, fleetHost: string, rosterSocket: string, agentSendPath: string, ): string { const parts = [shellArg(agentSendPath)]; if (rosterSocket) parts.push('-L', shellArg(rosterSocket)); const peerHost = resolvedHost(peer, fleetHost); if (peerHost !== selfHost) { if (!peer.ssh) { throw new Error( `Cross-host peer "${peer.name}" (${peerHost}) requires an explicit roster ssh target; refusing to substitute its host value.`, ); } parts.push('-H', shellArg(peer.ssh)); } parts.push('-s', shellArg(peer.name), '-m', '"…"'); return parts.join(' '); } /** Resolve one requested peer without fuzzy lookup. */ export function resolvePeerCommand( roster: FleetRoster, selfName: string, peerName: string, localHost: string, agentSendPath: string, ): PeerCommandResult { const self = roster.agents.find((agent) => agent.name === selfName); if (!self) return { ok: false, command: '', error: missingMemberError(roster, selfName) }; const peer = roster.agents.find((agent) => agent.name === peerName && agent.name !== selfName); if (!peer) { return { ok: false, command: '', error: `Peer "${peerName}" is absent from the fleet roster. Known exact peer names: ${knownNames(roster, selfName)}. ` + `Run \`mosaic agent comms-block ${selfName}\` to rediscover exact rendered rows; do not infer or fuzzy-match a tmux session.`, }; } try { return { ok: true, command: renderPeerReach( peer, resolvedHost(self, localHost), localHost, roster.tmux.socketName, agentSendPath, ), }; } catch (error) { return { ok: false, command: '', error: error instanceof Error ? error.message : String(error), }; } } interface ResolvedRow { readonly peer: FleetAgent; readonly host: string; readonly socket: string; readonly command: string; } function resolveRows(opts: FleetCommsOptions, self: FleetAgent): readonly ResolvedRow[] { const selfHost = resolvedHost(self, opts.localHost); return opts.roster.agents .filter((agent) => agent.name !== opts.selfName) .map( (peer): ResolvedRow => ({ peer, host: resolvedHost(peer, opts.localHost), socket: opts.roster.tmux.socketName, command: renderPeerReach( peer, selfHost, opts.localHost, opts.roster.tmux.socketName, opts.agentSendPath, ), }), ); } function commsGeneration( self: FleetAgent, selfHost: string, selfSocket: string, helper: string, rows: readonly ResolvedRow[], ): string { const canonical = JSON.stringify({ self: { ...self, resolvedHost: selfHost, resolvedSocket: selfSocket, helper }, peers: rows.map((row) => ({ ...row.peer, resolvedHost: row.host, resolvedSocket: row.socket, exactCommand: row.command, })), }); return createHash('sha256').update(canonical).digest('hex'); } /** Build the authoritative Markdown contract for one exact roster member. */ export function buildFleetCommsBlock(opts: FleetCommsOptions): string { const self = opts.roster.agents.find((agent) => agent.name === opts.selfName); if (!self) throw new Error(missingMemberError(opts.roster, opts.selfName)); const rows = resolveRows(opts, self); const selfHost = resolvedHost(self, opts.localHost); const selfSocket = opts.roster.tmux.socketName; const generation = commsGeneration(self, selfHost, selfSocket, opts.agentSendPath, rows); const orchestrator = rows.find((row) => row.peer.className === 'orchestrator'); const peerSection = rows.length === 0 ? 'This roster has no peers. Do not invent a target.' : `| Agent | Role | Host | Socket | Exact command | | ----- | ---- | ---- | ------ | ------------- | ${rows .map((row) => { const pointOfContact = row.peer.className === 'orchestrator' ? ' ← point of contact' : ''; return `| ${row.peer.name} | ${row.peer.className}${pointOfContact} | ${row.host} | ${displaySocket(row.socket)} | \`${row.command}\` |`; }) .join('\n')}`; const contact = orchestrator ? `Your point of contact is **${orchestrator.peer.name}**. Select that exact peer row for status, questions, and decisions.` : rows.length === 0 ? 'No peer coordination target exists in this roster.' : 'This roster has no orchestrator. Select an exact peer row for coordination.'; const soloAuthority = rows.length === 0 ? `\n## Solo authority boundaries\n\nThis member is normalized as role/class **${self.className}**. The roster grants no peer, orchestrator, or remote communication authority. Do not send, infer a target, or claim fleet coordination until an exact peer is added to the canonical roster and this block is recomposed.\n` : ''; return `# Fleet Comms — authoritative exact targets ## Local identity - Host: \`${selfHost}\` - Agent/session: \`${self.name}\` - Role/class: \`${self.className}\` - tmux socket: \`${displaySocket(selfSocket)}\` - Helper: \`${opts.agentSendPath}\` - Comms generation: \`${generation}\` The roster-resolved rows below are the only valid operational targets. Select the row whose Agent value exactly matches the requested peer. Never invent, substitute, or fuzzy-match host, session, socket, SSH, or helper-path values. If the peer is absent, stop and run \`mosaic agent comms-block ${self.name}\` to rediscover this exact member's rows; if it is still absent, report the unknown peer. ## Peers ${peerSection} ${contact} ${soloAuthority} ## Context freshness This block is a snapshot; Mosaic does not rewrite an active agent's context. Compare its Comms generation with fresh output from \`mosaic agent comms-block ${self.name}\`. If they differ, report stale composed context and have an authorized operator relaunch only this exact roster member with \`mosaic fleet restart ${self.name}\`. Do not restart or mutate a session automatically.`; } function validateAgentSendHelper(path: string, mosaicHome: string): string | undefined { try { readRegularFileSecure(path, { root: mosaicHome, executable: true }); return undefined; } catch (error) { const reason = error instanceof Error ? error.message : String(error); return `helper is unavailable or unsafe: ${path} (${reason})`; } } function helperFailureGuidance(reason: string): string { return `${reason}. Run \`mosaic update --repair-tools\` to restore the supported current-version helper and TOOLS contract, then retry exact-member composition; no active context or session was rewritten.`; } export function resolveFleetIdentity( mosaicHome: string, requestedName: string | undefined, localHost: string = shortHostname(), ): FleetIdentityResult { if (!requestedName) return { ok: true }; const agentSendPath = join(mosaicHome, 'tools', 'tmux', 'agent-send.sh'); const helperError = validateAgentSendHelper(agentSendPath, mosaicHome); if (helperError) return { ok: false, error: helperFailureGuidance(helperError) }; let rosterPath: string; try { rosterPath = resolveInstalledFleetRosterPath(mosaicHome); } catch (error) { return { ok: false, error: `cannot inspect fleet roster.yaml: ${error instanceof Error ? error.message : String(error)}; refusing JSON fallback because fallback is allowed only when YAML is absent`, }; } if (!existsSync(rosterPath)) { return { ok: false, error: `no fleet roster at ${join(mosaicHome, 'fleet', 'roster.yaml')} or ${join(mosaicHome, 'fleet', 'roster.json')}`, }; } let roster: FleetRoster; try { roster = parseFleetRosterV1( readRegularFileSecure(rosterPath, { root: mosaicHome }).content.toString('utf8'), rosterPath.endsWith('.json') ? 'json' : 'yaml', ); } catch (error) { return { ok: false, error: `invalid fleet roster at ${rosterPath}: ${error instanceof Error ? error.message : String(error)}`, }; } try { return { ok: true, identity: { roster, member: getRosterAgent(roster, requestedName), requestedName, agentSendPath, localHost, }, }; } catch { return { ok: false, error: missingMemberError(roster, requestedName) }; } } /** Render Fleet Comms from one already-resolved canonical member identity. */ export function buildResolvedFleetCommsBlock(identity: ResolvedFleetIdentity): string { return buildFleetCommsBlock({ selfName: identity.member.name, roster: identity.roster, localHost: identity.localHost, agentSendPath: identity.agentSendPath, }); } /** * Read and resolve the installed roster for runtime composition. A requested * fleet identity fails closed; only a genuinely non-fleet launch (no selfName) * is a quiet no-op. */ export function readFleetCommsBlock( mosaicHome: string, selfName: string | undefined, localHost: string = shortHostname(), ): CommsBlockResult { const resolved = resolveFleetIdentity(mosaicHome, selfName, localHost); if (!resolved.ok) return { ok: false, output: '', error: resolved.error }; if (!resolved.identity) return { ok: true, output: '' }; try { return { ok: true, output: buildResolvedFleetCommsBlock(resolved.identity) }; } catch (error) { return { ok: false, output: '', error: error instanceof Error ? error.message : String(error), }; } } /** Backing resolver for `mosaic agent comms-block `. */ export function resolveCommsBlock( mosaicHome: string, exactMember: string | undefined, ): CommsBlockResult { if (!exactMember) { return { ok: false, output: '', error: 'comms-block requires an exact argument', }; } return readFleetCommsBlock(mosaicHome, exactMember); } function expectedContractVersion(content: Buffer | string): boolean { return content.toString().includes(``); } function boundedContractDigest( path: string, mosaicHome: string, ): { digest?: string; versionOk: boolean } { try { const content = readRegularFileSecure(path, { root: mosaicHome, maxBytes: MAX_TOOLS_CONTRACT_BYTES, }).content; return { digest: createHash('sha256').update(content).digest('hex'), versionOk: expectedContractVersion(content), }; } catch { return { versionOk: false }; } } function replacementGuidance(): string { return `Run \`mosaic update --repair-tools\` to make a digest-qualified backup and restore the supported current-version TOOLS contract, then have an authorized operator explicitly relaunch the exact roster member. The active context was not rewritten.`; } /** Detect preserved installed TOOLS.md drift without changing it. */ export function renderToolsContractStatus(mosaicHome: string): string { const installedPath = join(mosaicHome, 'TOOLS.md'); const sourcePath = join(mosaicHome, 'defaults', 'TOOLS.md'); if (!existsSync(installedPath)) { return `# Fleet Comms Installation Status\n\nInstalled TOOLS.md is missing at \`${installedPath}\`. ${replacementGuidance()}`; } const installed = boundedContractDigest(installedPath, mosaicHome); const source = boundedContractDigest(sourcePath, mosaicHome); if (!source.digest || !source.versionOk) { return `# Fleet Comms Installation Status\n\nThe bounded framework source contract at \`${sourcePath}\` is unavailable or does not declare the expected \`${FLEET_COMMS_TOOLS_CONTRACT}\` version. Run \`mosaic update\` to restore framework source data, verify again, then have an authorized operator explicitly relaunch the exact roster member. The installed file and active context were not rewritten.`; } if (installed.versionOk && installed.digest === source.digest) return ''; return `# Fleet Comms Installation Status\n\nInstalled TOOLS.md is unavailable, has the wrong contract version, or does not byte-match the bounded framework source contract \`${FLEET_COMMS_TOOLS_CONTRACT}\`. ${replacementGuidance()}`; } export const DEFAULT_MOSAIC_HOME_FOR_COMMS = join(homedir(), '.config', 'mosaic');