Turn counts differ between the two agent loops: three messages one persists and the other only emits
- 主要言語
- Rust
- スター
- 54.2k
- フォーク
- 6.2k
- 平均マージ
- 3日 4時間
- マージ済み PR(30日)
- 240
説明
**Describe the bug**
The two agent loops persist different messages, so the same delegate run reports a different turn count depending on which loop executed it. `durable_assistant_turn_count` (`summon.rs:44`) derives turns from the persisted conversation, and `MaxTurnsOperation` charges the budget the same way, so anything one loop writes and the other only emits shifts the number by one.
Three cases, all on current main:
**1. The inference after a stop-hook denial**
The legacy loop deliberately does not charge it: `retrying_after_stop_hook_denial` skips the increment (`agent.rs:2639`). But the denial context is a `Message::user()` that is persisted without being emitted (`agent.rs:163`), so a conversation-derived count sees a user message closing the assistant block and charges the retry as a new turn.
**2. `MAX_TURNS_MESSAGE` on an exhausted budget**
The state machine persists it through `yielded_with` (`ops_maxturns.rs:66`); the legacy loop yields it and breaks without persisting (`agent.rs:2648`). It is an assistant, user-visible message, so an exhausted budget reports `max_turns + 1` on one path and `max_turns` on the other.
**3. A provider error that ends the turn**
The state machine persists every provider error: `error_outcome` builds `Message::from_provider_error` and returns it as a conversation effect (`goose-agent/src/inference.rs:305-310`), which is how `ExitOnErrorOperation` and `trailing_error` can see it at all. The legacy loop persists only `ProviderError::Authentication` (`agent.rs:3281`); refusal (`:3265`), network errors (`:3296`) and the general arm (`:3308`) are emitted and then `break` without being written. `with_error` marks the message `user_only`, so where it is persisted it is user-visible and counted.
**Expected behavior**
The same run reports the same number of turns regardless of `GOOSE_STATE_MACHINE`, and the budget charges what the telemetry reports.
**Why this is not fixable in the counter**
Both `durable_assistant_turn_count` and `MaxTurnsOperation` read the persisted conversation, which is the right source. The divergence is in what each loop writes to it, so it has to be settled by deciding, per case, whether the message belongs in the conversation.
**Please provide the following information**
- **OS & Arch:** Windows 11 x64
- **Interface:** both, this is loop-level
- **Version:** current main (e0e8625)
These came out of the review rounds on #11843, which I closed in favour of #11841; I put them on #11802 at the time but that issue closed with the merge, so they are here instead. I can fix whichever direction is chosen once this is Ready.
コントリビューションガイド
調査の方向性
The issue describes three cases where two agent loops (state machine and legacy) persist messages differently, affecting turn counts. Key files are `summon.rs:44`, `agent.rs` (lines 2639, 163, 2648, 3281, 3265, 3296, 3308), `ops_maxturns.rs:66`, and `goose-agent/src/inference.rs:305-310`. Start by understanding the conversation persistence logic in both loops for each case. Determine whether each disputed message should be in the conversation. Run tests to verify turn counts align after changes.
索引モデルが issue の本文から書いたものです。
評価
- 領域
- backend-api-design
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 45/100