In this tutorial, you’ll learn how to resurrect one of the first chatbots in computer science history—the famous Eliza, originally created in the 1960s! This not only pays homage to computing’s past but also demonstrates how the Agent Wire Protocol (AWP) can integrate seamlessly with any protocol. Let’s get started.

Prerequisites

Make sure to have the following installed:

Running Eliza via Docker

Let’s run Eliza in a Docker container and connect to her via telnet.

# Pull and run the Eliza Telnet server
docker build -t eliza:latest https://docs.agentwire.io/quickstart/eliza/Dockerfile
docker run --rm -it -p 2323:2323 eliza:latest

Once the container is running, we can connect to Eliza.

telnet localhost 2323

You should see Eliza greet you with:

$ telnet localhost 2323
Trying ::1...
Connected to localhost.
Escape character is '^]'.
=====================================================
ELIZA - The first chatbot (1966) by Joseph Weizenbaum
Running via Telnet - Type 'quit' to exit
=====================================================

Hello, I'm Eliza. How can I help you today?
> How are you?
What if I were ?
> What if you were?
What does this speculation lead to ?
> I am not sure to be honest.
Do you enjoy being not sure to be honest ?
> quit
Goodbye. It was nice talking to you!
Connection closed by foreign host.

Type quit to exit Telnet. Congratulations! You’ve just brought Eliza to life. Now let’s connect her to the Agent Wire Protocol.

Bridge Eliza with AWP

Now that we have our Eliza server running, let’s show how AWP can connect to any protocol—in this case, the somewhat arcane Telnet protocol.

Project Setup

Let’s create a new project for our AWP bridge:

# Create a directory for your project
mkdir awp-eliza && cd awp-eliza
npm init -y
npm install @agentwire/client telnetlib
npm install -D typescript ts-node @types/node @types/telnetlib
npx tsc --init --target ES2020 --module CommonJS --strict --esModuleInterop --skipLibCheck --outDir dist --rootDir src
mkdir src

AWP Recap

The Agent Wire Protocol is a framework that lets you connect agents to clients in a structured, event-driven way. Agents implement the AbstractAgent class from @agentwire/client and emit events for each piece of content, progress, etc.

ElizaAgent

We’ll create a special agent class, ElizaAgent, which:

  1. Launches a Telnet client to connect to our running Eliza server on localhost:2323.
  2. Sends the user’s last message to Eliza.
  3. Waits for the response, then emits AWP events to deliver that text back.

Create a file called src/eliza-agent.ts:

// src/eliza-agent.ts
import {
  AbstractAgent,
  RunAgent,
  RunAgentInput,
  EventType,
  BaseEvent,
  RunStartedEventSchema,
  TextMessageStartEventSchema,
  TextMessageContentEventSchema,
  TextMessageEndEventSchema,
  RunFinishedEventSchema,
} from "@agentwire/client"
import { Observable } from "rxjs"
import * as telnetlib from "telnetlib"

export class ElizaAgent extends AbstractAgent {
  protected run(input: RunAgentInput): RunAgent {
    // AWP calls `run` each time it wants to generate a response.
    return () => {
      // Extract user's latest message

      const lastUserMessage =
        [...input.messages].reverse().find((msg) => msg.role === "user")
          ?.content || "Hello, Eliza"

      return new Observable<BaseEvent>((observer) => {
        // 1) Emit RUN_STARTED
        observer.next(
          RunStartedEventSchema.parse({
            type: EventType.RUN_STARTED,
            threadId: input.threadId,
            runId: input.runId,
          }),
        )

        // Create a new telnet client
        const client = telnetlib.createConnection(
          {
            host: "127.0.0.1",
            port: 2323,
          },
          () => {
            console.log("Connected to Eliza telnet server")
          },
        )

        // Wait for initial greeting and prompt
        client.once("data", () => {
          // Once we receive the greeting, send the user message
          client.write(lastUserMessage + "\n")

          // Wait for the response from Eliza
          client.once("data", (data) => {
            // Get the response, removing the prompt
            let response = data.toString().trim()
            // Strip the trailing prompt if present
            if (response.endsWith(">")) {
              response = response.substring(0, response.length - 1).trim()
            }

            // Generate a message ID
            const messageId = Date.now().toString()

            // Emit typical AWP message events
            observer.next(
              TextMessageStartEventSchema.parse({
                type: EventType.TEXT_MESSAGE_START,
                messageId,
              }),
            )

            observer.next(
              TextMessageContentEventSchema.parse({
                type: EventType.TEXT_MESSAGE_CONTENT,
                messageId,
                delta: response,
              }),
            )

            observer.next(
              TextMessageEndEventSchema.parse({
                type: EventType.TEXT_MESSAGE_END,
                messageId,
              }),
            )

            // Emit RUN_FINISHED and complete
            observer.next(
              RunFinishedEventSchema.parse({
                type: EventType.RUN_FINISHED,
                threadId: input.threadId,
                runId: input.runId,
              }),
            )

            // Close the telnet connection
            client.end()

            observer.complete()
          })
        })

        // Handle errors
        client.on("error", (err) => {
          console.error("Telnet client error:", err)
          observer.error(err)
        })

        // Return a cleanup function
        return () => {
          if (client) {
            client.end()
          }
        }
      })
    }
  }
}

Explanation

  1. Connection We create a TelnetClient and connect to our Eliza Telnet server.

  2. User Message We find the last user message from the AWP input. If none exists, we default to “Hello, Eliza”.

  3. Emitting Events

    • RUN_STARTED means the agent started processing the request.
    • TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END together send the agent’s textual response to the AWP pipeline.
    • RUN_FINISHED means this run is over.
  4. Round-Trip

    • Write the user message to Eliza over Telnet.
    • Listen for the response from Eliza.
    • Emit that response as AWP events.

Putting It All Together

Create a main entry point file src/index.ts that sets up our AWP agent:

// src/index.ts
import { ElizaAgent } from "./eliza-agent"

// Create an instance of our agent
const elizaAgent = new ElizaAgent()
elizaAgent.messages = [
  { id: "1", role: "user", content: "I feel anxious about my job." },
]

// Example: Make a request to Eliza through AWP
const run = elizaAgent.runAgent({
  runId: "run1",
})

Run the application:

npx ts-node src/index.ts

Bridging AWP to Any Protocol

This example demonstrates how to bridge AWP with an existing protocol. The key components are:

  1. AWP Agent Implementation: Create a class that extends AbstractAgent and implements the run method.
  2. Implement run(): Set up an agent or connect to an existing protocol.
  3. Manage agent input: Send the right input to the agent (in our case, it’s the user’s last message).
  4. Send events: Handle the agent’s response and emit AWP events.

You can apply this pattern to connect AWP to virtually any protocol or service:

  • HTTP/REST APIs
  • WebSockets
  • MQTT for IoT devices
  • any other protocol

By following this pattern of creating protocol adapters around your AWP agents, you can make your AI agents available through any communication channel while maintaining a consistent agent implementation.

Conclusion

Congratulations! You’ve resurrected the first historical chatbot by serving Eliza over Telnet and bridged it to the Agent Wire Protocol. This tutorial demonstrates how easy it is to adopt AWP with any existing protocol—be it Telnet, WebSocket, custom REST calls, or something entirely else.