mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add experimental agents support (#22290)
feat: add AI chat system with agent tools and chat UI Introduce the chatd subsystem and Agents UI for AI-powered chat within Coder workspaces. - Add chatd package with chat loop, message compaction, prompt management, and LLM provider integration (OpenAI, Anthropic) - Add agent tools: create workspace, list/read templates, read/write/ edit files, execute commands - Add chat API endpoints with streaming, message editing, and durable reconnection - Add database schema and migrations for chats, chat messages, chat providers, and chat model configs - Add RBAC policies and dbauthz enforcement for chat resources - Add Agents UI pages with conversation timeline, queued messages list, diff viewer, and model configuration panel - Add comprehensive test coverage including coderd integration tests, chatd unit tests, and Storybook stories - Gate feature behind experiments flag --------- Co-authored-by: Cian Johnston <cian@coder.com> Co-authored-by: Danielle Maywood <danielle@themaywoods.com> Co-authored-by: Jeremy Ruppel <jeremy@coder.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Cian Johnston
Danielle Maywood
Jeremy Ruppel
Claude Sonnet 4.6
parent
67da4e8b56
commit
edee917d88
+15
-8
@@ -404,7 +404,9 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
||||
AvatarURL: member.AvatarURL,
|
||||
}
|
||||
|
||||
w, err := createWorkspace(ctx, aReq, apiKey.UserID, api, owner, req, r, nil)
|
||||
w, err := createWorkspace(ctx, aReq, apiKey.UserID, api, owner, req, &createWorkspaceOptions{
|
||||
remoteAddr: r.RemoteAddr,
|
||||
})
|
||||
if err != nil {
|
||||
httperror.WriteResponseError(ctx, rw, err)
|
||||
return
|
||||
@@ -500,7 +502,9 @@ func (api *API) postUserWorkspaces(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
defer commitAudit()
|
||||
|
||||
w, err := createWorkspace(ctx, aReq, apiKey.UserID, api, owner, req, r, nil)
|
||||
w, err := createWorkspace(ctx, aReq, apiKey.UserID, api, owner, req, &createWorkspaceOptions{
|
||||
remoteAddr: r.RemoteAddr,
|
||||
})
|
||||
if err != nil {
|
||||
httperror.WriteResponseError(ctx, rw, err)
|
||||
return
|
||||
@@ -522,6 +526,10 @@ type createWorkspaceOptions struct {
|
||||
// postCreateInTX is a function that is called within the transaction, after
|
||||
// the workspace is created but before the workspace build is created.
|
||||
postCreateInTX func(ctx context.Context, tx database.Store, workspace database.Workspace) error
|
||||
// remoteAddr is the IP address of the request initiator, used for
|
||||
// audit logging. HTTP handlers should pass r.RemoteAddr;
|
||||
// programmatic callers may leave it empty.
|
||||
remoteAddr string
|
||||
}
|
||||
|
||||
func createWorkspace(
|
||||
@@ -531,7 +539,6 @@ func createWorkspace(
|
||||
api *API,
|
||||
owner workspaceOwner,
|
||||
req codersdk.CreateWorkspaceRequest,
|
||||
r *http.Request,
|
||||
opts *createWorkspaceOptions,
|
||||
) (codersdk.Workspace, error) {
|
||||
if opts == nil {
|
||||
@@ -545,7 +552,7 @@ func createWorkspace(
|
||||
|
||||
// This is a premature auth check to avoid doing unnecessary work if the user
|
||||
// doesn't have permission to create a workspace.
|
||||
if !api.Authorize(r, policy.ActionCreate,
|
||||
if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionCreate,
|
||||
rbac.ResourceWorkspace.InOrg(template.OrganizationID).WithOwner(owner.ID.String())) {
|
||||
// If this check fails, return a proper unauthorized error to the user to indicate
|
||||
// what is going on.
|
||||
@@ -562,14 +569,14 @@ func createWorkspace(
|
||||
|
||||
// Do this upfront to save work. If this fails, the rest of the work
|
||||
// would be wasted.
|
||||
if !api.Authorize(r, policy.ActionCreate,
|
||||
if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionCreate,
|
||||
rbac.ResourceWorkspace.InOrg(template.OrganizationID).WithOwner(owner.ID.String())) {
|
||||
return codersdk.Workspace{}, httperror.ErrResourceNotFound
|
||||
}
|
||||
// The user also needs permission to use the template. At this point they have
|
||||
// read perms, but not necessarily "use". This is also checked in `db.InsertWorkspace`.
|
||||
// Doing this up front can save some work below if the user doesn't have permission.
|
||||
if !api.Authorize(r, policy.ActionUse, template) {
|
||||
if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionUse, template) {
|
||||
return codersdk.Workspace{}, httperror.NewResponseError(http.StatusForbidden, codersdk.Response{
|
||||
Message: fmt.Sprintf("Unauthorized access to use the template %q.", template.Name),
|
||||
Detail: "Although you are able to view the template, you are unable to create a workspace using it. " +
|
||||
@@ -801,9 +808,9 @@ func createWorkspace(
|
||||
db,
|
||||
api.FileCache,
|
||||
func(action policy.Action, object rbac.Objecter) bool {
|
||||
return api.Authorize(r, action, object)
|
||||
return api.HTTPAuth.AuthorizeContext(ctx, action, object)
|
||||
},
|
||||
audit.WorkspaceBuildBaggageFromRequest(r),
|
||||
audit.WorkspaceBuildBaggage{IP: opts.remoteAddr},
|
||||
)
|
||||
return err
|
||||
}, nil)
|
||||
|
||||
Reference in New Issue
Block a user