From fc654aad0475ffa6653f3ef7b1b32b9b76f8bb69 Mon Sep 17 00:00:00 2001 From: Phelipe Lorran <112786693+httpsphl@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:33:45 -0300 Subject: [PATCH] feat(agents): a default agent, welcome screen for any agent, full access, README - Settings.defaultAgent (default_agent in config.toml): the agent of new bots, the chief of a new crew, template bots and the bots the chief suggests. Settings has a picker when more than one agent is enabled. - system.status.agentChecks: the first-run screen shows each enabled agent and no longer treats a missing Claude Code as an error when another agent is ready. - A 'run any command without asking' switch over the command list of an agy bot (command(*), checked with the real agy). - README, CLAUDE.md and spec section 1 no longer say Botloft is only for Claude Code. A slow catalog test gets a 20 s limit. --- CLAUDE.md | 2 +- README.md | 54 ++++++--- app/src/features/account/GeneralSettings.tsx | 19 +++ app/src/features/account/settings.test.tsx | 1 + app/src/features/account/settingsIndex.ts | 1 + app/src/features/account/useDaemonSettings.ts | 1 + app/src/features/bots/BotDialog.tsx | 34 ++++-- app/src/features/catalog/agency.test.tsx | 2 +- app/src/features/onboarding/Welcome.tsx | 40 ++++++- app/src/features/onboarding/agents.test.tsx | 75 ++++++++++++ app/src/i18n/en/account.ts | 3 + app/src/i18n/en/bots.ts | 5 + app/src/i18n/en/onboarding.ts | 5 +- app/src/i18n/es/account.ts | 3 + app/src/i18n/es/bots.ts | 5 + app/src/i18n/es/onboarding.ts | 5 +- app/src/i18n/pt-BR/account.ts | 3 + app/src/i18n/pt-BR/bots.ts | 5 + app/src/i18n/pt-BR/onboarding.ts | 5 +- app/src/lib/fake.ts | 9 +- app/src/lib/protocol.gen.ts | 26 +++- crates/botloft-core/src/protocol/export.rs | 1 + crates/botloft-core/src/protocol/methods.rs | 14 +++ crates/botloft-core/src/protocol/settings.rs | 8 ++ crates/botloftd/src/agent/mod.rs | 14 +++ crates/botloftd/src/agent/patchdefault.py | 112 ++++++++++++++++++ crates/botloftd/src/config.rs | 4 + crates/botloftd/src/service/bots.rs | 13 +- crates/botloftd/src/service/catalog.rs | 2 +- crates/botloftd/src/service/crews.rs | 2 +- crates/botloftd/src/service/mod.rs | 36 +++++- crates/botloftd/src/settings.rs | 13 +- crates/botloftd/src/tools/suggest.rs | 17 ++- crates/botloftd/tests/agy_bot.rs | 57 +++++++++ crates/botloftd/tests/settings.rs | 6 +- docs/spec.md | 8 +- 36 files changed, 555 insertions(+), 55 deletions(-) create mode 100644 app/src/features/onboarding/agents.test.tsx create mode 100644 crates/botloftd/src/agent/patchdefault.py diff --git a/CLAUDE.md b/CLAUDE.md index f10860e9..992a67fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Botloft -Workspace desktop Windows-first, com código público sob a FSL (vira Apache 2.0 após dois anos), para rodar bots Claude Code sempre ligados que colaboram entre si. Daemon Rust (`botloftd`) + app Tauri v2 + React. +Workspace desktop Windows-first, com código público sob a FSL (vira Apache 2.0 após dois anos), para rodar bots de agentes de código sempre ligados que colaboram entre si: Claude Code, Antigravity (experimental) e, em seguida, Codex. A seção 30 da spec e o ADR 0003 dizem como cada agente entra. Daemon Rust (`botloftd`) + app Tauri v2 + React. ## Fonte de verdade diff --git a/README.md b/README.md index 0d518568..f0720801 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

Botloft

- A crew of Claude Code bots that stays on and works together on your computer. + A crew of AI coding bots that stays on and works together on your computer, on the agent you prefer.

@@ -32,8 +32,9 @@ Botloft with the Research crew open: seven bots, each with its own color, state and job -Botloft keeps a crew of [Claude Code](https://code.claude.com) bots running on your computer. Each bot has -its own folder, memory and conversation. They pass work to each other, keep going after you close the +Botloft keeps a crew of bots running on your computer, each on the AI coding agent you choose: +[Claude Code](https://code.claude.com), or [Antigravity](https://antigravity.google) as an experimental +option, with Codex next. Each bot has its own folder, memory and conversation. They pass work to each other, keep going after you close the window and pick up where they left off after a restart. You talk to each one like in a chat app, and you decide what they may do. @@ -55,8 +56,8 @@ you decide what they may do. - **Leave them running.** Bots keep working after you close Botloft. They start when you sign in, come back soon after a crash and keep the computer awake while they work. Botloft waits near the clock and tells you when a bot needs you. -- **Choose per bot.** How much it asks before acting, and which Claude model it uses. See how much - of your plan's usage is left. +- **Choose per bot.** Which agent it runs on, which model it uses, and how much it asks before + acting. With Claude Code, see how much of your plan's usage is left. - **Back up and move.** Save a sealed backup file, or sign in with your e-mail (no password) to keep a light copy in the cloud and restore it on another computer. The server cannot read it. Turn on automatic copies, every day or week and only when something changed, if you want them to go by @@ -76,10 +77,26 @@ you decide what they may do. Botloft speaks English, Portuguese (Brazil) and Spanish, follows your light or dark theme and updates itself. +## Agents + +| | Claude Code | Antigravity (experimental) | +|---|---|---| +| Chat, crew messages, tasks, routines, the live browser | yes | chat, crew messages and tasks; the rest is not checked yet | +| Asks you before acting | yes: each tool, with "always allow" | no: it cannot ask. It works with files in its own folders; commands you list per bot (or "full access") | +| Images | yes | yes, by file | +| Model | your plan's default, or one you pick | its own list; the effort is part of the name | +| Usage meter and "Compact" | yes | an estimate of the conversation size; no compaction | +| Isolation between crews | yes | folders are fenced for its file tools, not for commands you allow | + +Claude Code is the full experience. Antigravity is off until you add `experimental_agents = ["agy"]` +to Botloft's `config.toml`; then a new bot can be made on it, and Settings has "Agent for new +bots". Codex is the next agent. See [spec section 30](docs/spec.md) for what was checked. + ## Get started -You need [Claude Code](https://code.claude.com/docs/en/setup), installed and signed in once with -your Claude account. Bots run on your own Claude plan. +You need at least one agent installed and signed in once, with your own account: +[Claude Code](https://code.claude.com/docs/en/setup), or Antigravity (experimental, see +[Agents](#agents)). Bots run on your own plan with that agent. 1. Download Botloft for your computer from the [latest release](https://github.com/httpsphl/botloft/releases/latest): @@ -112,15 +129,16 @@ app. Your bots and their files stay either way (see flowchart LR app["Botloft app
Tauri + React"] -- "JSON-RPC over WebSocket
127.0.0.1 only" --> daemon["botloftd
Rust daemon"] daemon --> db[("SQLite")] - daemon -- "stdin and stdout
stream-json" --> bots["One claude -p
per bot"] + daemon -- "stdin and stdout
stream-json" --> bots["One agent process
per bot"] bots -- "Botloft's MCP tools
messages, tasks, requests" --> daemon ``` - **The daemon is the source of truth.** Closing the app never stops a bot. `botloftd` runs as a scheduled task of your Windows user, a systemd user service on Linux or a launch agent on macOS: it starts when you sign in and comes back soon if it dies. -- **Claude Code is the runtime.** Each bot is a real `claude -p` session with its own workspace, - memory (`CLAUDE.md`) and conversation. There is no custom agent SDK and no API key to manage. +- **Your agent is the runtime.** Each bot is a real session of the agent you chose (`claude -p` for + Claude Code, `agy -p` for Antigravity) with its own workspace, memory and conversation. There is + no custom agent SDK and no API key to manage. - **One conversation per bot.** Your messages, messages from other bots and notices from the daemon reach the bot in order. What it does comes back as a chat: replies, tool use and requests you allow or deny. @@ -134,7 +152,7 @@ The full design, from the protocol to the states of a bot, lives in [`docs/spec. - **Local first.** The daemon listens on `127.0.0.1`, and there is no telemetry. An account is optional: without one, Botloft uses the network for nothing beyond updates and what bots send to - Claude, which goes through Claude Code as when you use it yourself. With one, the server only ever + the agent's provider, which goes through the agent as when you use it yourself. With one, the server only ever holds a backup sealed with a password that never leaves your computer (see the FAQ). - **You decide what bots may do.** Per bot: ask before anything, accept edits, plan only, or decide on its own what needs your OK. Unless it decides on its own, a bot also asks before its first visit @@ -143,7 +161,7 @@ The full design, from the protocol to the states of a bot, lives in [`docs/spec. Each bot gets its own token per start, and the daemon keeps only its SHA-256 hash. - **Botloft is not a sandbox.** Every bot runs as your user account, so isolation between bots is cooperative: a bot can read other bots' folders if it decides to. Give bots only the permissions - you would give Claude Code directly. + you would give that agent directly. What Botloft sends over the network, what it changes on your computer and how to remove it are in the [code signing policy](docs/code-signing-policy.md). @@ -154,8 +172,8 @@ the [code signing policy](docs/code-signing-policy.md).

Does Botloft cost anything? No. Botloft is free to use, at home or at work, and its source code is public (see -[License](#license)). The bots run on Claude Code with -your own Claude account, so they use your plan's limits; Botloft shows how much is left. +[License](#license)). The bots run on the agent you chose, with +your own account, so they use your plan's limits; with Claude Code, Botloft shows how much is left.
@@ -212,8 +230,8 @@ Windows came first and has had the most use; if something does not work elsewher
Is Botloft made by Anthropic? -No. Botloft is an independent project. It uses Claude Code as installed on your computer -and is not affiliated with or endorsed by Anthropic. +No. Botloft is an independent project. It uses Claude Code and Antigravity as installed on your +computer and is not affiliated with or endorsed by Anthropic or Google.
## Status @@ -228,8 +246,8 @@ and a code-signed installer. `main` can be ahead of the latest release. Requirements: Rust stable (the MSVC toolchain on Windows), Node.js 24 and pnpm. On Linux, the app also needs the desktop libraries Tauri uses (on Ubuntu: `libwebkit2gtk-4.1-dev`, -`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real bots also needs Claude -Code (the native `claude` executable) installed and signed in. +`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real bots also needs an agent +installed and signed in: Claude Code (the native `claude` executable) or Antigravity (`agy`). ```powershell # Daemon and libraries diff --git a/app/src/features/account/GeneralSettings.tsx b/app/src/features/account/GeneralSettings.tsx index 874f52ee..054c988b 100644 --- a/app/src/features/account/GeneralSettings.tsx +++ b/app/src/features/account/GeneralSettings.tsx @@ -9,12 +9,16 @@ import { useLocale, useT, } from "../../i18n"; +import type { AgentKind } from "../../lib/protocol.gen"; import { SYSTEM } from "../../lib/system"; import { prefs, usePref } from "../../shell/prefs"; +import { useApp } from "../../store/context"; import { Select } from "../../ui/Select"; import { Section, Toggle } from "./settingsParts"; import { useDaemonSettings } from "./useDaemonSettings"; +const NO_AGENTS: AgentKind[] = []; + export function GeneralSettings() { const t = useT(); const s = t.account.settings; @@ -23,6 +27,7 @@ export function GeneralSettings() { const openAtSignIn = usePref(prefs.openAtSignIn); const nearClock = whenClosed === "keep" && tray; const { settings, change } = useDaemonSettings(); + const enabledAgents = useApp((state) => state.system?.enabledAgents ?? NO_AGENTS); const startWithWindows = settings?.startWithWindows ?? true; const { choice: locale } = useLocale(); const systemName = LOCALES.find((entry) => entry.id === systemLocale())?.name ?? "English"; @@ -36,6 +41,20 @@ export function GeneralSettings() {
({ + value: agent, + label: t.bots.dialog.agent.names[agent], + }))} + onChange={(agent) => change({ defaultAgent: agent })} + /> +

{s.defaultAgentHint}

+
+ )}
{ startWithWindows: false, keepAwake: false, approvalWaitMinutes: 60, + defaultAgent: "claude", }), ); expect( diff --git a/app/src/features/account/settingsIndex.ts b/app/src/features/account/settingsIndex.ts index 454d1591..e64b9194 100644 --- a/app/src/features/account/settingsIndex.ts +++ b/app/src/features/account/settingsIndex.ts @@ -32,6 +32,7 @@ export function settingsIndex(t: ReturnType): Entry[] { ...entry( "general", s.language, + s.defaultAgent, s.background, s.keepWorking, s.tray, diff --git a/app/src/features/account/useDaemonSettings.ts b/app/src/features/account/useDaemonSettings.ts index abd0c489..08854d63 100644 --- a/app/src/features/account/useDaemonSettings.ts +++ b/app/src/features/account/useDaemonSettings.ts @@ -23,6 +23,7 @@ export function useDaemonSettings(): { startWithWindows: update.startWithWindows ?? before.startWithWindows, keepAwake: update.keepAwake ?? before.keepAwake, approvalWaitMinutes: update.approvalWaitMinutes ?? before.approvalWaitMinutes, + defaultAgent: update.defaultAgent ?? before.defaultAgent, }); } attempt(failed, async () => putSettings(await api.call("settings.update", update))).then( diff --git a/app/src/features/bots/BotDialog.tsx b/app/src/features/bots/BotDialog.tsx index b94e481a..0bc8646d 100644 --- a/app/src/features/bots/BotDialog.tsx +++ b/app/src/features/bots/BotDialog.tsx @@ -14,6 +14,7 @@ import { useApi, useApp } from "../../store/context"; import { Button } from "../../ui/Button"; import { Dialog } from "../../ui/Dialog"; import { SelectField, TextArea, TextField } from "../../ui/Field"; +import { Toggle } from "../account/settingsParts"; import { BotAvatar } from "./BotAvatar"; import { ColorPicker } from "./ColorPicker"; @@ -41,8 +42,13 @@ export function BotDialog(props: Props) { const [commands, setCommands] = useState((editing?.allowedCommands ?? []).join("\n")); // Which agent runs the bot is chosen once, when it is made (spec 30). const agents = useApp((state) => state.system?.enabledAgents ?? CLAUDE_ONLY); - const [agent, setAgent] = useState("claude"); + const preferred = useApp((state) => state.settings?.defaultAgent ?? "claude"); + const [agent, setAgent] = useState(agents.includes(preferred) ? preferred : "claude"); const runsOn: AgentKind = editing?.agent ?? agent; + const fullAccess = commands + .split("\n") + .map((line) => line.trim()) + .includes("*"); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); @@ -148,14 +154,24 @@ export function BotDialog(props: Props) { )} {editing && editing.agent !== "claude" && ( -