cd /news/ai-agents/streaming-a-desktop-agent-to-your-ph… Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-98529] src=dev.to β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

Streaming a desktop agent to your phone with zero npm dependencies

A developer built DeepSeek Phone Harness, a mobile remote for DeepSeek Harness, using zero npm dependencies and a single Node process. The tool streams desktop agent events to a phone via WebSocket and REST, and the developer highlighted key architectural decisions, including a unified event stream and a fix for a deadlock caused by separate channels for approvals and questions.

read2 min views1 publishedAug 16, 2026

One WebSocket, one Node process, one HTML file. Here's the architecture behind DeepSeek Phone Harness, and the three decisions that made it work.

Last weekend I built DeepSeek Phone Harness β€” a mobile remote for DeepSeek Harness. The constraint I set myself: zero npm dependencies. Not because I'm a purist, but because a tool like this should be "clone and run," not "install 47 packages and hope."

Here's the architecture that made it work.

Phone browser (relay.html)
   β”‚  HTTPS / Tailscale
   β–Ό
Agent (Node, :8788)  ── http server + static page + WS relay
   β”‚ 127.0.0.1:3080
   β–Ό
DeepSeek Harness gateway (dsh web)

The Agent is a translator, not a replacement: it speaks REST to the phone and RPC to DSH.

DSH exposes events.mux

β€” a WebSocket stream carrying approvals, questions, and session events in one pipe. Instead of opening one connection per concern, I keep one persistent connection and route frames locally:

Frame Goes to
approval/requested
pending table β†’ phone approval cards
question/requested
pending table β†’ phone answer cards
session/event
ring buffer (200/session) β†’ phone stream polling

The phone polls /api/events?afterSeq=N

β€” each poll returns only new bytes, which keeps 4G streaming smooth.

The single most important bug I fixed: DSH agents call ask_user_question

through a separate channel from approvals. If you only listen for approval/requested

, the moment the agent asks something the task deadlocks forever β€” it's waiting for a human answer that the phone never shows.

The answer protocol had to match the Web GUI exactly:

{ "ok": true, "value": { "sessionId": "...", "answer": { "answers": [{ "id": "q1", "selected": ["..."] }] } } }

Every tool/call

event creates a tool card (name, icon, status, file path extracted from arguments). Every tool/result

pairs back by callId

and flips the card to done/failed. Tapping expands the full arguments and result.

One trap: DSH's tool/result

content is double-nested (content[].content[].text

) β€” my first version read one level too shallow and results came back empty. Lesson: capture a real payload before trusting any schema.

relay.html

)"Phone remote for a desktop agent" sounds like it needs a platform team. It doesn't. The barrier to entry for this whole category just collapsed β€” anyone with Node 22+ can build or extend one in a weekend.

The code is open (MIT):

Questions about the architecture? Happy to go deeper in the comments.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @deepseek phone harness 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/streaming-a-desktop-…] indexed:0 read:2min 2026-08-16 Β· β€”