Skip to content

feat(daemon): effort per bot, the context in use and compacting the conversation - #63

Merged
httpsphl merged 13 commits into
mainfrom
feat/bot-effort-context
Sep 30, 2026
Merged

httpsphl merged 13 commits into
mainfrom
feat/bot-effort-context

Conversation

@httpsphl

Copy link
Copy Markdown
Owner

The daemon half of two things the owner wants in each bot's chat: an effort level per bot, and the context window in use with a way to compact it. The app half (the slider and the meter in the composer) comes in the next PR, on top of this one.

What Claude Code does

Checked in the official docs and with real tests on 2.1.284; the details are in spec section 19.

  • Effort. --effort low|medium|high|xhigh|max works with -p, lasts for the session and is not kept on --resume. Without it, Sonnet 5.5 and Opus 5.5 run at medium and Fable 5.1 at high. Haiku 4.5 has no effort levels and ignores the flag without an error.
  • Context. A control request on stdin (get_context_usage, the Agent SDK's getContextUsage()) answers with the tokens in use, the window and the point where Claude Code compacts by itself (967k of 1M, 167k of 200k). It answers before the first turn and during a turn, and opens no turn. Each assistant event also carries the usage of its model request.
  • Compact. /compact written as a message compacts a headless session and waits behind a running turn. Claude Code compacting by itself prints the same events in the middle of a turn.

What changes

  • Effort is stored on the bot (bots.effort, migration 0010) and set with bots.setEffort. The bot restarts with --effort when nothing is in progress, like the model and the mode. default leaves the flag out.
  • When a process starts, the daemon asks what the session applies (get_settings). The model is now known before the first turn, and Bot.effortDefault holds the level the model uses by itself, or none for a model without levels, so the app can mark the recommended level.
  • Context: Bot.context (usedTokens, windowTokens, autoCompactTokens, compacting) and a bot.context notification. The daemon asks when a process starts, when a turn ends and after a compaction, and follows each model request in between. It is kept in memory only.
  • Compact: bots.compact writes /compact to the bot's process. The chat gets a notice (compacted, auto_compacted, compact_failed) instead of a turn line, and these notices do not change the conversation-list line.
  • Spec 7.4, 8.1, 8.2, 9.2, 11.2, 11.3 and 12, a new 8.6 "Contexto da conversa", and the findings in section 19.
  • chat/events.rs was over 300 lines: the account handlers moved to chat/account.rs. The store's bot tests moved to bots/tests.rs for the same reason.
  • The app only gains what it needs to compile: the new fields and the two methods in FakeBotloft.

Decisions

  • Claude Code also takes a new effort in the middle of a session (/effort low as a message, or a control request). The daemon restarts the process instead, as it does for the model: the command line stays the only source of what a bot runs.
  • The daemon only asks through control requests. Their wire format is not documented, so whatever it cannot learn stays empty in the app and nothing about the bot's work depends on it.

Tests

cargo fmt, clippy -D warnings, cargo test --workspace and pnpm check pass. New suites on FakeRuntime: bot_effort, context, compaction, rpc_effort_context.

Seen with the real Claude Code 2.1.284 on a dev daemon (Haiku, then Sonnet):

  • the model and effortDefault (none on Haiku, medium on Sonnet) reached the app before the first turn;
  • the context came when the bot started, moved on each model request and at the end of the turn;
  • bots.compact left the bot busy, the compacted notice entered the chat, the size dropped, and the bot still answered the word it had been told to remember;
  • after bots.setModel and bots.setEffort the bot's command line had --model sonnet --effort low;
  • the debug log had only ids and numbers.

Still to see

  • A level the model does not have (xhigh on an older model): not reachable with the aliases Botloft offers.
  • A compaction that fails on an API error.
  • Found on the way and not handled here (section 19, "CLAUDE.md do dono nos bots"): the owner's ~/.claude/CLAUDE.md is loaded by bots whose workspace sits inside the user folder.

Merging

This touches protocol/mod.rs, protocol/model.rs, export.rs, dispatch.rs, chat/events.rs, docs/spec.md and protocol.gen.ts, like #54 and #61. Whichever merges second needs cargo test -p botloft-core export_bindings again after resolving.

Delete is the last action of the bot's menu (header and right-click) and of the crew's menu. The confirmation names the bot or crew and says what goes (the conversation, routines and tasks, for good) and what stays (the folder, with its path). A message from a bot that left, archived or deleted, reads as from a bot no longer in the crew.
The other lists leave archived crews and bots out, so nothing showed them. archive.list returns both, the latest archived first, for the app to list and delete.
Settings gets an Archived part: the crews and the bots the owner archived, which the rest of the app does not show, with the same delete confirmation as the menus. The confirmation is now a component of its own (DeleteBot, DeleteCrew) and says "leaves" instead of "stops now" for what was already stopped.
…or good

With the shell's IFileOperation on a thread of its own (it needs a single-threaded apartment). A progress sink aborts any item the shell would destroy instead of recycling, so what the bin cannot take stays where it is. The tests use the real Recycle Bin, check the folder is in it and take it out again.
…n when asked

bots.delete and crews.delete take recycleFolder. The method answers at once; the folder moves afterwards, off the server's workers, tried again while the process that was just killed still holds its files, and folder.recycled tells the apps how it ended. Only a folder under the workspaces goes, and never one that holds a work folder the owner chose.
… Recycle Bin

Unchecked by default: the folder stays. Checked, the confirmation says the folder goes to the Recycle Bin, where it can still be got back, and a notice tells the owner if it could not go.
The bin records the long path, and the temporary folder of the CI runner comes in the short one (RUNNER~1), so the tests did not find the folder they had just moved.
…onversation

Spec 7.4, 8.6, 9.2, 11.2, 11.3, 12 and the findings in section 19.
bots.setEffort restarts the bot with --effort when nothing is in progress;
the daemon asks Claude Code what the session applies and how full the
conversation is (control requests on stdin), follows each model request,
and bots.compact writes /compact. The fake daemon of the app knows the
new fields and methods; the UI comes next.
Brings in main (#52, #53, #54, #55, #59, #62, #67) and feat/delete-recycle (#56, #58, #60), so this PR merges in any order with them.

- app/src/lib/fakeBots.ts, crates/botloftd/src/state.rs, docs/spec.md: both sides kept (bot.context next to bot.deleted, crew.deleted and folder.recycled; the context store next to the trash).
- crates/botloftd/src/supervisor/spawn.rs: both sides kept, without recording the session at launch (#67 records it from the first turn).
- tests/bot_effort.rs and tests/compaction.rs: the bot has a turn before its conversation is resumed, as #67 described.
httpsphl added a commit that referenced this pull request Sep 30, 2026
Brings in feat/effort-context-app (#64), which carries #61, #63, main and the delete stack with their conflicts resolved, so this PR merges in any order with them.

- docs/spec.md: the bot folder's settings.json keeps what Claude Code does not load in the bot (this branch), and rules\botloft.md how to explain each command to the owner (#61).
@httpsphl
httpsphl enabled auto-merge September 30, 2026 17:23
@httpsphl
httpsphl merged commit 16ff507 into main Sep 30, 2026
3 checks passed
@httpsphl httpsphl mentioned this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant