πŸ•·οΈ

The Wire

Boring On Purpose

Components speak events. The Hub executes. Everything else is yours.

A ByteBot app is a Hub and some satellites. The Hub executes tools and inference and arbitrates input. A satellite announces what it is, then renders events and sends input. In-proc, over stdio, or over HTTP, the events are the same lines of JSON.

what a satellite says it is

Kinds

ui
Renders events, emits user input and interrupts. Never executes a tool. May be the controller.
context
Serves render requests per turn. Usually the controller. proompt is one.
infer
Takes an inference request, streams chunks back. Never a controller. bb-infer is one.
toolsource
Registers tools, executes them when the Hub asks. Never a controller.
telemetry
Subscribes and observes. bb-logs is one.
bytebot.component.json

Handshake

{
  "schema_version": 1,
  "kind": "ui",
  "language": "python",
  "entry": { "type": "http",
             "stream": "http://site:8081/stream",
             "inbox":  "http://site:8081/inbox" }
}

The first event on any connection is bb_sync: kind, whether it can take input, whether it's primary, language, protocol version, capabilities. The Hub assigns a component id and, from then on, both sides send freely. A manifest tells the Hub how to reach you.

python sdk

A satellite

import bytebot as bb

async def bb_main():
    ctrl = await bb.sync(kind="ui", input_capable=True, primary=True)

    @ctrl.on_infer_chunk
    async def render(chunk):            # kind: block_start | block_delta | block_stop | loop_done
        await browser.push(chunk.scope, html_for(chunk))

    ctrl.on("input_strict", ask_the_human)
    await ctrl.start()

async def on_send(user_id, text):
    with scoped(user_id):               # CURRENT_SCOPE is the tenant wall
        await ctrl.send_user_event(None, text)

await bb.serve_http(bb_main, host="0.0.0.0", port=8081, send_acks=True)

That's a web UI. It owns its own process and event loop, serves /stream and /inbox, and turns chunks into HTML for whichever browser tab belongs to that scope.

app-as-control

A hub

from bytebot import ByteBot

hub = ByteBot(ui="http://site:8081", context="./context")   # url, path,
hub.attach("./ingest")                                       # github slug,
hub.attach_infer({"ref": "bb-infer", "opts": {...}}, primary=True)  # or package
await hub.start()

Component references are a URL, a local path, a GitHub slug, or an installed package. The Hub resolves each, reads its manifest, and wires it. Attach more later.

the spec, compressed

Rules

  1. Hub authority.Many parts decide. Only the Hub executes tools and inference and arbitrates input.
  2. Presentation is not control.A tool_call event is for rendering. Execution is requested with bb_tool_* control events.
  3. Scope on everything.Every event carries the app enclosure. Interrupts address a scope and stop only what's inside it.
  4. Per-turn context.Before each round the Hub asks the context component to render. Tools go in as XML when the model has no native tools.
  5. No coordinated dances.Any one-sided improvement is fine. Anything both sides must agree on goes in the spec.
as it crosses the wire

One turn

livehub Β· jsonl
{"event_type":"bb_sync","kind":"context","controller":true,"language":"python"}{"event_type":"bb_sync","kind":"infer","controller":false,"capabilities":{"api_shape":"anthropic"}}{"event_type":"bb_sync","kind":"ui","input_capable":true,"primary":true}{"event_type":"user_event","type":"message","scope":"u_7f3a","text":"ship it"}{"event_type":"bb_context_render_request","id":"r_11","scope":"u_7f3a"}{"event_type":"bb_context_render_result","in_reply_to":"r_11","success":true}{"event_type":"bb_infer_request","stream":true,"loop_id":"l_90","scope":"u_7f3a"}{"event_type":"infer_chunk","kind":"block_start","block_type":"tool_use","name":"create-user-file"}{"event_type":"bb_tool_call_request","name":"create-user-file","call_id":"c_3"}{"event_type":"bb_tool_call_result","call_id":"c_3","success":true}{"event_type":"infer_chunk","kind":"block_delta","block_type":"text","delta":"Done. "}{"event_type":"bb_infer_result","loop_id":"l_90","success":true,"scope":"u_7f3a"}