index#4486 landed nesting, upward messages, interrupt and metrics, but no way to watch or type to a running child from a browser. The consumer already exists in the ix repo: crates/tools/claude-html owns a claude -p child in stream-json, forwards raw lines verbatim over one websocket, renders the terminal layout including subagent lanes, and turns typed input into a stream-json user line plus an SDK control_request interrupt.
The blocker is ownership. claude-html spawns its own child; under the kernel the runner (IxMcp.Agents.CliRunner) owns every child's port, and Port.command/2 raises badarg from any other process. So the kernel has to publish.
What the kernel side needs:
- A per-agent raw-line broadcast from
CliRunner (it already parses every line) plus a replay buffer. Mirror claude-html's durability rule: drop stream_event deltas from replay or a reload re-renders every token.
- A transport an external process can consume. A unix socket per agent (
:gen_tcp with {:local, path}, no new dependency) carrying newline JSON out and claude-html's existing tagged input enum ({kind: "send"|"stop"}) in, mapping onto Agents.send/2 and Agents.interrupt/1.
- A tree view above the transcript:
Agents.graph/0 already emits board-ready nodes and edges, and Agents.top/0 now has per-agent CPU and memory to render beside them. Kernel-spawned grandchildren are separate OS processes, not parent_tool_use_id lanes, so the tree is a level above what claude-html renders today.
Deliberately not designed in #4486: shipping a socket protocol with no consumer in the same change would be guessing at the interface. This wants one change spanning both repos, or an index PR immediately followed by the ix one.
Known gap either way, inherited from claude-html: claude -p cannot prompt for permission, so a denied tool arrives after the fact as a red is_error tool result. An approve/deny UI needs the Agent SDK can_use_tool path.
index#4486 landed nesting, upward messages, interrupt and metrics, but no way to watch or type to a running child from a browser. The consumer already exists in the ix repo:
crates/tools/claude-htmlowns aclaude -pchild in stream-json, forwards raw lines verbatim over one websocket, renders the terminal layout including subagent lanes, and turns typed input into a stream-json user line plus an SDKcontrol_requestinterrupt.The blocker is ownership. claude-html spawns its own child; under the kernel the runner (
IxMcp.Agents.CliRunner) owns every child's port, andPort.command/2raisesbadargfrom any other process. So the kernel has to publish.What the kernel side needs:
CliRunner(it already parses every line) plus a replay buffer. Mirror claude-html's durability rule: dropstream_eventdeltas from replay or a reload re-renders every token.:gen_tcpwith{:local, path}, no new dependency) carrying newline JSON out and claude-html's existing tagged input enum ({kind: "send"|"stop"}) in, mapping ontoAgents.send/2andAgents.interrupt/1.Agents.graph/0already emits board-ready nodes and edges, andAgents.top/0now has per-agent CPU and memory to render beside them. Kernel-spawned grandchildren are separate OS processes, notparent_tool_use_idlanes, so the tree is a level above what claude-html renders today.Deliberately not designed in #4486: shipping a socket protocol with no consumer in the same change would be guessing at the interface. This wants one change spanning both repos, or an index PR immediately followed by the ix one.
Known gap either way, inherited from claude-html:
claude -pcannot prompt for permission, so a denied tool arrives after the fact as a redis_errortool result. An approve/deny UI needs the Agent SDKcan_use_toolpath.