TestRunAgentPersistsSubSessionToStore is the regression test for the background-agent spend leak: run_background_agent sub-sessions were added to the parent's in-memory object but never written to the session store, so their tokens and cost were invisible to anything reading the store ($0 recorded f
(t *testing.T)
| 4071 | // sub-session directly from runCollecting. This asserts the sub-session row |
| 4072 | // reaches the store and carries the worker's recorded usage. |
| 4073 | func TestRunAgentPersistsSubSessionToStore(t *testing.T) { |
| 4074 | t.Parallel() |
| 4075 | |
| 4076 | // Worker produces real output and usage so the sub-session has a transcript. |
| 4077 | workerStream := newStreamBuilder().AddContent("worker done").AddStopWithUsage(100, 50).Build() |
| 4078 | parentProv := &mockProvider{id: "test/mock-model", stream: &mockStream{}} |
| 4079 | workerProv := &mockProvider{id: "test/mock-model", stream: workerStream} |
| 4080 | |
| 4081 | worker := agent.New("worker", "Worker agent", agent.WithModel(workerProv)) |
| 4082 | root := agent.New("root", "Root agent", agent.WithModel(parentProv)) |
| 4083 | agent.WithSubAgents(worker)(root) |
| 4084 | |
| 4085 | tm := team.New(team.WithAgents(root, worker)) |
| 4086 | |
| 4087 | store := session.NewInMemorySessionStore() |
| 4088 | rt, err := NewLocalRuntime(t.Context(), tm, |
| 4089 | WithSessionCompaction(false), |
| 4090 | WithModelStore(mockModelStore{}), |
| 4091 | WithSessionStore(store), |
| 4092 | ) |
| 4093 | require.NoError(t, err) |
| 4094 | |
| 4095 | // The parent must exist in the store before a sub-session can be linked |
| 4096 | // to it. Use UpdateSession (what OnRunStart calls) so the store holds its |
| 4097 | // own copy of the parent — exactly as in the real flow — rather than |
| 4098 | // aliasing the runtime's in-memory object. |
| 4099 | sess := session.New(session.WithUserMessage("Test"), session.WithToolsApproved(true)) |
| 4100 | require.NoError(t, store.UpdateSession(t.Context(), sess)) |
| 4101 | |
| 4102 | result := rt.RunAgent(t.Context(), agenttool.RunParams{ |
| 4103 | AgentName: "worker", |
| 4104 | Task: "do something", |
| 4105 | ParentSession: sess, |
| 4106 | }) |
| 4107 | require.Empty(t, result.ErrMsg, "RunAgent should succeed") |
| 4108 | |
| 4109 | // The store's copy of the parent must now contain the sub-session — not |
| 4110 | // just the runtime's in-memory object. Before the fix this was empty, |
| 4111 | // so the background agent's work was recorded nowhere durable. |
| 4112 | stored, err := store.GetSession(t.Context(), sess.ID) |
| 4113 | require.NoError(t, err) |
| 4114 | |
| 4115 | var storedSubSessions []*session.Session |
| 4116 | for _, item := range stored.Messages { |
| 4117 | if item.SubSession != nil { |
| 4118 | storedSubSessions = append(storedSubSessions, item.SubSession) |
| 4119 | } |
| 4120 | } |
| 4121 | require.Len(t, storedSubSessions, 1, |
| 4122 | "the background sub-session must be persisted to the store, not just held in memory") |
| 4123 | |
| 4124 | // The persisted sub-session must carry the worker's token usage so spend |
| 4125 | // accounting that reads the store sees real numbers, not nothing. |
| 4126 | assert.Positive(t, storedSubSessions[0].InputTokens+storedSubSessions[0].OutputTokens, |
| 4127 | "persisted sub-session must carry the worker's recorded token usage") |
| 4128 | } |
| 4129 | |
| 4130 | // TestRunAgentPersistsSubSessionOnError covers the background-agent path |
nothing calls this directly
no test coverage detected