134 lines
11 KiB
Markdown
134 lines
11 KiB
Markdown
# LazyBoy
|
||
|
||
A local, general-purpose **GrokBot-like CLI agent**. It observes the environment, chooses tools, adapts to results, and verifies its work. Uses your existing xAI/OpenAI-compatible model configuration; no separate search API key.
|
||
|
||
For action tasks the agent briefly explains its approach, then starts. Multi-stage tasks get a short live plan. You can add instructions while it works, answer a question, or interrupt and resume. Simple questions remain simple answers.
|
||
|
||
## Start
|
||
|
||
```bash
|
||
# 可設項目與說明:複製 .env.example 成 .env 後填 LAZYBOY_API_KEY
|
||
# make start 會載入 .env
|
||
export LAZYBOY_API_KEY=your_key # or XAI_API_KEY
|
||
cargo run -p lazyboy -- agent
|
||
# API daemon (named Agents) + HTTP on :8787
|
||
cargo run -p lazyboy -- serve
|
||
# Independent frontend:
|
||
cd web && npm install && npm run dev
|
||
# Production UI: npm run build then lazyboy serve hosts web/dist
|
||
# Phone: same Wi-Fi, open the printed LAN URL, Add to Home Screen
|
||
# One-shot:
|
||
cargo run -p lazyboy -- run "整理目前目錄的資料,產生報告並讀回驗證"
|
||
# Resume:
|
||
cargo run -p lazyboy -- agent --session <id>
|
||
cargo run -p lazyboy -- run --session <id> "繼續"
|
||
```
|
||
|
||
While running, enter additional instructions to steer the next step. **Ctrl-C** or **`/stop`** stops the turn and saves its state. In the REPL, `/plan` shows the current plan, `/session` shows its ID, and `/exit` or `/quit` exits when idle. During work `/exit` and `/quit` stop the current turn first. At an explicit question, new text answers that question. Already queued idle inputs remain separate tasks.
|
||
|
||
Progress, plans and questions go to stderr; final answers go to stdout. `lazyboy chat` remains streaming chat without tools. `lazyboy web` serves the Grok Bot-shaped conversation UI (sidebar, bubbles, composer, **我的電腦** overlay) as a phone-installable PWA. `lazyboy help` lists commands and settings.
|
||
|
||
## Persistent agents and background work
|
||
|
||
Run `lazyboy serve` in one terminal, then create identities with `lazyboy agents create <name>` and open separate chats with `lazyboy agent --name <name>`. Agents learn private memories and public expertise from their conversations. They can delegate to an existing agent or create a temporary worker while you keep chatting.
|
||
|
||
In named-agent mode, ordinary text is new chat. Use `/tasks` and `/task <id> say|stop|resume` to manage background work. Closing a chat leaves the service and its tasks running. Agent identity and task ownership are separate: one agent can help another without losing its own conversation.
|
||
|
||
See [team setup and behavior](docs/TEAM.md) for commands, budgets, privacy boundaries and recovery. `LAZYBOY_DATA_DIR` defaults to `~/.lazyboy/team`; existing single-session commands keep their original behavior.
|
||
|
||
## Tools
|
||
|
||
| Capability | Tools |
|
||
| --- | --- |
|
||
| Voice | `send_message` (only user-visible channel; widget asks a question) |
|
||
| Progress and planning | `report_progress` (alias of send_message text), `update_plan` |
|
||
| Human interaction | `request_user_input`, `request_user_confirm`, `browser_handoff` |
|
||
| Completion | No-tool response ends the turn. `report_done` is optional. `report_blocked` when stuck. |
|
||
| Explicit local commands | `external_exec_command` (backgrounds after `block_until_ms`, default 30s), `external_await_command`, `external_write_stdin`, `external_shell` |
|
||
| Explicit local files | `external_list_dir`, `external_read_file`, `external_edit_file`, `external_write_file`, `external_grep` (regex), `external_glob`, `external_search_files` (literal) |
|
||
| Web | `web_fetch` (direct anonymous HTTP GET, HTML → text, short cache), `web_search` (remote search service); no browser login; Docker browser for login-gated pages |
|
||
| Background | `spawn_subagent` (returns immediately; `kind=computerUse` for desktop GUI), `check_subagent`, `message_subagent`, `stop_subagent`. Subagents and external command completion revive the turn; observe detached box commands with `await_shell`. |
|
||
| Desktop GUI | Parent: `screenshot` (read-only). Clicks: `spawn_subagent kind=computerUse`, which gets `computer` (screenshot/click/move/drag/type/key/scroll/wait via xdotool on `DISPLAY=:1`). One computerUse at a time. Passwords still `request_box_help`. |
|
||
| MCP | `get_mcp_tools`, `call_mcp_tool`, `get_mcp_server_status`, `add_mcp_server`, `remove_mcp_server`. Config: `~/.lazyboy/mcp.json` (or `LAZYBOY_MCP_CONFIG`). Prefer a connector over the browser for that service. |
|
||
| My computer (box) | Docker Linux desktop (XFCE + **Chromium** + 終端機 + xdotool). Default tools: `shell`, `read`, `await_shell`, `screenshot`. Profile: `/home/box/chrome-profile`; the browser helper connects inside Docker to the same desktop browser. Viewer:`lazyboy computer`。 |
|
||
| Browser observation | `browser_navigate`, `browser_snapshot`, `browser_read_page`, `browser_tabs` |
|
||
| Browser actions | `browser_click`, `browser_type`, `browser_press`, `browser_select`, `browser_scroll`, `browser_wait` |
|
||
| Browser file exchange | `browser_upload`, `browser_download` |
|
||
| Browser fallback | `browser_eval` |
|
||
|
||
Long commands return a session ID and incremental output; `external_write_stdin` polls, sends input, closes stdin or terminates the command. One foreground command at a time, piped I/O, default ten-minute deadline. The explicit local `external_shell` retains its 30-second limit. Default `shell` runs on the box and continues in the background when its foreground wait expires; use `await_shell` to observe it. Full command output is saved under `.lazyboy-output/`; large tool results are also stored there with a readable preview and path.
|
||
|
||
`external_read_file` accepts a zero-based line `offset` and line `limit`; `external_search_files` searches names or literal content; `external_edit_file` requires one unique exact match. File tools and browser file exchange check workspace paths and symlinks. Only `external_*` tools operate on the local machine; `shell` and `read` use the box.
|
||
|
||
## Browser surfaces
|
||
|
||
Docker mode includes Node, Playwright and Chromium in the image. The following setup is only for the explicit legacy local browser:
|
||
|
||
```bash
|
||
export LAZYBOY_BROWSER_SURFACE=local
|
||
cd tools/playwright
|
||
npm install
|
||
npx playwright install chromium
|
||
cd ../..
|
||
export LAZYBOY_BROWSER_HEADED=1 # visible browser, convenient for login handoff
|
||
cargo run -p lazyboy -- agent
|
||
```
|
||
|
||
Browser tools now default to **Docker Chromium**, attached to the desktop browser at `/home/box/chrome-profile`. Automation and human login share this profile; browser uploads/downloads use `/workspace` paths and explicit `copy_to_box`/`copy_from_box` transfer files. `browser_release` disconnects automation without closing the desktop. The old local browser is available only with `LAZYBOY_BROWSER_SURFACE=local`; its owner profiles are preserved and are not merged into Docker. See [surface comparison](docs/SURFACES.md) for differences and configuration.
|
||
|
||
`web_fetch` is a direct anonymous HTTP GET from the host: browser-like headers, no cookies, public hosts only (redirects are re-checked), body capped at 2 MiB, HTML reduced to readable text with link footnotes, non-UTF-8 charsets decoded, and results cached for ten minutes per URL. Only when a site refuses plain HTTP (401/403/429/503, or an empty JavaScript shell) and the model configuration is official xAI does it fall back to xAI native browsing; that result is marked `content_kind=model_rendered_web_content` and carries `fallback_reason`. `web_search` uses a remote search service with no browser cookies: official xAI configuration reuses the existing model API key for native web search; for the reconstructed AiService contract, set `LAZYBOY_WEB_BACKEND_URL` and, when required, `LAZYBOY_WEB_TOKEN`. That gateway must provide the reconstructed `RunWebSearch` JSON contract; a normal OpenAI-compatible chat endpoint alone does not provide it. Missing search configuration produces an explicit error, never a hidden browser fallback.
|
||
|
||
For login, OTP or captcha, `browser_handoff` shows the Docker viewer URL and parks the turn until you reply. For irreversible public actions, the agent follows the existing explicit-approval rule; plans themselves do not require approval.
|
||
|
||
## Settings
|
||
|
||
| Environment variable | Default / meaning |
|
||
| --- | --- |
|
||
| `LAZYBOY_API_KEY` | Falls back to `XAI_API_KEY`, then `OPENAI_API_KEY` |
|
||
| `LAZYBOY_BASE_URL` | `https://api.x.ai/v1`; fallback `OPENAI_BASE_URL` |
|
||
| `LAZYBOY_MODEL` | `grok-4.6` |
|
||
| `LAZYBOY_MAX_ROUNDS_TOTAL` | 5000 model steps **per user turn** (Grok Bot Sand-style). A step with no tool calls ends the turn. |
|
||
| `LAZYBOY_MAX_ROUNDS` | 12; local progress interval, no extra model calls |
|
||
| `LAZYBOY_CONTEXT_CHARS` | 100000 approximate UTF-8 bytes; legacy name |
|
||
| `LAZYBOY_SESSIONS_DIR` | `~/.lazyboy/sessions` |
|
||
| `LAZYBOY_PROGRESS` | `0` silences progress, but not questions |
|
||
| `LAZYBOY_BROWSER_HEADED` | `1` launches visible Chromium |
|
||
| `LAZYBOY_CONFIRM_AUTO` | Test-only approval/denial override; falls back to `LAZYBOY_HANDOFF_AUTO` |
|
||
| `LAZYBOY_HANDOFF_AUTO` | Test-only handoff override: `1` resume, `abort` deny |
|
||
|
||
Session stop reasons: `answer` (no tool calls — the Grok Bot end condition), `done` (optional `report_done`), `blocked`, `budget_exhausted`, `failed`, `cancelled`. One-shot exits 0 for answer/done, 1 for blocked/budget/failed, 130 for cancellation. The REPL remains usable after any turn outcome. Delivery is `send_message`; a no-tool response is not independent proof of arbitrary task correctness.
|
||
|
||
## Latency diagnostics
|
||
|
||
Set `LAZYBOY_TIMING=1` when starting the process to print elapsed milliseconds to
|
||
stderr for request preparation, model-slot queues, model rounds (including queue
|
||
wait), tool execution, and whole agent turns. These nested measurements must not
|
||
be added together. They contain no prompts, tool arguments, or credentials;
|
||
concurrent turns can interleave. Compare the same task and session length, and
|
||
count model rounds as well as wall time. This does not enable extra model calls.
|
||
|
||
Memory extraction starts only when no foreground chat is active and both
|
||
background slots are free; it acquires capacity without queuing. Deferred memory
|
||
remains durable and is retried by the scheduler. Already-running extraction is
|
||
allowed to finish, and continuous activity can delay memory updates. Work and
|
||
completion rules are unchanged: acknowledgement is not completion, required
|
||
results must still be observed, and sending a message alone does not end a turn.
|
||
|
||
## Validation
|
||
|
||
```bash
|
||
cargo test --workspace
|
||
cargo clippy --workspace --all-targets -- -D warnings
|
||
cargo build -p lazyboy
|
||
python3 tests/cli_flow.py
|
||
python3 tests/runtime_flow.py # includes a real 31-second command + local Chromium
|
||
python3 tests/browser_flow.py # headless Chromium, local fixture only
|
||
python3 tests/team_flow.py # daemon, two CLI clients, mock API + Chromium
|
||
cargo run -p lazyboy -- smoke
|
||
# Opt-in: uses your existing paid model configuration, max 12 requests:
|
||
python3 tests/live_cli.py
|
||
python3 tests/live_team.py # opt-in team workflow, root task ceiling 16
|
||
```
|
||
|
||
Design and Codex references: [CLI flow](docs/CLI-FLOW.md). Product scope: [PRODUCT](docs/PRODUCT.md). Acceptance scenarios: [ACCEPTANCE](docs/ACCEPTANCE.md). Older scenario playbooks remain optional examples, not hardcoded workflows: [scenarios](docs/scenarios/README.md).
|