Welcome back!
In Chapter 4: Message Routing Logic, we built the "Sorting Facility" that decides where a message goes. We handled local teammates (via files) and sub-agents (via memory).
But what happens when the recipient isn't "in the building"?
In this chapter, we explore Cross-Boundary Transport. This is how an agent sends a message to a completely different terminal window or even a different computer entirely.
Up until now, our agents have been like people in a locked room passing notes. They share a file system, so they can just leave files for each other.
But imagine this use case: You have an agent running on your Laptop. You have another agent running on a Cloud Server. You want the Laptop Agent to tell the Cloud Agent: "Deploy the website."
They don't share a hard drive. Leaving a file on the laptop won't help the server. We need a "wire" to connect them.
We support two types of "Long-Distance" calling:
How does the AI know how to dial these numbers? It uses a specific address format.
Usually, the AI finds these addresses using a tool called ListPeers (which discovers active sessions), but for SendMessage, we just need to know the format:
uds:/tmp/sock_file_123bridge:session_01AbCd...
When the AI types this into the to field, our tool knows exactly what to do.
Sending data over the internet to a remote session is risky. We don't want an AI accidentally sending sensitive code to the wrong server.
Therefore, Bridge messages require explicit human permission.
Here is the code inside checkPermissions that enforces this:
// Inside checkPermissions
async checkPermissions(input, _context) {
// Check if the address scheme is "bridge"
if (parseAddress(input.to).scheme === 'bridge') {
return {
behavior: 'ask', // STOP! Ask the human.
message: `Send message to Remote Control session ${input.to}?`,
// Security note: We force this check even in "Auto-Mode"
decisionReason: {
type: 'safetyCheck',
reason: 'Cross-machine bridge message requires explicit user consent'
}
}
}
// ... allow other types ...
}
Explanation:
bridge:, we return behavior: 'ask'.In Chapter 3: Structured Coordination Protocols, we learned about complex JSON objects for shutdowns and approvals.
We cannot send those over the bridge.
Why? Because coordinating state (like killing a process) across different computers is extremely complex and error-prone. We keep it simple: Text Only.
// Inside validateInput
if (parseAddress(input.to).scheme === 'bridge') {
// Rule: Message MUST be a string
if (typeof input.message !== 'string') {
return {
result: false,
message: 'structured messages cannot be sent cross-session โ only plain text',
}
}
}
What happens: If the AI tries to send a "Shutdown Request" to a remote server, the tool rejects it immediately with an error. The AI must send a polite text asking the remote agent to shut itself down instead.
If the address starts with uds:, we use a local socket client.
// Inside call() - UDS logic
if (addr.scheme === 'uds') {
// Import the socket client
const { sendToUdsSocket } = require('../../utils/udsClient.js')
try {
// Push the data into the pipe
await sendToUdsSocket(addr.target, input.message)
return { data: { success: true, message: `Sent to ${input.to}` } }
} catch (e) {
return { data: { success: false, message: `Failed: ${e.message}` } }
}
}
Explanation:
addr.target is the file path (e.g., /tmp/mysocket).
If the address starts with bridge:, we use the internet relay.
// Inside call() - Bridge logic
if (addr.scheme === 'bridge') {
// Check if we are still connected
if (!getReplBridgeHandle()) {
return { data: { success: false, message: 'Remote disconnected' } }
}
// Import the bridge handler
const { postInterClaudeMessage } = require('../../bridge/peerSessions.js')
// Send the payload to the cloud
const result = await postInterClaudeMessage(addr.target, input.message)
return {
data: {
success: result.ok,
message: result.ok ? 'Message Sent' : 'Failed'
}
}
}
Explanation:
getReplBridgeHandle() to ensure we are online.Let's look at the journey of a message from your Laptop to a Server.
We have now completed the functional core of the SendMessageTool.
The tool works! But there is one final piece of the puzzle. When the tool finishes, it returns a result to the AI. But how do we show this result to the Human User in a way that looks nice in the terminal?
In the final chapter, we will learn how to create beautiful, color-coded output.
Next Chapter: User Interface Presentation
Generated by Code IQ