From 6dbd914fb9416ef8db019b1b234cbb4b647aa536 Mon Sep 17 00:00:00 2001
From: Phelipe Lorran <112786693+httpsphl@users.noreply.github.com>
Date: Sat, 10 Oct 2026 18:53:00 -0300
Subject: [PATCH] feat(agents): Codex login and limit states, compaction and
connected tools (A3d)
- account/read at the handshake and codexErrorInfo unauthorized end in
auth_error with a notice; usageLimitExceeded pauses the bot until the
window's resetsAt.
- bots.compact works for Codex: thread/compact/start through the wrapper;
the compaction turn shows a notice and no turn line.
- The owner's connected tools go in the thread's mcp_servers, secrets in
place; a third-party tool's name is read from the request's message.
Checked with the real codex: compaction through the daemon.
---
app/src/features/chat/ContextMeter.tsx | 18 ++-
app/src/features/chat/agentBot.test.tsx | 23 +++
app/src/i18n/en/chat.ts | 1 +
app/src/i18n/es/chat.ts | 1 +
app/src/i18n/pt-BR/chat.ts | 1 +
crates/botloftd/src/agent/codex.rs | 4 +
crates/botloftd/src/agent/codex_decoder.rs | 117 +++++++++++++-
crates/botloftd/src/agent/codex_wire.rs | 71 +++++++--
crates/botloftd/src/agent/mod.rs | 9 ++
crates/botloftd/src/context/mod.rs | 32 ++--
crates/botloftd/src/supervisor/spawn.rs | 3 +
crates/botloftd/src/workspace/connected.rs | 70 +++++++++
crates/botloftd/src/workspace/mod.rs | 2 +-
crates/botloftd/tests/codex_bot.rs | 171 +++++++++++++++++++++
docs/spec.md | 7 +-
15 files changed, 497 insertions(+), 33 deletions(-)
diff --git a/app/src/features/chat/ContextMeter.tsx b/app/src/features/chat/ContextMeter.tsx
index 7ba43671..c21e6a27 100644
--- a/app/src/features/chat/ContextMeter.tsx
+++ b/app/src/features/chat/ContextMeter.tsx
@@ -39,6 +39,8 @@ export function ContextMeter({ bot, onLater }: { bot: Bot; onLater(text: string)
const near = usedTokens >= limit * NEAR;
const left = autoCompactTokens === null ? null : autoCompactTokens - usedTokens;
const claude = bot.agent === "claude";
+ // Claude Code and Codex answer a request to compact (spec 30).
+ const compactable = claude || bot.agent === "codex";
const running = bot.state === "idle" || bot.state === "busy" || bot.state === "needs_approval";
const compact = async () => {
@@ -77,17 +79,19 @@ export function ContextMeter({ bot, onLater }: { bot: Bot; onLater(text: string)
)}
- {!claude
+ {!compactable
? m.estimate
- : left === null
- ? m.noAuto
- : left > 0
- ? m.autoLeft(tokens(left), tokens(autoCompactTokens ?? 0))
- : m.autoNow}
+ : bot.agent === "codex"
+ ? m.byItself
+ : left === null
+ ? m.noAuto
+ : left > 0
+ ? m.autoLeft(tokens(left), tokens(autoCompactTokens ?? 0))
+ : m.autoNow}
{m.about(bot.name)}
{/* Only Claude Code answers a request to compact (spec 30). */}
- {claude && (
+ {compactable && (