packages feed

pms-infra-agent-socket-0.0.2.0: README.md

# pms-infra-agent-socket

----
## ⚠️ Caution

**Do not grant unrestricted control to AI.**  
Unsupervised use or misuse may lead to unintended consequences.  
All AI systems must remain strictly under human oversight and control.  
Use responsibly, with full awareness and at your own risk.  

----
## πŸ“˜ Overview

**`pms-infra-agent-socket`** is a Haskell infrastructure library that provides AI agents with direct, low-level control over socket-based communication endpoints.

Unlike higher-level abstractions, this library exposes raw byte-stream access over both TCP/IP and Unix Domain Sockets, allowing agents to handle protocol-specific exchanges such as Telnet IAC negotiation, binary protocol framing, and real-time I/O monitoring with fine-grained control.

The library is a core component of the [`pty-mcp-server`](https://github.com/phoityne/pty-mcp-server) ecosystem and implements the `agent-socket-*` family of MCP tools.

---

## πŸ”§ Provided MCP Tools

### `agent-socket-open`
Opens a socket connection for subsequent read/write operations.  
Supports both **TCP** (`host` + `port`) and **Unix Domain Socket** (`file`) connections via a single tool.

- `host` β€” TCP host name or IP address  
- `port` β€” TCP service name or port number  
- `file` β€” Unix domain socket path (absolute path required on Windows)  

Only one socket connection can be active at a time.

### `agent-socket-close`
Closes the active socket connection and releases all associated resources.

### `agent-socket-read`
Reads up to the specified number of bytes from the active socket connection and returns the data as a **UTF-8 string**.  
Returns an empty string if no data is available before timeout.

- `length` β€” Maximum number of bytes to read  

> ⚠️ If the received data contains non-UTF-8 bytes (e.g., binary protocols, IAC bytes), use `agent-socket-read-byte` instead.

### `agent-socket-read-byte`
Reads up to the specified number of bytes from the active socket connection and returns the data as an **uppercase hex string** (e.g., `FF0A1B41`).  
Use this for binary protocols or when precise byte-level inspection is required.

- `length` β€” Maximum number of bytes to read  

### `agent-socket-write`
Writes the specified **UTF-8 string** to the active socket connection.

- `data` β€” Text data to write  

> ⚠️ Escape sequences such as `\r\n` in the string are sent as literal characters. To send exact byte sequences (e.g., CR+LF = `0D0A`), use `agent-socket-write-byte`.

### `agent-socket-write-byte`
Decodes the specified **hex string** and writes the resulting bytes to the active socket connection.  
Use this for binary protocols or when precise byte-level control is required.

- `data` β€” Uppercase hex string to decode and write (e.g., `DEADBEEF0D0A`)  

---

## πŸ’‘ Usage Notes

### AI-to-AI communication

Any two AI agents that each have a `pty-mcp-server` instance can communicate with each other directly over TCP β€” no shared infrastructure, message broker, or custom integration required.

One agent uses the `agent-server-*` tools to act as a TCP server; the other uses the `agent-socket-*` tools (this library) to act as a TCP client. The agents exchange messages using a lightweight text protocol, and each agent interprets and responds to the other's messages autonomously.

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        AI Agent A           β”‚         β”‚        AI Agent B           β”‚
β”‚  (e.g. Claude on Machine A) β”‚         β”‚  (e.g. GPT on Machine B)    β”‚
β”‚                             β”‚         β”‚                             β”‚
β”‚  pty-mcp-server             β”‚  TCP    β”‚  pty-mcp-server             β”‚
β”‚  agent-server-listen :19999 │◄───────►│  agent-socket-open          β”‚
β”‚  agent-server-write         β”‚         β”‚  agent-socket-read          β”‚
β”‚  agent-server-events        β”‚         β”‚  agent-socket-write         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         Server role                              Client role
```

#### What this enables

- **Collaborative task solving** β€” Agent A breaks down a problem and delegates subtasks to Agent B, collecting results over the socket.
- **Cross-model review** β€” One model generates code or text; another model on a different machine reviews it and sends back comments.
- **Heterogeneous agent pipelines** β€” Chain agents of different models or specialisations (Claude, GPT, Gemini, …) into a processing pipeline across machines.
- **Autonomous negotiation** β€” Agents can exchange proposals, counter-proposals, and decisions without human involvement in each round-trip.
- **Distributed tool use** β€” Agent B may have access to tools (databases, sensors, local files) that Agent A does not. Agent A requests operations from Agent B over the socket.

#### Communication protocol

The `pty-mcp-server` ecosystem ships a lightweight handshake and messaging protocol for AI-to-AI sessions. Prompt skills (`skill_agent_server.md` / `skill_agent_client.md`) are provided so each agent knows exactly how to play its role.

**Handshake sequence:**
```
Server β†’ Client : HELLO? name?\r\n
Server ← Client : NAME: <name>\r\n
Server β†’ Client : RULES: MSG:<content>\r\n | REPLY:<content>\r\n | BYE\r\n | HEX:<hex>\r\n
Server ← Client : ACK\r\n
```

**Conversation:**
```
Server β†’ Client : MSG: <content>\r\n
Server ← Client : REPLY: <content>\r\n
```

**Graceful shutdown (server-initiated):**
```
Server β†’ Client : BYE\r\n
Server ← Client : ACK\r\n    ← server waits for this before closing
Server           : agent-server-close
```

#### Full session example

The following shows a complete exchange where Agent A (server role) delegates a task to Agent B (client role).

**Agent A β€” server side**
```
agent-server-listen host=172.16.0.43 port=19999
  β†’ "listening."

--- Agent B connects ---

agent-server-events
  β†’ [{ "tag": "ClientConnected" }]

agent-server-write-byte  "48454C4C4F3F206E616D653F0D0A"   ("HELLO? name?\r\n")

agent-server-events  (poll until BytesReceived)
  β†’ bytes: "4E414D453A20416765742D420D0A"   ("NAME: Agent-B\r\n")

agent-server-write-byte  "<hex of RULES: MSG:... | REPLY:... | BYE\r\n>"

agent-server-events  (poll for ACK)
  β†’ bytes: "41434B0D0A"   ("ACK\r\n")

--- handshake complete ---

agent-server-write-byte  "<hex of MSG: Please summarise this text: ...\r\n>"

agent-server-events  (poll for REPLY)
  β†’ bytes: "<hex of REPLY: Here is the summary: ...\r\n>"

agent-server-write-byte  "<hex of BYE\r\n>"

agent-server-events  (poll for ACK)
  β†’ bytes: "41434B0D0A"   ("ACK\r\n")

agent-server-close
```

**Agent B β€” client side**
```
agent-socket-open host=172.16.0.43 port=19999
  β†’ socket connected to 172.16.0.43:19999

agent-socket-read length=256
  β†’ "HELLO? name?\r\n"

agent-socket-write-byte  "<hex of NAME: Agent-B\r\n>"

agent-socket-read length=256
  β†’ "RULES: MSG:<content>\r\n | REPLY:<content>\r\n | BYE\r\n | HEX:<hex>\r\n"

agent-socket-write-byte  "<hex of ACK\r\n>"

--- handshake complete ---

agent-socket-read length=1024
  β†’ "MSG: Please summarise this text: ...\r\n"

--- Agent B processes the request autonomously ---

agent-socket-write-byte  "<hex of REPLY: Here is the summary: ...\r\n>"

agent-socket-read length=256
  β†’ "BYE\r\n"

agent-socket-write-byte  "<hex of ACK\r\n>"

agent-socket-close
```

> πŸ’‘ Always use `agent-socket-write-byte` (not `agent-socket-write`) to ensure `\r\n` is sent as correct CRLF bytes.  
> Generate hex strings with: `python3 -c "print('your message\r\n'.encode().hex())"`

---

### IAC / Telnet negotiation
Telnet IAC processing is the **responsibility of the agent**, not the library.  
Use `agent-socket-read-byte` and `agent-socket-write-byte` to handle IAC sequences at the byte level.

Example negotiation flow:
```
← FFFD18FFFD20FFFD23FFFD27   (Server: IAC DO TERMINAL-TYPE/SPEED/X-DISPLAY/NEW-ENVIRON)
β†’ FFFC18FFFC20FFFC23FFFC27   (Agent:  IAC WONT x4)
← FFFB03FFFD01FFFD1FFFFB05   (Server: IAC WILL SGA / DO ECHO / DO NAWS / WILL STATUS)
β†’ FFFD03FFFB01FFFC1FFFFD05   (Agent:  IAC DO SGA / WILL ECHO / WONT NAWS / DO STATUS)
← "localhost login: "         (Login prompt β€” read as UTF-8 string)
β†’ "phoityne\r\n"              (Username β€” send as bytes: 70686F6974796E650D0A)
← "Password: "                (Password prompt)
```

Full login session example (confirmed with a real Linux Telnet server):
```
agent-socket-open host=172.16.0.171 port=23
  β†’ socket connected to 172.16.0.171:23

← FFFD18FFFD20FFFD23FFFD27
    (IAC DO TERMINAL-TYPE / DO TERMINAL-SPEED / DO X-DISPLAY / DO NEW-ENVIRON)
β†’ FFFC18FFFC20FFFC23FFFC27
    (IAC WONT x4)

← FFFB03FFFD01FFFD1FFFFB05FFFD21
    (IAC WILL SGA / DO ECHO / DO NAWS / WILL STATUS / DO REMOTE-FLOW-CONTROL)
β†’ FFFD03FFFB01FFFC1FFFFD05FFFC21
    (IAC DO SGA / WILL ECHO / WONT NAWS / DO STATUS / WONT REMOTE-FLOW-CONTROL)

← FFFE01FFFB01 + "Kernel 6.12.0-...\r\nlocalhost login: "
β†’ 61692D6167656E740D0A   ("ai-agent\r\n"  β€” username as bytes)

← "ai-agent\r\nPassword: "
β†’ 61692D6167656E740D0A   ("ai-agent\r\n"  β€” password as bytes)

← "Last login: ...\r\n[ai-agent@localhost ~]$ "

β†’ 686F73746E616D650D0A   ("hostname\r\n")
← "hostname\r\nlocalhost.localdomain\r\n[ai-agent@localhost ~]$ "

β†’ 657869740D0A   ("exit\r\n")
← "exit\r\nlogout\r\n"

agent-socket-close
  β†’ socket is closed.
```

### Unix Domain Socket on Windows
Unix Domain Socket (`file` parameter) is supported on **Windows 10 build 1803 and later** at the OS level.  
However, the socket server side must be implemented in a runtime that supports `AF_UNIX` on Windows.

- **Python 3.13.7 (Windows)**: `socket.AF_UNIX` is unavailable unless **Developer Mode** is enabled.
- **PowerShell / .NET**: `System.Net.Sockets.UnixDomainSocketEndPoint` works without Developer Mode.
- **pty-mcp-server (Haskell)**: Confirmed working on Windows 11 via `Network.Socket` with `AF_UNIX`.

---

## πŸ—οΈ Architecture

### Module Structure

```
PMS.Infra.Agent.Socket
β”œβ”€β”€ DM.Type        -- Data type definitions (SocketData, AppData, tool parameter types)
β”œβ”€β”€ DM.Constant    -- Constants
β”œβ”€β”€ DS.Core        -- Core domain service logic
β”œβ”€β”€ DS.Utility     -- Utility functions (TCP connect, Unix Domain Socket connect)
└── App.Control    -- Application control: tool dispatch and socket lifecycle management
```

### Key Design Points

- **Single active connection**: Only one socket can be open at a time per server instance. State is managed via `STM.TMVar`.
- **Dual connection mode**: `agent-socket-open` supports both TCP (`host`/`port`) and Unix Domain Socket (`file`) via a single unified interface.
- **Non-blocking reads**: Read operations return immediately with available data, allowing the agent to poll at its own pace.
- **Byte-level I/F**: In addition to UTF-8 string I/F, hex string I/F is provided for binary protocol handling. IAC processing and protocol framing are delegated to the agent.

---

## πŸ“¦ Dependencies

- [`pms-domain-model`](https://github.com/phoityne/pms-domain-model)

---

## πŸ“œ Credits & License

- **Execution & Process Lead:** Claude Sonnet 4.6, Gemini 3 Flash, GPT-5.5
- **Direction & Policy:** phoityne
- **License:** Apache-2.0 β€” see [LICENSE](./LICENSE)

---