@@ -145,6 +145,51 @@ func TestFallbackStepUsageEstimatesToolResults(t *testing.T) {
145145 require.Equal(t, usage.InputTokens, usage.TotalTokens)
146146}
147147
148+func TestFallbackStepUsageSkipsClientToolResultsAsOutput(t *testing.T) {
149+ t.Parallel()
150+
151+ step := fantasy.StepResult{
152+ Response: fantasy.Response{
153+ Content: fantasy.ResponseContent{
154+ fantasy.ToolResultContent{
155+ ToolCallID: "tool-call-1",
156+ ToolName: "bash",
157+ Result: fantasy.ToolResultOutputContentText{
158+ Text: "large client-executed payload that should not count as model output tokens",
159+ },
160+ },
161+ },
162+ },
163+ }
164+
165+ usage, estimated := fallbackStepUsage(nil, step)
166+ require.False(t, estimated)
167+ require.Zero(t, usage.OutputTokens)
168+}
169+
170+func TestFallbackStepUsageCountsProviderToolResultsAsOutput(t *testing.T) {
171+ t.Parallel()
172+
173+ step := fantasy.StepResult{
174+ Response: fantasy.Response{
175+ Content: fantasy.ResponseContent{
176+ fantasy.ToolResultContent{
177+ ToolCallID: "tool-call-1",
178+ ToolName: "web_search",
179+ ProviderExecuted: true,
180+ ClientMetadata: "provider metadata",
181+ Result: fantasy.ToolResultOutputContentText{Text: "provider-executed result"},
182+ },
183+ },
184+ },
185+ }
186+
187+ usage, estimated := fallbackStepUsage(nil, step)
188+ require.True(t, estimated)
189+ require.Positive(t, usage.OutputTokens)
190+ require.Equal(t, usage.OutputTokens, usage.TotalTokens)
191+}
192+
148193func TestFallbackStepUsageReturnsZeroWithoutContent(t *testing.T) {
149194 t.Parallel()
150195
@@ -187,6 +232,24 @@ func TestUpdateSessionUsageKeepsCountersForZeroUsage(t *testing.T) {
187232 require.Equal(t, int64(456), currentSession.CompletionTokens)
188233}
189234
235+func TestUpdateSessionUsageReplacesCountersForPartialUsage(t *testing.T) {
236+ t.Parallel()
237+
238+ agent := &sessionAgent{}
239+ currentSession := &session.Session{
240+ ID: "session-id",
241+ PromptTokens: 123,
242+ CompletionTokens: 456,
243+ }
244+ model := Model{CatwalkCfg: catwalk.Model{CostPer1MIn: 10, CostPer1MOut: 20}}
245+ usage := fantasy.Usage{InputTokens: 789}
246+
247+ agent.updateSessionUsage(model, currentSession, usage, nil, false)
248+
249+ require.Equal(t, int64(789), currentSession.PromptTokens)
250+ require.Zero(t, currentSession.CompletionTokens)
251+}
252+
190253func TestUpdateSessionUsageAddsProviderCost(t *testing.T) {
191254 t.Parallel()
192255