Add a chat list and server-minted new chats on top of the desk API's
GET/POST /desk/chats:
- server_client.list_chats() / create_chat(), same shape and error
handling as list_outbox_files().
- Switching is config.set_session(id): a plain reassignment of the
SESSION_ID every call reads, persisted to session.json. The window
refuses to switch mid-turn rather than locking.
- gui/chat_list_window.py: a Chats dialog (Ctrl+K) with a QListWidget,
populated on open, with Refresh and New Chat. Network calls run as
QRunnables on the global QThreadPool and come back through Signals,
never from a slot. The header New chat (Ctrl+N) uses the same path.
- Switching clears the local transcript; there is no endpoint for a
past chat's messages. Bolt's memory is per user, so it carries over.
- Terminal: /chats, /chat <n|id>, and /new (server-minted id, local
fallback).
This commit also carries the uncommitted work since 54030a7 that turned
this Bolt-Pet fork into Bolt Console: the pet, wake word, TTS and screen
features removed; a Claude Code-style terminal client; the PySide6
window with tool cards, approvals and diffs; mic dictation through the
server's /desk/stt relay; filectl/JSON repairs; and detection of
invented tool results.
Build output (build/, dist/, Release/, *.spec) is now ignored.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
123 lines
5.8 KiB
Markdown
123 lines
5.8 KiB
Markdown
# Bolt Console
|
|
|
|
A desktop app for Bolt, in the style of Claude Code (with a terminal version too). You type, Bolt
|
|
answers, and when he needs to look at or change something on this machine you see each step as it
|
|
happens: reading files, listing folders, editing, writing, running commands. Changes wait for your
|
|
**Allow** on the step itself, and edits show a diff first.
|
|
|
|
It talks to the same Bolt server as Bolt-Pet, the Discord bot and the phone app, so it has the same
|
|
brain, memory, tools and persona. What it drops is the desktop pet around it: no sprite, no wake
|
|
word, no microphone, no speech, no screen reading.
|
|
|
|
The window is PySide6 with its own dark theme. Header: working folder (click to change) and an
|
|
**Ask first / Auto** switch. Below that: the conversation, with each tool call as a card that you can
|
|
click to expand, and a composer at the bottom (Enter sends, Shift+Enter adds a new line, Ctrl+L
|
|
clears, Ctrl+O picks a folder).
|
|
|
|
**Chats** (Ctrl+K) lists every conversation you've had with Bolt, from any device (console, pet,
|
|
phone), newest first. Open one to continue it. **New chat** (Ctrl+N, or the button in the Chats
|
|
dialog) asks the server for a fresh chat: new conversation history, but Bolt still remembers you,
|
|
because his memory belongs to you, not to a chat. Switching clears the window's transcript, since
|
|
the server has no endpoint for an old chat's messages, although Bolt still has them. The current
|
|
chat is remembered across restarts. The terminal version has `/chats`, `/chat <n>` and `/new`.
|
|
|
|
**Dictation:** the mic button next to Send (or Ctrl+M) records you and types what you say into the box
|
|
as you talk. Click again or pause for 3 seconds to stop, and Esc cancels. The text isn't sent until you
|
|
press Enter, so you can fix it first. It uses the same speech-to-text as Bolt-Pet: the Bolt server's
|
|
`/desk/stt` relay to Deepgram, so you don't need a Deepgram account. The server does count it against
|
|
your credits like a chat turn. `MIC_DEVICE` in `.env` picks the microphone.
|
|
|
|
The terminal version (`run-cli.bat` / `./run.sh --cli`) looks like this:
|
|
|
|
```
|
|
> what's taking up space in this repo?
|
|
|
|
● List(C:\work\site, recursive)
|
|
⎿ assets/
|
|
assets/hero.mp4 48211332B
|
|
... +212 lines
|
|
● Shell(du -sh assets/* | sort -h | tail -5)
|
|
Run this command? [y]es / [n]o / [a]lways this session y
|
|
⎿ 48M assets/hero.mp4
|
|
...
|
|
● Most of it is one file: assets/hero.mp4 at 48 MB. ...
|
|
```
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
cp .env.example .env # set BOLT_SERVER_URL and DESK_API_KEY
|
|
run.bat # Windows: the window
|
|
run-cli.bat # Windows: the terminal version
|
|
./run.sh [--cli] # macOS/Linux
|
|
```
|
|
|
|
The launchers create `.venv` on first run. Dependencies: `requests`, plus `PySide6` for the window
|
|
(the terminal version doesn't import Qt). Bolt works in the
|
|
directory you **launch from**, so you can put the launcher on your PATH and start it inside whatever
|
|
project you want him to work on.
|
|
|
|
```bash
|
|
run.bat --auto # don't ask before commands, writes and edits
|
|
run-cli.bat -p "summarise README.md" # one message, print the reply, exit
|
|
run-cli.bat --doctor [--deep] # check the install (--deep contacts the server)
|
|
```
|
|
|
|
## Using the terminal version
|
|
|
|
| Input | What it does |
|
|
|---|---|
|
|
| any text | sent to Bolt |
|
|
| `!<command>` | runs a shell command yourself. Nothing is sent to Bolt |
|
|
| `/cd <dir>`, `/pwd` | change or show the working directory |
|
|
| `/ask`, `/auto` | turn approval prompts on or off for this session |
|
|
| `/status` | server, session, approval mode, working directory |
|
|
| `/files` | download anything Bolt has queued for you |
|
|
| `/new` | start a fresh chat (same as New chat in the window) |
|
|
| `/chats`, `/chat <n>` | list your chats from every device; switch to number n (or a session id) |
|
|
| `/clear` | clear the screen. Bolt's memory lives on the server and is kept |
|
|
| `/help`, `/exit` | |
|
|
| line ending in `\` | keep typing on the next line |
|
|
|
|
Ctrl+C stops the spinner and gives the prompt back. The server may still finish that turn on its
|
|
side. At an approval prompt, Ctrl+C means no.
|
|
|
|
## How Bolt reaches this machine
|
|
|
|
The desk API has one tool channel: the server sends back a `command` string, the client runs it and
|
|
posts the output to `/desk/tool_result`, and this repeats until a final reply. The console decides
|
|
what each string means (`bolt_console/tools.py`):
|
|
|
|
- **`filectl {json}`**: list, read, write or edit a file (`file_ops.py`). This is what makes file
|
|
editing reliable, because the model never has to hand-quote a shell heredoc. Edits must match
|
|
exactly once. Common JSON slips, such as a raw `C:\Users\...` path, are repaired and the model is
|
|
told about the repair.
|
|
- **`petctl …` / `dialoguectl …`**: the pet's body and voice. These are not run. Bolt is told
|
|
there's no pet here.
|
|
- **anything else**: a real shell command, run in the working directory, with a
|
|
`COMMAND_TIMEOUT_SECONDS` limit (default 120s).
|
|
|
|
Reads and listings never ask. Shell commands, writes and edits ask first in `ask` mode (the
|
|
default). Edits show a diff before you answer.
|
|
|
|
## Security
|
|
|
|
Anything you approve runs as your user, with your permissions. In `auto` mode that means anything
|
|
the server asks for. Keep `DESK_API_KEY` private and don't expose the desk API to the open internet.
|
|
Each message includes your working directory and OS name (`CONSOLE_CONTEXT=false` to stop that).
|
|
|
|
## Development
|
|
|
|
```bash
|
|
QT_QPA_PLATFORM=offscreen .venv/bin/pytest tests/ # Windows: .venv\Scripts\pytest tests
|
|
```
|
|
|
|
`tests/test_gui.py` drives turns through the real window and its worker thread against the same
|
|
loopback server.
|
|
|
|
`tests/test_console.py` runs whole turns against a real HTTP server on a loopback port. Only the
|
|
keyboard is faked. Everything else takes its collaborators as arguments, so no test needs a real
|
|
terminal or server.
|
|
|
|
This project started as a copy of Bolt-Pet. See that repo for the voice/desktop-pet version.
|