TeleTUI is an AI-agent-first Julia TUI framework. It is a hard fork of Tachikoma.jl with server-authoritative ET-transport, FRANK diagnostics, and an agent attach API built in.
using Pkg
# Add General registry first (for transitive deps: JSON3, StructTypes, etc.)
# Install — FRANK is an optional weak dep, add it to enable diagnostics
Pkg.add(["TeleTUI", "FRANK"])
# Until General-registry registration merges, install from the repos:
# Pkg.add(url = "https://github.com/SuperSeriousLab/FRANK.jl")
# Pkg.add(url = "https://github.com/SuperSeriousLab/TeleTUI.jl")Verified install flow (clean depot, 2026-04-18): General registry + SuperSeriousLab registry → Pkg.add(["TeleTUI", "FRANK"]) → TeleTUI + FRANK installed, TeleTUIFRANKExt auto-activates. ~3 minutes on fresh install (precompilation of Tachikoma substrate).
using TeleTUI
# Define your app state
mutable struct Counter
n::Int
end
# Render function: write widgets into the Buffer
function render!(buf::TeleTUI.Buffer, state::Counter)
TeleTUI.set_string!(buf, 1, 1, "Count: $(state.n) (press q to quit, +/- to change)")
end
# Input handler: return updated state or nothing to quit
function handle(state::Counter, event::TeleTUI.InputEvent)
if TeleTUI.is_key(event, '+')
return Counter(state.n + 1)
elseif TeleTUI.is_key(event, '-')
return Counter(state.n - 1)
elseif TeleTUI.is_key(event, 'q')
return nothing # signals quit
end
return state
end
# Run with ET-transport (Unix socket, local)
run_et!(Counter(0), render!, handle)run_et! starts a Unix socket server and connects a local client
automatically. The session is addressable via SessionID for agent attach.
If FRANK is loaded before using TeleTUI, the TeleTUIFRANKExt extension activates
automatically and emits session lifecycle events to stderr as JSONL:
using FRANK # must come first
using TeleTUI
# Now session events emit to stderr:
# {"type":"session_create","session_id":"...","ts":"..."}
# {"type":"input_received","session_id":"...","event":"key:+","ts":"..."}
# ...
run_et!(Counter(0), render!, handle)Subscribe to events programmatically:
using FRANK
FRANK.subscribe!(function(event)
# event is a Dict with :type, :session_id, :ts, etc.
if event[:type] == "session_create"
@info "New session" id=event[:session_id]
end
end)Agents can observe or interact with a running session:
using TeleTUI, FRANK
session_id = nothing # captured from session_create event
FRANK.subscribe!(function(ev)
if ev[:type] == "session_create"
global session_id = ev[:session_id]
end
end)
# Start app in background
t = @async run_et!(Counter(0), render!, handle)
# Wait for session to start
sleep(0.1)
# Attach agent in :observe mode (read-only)
attach_agent(session_id, function(ev)
println("Agent sees: ", ev[:type])
end; mode = :observe)
# Attach agent in :interact mode (can inject input)
attach_agent(session_id, function(ev) end; mode = :interact)
# Inject synthetic input (requires :interact mode)
inject_input(session_id, TeleTUI.KeyEvent('+'))For remote access, use run_tcp! with TLS + bearer token auth:
using TeleTUI
# Server side
cert, key = TeleTUI.generate_self_signed() # generates RSA-2048 cert
token = TeleTUI.random_token()
server = run_tcp!(Counter(0), render!, handle;
host = "0.0.0.0",
port = 7878,
cert = cert,
key = key,
token = token
)
println("Token: ", token)
println("SPKI fingerprint: ", TeleTUI.spki_fingerprint(cert))# Client side — first connection pins the server cert (TOFU)
using TeleTUI
TeleTUI.connect_tcp("server.local", 7878;
token = "the-bearer-token",
spki_fingerprint = "sha256:AAAA..." # from server output
)Measured on the same machine (bench/local_overhead.jl):
| Transport | p50 latency |
|---|---|
| Unix socket | 9.2 µs |
| TCP loopback | 13.3 µs |
Use Unix socket for local sessions (default). Use TCP only for remote.
docs/wire-protocol.md— Buffer snapshot + cell-diff protocol specdocs/frank-integration.md— Full FRANK event schema + subscriber APIdocs/phase-3-auth-design.md— Auth model, TOFU pinning, peer UID gateCHANGELOG.md— Full per-version feature list