Upivia Textual TUI — chat with Yupi, watch multi-agent delegation, reuse the platform's governed orchestration.
Project description
upivia-tui
The Upivia terminal — a Textual TUI and the canonical CLI for Upivia (it replaced the older Node/OpenTUI CLI). It is a thin presentation layer over the platform's governed chat-turn pipeline — all orchestration, budgets, approvals, memory, and multi-agent delegation happen server-side.
What it does
- renders messages as bordered cards (OpenCode-style)
- renders markdown with Textual's native widget (clean tables/code/bold/italic)
- visualises multi-agent delegation in a live Team panel (+ worker detail view)
- lets the agent read/write/edit/run files in your launch directory, with per-session command permissions and inline +/- diff cards
- syncs in realtime with the web app over WebSocket, and can run headless
(
--background) so the web app edits files on this machine
Install & authenticate
uv tool install upivia-tui # or: pipx install upivia-tui (Python 3.10+)
upivia-tui init # authenticate (writes ~/.upivia/config.json)
upivia-tui # launch
Keyboard & commands
| Key | Action |
|---|---|
Ctrl+P |
Command palette (fuzzy) |
Ctrl+O |
Switch model (curated + full OpenRouter catalog) |
Ctrl+T |
Switch agent |
Ctrl+B |
Switch session (loads history) |
Ctrl+G |
Toggle Build / Plan mode |
Ctrl+L |
New / clear session |
Ctrl+Y |
Copy (selection, or last reply) — also drag-to-copy |
Ctrl+C / Ctrl+Q |
Quit |
All shortcuts work even while typing in the composer (priority bindings).
Ctrl+S is intentionally avoided (it triggers terminal flow-control on many
terminals).
Slash commands: /models /model <id> /agent /sessions /session
/new /rename <title> /delete /mode /autonomy /requests /worker
/export /compact /clear /help. The model picker and command palette
mirror the Node CLI; selecting a new model or agent starts a fresh session so
history stays consistent.
Autocomplete & worker detail
- Type
@in the composer to autocomplete project files (Tab/Enter to accept); mentioned files are read by the agent first. - Type
/to autocomplete slash commands from a fuzzy dropdown. - Click a worker in the Team panel (or run
/worker) to open its full activity stream — every thinking step, tool call, file write, and result. - The agent can navigate large codebases with
local_code_graph(Graphify); install withuv tool install graphifyy. It falls back tolocal_searchwhen graphify isn't present.
Copy & select
Textual runs in mouse-tracking mode, so the app handles selection itself:
- Drag with the mouse to select text — it auto-copies on release (via OSC 52, so it works over SSH and writes the system clipboard where available). This is the reliable path and needs no key.
- Ctrl+Y copies the current selection, or the last assistant reply if nothing is selected.
Ctrl+Shift+Calso works where the terminal forwards it — but many terminals intercept it for their own copy, which is why drag-to-copy and Ctrl+Y are the primary paths.- Your terminal's native selection still works too: hold Shift (most terminals) or Option/Alt (iTerm2/macOS) while dragging.
Local file editing
When the agent calls a local tool, the server pauses the turn with
local_tools_pending; this client executes it on disk and resumes. Supported
tools: local_read_file, local_write_file, local_edit_file,
local_list_dir, local_search, local_run_command.
- Sandbox: every path resolves under the directory you launched
upivia-tuifrom and can never escape it. - Command gating: the first time the agent wants to run a shell command, you're asked once which categories it may use (shell / install / servers / git / delete); the choice is enforced for the rest of the session.
- Diffs: writes and edits render a colorised +/- diff card inline.
Local browser automation
The TUI ships with built-in Playwright browser tools — install the TUI and
the agent can pilot a real local Chromium with zero manual setup. On the very
first browser call, the Chromium engine is auto-installed (one-time,
python -m playwright install chromium, with a "Installing local browser
engine (one-time setup)…" status line); after that it's instant. One persistent
headless browser/page is kept alive across tool calls for the session.
Tools: local_browser_open, local_browser_snapshot, local_browser_click,
local_browser_type, local_browser_press, local_browser_select,
local_browser_wait, local_browser_extract, local_browser_screenshot,
local_browser_close, local_browser_health.
Ref-based interaction contract: the agent calls local_browser_snapshot
first (or reads the snapshot embedded in open/click/type results), which
tags interactive elements with stable refs like e12 and returns them in an
LLM-friendly digest (role, name, text, visibility, links, forms, console
errors). Clicks/typing then target those refs, with visible-text and CSS
selectors as fallbacks. Refs refresh automatically after every action, so they
never go stale silently.
Safety notes:
- no tool ever returns cookies, localStorage, or other secrets
- downloads are disabled (
accept_downloads=False) - screenshots are saved as PNG files under
.upivia-screenshots/in your project directory — only the file path is returned, never inline image data - navigating to banking/payment-looking hosts attaches a warning to the result
- every failure returns a structured error with suggestions (valid refs, repair hints) instead of crashing the turn
Auth
It reads the same ~/.upivia/config.json that upivia init (the Node CLI)
writes. If you've authenticated the Node CLI, you're authenticated here.
Override for dev:
export UPIVIA_BASE_URL=http://localhost:3000
export UPIVIA_PAT=upivia_pat_...
Run
cd packages/upivia-tui
uv run upivia-tui # full TUI in the current directory
uv run upivia-tui --dir PATH # full TUI rooted at PATH
uv run upivia-tui --background # headless device daemon (no UI)
uv run upivia-tui --browser-headful # show the built-in browser window
Built-in local browser: headless vs headful
The local_browser_* tools drive a local Chromium that is headless by
default. To watch the agent work (or complete a login/captcha yourself),
opt in to a visible window with any of:
--browser-headfulCLI flagUPIVIA_BROWSER_HEADFUL=1environment variable"browserHeadful": truein~/.upivia/config.json
Realtime sync + machine-wide device
The TUI connects to the platform's WebSocket companion (wss://…/ws) and
registers as a live device:
- Chat turns flow over WS, so the web app and the TUI share one live session — messages and tool calls appear in both in real time (SSE fallback if the socket can't connect).
- While the TUI is open or
upivia-tui --backgroundis running, any web session attached to this device can read/write/edit/run files on this machine through the governedlocal_tools_pending→ execute →tool_resultloop. Attach a device from the web app's Sessions view. - The TopBar shows
● live(WS connected / device online) or○ local.
Set the working directory from the web app
When a device is live, the web Sessions header shows a subtle folder chip. Click
it to browse the device's directories and cd into one — type a path (Tab
completes against available sub-directories, Enter applies) or click a listed
folder. This rebases the directory the agent reads/writes/runs in, sandboxed to
the chosen root.
How it talks to the platform
REST + SSE go through the official Python SDK (upivia,
packages/sdk-py): upivia_tui/client.py is a thin adapter over
upivia.AsyncUpiviaClient (PAT auth). The realtime WebSocket
device link (upivia_tui/wsclient.py) speaks to the WS companion directly.
| Action | Endpoint |
|---|---|
| List agents | GET /api/v1/agents |
| Balance | GET /api/v1/balance |
| Create session | POST /api/v1/chat/sessions |
| Stream a turn | POST /api/v1/chat/turn (SSE) |
Delegation is driven by the delegation_started and agent_event SSE events
emitted by the chat route when Yupi spawns/delegates to worker agents.
Yupi delegates by default
The orchestration system prompt in app/api/v1/chat/turn/route.ts instructs
Yupi to delegate any non-trivial or multi-step work to specialist agents, only
working solo for genuinely trivial single-step actions.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file upivia_tui-0.1.0.tar.gz.
File metadata
- Download URL: upivia_tui-0.1.0.tar.gz
- Upload date:
- Size: 103.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a49a04b21e06bdbd13d2f8379541ba217a2dc7f8f68d606ea66192e81cd9baf2
|
|
| MD5 |
3ba03120d33bc19e0ceeac50a50d5cf1
|
|
| BLAKE2b-256 |
9bfaef5faf5f2e18667b3aa9d11b8f03ab72c161a458e6cce8da7de1e413d58e
|
File details
Details for the file upivia_tui-0.1.0-py3-none-any.whl.
File metadata
- Download URL: upivia_tui-0.1.0-py3-none-any.whl
- Upload date:
- Size: 68.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ab75dc04a5644e121d1433c523245272aeea7b3e8192e934f5c0b33510f282d
|
|
| MD5 |
04305385ec26ceba69f60be0799ff986
|
|
| BLAKE2b-256 |
2025172428d88b9de394f5211fb486bceb6d6b91b4e890854fba32e4f3c1a39e
|