ScreenMode Preferences on a Workbench screen in WinUAE, captured by AmiPilot.

28 September 2026

How we got AI to puppeteer the Amiga

Not playing the games. Driving the machine: click Save, swap a floppy, read a file back, and know the desktop survived a reboot — from a script an AI agent can run.

The AmiPilot note explained the eyes: a server inside the running Amiga that can click, type, read files, and capture the screen. This note is about the hands on the host side — and about the week we spent learning that “TCP port open” is not the same as “the Amiga is listening.”

Puppeteering, for us, means what Selenium does for a web page, but for Workbench 3.1. An AI coding agent (Cursor, a Python script, an acceptance runner) should be able to boot the acceptance disk, wait until the guest is genuinely ready, drive a WHDLoad Install drawer, insert the next ADF when the requester appears, and read back proof that the files landed where they ought to. Not one protocol. A stack of them, each honest about what it can and cannot do.

WinUAE’s serial bridge accepts a connection before the Amiga has opened serial.device. Connect too early and you get a session that looks half alive.

That was the trap that ate an evening. The host saw port 1234 listening. screens() sometimes answered. TREE "Workbench Screen" returned RC 5. launch() wrote nothing. Readiness scripts timed out on server-launching because they were waiting for a log file that is not the right witness while the server is still running. The fix was already in the acceptance script and we had not carried it everywhere: wait forty-five seconds after WinUAE starts before the first AmiPilot connect, then probe with handshake() and fs_list("SYS:"), not merely “socket up.”

A freshly booted Workbench screen with Scalos as a window, before backdrop is configured.
Before the checks pass: Scalos as a window, not a backdrop. AmiPilot captures this; the script fixes it.
WinUAE showing a Software Failure guru meditation.
When the guest crashes, AmiPilot goes down with it. The host screenshot is the witness.

Four layers, not one magic wire

AmiPilot is the guest automation server: menus, clicks, launch, fs_get. On quo it rides WinUAE’s TCP serial bridge at 127.0.0.1:1234, not the guest’s optional TCP listener — bsdsocket emulation is fine for Amiga clients, unreliable for host-visible servers.

WinUAE’s named pipe (\\.\pipe\WinUAE) is the emulator’s own control surface: insert floppy0, soft reset, IPC_QUIT so the hardfile flushes. WHDLoad live installs need AmiPilot and the pipe together: the Install drawer is UI; the disk swap is F12’s job on the host.

Host screenshots crop the WinUAE client when AmiPilot’s own capture hits size caps, or when WHDLoad owns the machine and the OS server is paused.

Shell I/O (sidecar, not a replacement): AmiPilot still drives the UI. A Rexx job queue (openclaw-amiga-bridge) runs clawd.rexx on the guest: host drops *.job, the daemon executes AmigaDOS, results land in out/*.out and *.res. Entry point: amiga_dos.py / amiga_bridge.py send. On WinUAE the queue lives on SYS:Docs/AmiPilot/bridge/ via AmiPilot fs_put — not a host folder drop on Agent: (directory mounts do not reliably sync List). Thomas Lübker’s amiagent adds the full MCP toolbelt when guest TCP :7846 is reachable (MiSTer; flaky on WinUAE).

Launching WinUAE where a human can see it

GUI programs started from bare SSH land in Windows session 0 and never get a desktop. The acceptance rig launches through an Interactive scheduled task so WinUAE appears in the logged-in RDP session. User-facing entry points:

~/startAmiga.sh           # acceptance disk
~/startInstallAmiga.sh    # WHDLoad install rig (+ Source: mount)

Both load a .uae with use_gui=no so emulation auto-starts. Do not pass -s use_gui=yes: that opens WinUAE’s config panel and stops at the Start button, waiting for a click no script should need.

Three traps that look like broken emulation

Scalos Backdrop has no window title. When the disk boots into backdrop mode, TREE "Workbench Screen" fails with RC 5. That is expected. Use menu("Scalos"), opendrawer, or file reads — the same lesson as the Scalos component note.

Nested directory mounts crash WinUAE. Mounting build/ as Source: and build/WHDLoad-games/ as a second volume nested inside it produced exit code 0xC0000005 and no window at all. One mount only; install to Source:WHDLoad-games/UserTools.

Cygwin Python cannot hold the pipe. winuae_pipe_cmd.ps1 is the backend; winuae_driver.py delegates to it automatically on quo.

The recipe that works

From the build host, with WinUAE visible in RDP:

cd ~/amiga-utils
# one-time on acceptance HDF (clawd on SYS:, queue base SYS:Docs/AmiPilot/bridge):
./scripts/install_agent_bridge.sh build/scripted-smoke/Workbench-acceptance.hdf

~/startInstallAmiga.sh
# wait — do not connect AmiPilot for 45 seconds
.venv-amipilot/bin/python scripts/wait_for_amipilot_guest.py \
  --port 1234 --startup-delay 45 --timeout 180
AMIGA_BRIDGE_BACKEND=amipilot python3 scripts/wait_for_agent_bridge.py --timeout 120
AMIGA_BRIDGE_BACKEND=amipilot python3 scripts/amiga_dos.py "List SYS:"
./scripts/install_whdload_games.sh --game another-world

If anything looks wedged: quit WinUAE through the pipe, relaunch, wait the full forty-five seconds, connect once. Polling AmiPilot during boot is how you poison the bridge.

Debugging the Rexx bridge (28 September 2026)

The afternoon was a lesson in layered failure modes. AmiPilot’s UI path worked from the first clean boot. The openclaw Rexx sidecar did not — and every symptom looked like a different bug until we added step logging to clawd.rexx and read the guest trace line by line.

Boot traps (System Request dialogs). (1) “Please insert volume Agent” — the acceptance .uae had Source/WHDLoad mounts but no Agent: filesystem slot, and startup wrote to Agent: before the mount existed. (2) “Please insert volume >NIL” — a typo in the Rexx launch line: Run >NIL: instead of Run <NIL: >NIL:. Both fixed in install_agent_bridge.sh and the acceptance config refresh.

Host folder drop dead end. The author’s design assumes a shared folder: the host writes *.job into in/, clawd.rexx polls with List. On WinUAE’s directory mount (Agent: → Cygwin path) the guest often never sees host-created files; jobs piled up in Agent:in/ while clawd.log showed only clawd started. AmiPilot Execute probes on List Agent:in hung for minutes — the shell was wedged by a stuck first job.

ARexx landmines. Step logging narrowed the hang to one line at a time: Lower() calls AmigaOS’s missing LOWER() builtin; SanitizeOneLine had literal newlines baked into the source; WriteTextFile still called RexxUtil’s Changestr. A corrupted duplicate CALL Log line after exec rc= stopped the interpreter silently. Fix: patch_clawd_rexx.py rewrites the vendored script for 3.x, and .res files are written with Echo >> instead of Rexx OPEN.

The working queue. Move job I/O to the guest HDF: SYS:Docs/AmiPilot/bridge/{in,out,log}. AmiPilot drops jobs with fs_put, reads results with fs_get — the same serial bridge as the UI, no WinUAE mount sync. Drop the job, then launch RX clawd.rexx SYS:Docs/AmiPilot/bridge (the listener often exits after one job on WinUAE). End-to-end proof on quo:

AMIGA_BRIDGE_BACKEND=amipilot python3 scripts/amiga_bridge.py send \
  --cmd "Echo bridge-finish-ok" --wait --json
# ok=1  stdout=bridge-finish-ok

AMIGA_BRIDGE_BACKEND=amipilot python3 scripts/amiga_dos.py "Version FULL" --json
# Kickstart 40.68, Workbench 40.42

collect_puppeteer_logs.sh and debug_agent_bridge_amipilot.py bundle host queue state plus guest bridge-boot.log — use them whenever a boot-time dialog flashes past before Workbench settles.

What is sorted, what is not

Working now: AmiPilot UI automation; launch scripts; boot log collection; WinUAE pipe; Rexx bridge via AmiPilot SYS: queue (amiga_bridge.py, amiga_dos.py, wait_for_agent_bridge.py with AMIGA_BRIDGE_BACKEND=amipilot); patched clawd.rexx on SYS:Docs/AmiPilot/.

Still open: bake SYS: bridge base into the acceptance HDF boot path (re-run install_agent_bridge.sh on the hardfile); every live WHDLoad install in the catalog; game launch proof while WHDLoad owns the CPU. Legacy Agent: host folder drop remains for MiSTer-style shared volumes — not reliable on WinUAE.

Scripts: amiga-utils. AmiPilot: sidick/amipilot. Bridge upstream: openclaw-amiga-bridge. The component deep-dive is The eyes. The words are ours.