gateway: a Claude subscription run knows the tools ToolSearch loaded before its agent is handed the results (TestToolSearchLoadKeepsTheRun failed on CI now and then; wizzy-yang's #1026 retried the assertion) — on the continue path run.continueWith answered the agent's MCP call before run.tools recorded the request's tools, so a turn that finished quickly could be shelved (shelve copies run.tools) or asked offers() without the tool ToolSearch had just loaded, and the next request started a new run; on a new run, run.tools was written without run.mu while the agent was already running. The tools are now recorded under run.mu before continueWith, and a new run's tools and search are set under one lock. A 50ms sleep after continueWith fails TestToolSearchLoadKeepsTheRun 5/5 on the old code with CI's message and passes with this; go test -race -count=100 -cpu 1,2,8 -run TestToolSearch and the gateway's subscription tests pass; gofmt, vet pass.
This commit is contained in:
1 parent
a8d798e2cb
commit
c8690ca0d2
1 file changed
+17
-10
@@ -3018,6 +3018,16 @@ func (s *Server) serveSubscription(w http.ResponseWriter, r *http.Request, from
|
||||
if name == "Claude Code" {
|
||||
err = run.setSafeguards(req)
|
||||
}
|
||||
// the tools are the run's before its agent is handed them: from
|
||||
// then on it may answer, finish the turn and be shelved, or be
|
||||
// asked whether it offers them, before this goroutine goes on
|
||||
if err == nil && more != nil {
|
||||
run.mu.Lock()
|
||||
for _, t := range req.Tools {
|
||||
run.tools[t.Name] = true
|
||||
}
|
||||
run.mu.Unlock()
|
||||
}
|
||||
if err == nil {
|
||||
events, err = run.continueWith(results, more)
|
||||
}
|
||||
@@ -3026,12 +3036,6 @@ func (s *Server) serveSubscription(w http.ResponseWriter, r *http.Request, from
|
||||
// it ended while it waited: a new one is told the whole
|
||||
// conversation
|
||||
run, how = nil, runExpired
|
||||
} else if more != nil {
|
||||
run.mu.Lock()
|
||||
for _, t := range req.Tools {
|
||||
run.tools[t.Name] = true
|
||||
}
|
||||
run.mu.Unlock()
|
||||
}
|
||||
}
|
||||
// how tool results found the run waiting on them, or why a new one is
|
||||
@@ -3042,15 +3046,18 @@ func (s *Server) serveSubscription(w http.ResponseWriter, r *http.Request, from
|
||||
if run == nil {
|
||||
run, events, err = start(r.Context(), req)
|
||||
if err == nil {
|
||||
run.tools = map[string]bool{}
|
||||
// its agent is running already: what it was told is set
|
||||
// under the run's lock, as offers and shelve read it
|
||||
tools := map[string]bool{}
|
||||
for _, t := range req.Tools {
|
||||
run.tools[t.Name] = true
|
||||
tools[t.Name] = true
|
||||
}
|
||||
run.mu.Lock()
|
||||
run.tools = tools
|
||||
if search.Name != "" {
|
||||
run.mu.Lock()
|
||||
run.search, run.searchName = s.webSearch, search.Name
|
||||
run.mu.Unlock()
|
||||
}
|
||||
run.mu.Unlock()
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user