upivia-tui 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. upivia_tui-0.1.0/.gitignore +7 -0
  2. upivia_tui-0.1.0/FEATURE-PARITY.md +83 -0
  3. upivia_tui-0.1.0/LICENSE +21 -0
  4. upivia_tui-0.1.0/PKG-INFO +227 -0
  5. upivia_tui-0.1.0/README.md +196 -0
  6. upivia_tui-0.1.0/pyproject.toml +55 -0
  7. upivia_tui-0.1.0/src/upivia_tui/__init__.py +11 -0
  8. upivia_tui-0.1.0/src/upivia_tui/__main__.py +138 -0
  9. upivia_tui-0.1.0/src/upivia_tui/app.py +1080 -0
  10. upivia_tui-0.1.0/src/upivia_tui/app.tcss +388 -0
  11. upivia_tui-0.1.0/src/upivia_tui/background.py +180 -0
  12. upivia_tui-0.1.0/src/upivia_tui/browsertools.py +871 -0
  13. upivia_tui-0.1.0/src/upivia_tui/client.py +359 -0
  14. upivia_tui-0.1.0/src/upivia_tui/commands.py +131 -0
  15. upivia_tui-0.1.0/src/upivia_tui/config.py +117 -0
  16. upivia_tui-0.1.0/src/upivia_tui/localtools.py +888 -0
  17. upivia_tui-0.1.0/src/upivia_tui/models.py +92 -0
  18. upivia_tui-0.1.0/src/upivia_tui/screens.py +396 -0
  19. upivia_tui-0.1.0/src/upivia_tui/version.py +1 -0
  20. upivia_tui-0.1.0/src/upivia_tui/widgets.py +417 -0
  21. upivia_tui-0.1.0/src/upivia_tui/wsclient.py +233 -0
  22. upivia_tui-0.1.0/tests/test_app_features.py +149 -0
  23. upivia_tui-0.1.0/tests/test_browsertools.py +268 -0
  24. upivia_tui-0.1.0/tests/test_client.py +120 -0
  25. upivia_tui-0.1.0/tests/test_config.py +52 -0
  26. upivia_tui-0.1.0/tests/test_cwd.py +54 -0
  27. upivia_tui-0.1.0/tests/test_diff.py +39 -0
  28. upivia_tui-0.1.0/tests/test_localtools.py +191 -0
  29. upivia_tui-0.1.0/tests/test_models.py +32 -0
  30. upivia_tui-0.1.0/tests/test_nav.py +76 -0
  31. upivia_tui-0.1.0/tests/test_trust_boundary.py +327 -0
  32. upivia_tui-0.1.0/tests/test_ws.py +297 -0
  33. upivia_tui-0.1.0/uv.lock +539 -0
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ *.egg-info/
6
+ dist/
7
+ build/
@@ -0,0 +1,83 @@
1
+ # Feature Parity: (removed) Node CLI (OpenTUI) โ†’ Textual TUI
2
+
3
+ > The Node/OpenTUI CLI was **removed** (DEC-119). The Textual TUI is now the
4
+ > canonical Upivia terminal. This table records what carried over.
5
+
6
+ Tracks every meaningful feature of `packages/upivia-cli` so nothing is lost in
7
+ the Python/Textual client (`packages/upivia-tui`). Status legend:
8
+ โœ… done ยท ๐Ÿ”ถ partial ยท โ›” intentionally dropped ยท โณ planned.
9
+
10
+ ## Core chat
11
+ | Feature | Node CLI | Textual | Notes |
12
+ |---|---|---|---|
13
+ | Streaming chat (SSE) | โœ… | โœ… | `client.stream_turn` |
14
+ | Boxed message cards | (borderless) | โœ… | OpenCode-style borders |
15
+ | Markdown rendering | ๐Ÿ”ถ custom | โœ… | native Textual `Markdown` (tables, code, lists) |
16
+ | Thinking/reasoning display | โœ… | ๐Ÿ”ถ | suppressed in main thread (kept quiet) |
17
+ | Tool call status lines | โœ… | โœ… | `tool_planned`/`executing`/`done` |
18
+ | Inline approval prompt | โœ… | ๐Ÿ”ถ | shows notice; resolve via web/`requests` |
19
+ | Copy text | ๐Ÿ”ถ Ctrl+Y | โœ… | auto-copy on drag-select + Ctrl+Y (OSC52); DEC-119 |
20
+
21
+ ## Local tools (on-disk)
22
+ | Feature | Node CLI | Textual | Notes |
23
+ |---|---|---|---|
24
+ | read/write/edit/list/search/run | โœ… | โœ… | `localtools.py`, CWD sandbox |
25
+ | Diff rendering for writes/edits | โœ… | โœ… | line-numbered, syntax-highlighted |
26
+ | Command permission policy | โœ… ask-once | โœ… | `CommandPolicyScreen` |
27
+ | code graph (graphify) | โœ… | โœ… | `local_code_graph` auto-build+query, graceful fallback (DEC-118) |
28
+ | MCP tools | โœ… | โ›” | out of scope for thin client |
29
+
30
+ ## Multi-agent
31
+ | Feature | Node CLI | Textual | Notes |
32
+ |---|---|---|---|
33
+ | Delegate-by-default orchestration | โœ… | โœ… | server prompt (DEC-114) |
34
+ | Live worker panes | โœ… | โœ… | `TeamPanel` from `agent_event` |
35
+ | Worker detail view | โœ… | โœ… | click worker / `/worker` โ†’ full activity stream (DEC-118) |
36
+ | Spawn/delegate visibility | โœ… | โœ… | status + spend + log |
37
+
38
+ ## Sessions
39
+ | Feature | Node CLI | Textual | Notes |
40
+ |---|---|---|---|
41
+ | New session | โœ… | โœ… | `/new`, Ctrl+L |
42
+ | List sessions | โœ… | โœ… | `/sessions`, palette |
43
+ | Switch session (+history) | โœ… | โœ… | picker loads history |
44
+ | Rename session | โœ… | โœ… | `/rename` |
45
+ | Delete session | โœ… | โœ… | `/delete` |
46
+
47
+ ## Navigation / control
48
+ | Feature | Node CLI | Textual | Notes |
49
+ |---|---|---|---|
50
+ | Command palette (Ctrl+P) | โœ… | โœ… | fuzzy command menu |
51
+ | Agent switcher | โœ… | โœ… | Ctrl+T / `/agent` |
52
+ | Model picker | โœ… | โœ… | Ctrl+O / `/models`, curated + live catalog |
53
+ | Team switch | โœ… | โ›” | uses config team; switch in Node CLI/web |
54
+ | Plan/Build mode | โœ… | โœ… | Tab / `/mode` |
55
+ | Autonomy toggle | โœ… | โœ… | `/autonomy` |
56
+ | Question/answer modal | โœ… | โœ… | options/multiselect/free-text |
57
+
58
+ ## Utilities
59
+ | Feature | Node CLI | Textual | Notes |
60
+ |---|---|---|---|
61
+ | Export to markdown | โœ… | โœ… | `/export` |
62
+ | Compact session | โœ… | โœ… | `/compact` |
63
+ | Pending requests + resolve/reject | โœ… | โœ… | `/requests` |
64
+ | Undo/redo | โœ… | โ›” | VCS-style undo is CLI-specific |
65
+ | Session-local VCS / branches | โœ… | โ›” | out of scope |
66
+ | Allocate budget (manager) | โœ… | โณ | `/allocate` (low priority) |
67
+ | @file mentions | โœ… | โœ… | composer dropdown, Tab/Enter accept (DEC-118) |
68
+ | Slash-command autocomplete | โœ… | โœ… | `/` dropdown, fuzzy (DEC-118) |
69
+ | Background/device mode | โœ… | โœ… | `upivia-tui --background` headless daemon (DEC-116) |
70
+
71
+ ## Realtime / device
72
+ | Feature | Node CLI | Textual | Notes |
73
+ |---|---|---|---|
74
+ | WebSocket realtime sync | โœ… | โœ… | chat turns over WS, webโ†”TUI fan-out, SSE fallback (DEC-116) |
75
+ | Live device (web edits this machine) | โœ… | โœ… | TUI open OR `--background`; any attached web session edits files here |
76
+ | Set working dir from web (cd + browse) | โ›” | โœ… | subtle cwd chip in web Sessions, tab-complete dirs (DEC-117) |
77
+ | attach_session (web drives device) | โœ… | โœ… | reattach over WS |
78
+
79
+ ## Auth / CLI
80
+ | Feature | Node CLI | Textual | Notes |
81
+ |---|---|---|---|
82
+ | First-run auth (`init`) | โœ… | โœ… | `upivia-tui init` writes shared config (DEC-119) |
83
+ | `whoami` | โœ… | โœ… | `upivia-tui whoami` (DEC-119) |
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Upivia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: upivia-tui
3
+ Version: 0.1.0
4
+ Summary: Upivia Textual TUI โ€” chat with Yupi, watch multi-agent delegation, reuse the platform's governed orchestration.
5
+ Project-URL: Homepage, https://www.upivia.com
6
+ Project-URL: Documentation, https://www.upivia.com/docs
7
+ Author: Upivia
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,ai-agents,cli,governance,terminal,tui,upivia
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Terminals
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: playwright>=1.45
25
+ Requires-Dist: textual>=0.80.0
26
+ Requires-Dist: upivia>=0.2.0
27
+ Requires-Dist: websockets>=12.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # upivia-tui
33
+
34
+ The **Upivia terminal** โ€” a [Textual](https://textual.textualize.io/) TUI and the
35
+ canonical CLI for Upivia (it replaced the older Node/OpenTUI CLI). It is a thin
36
+ presentation layer over the platform's governed chat-turn pipeline โ€” all
37
+ orchestration, budgets, approvals, memory, and multi-agent delegation happen
38
+ server-side.
39
+
40
+ ## What it does
41
+
42
+ - renders messages as bordered cards (OpenCode-style)
43
+ - renders markdown with Textual's native widget (clean tables/code/bold/italic)
44
+ - visualises multi-agent delegation in a live Team panel (+ worker detail view)
45
+ - lets the agent **read/write/edit/run** files in your launch directory, with
46
+ per-session command permissions and inline +/- diff cards
47
+ - syncs in realtime with the web app over WebSocket, and can run headless
48
+ (`--background`) so the web app edits files on this machine
49
+
50
+ ## Install & authenticate
51
+
52
+ ```bash
53
+ uv tool install upivia-tui # or: pipx install upivia-tui (Python 3.10+)
54
+ upivia-tui init # authenticate (writes ~/.upivia/config.json)
55
+ upivia-tui # launch
56
+ ```
57
+
58
+ ## Keyboard & commands
59
+
60
+ | Key | Action |
61
+ |---|---|
62
+ | `Ctrl+P` | Command palette (fuzzy) |
63
+ | `Ctrl+O` | Switch model (curated + full OpenRouter catalog) |
64
+ | `Ctrl+T` | Switch agent |
65
+ | `Ctrl+B` | Switch session (loads history) |
66
+ | `Ctrl+G` | Toggle Build / Plan mode |
67
+ | `Ctrl+L` | New / clear session |
68
+ | `Ctrl+Y` | Copy (selection, or last reply) โ€” also drag-to-copy |
69
+ | `Ctrl+C` / `Ctrl+Q` | Quit |
70
+
71
+ All shortcuts work even while typing in the composer (priority bindings).
72
+ `Ctrl+S` is intentionally avoided (it triggers terminal flow-control on many
73
+ terminals).
74
+
75
+ Slash commands: `/models` `/model <id>` `/agent` `/sessions` `/session`
76
+ `/new` `/rename <title>` `/delete` `/mode` `/autonomy` `/requests` `/worker`
77
+ `/export` `/compact` `/clear` `/help`. The model picker and command palette
78
+ mirror the Node CLI; selecting a new model or agent starts a fresh session so
79
+ history stays consistent.
80
+
81
+ ### Autocomplete & worker detail
82
+
83
+ - Type `@` in the composer to autocomplete **project files** (Tab/Enter to
84
+ accept); mentioned files are read by the agent first.
85
+ - Type `/` to autocomplete **slash commands** from a fuzzy dropdown.
86
+ - Click a worker in the Team panel (or run `/worker`) to open its **full
87
+ activity stream** โ€” every thinking step, tool call, file write, and result.
88
+ - The agent can navigate large codebases with `local_code_graph` (Graphify);
89
+ install with `uv tool install graphifyy`. It falls back to `local_search`
90
+ when graphify isn't present.
91
+
92
+ ## Copy & select
93
+
94
+ Textual runs in mouse-tracking mode, so the app handles selection itself:
95
+
96
+ - **Drag** with the mouse to select text โ€” it **auto-copies on release** (via
97
+ OSC 52, so it works over SSH and writes the system clipboard where available).
98
+ This is the reliable path and needs no key.
99
+ - **Ctrl+Y** copies the current selection, or the last assistant reply if
100
+ nothing is selected.
101
+ - `Ctrl+Shift+C` also works *where the terminal forwards it* โ€” but many
102
+ terminals intercept it for their own copy, which is why drag-to-copy and
103
+ Ctrl+Y are the primary paths.
104
+ - Your terminal's native selection still works too: hold **Shift** (most
105
+ terminals) or **Option/Alt** (iTerm2/macOS) while dragging.
106
+
107
+ ## Local file editing
108
+ When the agent calls a local tool, the server pauses the turn with
109
+ `local_tools_pending`; this client executes it on disk and resumes. Supported
110
+ tools: `local_read_file`, `local_write_file`, `local_edit_file`,
111
+ `local_list_dir`, `local_search`, `local_run_command`.
112
+
113
+ - **Sandbox:** every path resolves under the directory you launched
114
+ `upivia-tui` from and can never escape it.
115
+ - **Command gating:** the first time the agent wants to run a shell command,
116
+ you're asked once which categories it may use (shell / install / servers /
117
+ git / delete); the choice is enforced for the rest of the session.
118
+ - **Diffs:** writes and edits render a colorised +/- diff card inline.
119
+
120
+ ## Local browser automation
121
+
122
+ The TUI ships with **built-in Playwright browser tools** โ€” install the TUI and
123
+ the agent can pilot a real local Chromium with zero manual setup. On the very
124
+ first browser call, the Chromium engine is auto-installed (one-time,
125
+ `python -m playwright install chromium`, with a "Installing local browser
126
+ engine (one-time setup)โ€ฆ" status line); after that it's instant. One persistent
127
+ headless browser/page is kept alive across tool calls for the session.
128
+
129
+ Tools: `local_browser_open`, `local_browser_snapshot`, `local_browser_click`,
130
+ `local_browser_type`, `local_browser_press`, `local_browser_select`,
131
+ `local_browser_wait`, `local_browser_extract`, `local_browser_screenshot`,
132
+ `local_browser_close`, `local_browser_health`.
133
+
134
+ **Ref-based interaction contract:** the agent calls `local_browser_snapshot`
135
+ first (or reads the snapshot embedded in `open`/`click`/`type` results), which
136
+ tags interactive elements with stable refs like `e12` and returns them in an
137
+ LLM-friendly digest (role, name, text, visibility, links, forms, console
138
+ errors). Clicks/typing then target those refs, with visible-text and CSS
139
+ selectors as fallbacks. Refs refresh automatically after every action, so they
140
+ never go stale silently.
141
+
142
+ Safety notes:
143
+
144
+ - no tool ever returns cookies, localStorage, or other secrets
145
+ - downloads are disabled (`accept_downloads=False`)
146
+ - screenshots are saved as PNG files under `.upivia-screenshots/` in your
147
+ project directory โ€” only the file path is returned, never inline image data
148
+ - navigating to banking/payment-looking hosts attaches a warning to the result
149
+ - every failure returns a structured error with suggestions (valid refs,
150
+ repair hints) instead of crashing the turn
151
+
152
+ ## Auth
153
+
154
+ It reads the **same** `~/.upivia/config.json` that `upivia init` (the Node CLI)
155
+ writes. If you've authenticated the Node CLI, you're authenticated here.
156
+
157
+ Override for dev:
158
+
159
+ ```bash
160
+ export UPIVIA_BASE_URL=http://localhost:3000
161
+ export UPIVIA_PAT=upivia_pat_...
162
+ ```
163
+
164
+ ## Run
165
+
166
+ ```bash
167
+ cd packages/upivia-tui
168
+ uv run upivia-tui # full TUI in the current directory
169
+ uv run upivia-tui --dir PATH # full TUI rooted at PATH
170
+ uv run upivia-tui --background # headless device daemon (no UI)
171
+ uv run upivia-tui --browser-headful # show the built-in browser window
172
+ ```
173
+
174
+ ### Built-in local browser: headless vs headful
175
+
176
+ The `local_browser_*` tools drive a local Chromium that is **headless by
177
+ default**. To watch the agent work (or complete a login/captcha yourself),
178
+ opt in to a visible window with any of:
179
+
180
+ - `--browser-headful` CLI flag
181
+ - `UPIVIA_BROWSER_HEADFUL=1` environment variable
182
+ - `"browserHeadful": true` in `~/.upivia/config.json`
183
+
184
+ ## Realtime sync + machine-wide device
185
+
186
+ The TUI connects to the platform's WebSocket companion (`wss://โ€ฆ/ws`) and
187
+ registers as a **live device**:
188
+
189
+ - Chat turns flow over WS, so the **web app and the TUI share one live
190
+ session** โ€” messages and tool calls appear in both in real time (SSE fallback
191
+ if the socket can't connect).
192
+ - While the TUI is open **or** `upivia-tui --background` is running, **any web
193
+ session attached to this device can read/write/edit/run files on this
194
+ machine** through the governed `local_tools_pending` โ†’ execute โ†’
195
+ `tool_result` loop. Attach a device from the web app's Sessions view.
196
+ - The TopBar shows `โ— live` (WS connected / device online) or `โ—‹ local`.
197
+
198
+ ### Set the working directory from the web app
199
+
200
+ When a device is live, the web Sessions header shows a subtle folder chip. Click
201
+ it to browse the device's directories and `cd` into one โ€” type a path (Tab
202
+ completes against available sub-directories, Enter applies) or click a listed
203
+ folder. This rebases the directory the agent reads/writes/runs in, sandboxed to
204
+ the chosen root.
205
+
206
+ ## How it talks to the platform
207
+
208
+ REST + SSE go through the official Python SDK (`upivia`,
209
+ `packages/sdk-py`): `upivia_tui/client.py` is a thin adapter over
210
+ `upivia.AsyncUpiviaClient` (PAT auth). The realtime WebSocket
211
+ device link (`upivia_tui/wsclient.py`) speaks to the WS companion directly.
212
+
213
+ | Action | Endpoint |
214
+ |-------------------|-------------------------------------------|
215
+ | List agents | `GET /api/v1/agents` |
216
+ | Balance | `GET /api/v1/balance` |
217
+ | Create session | `POST /api/v1/chat/sessions` |
218
+ | Stream a turn | `POST /api/v1/chat/turn` (SSE) |
219
+
220
+ Delegation is driven by the `delegation_started` and `agent_event` SSE events
221
+ emitted by the chat route when Yupi spawns/delegates to worker agents.
222
+
223
+ ## Yupi delegates by default
224
+
225
+ The orchestration system prompt in `app/api/v1/chat/turn/route.ts` instructs
226
+ Yupi to delegate any non-trivial or multi-step work to specialist agents, only
227
+ working solo for genuinely trivial single-step actions.
@@ -0,0 +1,196 @@
1
+ # upivia-tui
2
+
3
+ The **Upivia terminal** โ€” a [Textual](https://textual.textualize.io/) TUI and the
4
+ canonical CLI for Upivia (it replaced the older Node/OpenTUI CLI). It is a thin
5
+ presentation layer over the platform's governed chat-turn pipeline โ€” all
6
+ orchestration, budgets, approvals, memory, and multi-agent delegation happen
7
+ server-side.
8
+
9
+ ## What it does
10
+
11
+ - renders messages as bordered cards (OpenCode-style)
12
+ - renders markdown with Textual's native widget (clean tables/code/bold/italic)
13
+ - visualises multi-agent delegation in a live Team panel (+ worker detail view)
14
+ - lets the agent **read/write/edit/run** files in your launch directory, with
15
+ per-session command permissions and inline +/- diff cards
16
+ - syncs in realtime with the web app over WebSocket, and can run headless
17
+ (`--background`) so the web app edits files on this machine
18
+
19
+ ## Install & authenticate
20
+
21
+ ```bash
22
+ uv tool install upivia-tui # or: pipx install upivia-tui (Python 3.10+)
23
+ upivia-tui init # authenticate (writes ~/.upivia/config.json)
24
+ upivia-tui # launch
25
+ ```
26
+
27
+ ## Keyboard & commands
28
+
29
+ | Key | Action |
30
+ |---|---|
31
+ | `Ctrl+P` | Command palette (fuzzy) |
32
+ | `Ctrl+O` | Switch model (curated + full OpenRouter catalog) |
33
+ | `Ctrl+T` | Switch agent |
34
+ | `Ctrl+B` | Switch session (loads history) |
35
+ | `Ctrl+G` | Toggle Build / Plan mode |
36
+ | `Ctrl+L` | New / clear session |
37
+ | `Ctrl+Y` | Copy (selection, or last reply) โ€” also drag-to-copy |
38
+ | `Ctrl+C` / `Ctrl+Q` | Quit |
39
+
40
+ All shortcuts work even while typing in the composer (priority bindings).
41
+ `Ctrl+S` is intentionally avoided (it triggers terminal flow-control on many
42
+ terminals).
43
+
44
+ Slash commands: `/models` `/model <id>` `/agent` `/sessions` `/session`
45
+ `/new` `/rename <title>` `/delete` `/mode` `/autonomy` `/requests` `/worker`
46
+ `/export` `/compact` `/clear` `/help`. The model picker and command palette
47
+ mirror the Node CLI; selecting a new model or agent starts a fresh session so
48
+ history stays consistent.
49
+
50
+ ### Autocomplete & worker detail
51
+
52
+ - Type `@` in the composer to autocomplete **project files** (Tab/Enter to
53
+ accept); mentioned files are read by the agent first.
54
+ - Type `/` to autocomplete **slash commands** from a fuzzy dropdown.
55
+ - Click a worker in the Team panel (or run `/worker`) to open its **full
56
+ activity stream** โ€” every thinking step, tool call, file write, and result.
57
+ - The agent can navigate large codebases with `local_code_graph` (Graphify);
58
+ install with `uv tool install graphifyy`. It falls back to `local_search`
59
+ when graphify isn't present.
60
+
61
+ ## Copy & select
62
+
63
+ Textual runs in mouse-tracking mode, so the app handles selection itself:
64
+
65
+ - **Drag** with the mouse to select text โ€” it **auto-copies on release** (via
66
+ OSC 52, so it works over SSH and writes the system clipboard where available).
67
+ This is the reliable path and needs no key.
68
+ - **Ctrl+Y** copies the current selection, or the last assistant reply if
69
+ nothing is selected.
70
+ - `Ctrl+Shift+C` also works *where the terminal forwards it* โ€” but many
71
+ terminals intercept it for their own copy, which is why drag-to-copy and
72
+ Ctrl+Y are the primary paths.
73
+ - Your terminal's native selection still works too: hold **Shift** (most
74
+ terminals) or **Option/Alt** (iTerm2/macOS) while dragging.
75
+
76
+ ## Local file editing
77
+ When the agent calls a local tool, the server pauses the turn with
78
+ `local_tools_pending`; this client executes it on disk and resumes. Supported
79
+ tools: `local_read_file`, `local_write_file`, `local_edit_file`,
80
+ `local_list_dir`, `local_search`, `local_run_command`.
81
+
82
+ - **Sandbox:** every path resolves under the directory you launched
83
+ `upivia-tui` from and can never escape it.
84
+ - **Command gating:** the first time the agent wants to run a shell command,
85
+ you're asked once which categories it may use (shell / install / servers /
86
+ git / delete); the choice is enforced for the rest of the session.
87
+ - **Diffs:** writes and edits render a colorised +/- diff card inline.
88
+
89
+ ## Local browser automation
90
+
91
+ The TUI ships with **built-in Playwright browser tools** โ€” install the TUI and
92
+ the agent can pilot a real local Chromium with zero manual setup. On the very
93
+ first browser call, the Chromium engine is auto-installed (one-time,
94
+ `python -m playwright install chromium`, with a "Installing local browser
95
+ engine (one-time setup)โ€ฆ" status line); after that it's instant. One persistent
96
+ headless browser/page is kept alive across tool calls for the session.
97
+
98
+ Tools: `local_browser_open`, `local_browser_snapshot`, `local_browser_click`,
99
+ `local_browser_type`, `local_browser_press`, `local_browser_select`,
100
+ `local_browser_wait`, `local_browser_extract`, `local_browser_screenshot`,
101
+ `local_browser_close`, `local_browser_health`.
102
+
103
+ **Ref-based interaction contract:** the agent calls `local_browser_snapshot`
104
+ first (or reads the snapshot embedded in `open`/`click`/`type` results), which
105
+ tags interactive elements with stable refs like `e12` and returns them in an
106
+ LLM-friendly digest (role, name, text, visibility, links, forms, console
107
+ errors). Clicks/typing then target those refs, with visible-text and CSS
108
+ selectors as fallbacks. Refs refresh automatically after every action, so they
109
+ never go stale silently.
110
+
111
+ Safety notes:
112
+
113
+ - no tool ever returns cookies, localStorage, or other secrets
114
+ - downloads are disabled (`accept_downloads=False`)
115
+ - screenshots are saved as PNG files under `.upivia-screenshots/` in your
116
+ project directory โ€” only the file path is returned, never inline image data
117
+ - navigating to banking/payment-looking hosts attaches a warning to the result
118
+ - every failure returns a structured error with suggestions (valid refs,
119
+ repair hints) instead of crashing the turn
120
+
121
+ ## Auth
122
+
123
+ It reads the **same** `~/.upivia/config.json` that `upivia init` (the Node CLI)
124
+ writes. If you've authenticated the Node CLI, you're authenticated here.
125
+
126
+ Override for dev:
127
+
128
+ ```bash
129
+ export UPIVIA_BASE_URL=http://localhost:3000
130
+ export UPIVIA_PAT=upivia_pat_...
131
+ ```
132
+
133
+ ## Run
134
+
135
+ ```bash
136
+ cd packages/upivia-tui
137
+ uv run upivia-tui # full TUI in the current directory
138
+ uv run upivia-tui --dir PATH # full TUI rooted at PATH
139
+ uv run upivia-tui --background # headless device daemon (no UI)
140
+ uv run upivia-tui --browser-headful # show the built-in browser window
141
+ ```
142
+
143
+ ### Built-in local browser: headless vs headful
144
+
145
+ The `local_browser_*` tools drive a local Chromium that is **headless by
146
+ default**. To watch the agent work (or complete a login/captcha yourself),
147
+ opt in to a visible window with any of:
148
+
149
+ - `--browser-headful` CLI flag
150
+ - `UPIVIA_BROWSER_HEADFUL=1` environment variable
151
+ - `"browserHeadful": true` in `~/.upivia/config.json`
152
+
153
+ ## Realtime sync + machine-wide device
154
+
155
+ The TUI connects to the platform's WebSocket companion (`wss://โ€ฆ/ws`) and
156
+ registers as a **live device**:
157
+
158
+ - Chat turns flow over WS, so the **web app and the TUI share one live
159
+ session** โ€” messages and tool calls appear in both in real time (SSE fallback
160
+ if the socket can't connect).
161
+ - While the TUI is open **or** `upivia-tui --background` is running, **any web
162
+ session attached to this device can read/write/edit/run files on this
163
+ machine** through the governed `local_tools_pending` โ†’ execute โ†’
164
+ `tool_result` loop. Attach a device from the web app's Sessions view.
165
+ - The TopBar shows `โ— live` (WS connected / device online) or `โ—‹ local`.
166
+
167
+ ### Set the working directory from the web app
168
+
169
+ When a device is live, the web Sessions header shows a subtle folder chip. Click
170
+ it to browse the device's directories and `cd` into one โ€” type a path (Tab
171
+ completes against available sub-directories, Enter applies) or click a listed
172
+ folder. This rebases the directory the agent reads/writes/runs in, sandboxed to
173
+ the chosen root.
174
+
175
+ ## How it talks to the platform
176
+
177
+ REST + SSE go through the official Python SDK (`upivia`,
178
+ `packages/sdk-py`): `upivia_tui/client.py` is a thin adapter over
179
+ `upivia.AsyncUpiviaClient` (PAT auth). The realtime WebSocket
180
+ device link (`upivia_tui/wsclient.py`) speaks to the WS companion directly.
181
+
182
+ | Action | Endpoint |
183
+ |-------------------|-------------------------------------------|
184
+ | List agents | `GET /api/v1/agents` |
185
+ | Balance | `GET /api/v1/balance` |
186
+ | Create session | `POST /api/v1/chat/sessions` |
187
+ | Stream a turn | `POST /api/v1/chat/turn` (SSE) |
188
+
189
+ Delegation is driven by the `delegation_started` and `agent_event` SSE events
190
+ emitted by the chat route when Yupi spawns/delegates to worker agents.
191
+
192
+ ## Yupi delegates by default
193
+
194
+ The orchestration system prompt in `app/api/v1/chat/turn/route.ts` instructs
195
+ Yupi to delegate any non-trivial or multi-step work to specialist agents, only
196
+ working solo for genuinely trivial single-step actions.
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "upivia-tui"
3
+ version = "0.1.0"
4
+ description = "Upivia Textual TUI โ€” chat with Yupi, watch multi-agent delegation, reuse the platform's governed orchestration."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Upivia" }]
9
+ keywords = ["upivia", "ai-agents", "tui", "terminal", "cli", "agents", "governance"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Environment :: Console",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Terminals",
22
+ ]
23
+ dependencies = [
24
+ "textual>=0.80.0",
25
+ "httpx>=0.27.0",
26
+ "websockets>=12.0",
27
+ "playwright>=1.45",
28
+ "upivia>=0.2.0",
29
+ ]
30
+
31
+ [tool.uv.sources]
32
+ upivia = { path = "../sdk-py", editable = true }
33
+
34
+ [project.urls]
35
+ Homepage = "https://www.upivia.com"
36
+ Documentation = "https://www.upivia.com/docs"
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=8.0",
41
+ ]
42
+
43
+ [project.scripts]
44
+ upivia-tui = "upivia_tui.__main__:main"
45
+ upivia = "upivia_tui.__main__:main"
46
+
47
+ [build-system]
48
+ requires = ["hatchling"]
49
+ build-backend = "hatchling.build"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/upivia_tui"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
@@ -0,0 +1,11 @@
1
+ """Upivia Textual TUI.
2
+
3
+ A thin Python client that reuses the platform's governed chat-turn pipeline
4
+ (`POST /api/v1/chat/turn`, SSE streaming). All orchestration, budgets,
5
+ approvals, memory, and multi-agent delegation happen server-side โ€” this is a
6
+ presentation layer only.
7
+ """
8
+
9
+ from .version import __version__
10
+
11
+ __all__ = ["__version__"]