Skip to content

Commit c60dcad

Browse files
authored
Merge pull request #249 from httpsphl/feat/codex-agent
feat(agents): Codex bots, experimental (A3a of spec 30)
2 parents 41fe34d + 93d126d commit c60dcad

22 files changed

Lines changed: 1369 additions & 54 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: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
</picture>
3434

3535
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 [Antigravity](https://antigravity.google) as an experimental
37-
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
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 has its own folder, memory and conversation. They pass work to each other, keep going after you close the
3838
window and pick up where they left off after a restart. You talk to each one like in a chat app, and
3939
you decide what they may do.
4040

@@ -88,15 +88,18 @@ itself.
8888
| Usage meter and "Compact" | yes | an estimate of the conversation size; no compaction |
8989
| Isolation between crews | yes | folders are fenced for its file tools, not for commands you allow |
9090

91-
Claude Code is the full experience. Antigravity is off until you add `experimental_agents = ["agy"]`
92-
to Botloft's `config.toml`; then a new bot can be made on it, and Settings has "Agent for new
93-
bots". Codex is the next agent. See [spec section 30](docs/spec.md) for what was checked.
91+
Claude Code is the full experience. Antigravity and Codex are off until you list them in Botloft's
92+
`config.toml` (`experimental_agents = ["agy"]`, `["codex"]` or both); then a new bot can be made on
93+
them, and Settings has "Agent for new bots". A Codex bot reads files, uses the crew's tools and
94+
answers, with the exact size of its conversation; it cannot change files or run commands yet, and
95+
asking you before it acts is the next step. See [spec section 30](docs/spec.md) for what was
96+
checked.
9497

9598
## Get started
9699

97100
You need at least one agent installed and signed in once, with your own account:
98-
[Claude Code](https://code.claude.com/docs/en/setup), or Antigravity (experimental, see
99-
[Agents](#agents)). Bots run on your own plan with that agent.
101+
[Claude Code](https://code.claude.com/docs/en/setup), Antigravity or Codex
102+
(experimental, see [Agents](#agents)). Bots run on your own plan with that agent.
100103

101104
1. Download Botloft for your computer from the
102105
[latest release](https://github.com/httpsphl/botloft/releases/latest):
@@ -137,7 +140,7 @@ flowchart LR
137140
scheduled task of your Windows user, a systemd user service on Linux or a launch agent on macOS:
138141
it starts when you sign in and comes back soon if it dies.
139142
- **Your agent is the runtime.** Each bot is a real session of the agent you chose (`claude -p` for
140-
Claude Code, `agy -p` for Antigravity) with its own workspace, memory and conversation. There is
143+
Claude Code, `agy -p` for Antigravity, `codex app-server` for Codex) with its own workspace, memory and conversation. There is
141144
no custom agent SDK and no API key to manage.
142145
- **One conversation per bot.** Your messages, messages from other bots and notices from the daemon
143146
reach the bot in order. What it does comes back as a chat: replies, tool use and requests you
@@ -230,8 +233,8 @@ Windows came first and has had the most use; if something does not work elsewher
230233
<details>
231234
<summary><b>Is Botloft made by Anthropic?</b></summary>
232235

233-
No. Botloft is an independent project. It uses Claude Code and Antigravity as installed on your
234-
computer and is not affiliated with or endorsed by Anthropic or Google.
236+
No. Botloft is an independent project. It uses Claude Code, Antigravity and Codex as installed on your
237+
computer and is not affiliated with or endorsed by Anthropic, Google or OpenAI.
235238
</details>
236239

237240
## Status
@@ -247,7 +250,7 @@ and a code-signed installer. `main` can be ahead of the latest release.
247250
Requirements: Rust stable (the MSVC toolchain on Windows), Node.js 24 and pnpm. On Linux, the app
248251
also needs the desktop libraries Tauri uses (on Ubuntu: `libwebkit2gtk-4.1-dev`,
249252
`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real bots also needs an agent
250-
installed and signed in: Claude Code (the native `claude` executable) or Antigravity (`agy`).
253+
installed and signed in: Claude Code (the native `claude` executable), Antigravity (`agy`) or Codex.
251254

252255
```powershell
253256
# 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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,19 @@ 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(/cannot change files or run commands yet/);
46+
expect(dialog.queryByLabelText("Model")).toBeNull();
47+
});
48+
});

‎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 can read files, use the crew's tools and answer, but it cannot change files or run commands yet. Asking you before it acts is coming next.",
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 lee archivos, usa las herramientas del equipo y responde, pero todavía no cambia archivos ni ejecuta comandos. Preguntarte antes de actuar viene a continuació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 lê arquivos, usa as ferramentas da equipe e responde, mas ainda não muda arquivos nem roda comandos. Perguntar a você antes de agir vem a seguir.",
116118
},
117119
color: "Cor",
118120
swatch: (color: string) => `Cor ${color}`,

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,7 @@ impl Agent for AgyAgent {
115115
cli.join("settings.json"),
116116
pretty(&settings(files.fenced, files.allowed_commands)),
117117
)?;
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(())
118+
super::copy_rules_to_agents_md(files.workspace)
129119
}
130120

131121
fn speaks_control(&self) -> bool {

0 commit comments

Comments
 (0)