diff --git a/coderd/x/chatd/chattool/createworkspace.go b/coderd/x/chatd/chattool/createworkspace.go index b57f0e031c..be04be61e4 100644 --- a/coderd/x/chatd/chattool/createworkspace.go +++ b/coderd/x/chatd/chattool/createworkspace.go @@ -251,14 +251,6 @@ func CreateWorkspace(organizationID uuid.UUID, db database.Store, options Create } } - // The agent is now online — re-fire so callers can - // load instruction files from the running agent. - if options.OnChatUpdated != nil { - if latest, err := db.GetChatByID(ctx, options.ChatID); err == nil { - options.OnChatUpdated(latest) - } - } - result := map[string]any{ "created": true, "workspace_name": workspace.FullName(), @@ -291,6 +283,19 @@ func CreateWorkspace(organizationID uuid.UUID, db database.Store, options Create } } + // Re-fire after the agent is fully ready so callers + // can load instruction files (AGENTS.md) from the + // running agent. This must happen after + // waitForAgentReady — firing earlier (e.g. right + // after waitForBuild) races with the agent startup + // and the connection usually times out before the + // agent is reachable. + if options.OnChatUpdated != nil { + if latest, err := db.GetChatByID(ctx, options.ChatID); err == nil { + options.OnChatUpdated(latest) + } + } + return toolResponse(result), nil }) } diff --git a/coderd/x/chatd/chattool/startworkspace.go b/coderd/x/chatd/chattool/startworkspace.go index c703cab077..470be41522 100644 --- a/coderd/x/chatd/chattool/startworkspace.go +++ b/coderd/x/chatd/chattool/startworkspace.go @@ -134,14 +134,17 @@ func StartWorkspace(options StartWorkspaceOptions) fantasy.AgentTool { build.ID, )), nil } - // The agent is now online — re-fire so callers can - // load instruction files from the running agent. + resp, respErr := waitForAgentAndRespond(ctx, options.DB, options.AgentConnFn, ws, build.ID) + // Re-fire after the agent is fully ready so + // callers can load instruction files (AGENTS.md). + // This must happen after waitForAgentAndRespond — + // firing earlier races with agent startup. if options.OnChatUpdated != nil { if latest, err := options.DB.GetChatByID(ctx, options.ChatID); err == nil { options.OnChatUpdated(latest) } } - return waitForAgentAndRespond(ctx, options.DB, options.AgentConnFn, ws, build.ID) + return resp, respErr case database.ProvisionerJobStatusSucceeded: // If the latest successful build is a start // transition, the workspace should be running. @@ -197,15 +200,17 @@ func StartWorkspace(options StartWorkspaceOptions) fantasy.AgentTool { )), nil } - // The agent is now online — re-fire so callers can - // load instruction files from the running agent. + resp, respErr := waitForAgentAndRespond(ctx, options.DB, options.AgentConnFn, ws, startBuild.ID) + // Re-fire after the agent is fully ready so + // callers can load instruction files (AGENTS.md). + // This must happen after waitForAgentAndRespond — + // firing earlier races with agent startup. if options.OnChatUpdated != nil { if latest, err := options.DB.GetChatByID(ctx, options.ChatID); err == nil { options.OnChatUpdated(latest) } } - - return waitForAgentAndRespond(ctx, options.DB, options.AgentConnFn, ws, startBuild.ID) + return resp, respErr }) }