agent: keep 'calling:' state during tool execution, not 'thinking'

Previously, the Output callback set state to 'thinking' on every tool
event (including streaming chunks), so the state flickered immediately
away from 'calling:'. Now:

- Tool events no longer trigger setState('thinking')
- The loop emits a 'state' event with 'thinking' at the top of each
  iteration (before calling the backend), which is the correct time
  to transition.
- New 'state' event role in Output callback for explicit state transitions.

This keeps the detach button visible in the GUI during tool execution.
This commit is contained in:
Levi Neely 2026-07-29 09:33:46 +02:00
parent a9642a9be1
commit fb21228ca1
2 changed files with 3 additions and 1 deletions

View File

@ -1320,8 +1320,9 @@ func (s *agent) executeTurn(ctx context.Context, input string) string {
s.setState("calling: " + ev.Name)
s.auditLog.Debug("call: %s %s", ev.Name, auditTruncate(string(ev.Content)))
case "tool":
s.setState("thinking")
s.auditLog.Debug("result: %s %s", ev.Name, auditTruncate(ev.Content))
case "state":
s.setState(ev.Content)
case "limitretry":
s.setState("limitretry")
case "error":

View File

@ -91,6 +91,7 @@ func run(ctx context.Context, cfg agentConfig, state state) error {
}
for {
emit(cfg, Event{Role: "state", Content: "thinking"})
// Proactive context gate: strip cold material before calling the backend.
if budget := contextBudget(ctx, cfg.Backend); budget > 0 && state.estimateTokens() > budget {
state.stripCold(ctx, cfg.Backend)