TETHER: Supervising a Real Agent Run

An agent that can send email, spend money and call people is not a demo problem anymore. It is a supervision problem. Fo's MCP surface is five tools: ask_fo, get_fo_reply, list_fo_conversations, get_fo_conversation, start_fo_call. Calling them is easy. Knowing what happened after you called them is not, and that gap is the product.

TETHER is my answer: an installable web app that supervises a real Fo run from the moment you submit a task to the moment it ends. I built it over a weekend for the GSD hackathon. This post is how it works and why it works that way.

The dispatch problem

The reload case is where most agent clients quietly lie, so here is exactly what TETHER does. The dangerous window is a dispatch that might have succeeded: the request left, the network died, no conversation id came back. Retrying there can duplicate a real action, and Fo's actions are real. A duplicate is not a glitch. It is a second email to a real person.

So the rule is mechanical. One ask_fo per submission. The run is persisted before anything is dispatched. If the outcome is ambiguous, the run moves to outcome_unknown and stays there until a human looks at it. There is no automatic replay path anywhere in the code. A local idempotency key stops double clicks, and that is all it claims to do.

State itself is a small machine: draft, dispatching, working, waiting_for_user, back to working, completed. The failure branches are first class: failed as Fo reported it, connection_interrupted, outcome_unknown, locally_stopped_observing. That last one matters more than it looks. TETHER stopping its polling says nothing about Fo stopping, and confusing the two is how monitoring UIs end up telling comforting lies. Reload the page mid-run and you come back to the same run: TETHER re-attaches to the existing conversation, read-only, and never sends the task again.

Reported versus observed

Every run ends in two panels. One holds what Fo reported: its status, its text, its own account of the work. The other holds what TETHER observed from this side of the wire: timestamps and state transitions. Terminal outcomes carry the label "Fo-reported; not independently verified". You cannot grade an action agent on its final text, because the world state is the actual result. The two-panel split is that point enforced as UI rather than mentioned in a footnote.

Presence without pretending

Pip, the mascot, has a fixed animation set: idle, notice, thinking, needs_you, success_soft, sleep. Each one maps straight from application state. Pip has no mood model, never celebrates before a terminal response exists, and never shows a state the app is not actually in. The island is the same idea at window level: a small pill carrying live status and Fo's current question above whatever else is open. It is a web feature that runs anywhere the browser does. A native island is on the roadmap and does not exist.

The stack, on purpose

Node 22, plain ES modules, two runtime dependencies: the MCP SDK and zod. The client is vanilla JavaScript, installable as a PWA on phone, tablet and desktop. The backend listens on loopback on the owner's machine. The public demo is a static build with simulated fixture runs: no live API origin, no credentials, no way to wire it to a real Fo. What you can try today is the supervision, not my agent.

What the tests prove

The suite sits at 70 passing tests. The ones I care about: duplicate submit blocked, two poll loops coalesced into one, a stale working response arriving after a terminal state and getting rejected, reload during dispatch. They run against a fake MCP adapter, deterministically, and they prove TETHER behaves. They prove nothing about Fo, which is why they are reported separately from live results.

What is not done

Devices are the honest frontier. MQTT runs live in the demo; Home Assistant and Bluetooth heart rate exist as device-ready code, untested on real hardware. Voice through start_fo_call and the wider device mesh are designed and deliberately not built this sprint. MCP compatibility does not make a device compatible; every surface needs a real adapter tested on real hardware.

TETHER is an unofficial companion project for Fo, not affiliated with or endorsed by Wajo.

Repo: https://github.com/yethikrishna/tether

Demo: https://tether-demo-ey9.pages.dev/src/client/

← back to the journal