diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index e4cee2333efc4..b37ffe45e95c6 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -1553,7 +1553,8 @@ CREATE TABLE template_versions (
external_auth_providers jsonb DEFAULT '[]'::jsonb NOT NULL,
message character varying(1048576) DEFAULT ''::character varying NOT NULL,
archived boolean DEFAULT false NOT NULL,
- source_example_id text
+ source_example_id text,
+ has_ai_task boolean DEFAULT false NOT NULL
);
COMMENT ON COLUMN template_versions.external_auth_providers IS 'IDs of External auth providers for a specific template version';
@@ -1583,6 +1584,7 @@ CREATE VIEW template_version_with_user AS
template_versions.message,
template_versions.archived,
template_versions.source_example_id,
+ template_versions.has_ai_task,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username,
COALESCE(visible_users.name, ''::text) AS created_by_name
@@ -2080,7 +2082,9 @@ CREATE TABLE workspace_builds (
reason build_reason DEFAULT 'initiator'::build_reason NOT NULL,
daily_cost integer DEFAULT 0 NOT NULL,
max_deadline timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
- template_version_preset_id uuid
+ template_version_preset_id uuid,
+ has_ai_task boolean DEFAULT false NOT NULL,
+ ai_tasks_sidebar_app_id uuid
);
CREATE VIEW workspace_build_with_user AS
@@ -2099,6 +2103,8 @@ CREATE VIEW workspace_build_with_user AS
workspace_builds.daily_cost,
workspace_builds.max_deadline,
workspace_builds.template_version_preset_id,
+ workspace_builds.has_ai_task,
+ workspace_builds.ai_tasks_sidebar_app_id,
COALESCE(visible_users.avatar_url, ''::text) AS initiator_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS initiator_by_username,
COALESCE(visible_users.name, ''::text) AS initiator_by_name
@@ -2667,6 +2673,8 @@ CREATE INDEX idx_tailnet_tunnels_dst_id ON tailnet_tunnels USING hash (dst_id);
CREATE INDEX idx_tailnet_tunnels_src_id ON tailnet_tunnels USING hash (src_id);
+CREATE INDEX idx_template_versions_has_ai_task ON template_versions USING btree (has_ai_task);
+
CREATE UNIQUE INDEX idx_unique_preset_name ON template_version_presets USING btree (name, template_version_id);
CREATE INDEX idx_user_deleted_deleted_at ON user_deleted USING btree (deleted_at);
@@ -3063,6 +3071,9 @@ ALTER TABLE ONLY workspace_apps
ALTER TABLE ONLY workspace_build_parameters
ADD CONSTRAINT workspace_build_parameters_workspace_build_id_fkey FOREIGN KEY (workspace_build_id) REFERENCES workspace_builds(id) ON DELETE CASCADE;
+ALTER TABLE ONLY workspace_builds
+ ADD CONSTRAINT workspace_builds_ai_tasks_sidebar_app_id_fkey FOREIGN KEY (ai_tasks_sidebar_app_id) REFERENCES workspace_apps(id);
+
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go
index d6b87ddff5376..eaec2d2495337 100644
--- a/coderd/database/foreign_key_constraint.go
+++ b/coderd/database/foreign_key_constraint.go
@@ -82,6 +82,7 @@ const (
ForeignKeyWorkspaceAppStatusesWorkspaceID ForeignKeyConstraint = "workspace_app_statuses_workspace_id_fkey" // ALTER TABLE ONLY workspace_app_statuses ADD CONSTRAINT workspace_app_statuses_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
ForeignKeyWorkspaceAppsAgentID ForeignKeyConstraint = "workspace_apps_agent_id_fkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
ForeignKeyWorkspaceBuildParametersWorkspaceBuildID ForeignKeyConstraint = "workspace_build_parameters_workspace_build_id_fkey" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_fkey FOREIGN KEY (workspace_build_id) REFERENCES workspace_builds(id) ON DELETE CASCADE;
+ ForeignKeyWorkspaceBuildsAiTasksSidebarAppID ForeignKeyConstraint = "workspace_builds_ai_tasks_sidebar_app_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_ai_tasks_sidebar_app_id_fkey FOREIGN KEY (ai_tasks_sidebar_app_id) REFERENCES workspace_apps(id);
ForeignKeyWorkspaceBuildsJobID ForeignKeyConstraint = "workspace_builds_job_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyWorkspaceBuildsTemplateVersionID ForeignKeyConstraint = "workspace_builds_template_version_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_template_version_id_fkey FOREIGN KEY (template_version_id) REFERENCES template_versions(id) ON DELETE CASCADE;
ForeignKeyWorkspaceBuildsTemplateVersionPresetID ForeignKeyConstraint = "workspace_builds_template_version_preset_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_template_version_preset_id_fkey FOREIGN KEY (template_version_preset_id) REFERENCES template_version_presets(id) ON DELETE SET NULL;
diff --git a/coderd/database/migrations/000335_ai_tasks.down.sql b/coderd/database/migrations/000335_ai_tasks.down.sql
new file mode 100644
index 0000000000000..b4684184b182b
--- /dev/null
+++ b/coderd/database/migrations/000335_ai_tasks.down.sql
@@ -0,0 +1,77 @@
+DROP VIEW workspace_build_with_user;
+
+DROP VIEW template_version_with_user;
+
+DROP INDEX idx_template_versions_has_ai_task;
+
+ALTER TABLE
+ template_versions DROP COLUMN has_ai_task;
+
+ALTER TABLE
+ workspace_builds DROP CONSTRAINT workspace_builds_ai_tasks_sidebar_app_id_fkey;
+
+ALTER TABLE
+ workspace_builds DROP COLUMN ai_tasks_sidebar_app_id;
+
+ALTER TABLE
+ workspace_builds DROP COLUMN has_ai_task;
+
+-- Recreate `workspace_build_with_user` as defined in dump.sql
+CREATE VIEW workspace_build_with_user AS
+SELECT
+ workspace_builds.id,
+ workspace_builds.created_at,
+ workspace_builds.updated_at,
+ workspace_builds.workspace_id,
+ workspace_builds.template_version_id,
+ workspace_builds.build_number,
+ workspace_builds.transition,
+ workspace_builds.initiator_id,
+ workspace_builds.provisioner_state,
+ workspace_builds.job_id,
+ workspace_builds.deadline,
+ workspace_builds.reason,
+ workspace_builds.daily_cost,
+ workspace_builds.max_deadline,
+ workspace_builds.template_version_preset_id,
+ COALESCE(visible_users.avatar_url, '' :: text) AS initiator_by_avatar_url,
+ COALESCE(visible_users.username, '' :: text) AS initiator_by_username,
+ COALESCE(visible_users.name, '' :: text) AS initiator_by_name
+FROM
+ (
+ workspace_builds
+ LEFT JOIN visible_users ON (
+ (workspace_builds.initiator_id = visible_users.id)
+ )
+ );
+
+COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.';
+
+-- Recreate `template_version_with_user` as defined in dump.sql
+CREATE VIEW template_version_with_user AS
+SELECT
+ template_versions.id,
+ template_versions.template_id,
+ template_versions.organization_id,
+ template_versions.created_at,
+ template_versions.updated_at,
+ template_versions.name,
+ template_versions.readme,
+ template_versions.job_id,
+ template_versions.created_by,
+ template_versions.external_auth_providers,
+ template_versions.message,
+ template_versions.archived,
+ template_versions.source_example_id,
+ COALESCE(visible_users.avatar_url, '' :: text) AS created_by_avatar_url,
+ COALESCE(visible_users.username, '' :: text) AS created_by_username,
+ COALESCE(visible_users.name, '' :: text) AS created_by_name
+FROM
+ (
+ template_versions
+ LEFT JOIN visible_users ON (
+ (template_versions.created_by = visible_users.id)
+ )
+ );
+
+COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
diff --git a/coderd/database/migrations/000335_ai_tasks.up.sql b/coderd/database/migrations/000335_ai_tasks.up.sql
new file mode 100644
index 0000000000000..4aed761b568a5
--- /dev/null
+++ b/coderd/database/migrations/000335_ai_tasks.up.sql
@@ -0,0 +1,103 @@
+-- Determines if a coder_ai_task resource was included in a
+-- workspace build.
+ALTER TABLE
+ workspace_builds
+ADD
+ COLUMN has_ai_task BOOLEAN NOT NULL DEFAULT FALSE;
+
+-- The app that is displayed in the ai tasks sidebar.
+ALTER TABLE
+ workspace_builds
+ADD
+ COLUMN ai_tasks_sidebar_app_id UUID DEFAULT NULL;
+
+ALTER TABLE
+ workspace_builds
+ADD
+ CONSTRAINT workspace_builds_ai_tasks_sidebar_app_id_fkey FOREIGN KEY (ai_tasks_sidebar_app_id) REFERENCES workspace_apps(id);
+
+-- Determines if a coder_ai_task resource is defined in a template version.
+ALTER TABLE
+ template_versions
+ADD
+ COLUMN has_ai_task BOOLEAN NOT NULL DEFAULT FALSE;
+
+-- The Tasks tab will be rendered in the UI only if there's at least one template version with has_ai_task set to true.
+-- The query to determine this will be run on every UI render, and this index speeds it up.
+-- SELECT EXISTS (SELECT 1 FROM template_versions WHERE has_ai_task = TRUE);
+CREATE INDEX idx_template_versions_has_ai_task ON template_versions USING btree (has_ai_task);
+
+DROP VIEW workspace_build_with_user;
+
+-- We're adding the has_ai_task and ai_tasks_sidebar_app_id columns.
+CREATE VIEW workspace_build_with_user AS
+SELECT
+ workspace_builds.id,
+ workspace_builds.created_at,
+ workspace_builds.updated_at,
+ workspace_builds.workspace_id,
+ workspace_builds.template_version_id,
+ workspace_builds.build_number,
+ workspace_builds.transition,
+ workspace_builds.initiator_id,
+ workspace_builds.provisioner_state,
+ workspace_builds.job_id,
+ workspace_builds.deadline,
+ workspace_builds.reason,
+ workspace_builds.daily_cost,
+ workspace_builds.max_deadline,
+ workspace_builds.template_version_preset_id,
+ workspace_builds.has_ai_task,
+ workspace_builds.ai_tasks_sidebar_app_id,
+ COALESCE(
+ visible_users.avatar_url,
+ '' :: text
+ ) AS initiator_by_avatar_url,
+ COALESCE(
+ visible_users.username,
+ '' :: text
+ ) AS initiator_by_username,
+ COALESCE(visible_users.name, '' :: text) AS initiator_by_name
+FROM
+ (
+ workspace_builds
+ LEFT JOIN visible_users ON (
+ (
+ workspace_builds.initiator_id = visible_users.id
+ )
+ )
+ );
+
+COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.';
+
+DROP VIEW template_version_with_user;
+
+-- We're adding the has_ai_task column.
+CREATE VIEW template_version_with_user AS
+SELECT
+ template_versions.id,
+ template_versions.template_id,
+ template_versions.organization_id,
+ template_versions.created_at,
+ template_versions.updated_at,
+ template_versions.name,
+ template_versions.readme,
+ template_versions.job_id,
+ template_versions.created_by,
+ template_versions.external_auth_providers,
+ template_versions.message,
+ template_versions.archived,
+ template_versions.source_example_id,
+ template_versions.has_ai_task,
+ COALESCE(visible_users.avatar_url, '' :: text) AS created_by_avatar_url,
+ COALESCE(visible_users.username, '' :: text) AS created_by_username,
+ COALESCE(visible_users.name, '' :: text) AS created_by_name
+FROM
+ (
+ template_versions
+ LEFT JOIN visible_users ON (
+ (template_versions.created_by = visible_users.id)
+ )
+ );
+
+COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 69ae70b6c3bd3..2533c9a843501 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -3355,6 +3355,7 @@ type TemplateVersion struct {
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`
SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"`
+ HasAITask bool `db:"has_ai_task" json:"has_ai_task"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
CreatedByName string `db:"created_by_name" json:"created_by_name"`
@@ -3431,6 +3432,7 @@ type TemplateVersionTable struct {
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`
SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"`
+ HasAITask bool `db:"has_ai_task" json:"has_ai_task"`
}
type TemplateVersionTerraformValue struct {
@@ -3845,6 +3847,8 @@ type WorkspaceBuild struct {
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"`
TemplateVersionPresetID uuid.NullUUID `db:"template_version_preset_id" json:"template_version_preset_id"`
+ HasAITask bool `db:"has_ai_task" json:"has_ai_task"`
+ AITasksSidebarAppID uuid.NullUUID `db:"ai_tasks_sidebar_app_id" json:"ai_tasks_sidebar_app_id"`
InitiatorByAvatarUrl string `db:"initiator_by_avatar_url" json:"initiator_by_avatar_url"`
InitiatorByUsername string `db:"initiator_by_username" json:"initiator_by_username"`
InitiatorByName string `db:"initiator_by_name" json:"initiator_by_name"`
@@ -3874,6 +3878,8 @@ type WorkspaceBuildTable struct {
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"`
TemplateVersionPresetID uuid.NullUUID `db:"template_version_preset_id" json:"template_version_preset_id"`
+ HasAITask bool `db:"has_ai_task" json:"has_ai_task"`
+ AITasksSidebarAppID uuid.NullUUID `db:"ai_tasks_sidebar_app_id" json:"ai_tasks_sidebar_app_id"`
}
type WorkspaceLatestBuild struct {
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index eec91c7586d61..92c912a55705a 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -11349,7 +11349,7 @@ FROM
-- Scope an archive to a single template and ignore already archived template versions
(
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task
FROM
template_versions
WHERE
@@ -11450,7 +11450,7 @@ func (q *sqlQuerier) ArchiveUnusedTemplateVersions(ctx context.Context, arg Arch
const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11488,6 +11488,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11497,7 +11498,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11521,6 +11522,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11530,7 +11532,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11554,6 +11556,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11563,7 +11566,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11593,6 +11596,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11602,7 +11606,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11632,6 +11636,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11651,7 +11656,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many
SELECT
- id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name
FROM
template_version_with_user AS template_versions
WHERE
@@ -11728,6 +11733,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -11746,7 +11752,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
}
const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many
-SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE created_at > $1
+SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, has_ai_task, created_by_avatar_url, created_by_username, created_by_name FROM template_version_with_user AS template_versions WHERE created_at > $1
`
func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) {
@@ -11772,6 +11778,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create
&i.Message,
&i.Archived,
&i.SourceExampleID,
+ &i.HasAITask,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -14178,7 +14185,7 @@ const getWorkspaceAgentAndLatestBuildByAuthToken = `-- name: GetWorkspaceAgentAn
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite, workspaces.next_start_at,
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_agents.api_key_scope,
- workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.template_version_preset_id, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username, workspace_build_with_user.initiator_by_name
+ workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.template_version_preset_id, workspace_build_with_user.has_ai_task, workspace_build_with_user.ai_tasks_sidebar_app_id, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username, workspace_build_with_user.initiator_by_name
FROM
workspace_agents
JOIN
@@ -14284,6 +14291,8 @@ func (q *sqlQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Cont
&i.WorkspaceBuild.DailyCost,
&i.WorkspaceBuild.MaxDeadline,
&i.WorkspaceBuild.TemplateVersionPresetID,
+ &i.WorkspaceBuild.HasAITask,
+ &i.WorkspaceBuild.AITasksSidebarAppID,
&i.WorkspaceBuild.InitiatorByAvatarUrl,
&i.WorkspaceBuild.InitiatorByUsername,
&i.WorkspaceBuild.InitiatorByName,
@@ -16853,7 +16862,7 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins
}
const getActiveWorkspaceBuildsByTemplateID = `-- name: GetActiveWorkspaceBuildsByTemplateID :many
-SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
+SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.has_ai_task, wb.ai_tasks_sidebar_app_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
@@ -16908,6 +16917,8 @@ func (q *sqlQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, t
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17007,7 +17018,7 @@ func (q *sqlQuerier) GetFailedWorkspaceBuildsByTemplateID(ctx context.Context, a
const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one
SELECT
- id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
+ id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
FROM
workspace_build_with_user AS workspace_builds
WHERE
@@ -17037,6 +17048,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, w
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17045,7 +17058,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, w
}
const getLatestWorkspaceBuilds = `-- name: GetLatestWorkspaceBuilds :many
-SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
+SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.has_ai_task, wb.ai_tasks_sidebar_app_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
@@ -17084,6 +17097,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17102,7 +17117,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB
}
const getLatestWorkspaceBuildsByWorkspaceIDs = `-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many
-SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
+SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.template_version_preset_id, wb.has_ai_task, wb.ai_tasks_sidebar_app_id, wb.initiator_by_avatar_url, wb.initiator_by_username, wb.initiator_by_name
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
@@ -17143,6 +17158,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context,
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17162,7 +17179,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context,
const getWorkspaceBuildByID = `-- name: GetWorkspaceBuildByID :one
SELECT
- id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
+ id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
FROM
workspace_build_with_user AS workspace_builds
WHERE
@@ -17190,6 +17207,8 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17199,7 +17218,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W
const getWorkspaceBuildByJobID = `-- name: GetWorkspaceBuildByJobID :one
SELECT
- id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
+ id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
FROM
workspace_build_with_user AS workspace_builds
WHERE
@@ -17227,6 +17246,8 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17236,7 +17257,7 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU
const getWorkspaceBuildByWorkspaceIDAndBuildNumber = `-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one
SELECT
- id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
+ id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
FROM
workspace_build_with_user AS workspace_builds
WHERE
@@ -17268,6 +17289,8 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Co
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17344,7 +17367,7 @@ func (q *sqlQuerier) GetWorkspaceBuildStatsByTemplates(ctx context.Context, sinc
const getWorkspaceBuildsByWorkspaceID = `-- name: GetWorkspaceBuildsByWorkspaceID :many
SELECT
- id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
+ id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name
FROM
workspace_build_with_user AS workspace_builds
WHERE
@@ -17415,6 +17438,8 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
@@ -17433,7 +17458,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge
}
const getWorkspaceBuildsCreatedAfter = `-- name: GetWorkspaceBuildsCreatedAfter :many
-SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user WHERE created_at > $1
+SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, template_version_preset_id, has_ai_task, ai_tasks_sidebar_app_id, initiator_by_avatar_url, initiator_by_username, initiator_by_name FROM workspace_build_with_user WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error) {
@@ -17461,6 +17486,8 @@ func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, created
&i.DailyCost,
&i.MaxDeadline,
&i.TemplateVersionPresetID,
+ &i.HasAITask,
+ &i.AITasksSidebarAppID,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
&i.InitiatorByName,
diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml
index b43281a3f1051..79b4b21f4d83f 100644
--- a/coderd/database/sqlc.yaml
+++ b/coderd/database/sqlc.yaml
@@ -147,6 +147,8 @@ sql:
crypto_key_feature_workspace_apps_api_key: CryptoKeyFeatureWorkspaceAppsAPIKey
crypto_key_feature_oidc_convert: CryptoKeyFeatureOIDCConvert
stale_interval_ms: StaleIntervalMS
+ has_ai_task: HasAITask
+ ai_tasks_sidebar_app_id: AITasksSidebarAppID
rules:
- name: do-not-use-public-schema-in-queries
message: "do not use public schema in queries"
diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md
index 4ed07cdc9dfb6..080e864fcb866 100644
--- a/docs/admin/security/audit-logs.md
+++ b/docs/admin/security/audit-logs.md
@@ -27,11 +27,11 @@ We track the following resources:
| OrganizationSyncSettings
|
Field | Tracked |
| assign_default | true |
field | true |
mapping | true |
|
| RoleSyncSettings
| Field | Tracked |
| field | true |
mapping | true |
|
| Template
write, delete | Field | Tracked |
| active_version_id | true |
activity_bump | true |
allow_user_autostart | true |
allow_user_autostop | true |
allow_user_cancel_workspace_jobs | true |
autostart_block_days_of_week | true |
autostop_requirement_days_of_week | true |
autostop_requirement_weeks | true |
created_at | false |
created_by | true |
created_by_avatar_url | false |
created_by_name | false |
created_by_username | false |
default_ttl | true |
deleted | false |
deprecated | true |
description | true |
display_name | true |
failure_ttl | true |
group_acl | true |
icon | true |
id | true |
max_port_sharing_level | true |
name | true |
organization_display_name | false |
organization_icon | false |
organization_id | false |
organization_name | false |
provisioner | true |
require_active_version | true |
time_til_dormant | true |
time_til_dormant_autodelete | true |
updated_at | false |
use_classic_parameter_flow | true |
user_acl | true |
|
-| TemplateVersion
create, write | Field | Tracked |
| archived | true |
created_at | false |
created_by | true |
created_by_avatar_url | false |
created_by_name | false |
created_by_username | false |
external_auth_providers | false |
id | true |
job_id | false |
message | false |
name | true |
organization_id | false |
readme | true |
source_example_id | false |
template_id | true |
updated_at | false |
|
+| TemplateVersion
create, write | Field | Tracked |
| archived | true |
created_at | false |
created_by | true |
created_by_avatar_url | false |
created_by_name | false |
created_by_username | false |
external_auth_providers | false |
has_ai_task | false |
id | true |
job_id | false |
message | false |
name | true |
organization_id | false |
readme | true |
source_example_id | false |
template_id | true |
updated_at | false |
|
| User
create, write, delete | Field | Tracked |
| avatar_url | false |
created_at | false |
deleted | true |
email | true |
github_com_user_id | false |
hashed_one_time_passcode | false |
hashed_password | true |
id | true |
is_system | true |
last_seen_at | false |
login_type | true |
name | true |
one_time_passcode_expires_at | true |
quiet_hours_schedule | true |
rbac_roles | true |
status | true |
updated_at | false |
username | true |
|
| WorkspaceAgent
connect, disconnect | Field | Tracked |
| api_key_scope | false |
api_version | false |
architecture | false |
auth_instance_id | false |
auth_token | false |
connection_timeout_seconds | false |
created_at | false |
directory | false |
disconnected_at | false |
display_apps | false |
display_order | false |
environment_variables | false |
expanded_directory | false |
first_connected_at | false |
id | false |
instance_metadata | false |
last_connected_at | false |
last_connected_replica_id | false |
lifecycle_state | false |
logs_length | false |
logs_overflowed | false |
motd_file | false |
name | false |
operating_system | false |
parent_id | false |
ready_at | false |
resource_id | false |
resource_metadata | false |
started_at | false |
subsystems | false |
troubleshooting_url | false |
updated_at | false |
version | false |
|
| WorkspaceApp
open, close | Field | Tracked |
| agent_id | false |
command | false |
created_at | false |
display_group | false |
display_name | false |
display_order | false |
external | false |
health | false |
healthcheck_interval | false |
healthcheck_threshold | false |
healthcheck_url | false |
hidden | false |
icon | false |
id | false |
open_in | false |
sharing_level | false |
slug | false |
subdomain | false |
url | false |
|
-| WorkspaceBuild
start, stop | Field | Tracked |
| build_number | false |
created_at | false |
daily_cost | false |
deadline | false |
id | false |
initiator_by_avatar_url | false |
initiator_by_name | false |
initiator_by_username | false |
initiator_id | false |
job_id | false |
max_deadline | false |
provisioner_state | false |
reason | false |
template_version_id | true |
template_version_preset_id | false |
transition | false |
updated_at | false |
workspace_id | false |
|
+| WorkspaceBuild
start, stop | Field | Tracked |
| ai_tasks_sidebar_app_id | false |
build_number | false |
created_at | false |
daily_cost | false |
deadline | false |
has_ai_task | false |
id | false |
initiator_by_avatar_url | false |
initiator_by_name | false |
initiator_by_username | false |
initiator_id | false |
job_id | false |
max_deadline | false |
provisioner_state | false |
reason | false |
template_version_id | true |
template_version_preset_id | false |
transition | false |
updated_at | false |
workspace_id | false |
|
| WorkspaceProxy
| Field | Tracked |
| created_at | true |
deleted | false |
derp_enabled | true |
derp_only | true |
display_name | true |
icon | true |
id | true |
name | true |
region_id | true |
token_hashed_secret | true |
updated_at | false |
url | true |
version | true |
wildcard_hostname | true |
|
| WorkspaceTable
| Field | Tracked |
| automatic_updates | true |
autostart_schedule | true |
created_at | false |
deleted | false |
deleting_at | true |
dormant_at | true |
favorite | true |
id | true |
last_used_at | false |
name | true |
next_start_at | true |
organization_id | false |
owner_id | true |
template_id | true |
ttl | true |
updated_at | false |
|
diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go
index d52632996ba26..ffb79810ee2c3 100644
--- a/enterprise/audit/table.go
+++ b/enterprise/audit/table.go
@@ -135,6 +135,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"created_by_name": ActionIgnore,
"archived": ActionTrack,
"source_example_id": ActionIgnore, // Never changes.
+ "has_ai_task": ActionIgnore, // Never changes.
},
&database.User{}: {
"id": ActionTrack,
@@ -193,6 +194,8 @@ var auditableResourcesTypes = map[any]map[string]Action{
"initiator_by_username": ActionIgnore,
"initiator_by_name": ActionIgnore,
"template_version_preset_id": ActionIgnore, // Never changes.
+ "has_ai_task": ActionIgnore, // Never changes.
+ "ai_tasks_sidebar_app_id": ActionIgnore, // Never changes.
},
&database.AuditableGroup{}: {
"id": ActionTrack,