Skip to content

Commit 729aa54

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/pr248
# Conflicts: # README.md
2 parents ac18bac + 62c9467 commit 729aa54

25 files changed

Lines changed: 1771 additions & 67 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Botloft
22

3-
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.
3+
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 e Codex (os dois últimos experimentais). A seção 30 da spec e o ADR 0003 dizem como cada agente entra. Daemon Rust (`botloftd`) + app Tauri v2 + React.
44

55
## Fonte de verdade
66

‎README.md‎

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
<img src="docs/assets/hero-light.png" alt="Botloft with the Research crew open: seven bots, each with its own color, state and job">
3333
</picture>
3434

35-
Botloft keeps a crew of [Claude Code](https://code.claude.com) bots running on your computer. Each
36-
bot has its own folder, memory and conversation. They pass work to each other, keep going after you
35+
Botloft keeps a crew of bots running on your computer, each on the AI coding agent you choose:
36+
[Claude Code](https://code.claude.com), or, as experimental options,
37+
[Antigravity](https://antigravity.google) and [Codex](https://developers.openai.com/codex). Each bot
38+
has its own folder, memory and conversation. They pass work to each other, keep going after you
3739
close the window and pick up where they left off after a restart. You talk to each one like in a
3840
chat app, and you decide what they may do.
3941

40-
[Antigravity](https://antigravity.google) works as an experimental second agent on `main`, but no
41-
release has it yet. Codex is next (see [Agents](#agents)).
42+
The latest release, 0.14.0, runs bots on Claude Code. Antigravity and Codex are on `main` and in no
43+
release yet (see [Agents](#agents)).
4244

4345
## What you can do
4446

@@ -103,8 +105,8 @@ itself.
103105

104106
## Agents
105107

106-
Antigravity is only on `main` for now: no release has it yet. In a build of `main`, the two agents
107-
compare like this:
108+
Antigravity and Codex are only on `main` for now: no release has them yet. In a build of `main`,
109+
Claude Code and Antigravity compare like this:
108110

109111
| | Claude Code | Antigravity (experimental) |
110112
|---|---|---|
@@ -115,15 +117,18 @@ compare like this:
115117
| Usage meter and "Compact" | yes | an estimate of the conversation size; no compaction |
116118
| Isolation between crews | yes | folders are fenced for its file tools, not for commands you allow |
117119

118-
Claude Code is the full experience. Antigravity is off until you add `experimental_agents = ["agy"]`
119-
to Botloft's `config.toml`; then a new bot can be made on it, and Settings has "Agent for new
120-
bots". Codex is the next agent. See [spec section 30](docs/spec.md) for what was checked.
120+
Claude Code is the full experience. Antigravity and Codex are off until you list them in Botloft's
121+
`config.toml` (`experimental_agents = ["agy"]`, `["codex"]` or both); then a new bot can be made on
122+
them, and Settings has "Agent for new bots". A Codex bot asks you before it changes a file or runs a
123+
command, like Claude Code, and shows the exact size of its conversation; it can still read any file,
124+
and it has no model picker or images yet. See [spec section 30](docs/spec.md) for what was
125+
checked.
121126

122127
## Get started
123128

124129
You need [Claude Code](https://code.claude.com/docs/en/setup), installed and signed in once with
125-
your own account. Bots run on your own plan. (Antigravity, the experimental second agent, is only on
126-
`main` for now; see [Agents](#agents).)
130+
your own account. Bots run on your own plan. (Antigravity and Codex, the experimental agents, are
131+
only on `main` for now; see [Agents](#agents).)
127132

128133
1. Download Botloft for your computer from the
129134
[latest release](https://github.com/httpsphl/botloft/releases/latest):
@@ -163,9 +168,9 @@ flowchart LR
163168
- **The daemon is the source of truth.** Closing the app never stops a bot. `botloftd` runs as a
164169
scheduled task of your Windows user, a systemd user service on Linux or a launch agent on macOS:
165170
it starts when you sign in and comes back soon if it dies.
166-
- **Your agent is the runtime.** Each bot is a real session of its agent (`claude -p` for Claude
167-
Code; `agy -p` for Antigravity, on `main` only) with its own workspace, memory and conversation.
168-
There is no custom agent SDK and no API key to manage.
171+
- **Your agent is the runtime.** Each bot is a real session of the agent you chose (`claude -p` for
172+
Claude Code; `agy -p` for Antigravity and `codex app-server` for Codex, on `main` only) with its
173+
own workspace, memory and conversation. There is no custom agent SDK and no API key to manage.
169174
- **One conversation per bot.** Your messages, messages from other bots and notices from the daemon
170175
reach the bot in order. What it does comes back as a chat: replies, tool use and requests you
171176
allow or deny.
@@ -262,8 +267,8 @@ Windows came first and has had the most use; if something does not work elsewher
262267
<details>
263268
<summary><b>Is Botloft made by Anthropic?</b></summary>
264269

265-
No. Botloft is an independent project. It uses Claude Code (and, on `main`, Antigravity) as
266-
installed on your computer and is not affiliated with or endorsed by Anthropic or Google.
270+
No. Botloft is an independent project. It uses Claude Code (and, on `main`, Antigravity and Codex)
271+
as installed on your computer and is not affiliated with or endorsed by Anthropic, Google or OpenAI.
267272
</details>
268273

269274
## Status
@@ -273,16 +278,16 @@ that keep running in the background, the chat, crews with a Chief, ready-made bo
273278
agency, messages and tasks between bots, routines, the live browser, screens, "always allow" for
274279
requests, questions from bots, chat search, desktop use on Windows, connected tools, backups with an
275280
optional cloud copy, the phone page, the installer and updates. Still open: a code-signed installer.
276-
`main` is ahead of the latest release; among other things, it has Antigravity as an experimental
277-
agent.
281+
`main` is ahead of the latest release; among other things, it has Antigravity and Codex as
282+
experimental agents.
278283

279284
<details>
280285
<summary><b>Development</b></summary>
281286

282287
Requirements: Rust stable (the MSVC toolchain on Windows), Node.js 24 and pnpm. On Linux, the app
283288
also needs the desktop libraries Tauri uses (on Ubuntu: `libwebkit2gtk-4.1-dev`,
284289
`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real bots also needs an agent
285-
installed and signed in: Claude Code (the native `claude` executable) or Antigravity (`agy`).
290+
installed and signed in: Claude Code (the native `claude` executable), Antigravity (`agy`) or Codex.
286291

287292
```powershell
288293
# Daemon and libraries

‎app/src/features/bots/BotDialog.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,14 @@ export function BotDialog(props: Props) {
148148
/>
149149
{agent !== "claude" && (
150150
<p role="note" className="rounded-lg bg-sunken p-2.5 text-ink-soft text-xs">
151-
{t.bots.dialog.agent.experimental}
151+
{agent === "codex"
152+
? t.bots.dialog.agent.experimentalCodex
153+
: t.bots.dialog.agent.experimental}
152154
</p>
153155
)}
154156
</div>
155157
)}
156-
{editing && editing.agent !== "claude" && (
158+
{editing && editing.agent === "agy" && (
157159
<>
158160
<Toggle
159161
label={t.bots.dialog.fullAccess.title}

‎app/src/features/bots/agentPicker.test.tsx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,21 @@ describe("choosing the agent of a new bot", () => {
3030
expect(dialog.queryByLabelText("Model")).toBeNull();
3131
});
3232
});
33+
34+
describe("Codex in the new-bot dialog", () => {
35+
test("says what it cannot do yet", async () => {
36+
const fake = new FakeBotloft();
37+
fake.system = { ...fake.system, enabledAgents: ["claude", "codex"] };
38+
fake.addCrew("Ops");
39+
renderApp(fake);
40+
await crewOpened("Ops");
41+
const [button] = await screen.findAllByRole("button", { name: "New bot" });
42+
fireEvent.click(button as HTMLElement);
43+
const dialog = within(await screen.findByRole("dialog", { name: "New bot" }));
44+
fireEvent.change(dialog.getByLabelText("Agent"), { target: { value: "codex" } });
45+
expect(dialog.getByRole("note").textContent).toMatch(
46+
/asks you before it changes a file or runs a command/,
47+
);
48+
expect(dialog.queryByLabelText("Model")).toBeNull();
49+
});
50+
});

‎app/src/features/chat/ChatComposer.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export const ChatComposer = memo(function ChatComposer({
254254
<ModelPicker bot={bot} onLater={setLater} />
255255
</>
256256
) : (
257-
<AgentModelPicker bot={bot} onLater={setLater} />
257+
bot.agent === "agy" && <AgentModelPicker bot={bot} onLater={setLater} />
258258
)}
259259
<button
260260
type="submit"

‎app/src/i18n/en/bots.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ export const bots = {
116116
},
117117
experimental:
118118
"Experimental. This bot cannot ask you before it acts. It can work with files in its own folders and use the crew's tools, but it cannot run commands, and it has no usage meter or model choice.",
119+
experimentalCodex:
120+
"Experimental. This bot asks you before it changes a file or runs a command, like Claude Code. It can still read any file, and it has no model or image choice yet.",
119121
},
120122
color: "Color",
121123
swatch: (color: string) => `Color ${color}`,

‎app/src/i18n/es/bots.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export const bots: Messages["bots"] = {
114114
},
115115
experimental:
116116
"Experimental. Este bot no puede preguntarte antes de actuar. Trabaja con archivos de sus propias carpetas y usa las herramientas del equipo, pero no ejecuta comandos, y no tiene medidor de uso ni elección de modelo.",
117+
experimentalCodex:
118+
"Experimental. Este bot te pregunta antes de cambiar un archivo o ejecutar un comando, como Claude Code. Todavía puede leer cualquier archivo, y no tiene elección de modelo ni de imagen aún.",
117119
},
118120
color: "Color",
119121
swatch: (color: string) => `Color ${color}`,

‎app/src/i18n/pt-BR/bots.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ export const bots: Messages["bots"] = {
113113
},
114114
experimental:
115115
"Experimental. Este bot não consegue perguntar a você antes de agir. Ele trabalha com arquivos das próprias pastas e usa as ferramentas da equipe, mas não roda comandos, e não tem medidor de uso nem escolha de modelo.",
116+
experimentalCodex:
117+
"Experimental. Este bot pergunta a você antes de mudar um arquivo ou rodar um comando, como o Claude Code. Ele ainda pode ler qualquer arquivo, e não tem escolha de modelo nem de imagem ainda.",
116118
},
117119
color: "Cor",
118120
swatch: (color: string) => `Cor ${color}`,

‎crates/botloftd/src/agent/agy.rs‎

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use std::ffi::OsString;
1212
use std::io;
1313
use std::path::{Path, PathBuf};
14+
use std::sync::Arc;
1415

1516
use botloft_core::ids::BotId;
1617
use botloft_core::protocol::{AgentKind, TokenUsage};
@@ -115,17 +116,7 @@ impl Agent for AgyAgent {
115116
cli.join("settings.json"),
116117
pretty(&settings(files.fenced, files.allowed_commands)),
117118
)?;
118-
// The rules `prepare_bot` wrote for Claude Code are the bot's rules;
119-
// `agy` reads `AGENTS.md` from its folder.
120-
let rules = files
121-
.workspace
122-
.join(".claude")
123-
.join("rules")
124-
.join("botloft.md");
125-
if let Ok(text) = std::fs::read_to_string(rules) {
126-
std::fs::write(files.workspace.join("AGENTS.md"), text)?;
127-
}
128-
Ok(())
119+
super::copy_rules_to_agents_md(files.workspace)
129120
}
130121

131122
fn speaks_control(&self) -> bool {
@@ -209,7 +200,7 @@ impl OutputDecoder for AgyDecoder {
209200
Some(text)
210201
}
211202

212-
fn handle(&mut self, daemon: &Daemon, bot: &BotId, generation: u64, event: &Value) {
203+
fn handle(&mut self, daemon: &Arc<Daemon>, bot: &BotId, generation: u64, event: &Value) {
213204
if !daemon.supervisor.is_current(bot, generation) {
214205
return;
215206
}

‎crates/botloftd/src/agent/claude.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::ffi::OsString;
55
use std::io;
66
use std::path::{Path, PathBuf};
7+
use std::sync::Arc;
78

89
use botloft_core::ids::BotId;
910
use botloft_core::protocol::AgentKind;
@@ -143,7 +144,7 @@ impl OutputDecoder for ClaudeDecoder {
143144
claude::live_text(event)
144145
}
145146

146-
fn handle(&mut self, daemon: &Daemon, bot: &BotId, generation: u64, event: &Value) {
147+
fn handle(&mut self, daemon: &Arc<Daemon>, bot: &BotId, generation: u64, event: &Value) {
147148
claude::apply(daemon, bot, generation, event);
148149
}
149150
}

0 commit comments

Comments
 (0)