Skip to Content

Desktop Inspector Chat

The desktop app’s machine inspector ships with a per-machine chat panel. Open a machine, switch to the chat tab, and you are talking directly to the agent on that remote machine — not to your local agent and not to a proxy. Replies stream from the remote agent’s mouth to your screen with no paraphrasing in between. We call this pattern Path A.

Why a direct pipe

The naive design is “local agent, remote tools”: your laptop’s LLM runs the chat loop, calls ask_agent for each remote question, and surfaces the reply. That works (it is the basis of CLI machine chat), but it has one quality problem — the local LLM tends to paraphrase the remote agent’s reply, smoothing it into the local model’s voice and dropping technical detail.

For a debugging-focused inspector tab, that is the wrong default. We want the operator to see exactly what prod-api-1’s agent thinks, formatted by prod-api-1’s prompt and tools. Path A bypasses the local LLM entirely and pipes the remote stream directly to the UI.

The implementation lives in internal/desktop/services/chat/service_send_remote.go and internal/chat/CLAUDE.md §Per-machine chat.

Session ID convention

Path A is keyed off the session ID format. The frontend mints session IDs in the shape:

machine_<uuid>_<hex>

For example: machine_8f23a4b0-d5c7-4f02-9c11-7b1e8a36f1d2_a3f9. The desktop chat service decodes the prefix, extracts the target machine UUID, and threads it as chat.Optional.TargetMachineID into the session.

When runSession sees a non-empty TargetMachineID, it skips the local LLM and dispatches directly to remoteagent.AskStream.

How a turn flows

  1. User types a message in the inspector chat panel.
  2. Frontend posts the message to the desktop chat service with the machine_<uuid>_<hex> session ID.
  3. Service decodes the target machine UUID, calls chat.Send with TargetMachineID set.
  4. Session router sees a non-empty target → bypasses local LLM → calls remoteagent.AskStream (see server-to-server for the funnel).
  5. Remote agent runs the prompt under its own system prompt, tools, and permission rules.
  6. Stream comes back as chat:chunk events, each forwarded directly to the UI as TOKEN, TOOL_START, TOOL_END, or ERROR events.
  7. Final text is persisted via Session.RecordTurn() so the transcript survives a restart and can be resumed.

The local agent never gets a turn. Your laptop is doing transport-and-display, nothing else.

The remote agent’s prompt and tools shape the reply. If the inspector chat feels different per machine, that is by design — you are talking to that machine’s agent loop.

What the UI shows

The inspector chat panel renders the same event stream the agent loop emits:

  • Token chunks — appended to the current reply bubble.
  • Tool start / end — collapsible cards showing the remote agent’s tool call and its result.
  • Errors — surfaced inline (the remote agent reported a failure).
  • Cancellation — the user pressed stop; the remote run is told to cancel via context cancellation.

Because the stream is the remote agent’s actual output, you see what it did: which files it read, which commands it ran, which other machines it asked. No paraphrasing layer.

Permission gate fires on the remote

The receiver’s permissions.yaml decides what the remote agent may do in service of your prompt. If you ask “delete /tmp/foo” in the inspector chat for prod-api-1, that delete is gated by prod-api-1’s rules, not your laptop’s.

The self-to-self exception applies: if you and the remote machine share the same OAuth identity (verified via CallerHostname), the gate is bypassed. This is what makes single-operator multi-machine flow ergonomic. For team setups where you want hard gates everywhere, run remote agents under a different identity. See ../concepts/permissions.

Self-to-self calls (same OAuth user) skip the permission gate by design. Pair-debug with a teammate watching the inspector chat — that teammate’s permission rules apply, not yours.

Restart resume

Session.RecordTurn() is the bridge to durability. Each completed turn (user prompt + remote reply) is recorded so:

  • Closing the inspector tab and reopening it restores the transcript.
  • A desktop app restart preserves the conversation.
  • Multiple desktop instances looking at the same machine see the same history.

The remote run itself is not persisted — only the transcript. If the remote agent was mid-stream when the desktop app closed, the run was cancelled and you start fresh next turn.

Streaming caveat (resolved)

There was a stretch of 2026-04 when daemons sent the entire reply as one final envelope without intermediate TOKEN events. During that window, the desktop direct pipe used the unary Ask so the user saw the reply at-once instead of mid-stream. That gap is closed in 2026-04-26 and later daemons; the desktop now consumes AskStream for true per-token streaming. See report 01 §9 for the incident note.

If you connect to a daemon older than that fix, the inspector chat still works — the UI just receives one big TOKEN at the end of the run instead of a stream.

Path A vs Path B

Path A (this page)Path B (CLI —machine)
SurfaceDesktop inspector tab.cmdop chat --machine X in a terminal.
LLM running the loopRemote agent.Local agent.
Tool callsRemote agent’s full tool surface.Local tools, plus ask_agent to dispatch remotely.
OutputDirect stream from remote.Local LLM’s synthesis.
Best forDebugging, inspecting, understanding what the remote thinks.Multi-machine prompts where you want the local LLM to coordinate.
Permission gateOn the remote.On the local and on the remote (per call).

Both paths are first-class. Most operators end up using both — Path A for “look at this machine and explain” and Path B for “across these machines, find which one is degraded.”

Configuring the inspector chat

The inspector chat respects the same workspace and credential model as every other Connect surface:

  • The active workspace decides which machines have an inspector tab.
  • The workspace’s API key (or OAuth token) authenticates the underlying remoteagent.AskStream.
  • Per-machine passwords still apply — the desktop prompts inline if the remote requires one.

There is no separate config for the inspector. Open a machine, open chat, type.

ask_agent_stream is the funnel underneath Path A.

Path B — local LLM with remote tools.

Streaming auth, machine passwords, session token cache.

The conceptual model behind both paths.

Last updated on