From a935a7aeff08aca8ba6366e5413cb36724807850 Mon Sep 17 00:00:00 2001 From: chrisakinrinade Date: Mon, 21 Sep 2026 09:17:03 -0400 Subject: [PATCH 1/4] add self service agent to workbench --- .../create-edit/WorkbenchCreateOrEdit.tsx | 4 +- .../create-edit/WorkbenchFormSteps.tsx | 26 ++++++ js/console/src/generated/graphql.ts | 23 +++++ .../generated/persisted-queries/client.json | 19 +++++ js/console/src/graph/workbench.graphql | 1 + .../workbench/self_service/catalog_search.ex | 84 +++++++++++++++++++ .../self_service/get_pr_automation.ex | 75 +++++++++++++++++ .../self_service/invoke_pr_automation.ex | 77 +++++++++++++++++ lib/console/ai/tools/workbench/subagent.ex | 3 +- lib/console/ai/tools/workbench/subagents.ex | 1 + lib/console/ai/workbench/engine.ex | 3 +- lib/console/ai/workbench/environment.ex | 4 + .../ai/workbench/subagents/self_service.ex | 67 +++++++++++++++ lib/console/graphql/deployments/workbench.ex | 2 + lib/console/schema/workbench.ex | 4 +- lib/console/schema/workbench_job_activity.ex | 3 +- lib/console/schema/workbench_skill.ex | 3 +- priv/prompts/workbench/job.md.eex | 4 + priv/prompts/workbench/self_service.md.eex | 37 ++++++++ .../self_service/catalog_search.json | 10 +++ .../self_service/get_pr_automation.json | 14 ++++ .../self_service/invoke_pr_automation.json | 22 +++++ schema/schema.graphql | 8 ++ .../self_service/catalog_search_test.exs | 35 ++++++++ .../self_service/get_pr_automation_test.exs | 52 ++++++++++++ .../invoke_pr_automation_test.exs | 54 ++++++++++++ .../ai/tools/workbench/subagents_test.exs | 28 +++++++ .../console/ai/workbench/environment_test.exs | 14 ++++ .../workbench/subagents/self_service_test.exs | 72 ++++++++++++++++ .../deployments/workbench_mutations_test.exs | 6 ++ 30 files changed, 749 insertions(+), 6 deletions(-) create mode 100644 lib/console/ai/tools/workbench/self_service/catalog_search.ex create mode 100644 lib/console/ai/tools/workbench/self_service/get_pr_automation.ex create mode 100644 lib/console/ai/tools/workbench/self_service/invoke_pr_automation.ex create mode 100644 lib/console/ai/workbench/subagents/self_service.ex create mode 100644 priv/prompts/workbench/self_service.md.eex create mode 100644 priv/tools/workbench/self_service/catalog_search.json create mode 100644 priv/tools/workbench/self_service/get_pr_automation.json create mode 100644 priv/tools/workbench/self_service/invoke_pr_automation.json create mode 100644 test/console/ai/tools/workbench/self_service/catalog_search_test.exs create mode 100644 test/console/ai/tools/workbench/self_service/get_pr_automation_test.exs create mode 100644 test/console/ai/tools/workbench/self_service/invoke_pr_automation_test.exs create mode 100644 test/console/ai/workbench/subagents/self_service_test.exs diff --git a/js/console/src/components/workbenches/workbench/create-edit/WorkbenchCreateOrEdit.tsx b/js/console/src/components/workbenches/workbench/create-edit/WorkbenchCreateOrEdit.tsx index 596f3622d3..7822db9bbb 100644 --- a/js/console/src/components/workbenches/workbench/create-edit/WorkbenchCreateOrEdit.tsx +++ b/js/console/src/components/workbenches/workbench/create-edit/WorkbenchCreateOrEdit.tsx @@ -588,7 +588,8 @@ function sanitizeInitialForm({ readBindings, writeBindings, }: WorkbenchFragment): WorkbenchFormState { - const { infrastructure, coding, observability } = configuration ?? {} + const { infrastructure, coding, observability, selfService } = + configuration ?? {} const { kubernetes, services, stacks, podLogs, vulnerabilities } = infrastructure ?? {} const { logs, metrics } = observability ?? {} @@ -628,6 +629,7 @@ function sanitizeInitialForm({ repositoryId: repository?.id ?? null, overrideBotUser: false, configuration: { + selfService: selfService ?? false, infrastructure: { kubernetes, services, diff --git a/js/console/src/components/workbenches/workbench/create-edit/WorkbenchFormSteps.tsx b/js/console/src/components/workbenches/workbench/create-edit/WorkbenchFormSteps.tsx index c6b8bbda35..9302d9c295 100644 --- a/js/console/src/components/workbenches/workbench/create-edit/WorkbenchFormSteps.tsx +++ b/js/console/src/components/workbenches/workbench/create-edit/WorkbenchFormSteps.tsx @@ -128,6 +128,7 @@ export function WorkbenchSetupStep({ }: WorkbenchFormStepProps) { const theme = useTheme() const update = createFormUpdater(setFormState) + const selfService = formState.configuration?.selfService const infra = formState.configuration?.infrastructure const observability = formState.configuration?.observability const capabilityCheckboxGridCss = { @@ -172,6 +173,31 @@ export function WorkbenchSetupStep({ direction="column" gap="large" > + + + + Enable Plural catalog and PR automation workflows for repeatable + GitOps provisioning. Prefer this for clear golden paths; undefined + or custom code changes still go through the coding agent. + + + + update((d) => { + d.configuration ??= {} + d.configuration.selfService = checked + }) + } + /> + + + ; /** observability capabilities */ observability?: Maybe; + /** self-service subagent capability enabled */ + selfService?: Maybe; }; export type WorkbenchConfigurationAttributes = { @@ -16891,6 +16893,8 @@ export type WorkbenchConfigurationAttributes = { infrastructure?: InputMaybe; /** observability capabilities (logs, metrics) */ observability?: InputMaybe; + /** enable the self-service subagent for catalog and PR automation workflows */ + selfService?: InputMaybe; }; export type WorkbenchConnection = { @@ -17513,6 +17517,7 @@ export enum WorkbenchJobActivityType { Observability = 'OBSERVABILITY', Plan = 'PLAN', Search = 'SEARCH', + SelfService = 'SELF_SERVICE', Skill = 'SKILL', Ticketing = 'TICKETING', User = 'USER', @@ -18027,6 +18032,7 @@ export enum WorkbenchSkillSubagent { Observability = 'OBSERVABILITY', Orchestrator = 'ORCHESTRATOR', Search = 'SEARCH', + SelfService = 'SELF_SERVICE', Skill = 'SKILL' } @@ -22788,7 +22794,11 @@ export type IssueWebhookTinyFragment = { __typename?: 'IssueWebhook', id: string export type WorkbenchWebhookTinyFragment = { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null }; +<<<<<<< Updated upstream export type WorkbenchFragment = { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, victoriaLogs?: { __typename?: 'WorkbenchToolVictoriaLogsConnection', url?: string | null, username?: string | null, accountId?: string | null, projectId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, tokenType?: SplunkTokenType | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null }; +======= +export type WorkbenchFragment = { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', selfService?: boolean | null, infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null }; +>>>>>>> Stashed changes export type WorkbenchToolTinyFragment = { __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null }; @@ -22906,7 +22916,11 @@ export type WorkbenchQueryVariables = Exact<{ }>; +<<<<<<< Updated upstream export type WorkbenchQuery = { __typename?: 'RootQueryType', workbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, victoriaLogs?: { __typename?: 'WorkbenchToolVictoriaLogsConnection', url?: string | null, username?: string | null, accountId?: string | null, projectId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, tokenType?: SplunkTokenType | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +======= +export type WorkbenchQuery = { __typename?: 'RootQueryType', workbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', selfService?: boolean | null, infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +>>>>>>> Stashed changes export type WorkbenchAccessibleUserFragment = { __typename?: 'User', id: string, name: string, email: string, profile?: string | null }; @@ -23227,7 +23241,11 @@ export type CreateWorkbenchMutationVariables = Exact<{ }>; +<<<<<<< Updated upstream export type CreateWorkbenchMutation = { __typename?: 'RootMutationType', createWorkbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, victoriaLogs?: { __typename?: 'WorkbenchToolVictoriaLogsConnection', url?: string | null, username?: string | null, accountId?: string | null, projectId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, tokenType?: SplunkTokenType | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +======= +export type CreateWorkbenchMutation = { __typename?: 'RootMutationType', createWorkbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', selfService?: boolean | null, infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +>>>>>>> Stashed changes export type UpdateWorkbenchMutationVariables = Exact<{ id: Scalars['ID']['input']; @@ -23235,7 +23253,11 @@ export type UpdateWorkbenchMutationVariables = Exact<{ }>; +<<<<<<< Updated upstream export type UpdateWorkbenchMutation = { __typename?: 'RootMutationType', updateWorkbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, victoriaLogs?: { __typename?: 'WorkbenchToolVictoriaLogsConnection', url?: string | null, username?: string | null, accountId?: string | null, projectId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, tokenType?: SplunkTokenType | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +======= +export type UpdateWorkbenchMutation = { __typename?: 'RootMutationType', updateWorkbench?: { __typename?: 'Workbench', systemPrompt?: string | null, id: string, name: string, description?: string | null, agentRuntime?: { __typename?: 'AgentRuntime', id: string, name: string, allowedRepositories?: Array | null, type: AgentRuntimeType } | null, repository?: { __typename?: 'GitRepository', id: string } | null, configuration?: { __typename?: 'WorkbenchConfiguration', selfService?: boolean | null, infrastructure?: { __typename?: 'WorkbenchInfrastructure', services?: boolean | null, stacks?: boolean | null, kubernetes?: boolean | null, podLogs?: boolean | null, vulnerabilities?: boolean | null, sentinels?: boolean | null } | null, observability?: { __typename?: 'WorkbenchObservability', logs?: boolean | null, metrics?: boolean | null } | null, coding?: { __typename?: 'WorkbenchCoding', mode?: AgentRunMode | null, repositories?: Array | null, enableBabysitting?: boolean | null } | null } | null, modes?: { __typename?: 'WorkbenchJobModes', plan?: boolean | null, verification?: boolean | null, model?: { __typename?: 'WorkbenchJobModel', provider?: AiProvider | null, model?: string | null } | null, coding?: { __typename?: 'WorkbenchJobCodingModes', approval?: boolean | null, babysit?: boolean | null, review?: boolean | null } | null, budget?: { __typename?: 'WorkbenchJobBudget', cost?: number | null, tokens?: number | null } | null, kubernetes?: { __typename?: 'WorkbenchJobKubernetesModes', update?: boolean | null, delete?: boolean | null, exec?: boolean | null, drain?: boolean | null, excludeNamespaces?: Array | null, requireNamespaces?: Array | null } | null } | null, budget?: { __typename?: 'WorkbenchBudget', enabled?: boolean | null, maximum?: number | null, minFree?: number | null, unit?: WorkbenchBudgetUnit | null, last?: number | null, lastUpdated?: string | null } | null, skills?: { __typename?: 'WorkbenchSkills', files?: Array | null, ref?: { __typename?: 'GitRef', ref: string, folder: string } | null } | null, workbenchSkills?: { __typename?: 'WorkbenchSkillConnection', edges?: Array<{ __typename?: 'WorkbenchSkillEdge', node?: { __typename?: 'WorkbenchSkill', id: string, name?: string | null, description?: string | null, contents?: string | null, subagents?: Array | null } | null } | null> | null } | null, workbenchKnowledge?: { __typename?: 'WorkbenchKnowledgeConnection', edges?: Array<{ __typename?: 'WorkbenchKnowledgeEdge', node?: { __typename?: 'WorkbenchKnowledge', id: string, name?: string | null, description?: string | null, knowledge?: string | null, labels?: Array | null, usages?: number | null, lastUsedAt?: string | null } | null } | null> | null } | null, tools?: Array<{ __typename?: 'WorkbenchTool', id: string, name: string, tool: WorkbenchToolType, categories?: Array | null, approval?: boolean | null, scmConnection?: { __typename?: 'ScmConnection', id: string, name: string, type: ScmType } | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'WorkbenchToolConfiguration', http?: { __typename?: 'WorkbenchToolHttpConfiguration', url?: string | null, method?: string | null, body?: string | null, inputSchema?: Record | null, headers?: Array<{ __typename?: 'WorkbenchToolHttpHeader', name?: string | null, value?: string | null } | null> | null } | null, datadog?: { __typename?: 'WorkbenchToolDatadogConnection', site?: string | null } | null, elastic?: { __typename?: 'WorkbenchToolElasticConnection', index: string, url: string, username: string } | null, opensearch?: { __typename?: 'WorkbenchToolOpensearchConnection', host: string, index: string, awsAccessKeyId?: string | null, awsRegion?: string | null, assumeRoleArn?: string | null, usePodIdentity?: boolean | null } | null, loki?: { __typename?: 'WorkbenchToolLokiConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, prometheus?: { __typename?: 'WorkbenchToolPrometheusConnection', url?: string | null, username?: string | null, tenantId?: string | null, awsSigv4?: boolean | null, awsAccessKeyId?: string | null, awsRegion?: string | null } | null, tempo?: { __typename?: 'WorkbenchToolTempoConnection', url?: string | null, username?: string | null, tenantId?: string | null } | null, jaeger?: { __typename?: 'WorkbenchToolJaegerConnection', url?: string | null, username?: string | null } | null, atlassian?: { __typename?: 'WorkbenchToolAtlassianConnection', email?: string | null, url: string } | null, linear?: { __typename?: 'WorkbenchToolLinearConnection', url: string } | null, slack?: { __typename?: 'WorkbenchToolSlackConnection', url: string } | null, pagerduty?: { __typename?: 'WorkbenchToolPagerdutyConnection', url: string } | null, teams?: { __typename?: 'WorkbenchToolTeamsConnection', clientId?: string | null, tenantId?: string | null } | null, splunk?: { __typename?: 'WorkbenchToolSplunkConnection', url?: string | null, username?: string | null } | null, cloudwatch?: { __typename?: 'WorkbenchToolCloudwatchConnection', logGroupNames?: Array | null, region?: string | null, roleArn?: string | null, roleSessionName?: string | null } | null, azure?: { __typename?: 'WorkbenchToolAzureConnection', subscriptionId?: string | null, tenantId?: string | null, clientId?: string | null, prometheusUrl?: string | null } | null, dynatrace?: { __typename?: 'WorkbenchToolDynatraceConnection', url?: string | null } | null, sentry?: { __typename?: 'WorkbenchToolSentryConnection', url?: string | null } | null, github?: { __typename?: 'WorkbenchToolGithubConnection', url: string, toolset?: string | null, appId?: string | null, installationId?: string | null } | null, gitlab?: { __typename?: 'WorkbenchToolGitlabConnection', url?: string | null } | null, bitbucket?: { __typename?: 'WorkbenchToolBitbucketConnection', url?: string | null } | null, bitbucketDatacenter?: { __typename?: 'WorkbenchToolBitbucketDatacenterConnection', url?: string | null } | null, azureDevops?: { __typename?: 'WorkbenchToolAzureDevopsConnection', url?: string | null } | null, lambda?: { __typename?: 'WorkbenchToolLambdaConnection', lambdaArn?: string | null, description?: string | null, inputSchema?: Record | null } | null, cloudRun?: { __typename?: 'WorkbenchToolCloudRunConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, azureFunction?: { __typename?: 'WorkbenchToolAzureFunctionConnection', identifier?: string | null, description?: string | null, inputSchema?: Record | null } | null, docker?: { __typename?: 'WorkbenchToolDockerConnection', url?: string | null, provider?: HelmAuthProvider | null, proxy?: { __typename?: 'HttpProxyConfiguration', url: string, noproxy?: string | null } | null } | null } | null, cloudConnection?: { __typename?: 'CloudConnection', id: string, name: string, provider: Provider } | null, mcpServer?: { __typename?: 'McpServer', id: string, name: string, url: string } | null } | null> | null, readBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, writeBindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, botUser?: { __typename?: 'User', id: string, name: string, email: string, profile?: string | null } | null, webhooks?: { __typename?: 'WorkbenchWebhookConnection', edges?: Array<{ __typename?: 'WorkbenchWebhookEdge', node?: { __typename?: 'WorkbenchWebhook', id: string, name?: string | null, priority?: number | null, webhook?: { __typename?: 'ObservabilityWebhook', id: string, type: ObservabilityWebhookType } | null, issueWebhook?: { __typename?: 'IssueWebhook', id: string, provider: IssueWebhookProvider } | null } | null } | null> | null } | null } | null }; +>>>>>>> Stashed changes export type UpdateWorkbenchKnowledgeMutationVariables = Exact<{ id: Scalars['ID']['input']; @@ -28958,6 +28980,7 @@ export const WorkbenchFragmentDoc = gql` id } configuration { + selfService infrastructure { services stacks diff --git a/js/console/src/generated/persisted-queries/client.json b/js/console/src/generated/persisted-queries/client.json index 6afd332e9e..2c5c88a3ed 100644 --- a/js/console/src/generated/persisted-queries/client.json +++ b/js/console/src/generated/persisted-queries/client.json @@ -1972,10 +1972,17 @@ "name": "WorkbenchesAlerts", "body": "query WorkbenchesAlerts($first: Int = 100, $after: String) {\n workbenchAlerts(first: $first, after: $after) {\n pageInfo {\n ...PageInfo\n __typename\n }\n edges {\n node {\n ...Alert\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment PageInfo on PageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n __typename\n}\n\nfragment Alert on Alert {\n id\n title\n message\n type\n severity\n state\n fingerprint\n url\n annotations\n tags {\n id\n name\n value\n __typename\n }\n insight {\n ...AiInsight\n __typename\n }\n resolution {\n ...AlertResolution\n __typename\n }\n workbench {\n id\n __typename\n }\n workbenchJob {\n id\n status\n __typename\n }\n updatedAt\n __typename\n}\n\nfragment AiInsight on AiInsight {\n id\n text\n summary\n sha\n freshness\n updatedAt\n insertedAt\n error {\n message\n source\n __typename\n }\n ...AiInsightContext\n __typename\n}\n\nfragment AiInsightContext on AiInsight {\n evidence {\n ...AiInsightEvidence\n __typename\n }\n cluster {\n id\n name\n distro\n provider {\n cloud\n __typename\n }\n __typename\n }\n clusterInsightComponent {\n id\n group\n version\n kind\n name\n namespace\n cluster {\n ...ClusterMinimal\n __typename\n }\n __typename\n }\n service {\n id\n name\n cluster {\n ...ClusterMinimal\n __typename\n }\n __typename\n }\n serviceComponent {\n id\n group\n version\n kind\n name\n namespace\n service {\n id\n name\n cluster {\n ...ClusterMinimal\n __typename\n }\n __typename\n }\n __typename\n }\n stack {\n id\n name\n type\n __typename\n }\n stackRun {\n id\n message\n type\n stack {\n id\n name\n __typename\n }\n __typename\n }\n alert {\n id\n title\n message\n __typename\n }\n __typename\n}\n\nfragment AiInsightEvidence on AiInsightEvidence {\n id\n type\n logs {\n ...LogsEvidence\n __typename\n }\n pullRequest {\n ...PullRequestEvidence\n __typename\n }\n alert {\n ...AlertEvidence\n __typename\n }\n knowledge {\n ...KnowledgeEvidence\n __typename\n }\n insertedAt\n updatedAt\n __typename\n}\n\nfragment LogsEvidence on LogsEvidence {\n clusterId\n serviceId\n line\n lines {\n ...LogLine\n __typename\n }\n __typename\n}\n\nfragment LogLine on LogLine {\n facets {\n ...LogFacet\n __typename\n }\n log\n timestamp\n __typename\n}\n\nfragment LogFacet on LogFacet {\n key\n value\n __typename\n}\n\nfragment PullRequestEvidence on PullRequestEvidence {\n contents\n filename\n patch\n repo\n sha\n title\n url\n __typename\n}\n\nfragment AlertEvidence on AlertEvidence {\n alertId\n title\n resolution\n __typename\n}\n\nfragment KnowledgeEvidence on KnowledgeEvidence {\n name\n observations\n type\n __typename\n}\n\nfragment ClusterMinimal on Cluster {\n id\n name\n handle\n provider {\n name\n cloud\n __typename\n }\n distro\n __typename\n}\n\nfragment AlertResolution on AlertResolution {\n resolution\n __typename\n}" }, +<<<<<<< Updated upstream "sha256:3410568fb29e4a607727eb000b477147ef83441dbf0e4578f90a3bc1c0e1641f": { "type": "query", "name": "Workbench", "body": "query Workbench($id: ID, $name: String) {\n workbench(id: $id, name: $name) {\n ...Workbench\n __typename\n }\n}\n\nfragment Workbench on Workbench {\n ...WorkbenchTiny\n systemPrompt\n agentRuntime {\n id\n name\n allowedRepositories\n __typename\n }\n repository {\n id\n __typename\n }\n configuration {\n infrastructure {\n services\n stacks\n kubernetes\n podLogs\n vulnerabilities\n sentinels\n __typename\n }\n observability {\n logs\n metrics\n __typename\n }\n coding {\n mode\n repositories\n enableBabysitting\n __typename\n }\n __typename\n }\n modes {\n ...WorkbenchJobModesFields\n __typename\n }\n budget {\n enabled\n maximum\n minFree\n unit\n last\n lastUpdated\n __typename\n }\n skills {\n ref {\n ref\n folder\n __typename\n }\n files\n __typename\n }\n workbenchSkills(first: 500) {\n edges {\n node {\n id\n name\n description\n contents\n subagents\n __typename\n }\n __typename\n }\n __typename\n }\n workbenchKnowledge(first: 50) {\n edges {\n node {\n id\n name\n description\n knowledge\n labels\n usages\n lastUsedAt\n __typename\n }\n __typename\n }\n __typename\n }\n tools {\n ...WorkbenchTool\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n botUser {\n id\n name\n email\n profile\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTiny on Workbench {\n id\n name\n description\n agentRuntime {\n id\n name\n type\n __typename\n }\n tools {\n ...WorkbenchToolTiny\n __typename\n }\n webhooks(first: 50) {\n edges {\n node {\n ...WorkbenchWebhookTiny\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment WorkbenchWebhookTiny on WorkbenchWebhook {\n id\n name\n priority\n webhook {\n id\n type\n __typename\n }\n issueWebhook {\n ...IssueWebhookTiny\n __typename\n }\n __typename\n}\n\nfragment IssueWebhookTiny on IssueWebhook {\n id\n provider\n __typename\n}\n\nfragment WorkbenchJobModesFields on WorkbenchJobModes {\n plan\n verification\n model {\n provider\n model\n __typename\n }\n coding {\n approval\n babysit\n review\n __typename\n }\n budget {\n cost\n tokens\n __typename\n }\n kubernetes {\n update\n delete\n exec\n drain\n excludeNamespaces\n requireNamespaces\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n victoriaLogs {\n url\n username\n accountId\n projectId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n tokenType\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" +======= + "sha256:dca054cab04c0d2b5943d0a40745f1855de7565f7487c34264fe2c3b4d05b40b": { + "type": "query", + "name": "Workbench", + "body": "query Workbench($id: ID, $name: String) {\n workbench(id: $id, name: $name) {\n ...Workbench\n __typename\n }\n}\n\nfragment Workbench on Workbench {\n ...WorkbenchTiny\n systemPrompt\n agentRuntime {\n id\n name\n allowedRepositories\n __typename\n }\n repository {\n id\n __typename\n }\n configuration {\n selfService\n infrastructure {\n services\n stacks\n kubernetes\n podLogs\n vulnerabilities\n sentinels\n __typename\n }\n observability {\n logs\n metrics\n __typename\n }\n coding {\n mode\n repositories\n enableBabysitting\n __typename\n }\n __typename\n }\n modes {\n ...WorkbenchJobModesFields\n __typename\n }\n budget {\n enabled\n maximum\n minFree\n unit\n last\n lastUpdated\n __typename\n }\n skills {\n ref {\n ref\n folder\n __typename\n }\n files\n __typename\n }\n workbenchSkills(first: 500) {\n edges {\n node {\n id\n name\n description\n contents\n subagents\n __typename\n }\n __typename\n }\n __typename\n }\n workbenchKnowledge(first: 50) {\n edges {\n node {\n id\n name\n description\n knowledge\n labels\n usages\n lastUsedAt\n __typename\n }\n __typename\n }\n __typename\n }\n tools {\n ...WorkbenchTool\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n botUser {\n id\n name\n email\n profile\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTiny on Workbench {\n id\n name\n description\n agentRuntime {\n id\n name\n type\n __typename\n }\n tools {\n ...WorkbenchToolTiny\n __typename\n }\n webhooks(first: 50) {\n edges {\n node {\n ...WorkbenchWebhookTiny\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment WorkbenchWebhookTiny on WorkbenchWebhook {\n id\n name\n priority\n webhook {\n id\n type\n __typename\n }\n issueWebhook {\n ...IssueWebhookTiny\n __typename\n }\n __typename\n}\n\nfragment IssueWebhookTiny on IssueWebhook {\n id\n provider\n __typename\n}\n\nfragment WorkbenchJobModesFields on WorkbenchJobModes {\n plan\n verification\n model {\n provider\n model\n __typename\n }\n coding {\n approval\n babysit\n review\n __typename\n }\n budget {\n cost\n tokens\n __typename\n }\n kubernetes {\n update\n delete\n exec\n drain\n excludeNamespaces\n requireNamespaces\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" +>>>>>>> Stashed changes }, "sha256:6c3686838d1372371451664e8d6b593929eeca76e340065ed6dda5691e325b56": { "type": "query", @@ -2167,6 +2174,7 @@ "name": "WorkbenchTool", "body": "query WorkbenchTool($id: ID, $name: String) {\n workbenchTool(id: $id, name: $name) {\n ...WorkbenchTool\n __typename\n }\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n victoriaLogs {\n url\n username\n accountId\n projectId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n tokenType\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" }, +<<<<<<< Updated upstream "sha256:2714b78db9212b8236f8a5d8b5c83873f359071512e7420e148f3b45aa843a72": { "type": "mutation", "name": "CreateWorkbench", @@ -2176,6 +2184,17 @@ "type": "mutation", "name": "UpdateWorkbench", "body": "mutation UpdateWorkbench($id: ID!, $attributes: WorkbenchAttributes!) {\n updateWorkbench(id: $id, attributes: $attributes) {\n ...Workbench\n __typename\n }\n}\n\nfragment Workbench on Workbench {\n ...WorkbenchTiny\n systemPrompt\n agentRuntime {\n id\n name\n allowedRepositories\n __typename\n }\n repository {\n id\n __typename\n }\n configuration {\n infrastructure {\n services\n stacks\n kubernetes\n podLogs\n vulnerabilities\n sentinels\n __typename\n }\n observability {\n logs\n metrics\n __typename\n }\n coding {\n mode\n repositories\n enableBabysitting\n __typename\n }\n __typename\n }\n modes {\n ...WorkbenchJobModesFields\n __typename\n }\n budget {\n enabled\n maximum\n minFree\n unit\n last\n lastUpdated\n __typename\n }\n skills {\n ref {\n ref\n folder\n __typename\n }\n files\n __typename\n }\n workbenchSkills(first: 500) {\n edges {\n node {\n id\n name\n description\n contents\n subagents\n __typename\n }\n __typename\n }\n __typename\n }\n workbenchKnowledge(first: 50) {\n edges {\n node {\n id\n name\n description\n knowledge\n labels\n usages\n lastUsedAt\n __typename\n }\n __typename\n }\n __typename\n }\n tools {\n ...WorkbenchTool\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n botUser {\n id\n name\n email\n profile\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTiny on Workbench {\n id\n name\n description\n agentRuntime {\n id\n name\n type\n __typename\n }\n tools {\n ...WorkbenchToolTiny\n __typename\n }\n webhooks(first: 50) {\n edges {\n node {\n ...WorkbenchWebhookTiny\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment WorkbenchWebhookTiny on WorkbenchWebhook {\n id\n name\n priority\n webhook {\n id\n type\n __typename\n }\n issueWebhook {\n ...IssueWebhookTiny\n __typename\n }\n __typename\n}\n\nfragment IssueWebhookTiny on IssueWebhook {\n id\n provider\n __typename\n}\n\nfragment WorkbenchJobModesFields on WorkbenchJobModes {\n plan\n verification\n model {\n provider\n model\n __typename\n }\n coding {\n approval\n babysit\n review\n __typename\n }\n budget {\n cost\n tokens\n __typename\n }\n kubernetes {\n update\n delete\n exec\n drain\n excludeNamespaces\n requireNamespaces\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n victoriaLogs {\n url\n username\n accountId\n projectId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n tokenType\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" +======= + "sha256:8ad820c8d0f931d3ee0d02d26a5e8ba648e12a73ea1c7db46b3181622e3a8737": { + "type": "mutation", + "name": "CreateWorkbench", + "body": "mutation CreateWorkbench($attributes: WorkbenchAttributes!) {\n createWorkbench(attributes: $attributes) {\n ...Workbench\n __typename\n }\n}\n\nfragment Workbench on Workbench {\n ...WorkbenchTiny\n systemPrompt\n agentRuntime {\n id\n name\n allowedRepositories\n __typename\n }\n repository {\n id\n __typename\n }\n configuration {\n selfService\n infrastructure {\n services\n stacks\n kubernetes\n podLogs\n vulnerabilities\n sentinels\n __typename\n }\n observability {\n logs\n metrics\n __typename\n }\n coding {\n mode\n repositories\n enableBabysitting\n __typename\n }\n __typename\n }\n modes {\n ...WorkbenchJobModesFields\n __typename\n }\n budget {\n enabled\n maximum\n minFree\n unit\n last\n lastUpdated\n __typename\n }\n skills {\n ref {\n ref\n folder\n __typename\n }\n files\n __typename\n }\n workbenchSkills(first: 500) {\n edges {\n node {\n id\n name\n description\n contents\n subagents\n __typename\n }\n __typename\n }\n __typename\n }\n workbenchKnowledge(first: 50) {\n edges {\n node {\n id\n name\n description\n knowledge\n labels\n usages\n lastUsedAt\n __typename\n }\n __typename\n }\n __typename\n }\n tools {\n ...WorkbenchTool\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n botUser {\n id\n name\n email\n profile\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTiny on Workbench {\n id\n name\n description\n agentRuntime {\n id\n name\n type\n __typename\n }\n tools {\n ...WorkbenchToolTiny\n __typename\n }\n webhooks(first: 50) {\n edges {\n node {\n ...WorkbenchWebhookTiny\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment WorkbenchWebhookTiny on WorkbenchWebhook {\n id\n name\n priority\n webhook {\n id\n type\n __typename\n }\n issueWebhook {\n ...IssueWebhookTiny\n __typename\n }\n __typename\n}\n\nfragment IssueWebhookTiny on IssueWebhook {\n id\n provider\n __typename\n}\n\nfragment WorkbenchJobModesFields on WorkbenchJobModes {\n plan\n verification\n model {\n provider\n model\n __typename\n }\n coding {\n approval\n babysit\n review\n __typename\n }\n budget {\n cost\n tokens\n __typename\n }\n kubernetes {\n update\n delete\n exec\n drain\n excludeNamespaces\n requireNamespaces\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" + }, + "sha256:50d68bcc608b515921c783ecb8f26f52d08ebf5e05c07940ea76042c8173af4c": { + "type": "mutation", + "name": "UpdateWorkbench", + "body": "mutation UpdateWorkbench($id: ID!, $attributes: WorkbenchAttributes!) {\n updateWorkbench(id: $id, attributes: $attributes) {\n ...Workbench\n __typename\n }\n}\n\nfragment Workbench on Workbench {\n ...WorkbenchTiny\n systemPrompt\n agentRuntime {\n id\n name\n allowedRepositories\n __typename\n }\n repository {\n id\n __typename\n }\n configuration {\n selfService\n infrastructure {\n services\n stacks\n kubernetes\n podLogs\n vulnerabilities\n sentinels\n __typename\n }\n observability {\n logs\n metrics\n __typename\n }\n coding {\n mode\n repositories\n enableBabysitting\n __typename\n }\n __typename\n }\n modes {\n ...WorkbenchJobModesFields\n __typename\n }\n budget {\n enabled\n maximum\n minFree\n unit\n last\n lastUpdated\n __typename\n }\n skills {\n ref {\n ref\n folder\n __typename\n }\n files\n __typename\n }\n workbenchSkills(first: 500) {\n edges {\n node {\n id\n name\n description\n contents\n subagents\n __typename\n }\n __typename\n }\n __typename\n }\n workbenchKnowledge(first: 50) {\n edges {\n node {\n id\n name\n description\n knowledge\n labels\n usages\n lastUsedAt\n __typename\n }\n __typename\n }\n __typename\n }\n tools {\n ...WorkbenchTool\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n botUser {\n id\n name\n email\n profile\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTiny on Workbench {\n id\n name\n description\n agentRuntime {\n id\n name\n type\n __typename\n }\n tools {\n ...WorkbenchToolTiny\n __typename\n }\n webhooks(first: 50) {\n edges {\n node {\n ...WorkbenchWebhookTiny\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment WorkbenchToolTiny on WorkbenchTool {\n id\n name\n tool\n categories\n approval\n cloudConnection {\n ...CloudConnectionTiny\n __typename\n }\n mcpServer {\n id\n name\n url\n __typename\n }\n __typename\n}\n\nfragment CloudConnectionTiny on CloudConnection {\n id\n name\n provider\n __typename\n}\n\nfragment WorkbenchWebhookTiny on WorkbenchWebhook {\n id\n name\n priority\n webhook {\n id\n type\n __typename\n }\n issueWebhook {\n ...IssueWebhookTiny\n __typename\n }\n __typename\n}\n\nfragment IssueWebhookTiny on IssueWebhook {\n id\n provider\n __typename\n}\n\nfragment WorkbenchJobModesFields on WorkbenchJobModes {\n plan\n verification\n model {\n provider\n model\n __typename\n }\n coding {\n approval\n babysit\n review\n __typename\n }\n budget {\n cost\n tokens\n __typename\n }\n kubernetes {\n update\n delete\n exec\n drain\n excludeNamespaces\n requireNamespaces\n __typename\n }\n __typename\n}\n\nfragment WorkbenchTool on WorkbenchTool {\n ...WorkbenchToolTiny\n scmConnection {\n id\n name\n type\n __typename\n }\n readBindings {\n ...PolicyBinding\n __typename\n }\n writeBindings {\n ...PolicyBinding\n __typename\n }\n configuration {\n http {\n url\n method\n headers {\n name\n value\n __typename\n }\n body\n inputSchema\n __typename\n }\n datadog {\n site\n __typename\n }\n elastic {\n index\n url\n username\n __typename\n }\n opensearch {\n host\n index\n awsAccessKeyId\n awsRegion\n assumeRoleArn\n usePodIdentity\n __typename\n }\n loki {\n url\n username\n tenantId\n __typename\n }\n prometheus {\n url\n username\n tenantId\n awsSigv4\n awsAccessKeyId\n awsRegion\n __typename\n }\n tempo {\n url\n username\n tenantId\n __typename\n }\n jaeger {\n url\n username\n __typename\n }\n atlassian {\n email\n url\n __typename\n }\n linear {\n url\n __typename\n }\n slack {\n url\n __typename\n }\n pagerduty {\n url\n __typename\n }\n teams {\n clientId\n tenantId\n __typename\n }\n splunk {\n url\n username\n __typename\n }\n cloudwatch {\n logGroupNames\n region\n roleArn\n roleSessionName\n __typename\n }\n azure {\n subscriptionId\n tenantId\n clientId\n prometheusUrl\n __typename\n }\n dynatrace {\n url\n __typename\n }\n sentry {\n url\n __typename\n }\n github {\n url\n toolset\n appId\n installationId\n __typename\n }\n gitlab {\n url\n __typename\n }\n bitbucket {\n url\n __typename\n }\n bitbucketDatacenter {\n url\n __typename\n }\n azureDevops {\n url\n __typename\n }\n lambda {\n lambdaArn\n description\n inputSchema\n __typename\n }\n cloudRun {\n identifier\n description\n inputSchema\n __typename\n }\n azureFunction {\n identifier\n description\n inputSchema\n __typename\n }\n docker {\n url\n provider\n proxy {\n url\n noproxy\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n}\n\nfragment PolicyBinding on PolicyBinding {\n id\n user {\n id\n name\n email\n __typename\n }\n group {\n id\n name\n __typename\n }\n __typename\n}" +>>>>>>> Stashed changes }, "sha256:9d48f13216f613d1a0265a4c8453d9ac1dfe8a3f75e1875a63d591c76afee930": { "type": "mutation", diff --git a/js/console/src/graph/workbench.graphql b/js/console/src/graph/workbench.graphql index 2d3a61ffca..6f17346f07 100644 --- a/js/console/src/graph/workbench.graphql +++ b/js/console/src/graph/workbench.graphql @@ -90,6 +90,7 @@ fragment Workbench on Workbench { id } configuration { + selfService infrastructure { services stacks diff --git a/lib/console/ai/tools/workbench/self_service/catalog_search.ex b/lib/console/ai/tools/workbench/self_service/catalog_search.ex new file mode 100644 index 0000000000..e40a8884f1 --- /dev/null +++ b/lib/console/ai/tools/workbench/self_service/catalog_search.ex @@ -0,0 +1,84 @@ +defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do + use Console.AI.Tools.Workbench.Base + alias Console.Repo + alias Console.AI.Tool + alias Console.Deployments.{Git, Policies} + alias Console.Schema.{Catalog, PrAutomation} + + embedded_schema do + field :query, :string + end + + @valid ~w(query)a + @json_schema Console.priv_file!("tools/workbench/self_service/catalog_search.json") |> Jason.decode!() + + def json_schema(), do: @json_schema + def name(), do: "workbench_catalog_search" + def description(), do: """ + Search Plural catalogs and PR automations that are available to the current user. + Prefer this when you need a relevant golden path but do not yet know which catalog or automation fits. + Falls back to name search when semantic search is unavailable. + """ + + def changeset(model, attrs) do + model + |> cast(attrs, @valid) + |> validate_required([:query]) + end + + def implement(%__MODULE__{query: query}) do + case Tool.actor() do + %{} = user -> + case Git.catalog_search(query, user: user) do + {:ok, results} -> format_results(results) + {:error, _} -> fallback_search(query, user) + end + _ -> + {:ok, "not logged in"} + end + end + + defp fallback_search(query, user) do + catalogs = + Catalog.search(query) + |> Catalog.for_user(user) + |> Repo.all() + |> Enum.map(&%{catalog: Map.take(&1, [:id, :name, :description, :category])}) + + pr_automations = + PrAutomation.search(query) + |> Repo.all() + |> Repo.preload([:catalog]) + |> Enum.filter(&readable?(&1, user)) + |> Enum.map(fn pra -> + %{ + pr_automation: Map.take(pra, [:id, :name, :documentation, :title, :branch]) + |> Map.put(:description, pra.documentation) + |> Map.put(:catalog, pra.catalog && Map.take(pra.catalog, [:id, :name])) + } + end) + + Jason.encode(catalogs ++ pr_automations) + end + + defp readable?(%PrAutomation{catalog: %Catalog{} = catalog}, user), + do: match?({:ok, _}, Policies.allow(catalog, user, :read)) + defp readable?(%PrAutomation{} = pra, user), + do: match?({:ok, _}, Policies.allow(pra, user, :create)) + + defp format_results(results) do + Enum.map(results, fn + %{catalog: %Catalog{} = catalog} -> + %{catalog: Map.take(catalog, [:id, :name, :description, :category])} + %{pr_automation: %PrAutomation{} = pra} -> + %{ + pr_automation: + Map.take(pra, [:id, :name, :documentation, :title, :branch]) + |> Map.put(:description, pra.documentation) + } + other -> + other + end) + |> Jason.encode() + end +end diff --git a/lib/console/ai/tools/workbench/self_service/get_pr_automation.ex b/lib/console/ai/tools/workbench/self_service/get_pr_automation.ex new file mode 100644 index 0000000000..863c92d80c --- /dev/null +++ b/lib/console/ai/tools/workbench/self_service/get_pr_automation.ex @@ -0,0 +1,75 @@ +defmodule Console.AI.Tools.Workbench.SelfService.GetPrAutomation do + use Console.AI.Tools.Workbench.Base + alias Console.Repo + alias Console.AI.Tool + alias Console.Deployments.{Git, Policies} + alias Console.Schema.PrAutomation + + embedded_schema do + field :pr_automation_id, :string + field :name, :string + end + + @valid ~w(pr_automation_id name)a + @json_schema Console.priv_file!("tools/workbench/self_service/get_pr_automation.json") |> Jason.decode!() + @fields ~w(id name documentation title message branch branch_prefix identifier configuration icon dark_icon)a + + def json_schema(), do: @json_schema + def name(), do: "workbench_get_pr_automation" + def description(), do: """ + Fetch a single PR automation by id or name, including documentation, branch metadata, + configuration fields, and confirmation requirements. Use this before invoking an automation + so you can fill a valid context. + """ + + def changeset(model, attrs) do + model + |> cast(attrs, @valid) + |> validate_one_of() + end + + defp validate_one_of(cs) do + case {get_field(cs, :pr_automation_id), get_field(cs, :name)} do + {id, _} when is_binary(id) and byte_size(id) > 0 -> cs + {_, name} when is_binary(name) and byte_size(name) > 0 -> cs + _ -> add_error(cs, :pr_automation_id, "either pr_automation_id or name is required") + end + end + + def implement(%__MODULE__{} = model) do + with %{} = user <- Tool.actor(), + %PrAutomation{} = pra <- fetch(model), + {:ok, _} <- Policies.allow(pra, user, :read) do + pra + |> Repo.preload([:catalog]) + |> format() + |> Jason.encode() + else + nil -> {:ok, "PR automation not found"} + {:error, _} -> {:ok, "You do not have access to this PR automation"} + _ -> {:ok, "not logged in"} + end + end + + defp fetch(%__MODULE__{pr_automation_id: id}) when is_binary(id) and byte_size(id) > 0, + do: Git.get_pr_automation(id) + defp fetch(%__MODULE__{name: name}) when is_binary(name) and byte_size(name) > 0, + do: Git.get_pr_automation_by_name(name) + defp fetch(_), do: nil + + defp format(%PrAutomation{} = pra) do + Map.take(pra, @fields) + |> Map.put(:description, pra.documentation) + |> Map.put(:catalog, format_catalog(pra.catalog)) + |> Map.put(:confirmation, format_confirmation(pra.confirmation)) + |> Console.mapify() + end + + defp format_catalog(%{id: id, name: name, description: description, category: category}), + do: %{id: id, name: name, description: description, category: category} + defp format_catalog(_), do: nil + + defp format_confirmation(%{text: text, checklist: checklist}), + do: %{text: text, checklist: Enum.map(checklist || [], &Map.take(&1, [:label]))} + defp format_confirmation(_), do: nil +end diff --git a/lib/console/ai/tools/workbench/self_service/invoke_pr_automation.ex b/lib/console/ai/tools/workbench/self_service/invoke_pr_automation.ex new file mode 100644 index 0000000000..8f5b9ec81e --- /dev/null +++ b/lib/console/ai/tools/workbench/self_service/invoke_pr_automation.ex @@ -0,0 +1,77 @@ +defmodule Console.AI.Tools.Workbench.SelfService.InvokePrAutomation do + use Console.AI.Tools.Workbench.Base + alias Console.AI.Tool + alias Console.Deployments.Git + alias Console.Schema.{WorkbenchJob, PullRequest} + + embedded_schema do + field :pr_automation_id, :string + field :context, :string + field :branch, :string + field :identifier, :string + field :job, :map, virtual: true + end + + @valid ~w(pr_automation_id context branch identifier)a + @json_schema Console.priv_file!("tools/workbench/self_service/invoke_pr_automation.json") |> Jason.decode!() + + def json_schema(_), do: @json_schema + def name(_), do: "workbench_invoke_pr_automation" + def description(_), do: """ + Invoke a PR automation to create a pull request for a clear GitOps provisioning pathway. + The generated pull request is automatically associated with the current workbench job. + Call this only after confirming a relevant automation and filling a valid context. Prefer a single invocation. + """ + + def changeset(model, attrs) do + model + |> cast(attrs, @valid) + |> validate_required([:pr_automation_id, :branch]) + end + + def implement(%__MODULE__{pr_automation_id: pra_id, branch: branch} = model) do + with %{} = user <- Tool.actor(), + %WorkbenchJob{id: job_id, workbench_id: workbench_id} <- job(model), + {:ok, %PullRequest{} = pr} <- + Git.create_pull_request( + %{workbench_job_id: job_id, workbench_id: workbench_id}, + get_context(model), + pra_id, + branch, + model.identifier, + user + ) do + Jason.encode(%{ + id: pr.id, + url: pr.url, + title: pr.title, + status: pr.status, + workbench_job_id: pr.workbench_job_id, + workbench_id: pr.workbench_id + }) + else + nil -> {:ok, "no workbench job or user available for this invocation"} + {:error, %Ecto.Changeset{} = cs} -> + {:ok, "failed to create pull request: #{inspect(Console.GraphQl.Helpers.resolve_changeset(cs))}"} + {:error, err} when is_binary(err) -> {:ok, "failed to create pull request: #{err}"} + {:error, err} -> {:ok, "failed to create pull request: #{inspect(err)}"} + err -> {:ok, "failed to create pull request: #{inspect(err)}"} + end + end + + defp job(%__MODULE__{job: %WorkbenchJob{} = job}), do: job + defp job(_) do + case Tool.context() do + %{job: %WorkbenchJob{} = job} -> job + _ -> nil + end + end + + defp get_context(%__MODULE__{context: ctx}) when is_binary(ctx) do + case Jason.decode(ctx) do + {:ok, %{} = map} -> map + _ -> %{} + end + end + defp get_context(_), do: %{} +end diff --git a/lib/console/ai/tools/workbench/subagent.ex b/lib/console/ai/tools/workbench/subagent.ex index e641a42f7f..cf854aa894 100644 --- a/lib/console/ai/tools/workbench/subagent.ex +++ b/lib/console/ai/tools/workbench/subagent.ex @@ -11,7 +11,8 @@ defmodule Console.AI.Tools.Workbench.Subagent do history: 5, search: 6, verify: 7, - monitoring: 8 + monitoring: 8, + self_service: 9 embedded_schema do field :subagents, {:array, Subagent}, virtual: true diff --git a/lib/console/ai/tools/workbench/subagents.ex b/lib/console/ai/tools/workbench/subagents.ex index 203474aad2..074bfbc0c9 100644 --- a/lib/console/ai/tools/workbench/subagents.ex +++ b/lib/console/ai/tools/workbench/subagents.ex @@ -46,6 +46,7 @@ defmodule Console.AI.Tools.Workbench.Subagents do defp subagent_description(_, :history, _, _), do: "Invoke a history subagent to search past workbench activities. Useful to remember what has been done so far, with regex support for finding past work." defp subagent_description(_, :search, _, _), do: "Invoke a web search subagent to search the public web for information. Useful to find documentation, public pricing information, and anything else that's not specific to deployed infrastructure." defp subagent_description(_, :verify, _, _), do: "Invoke a verification subagent to verify the job was successfully completed based on infrastructure and observability state." + defp subagent_description(_, :self_service, _, _), do: "Invoke a self-service subagent to discover Plural catalogs and PR automations, then invoke a clear GitOps provisioning pathway. Prefer this for repeatable golden-path provisioning; punt undefined or custom code work to the coding subagent." defp subagent_description(_, _, _, _), do: "Unknown subagent" defp infra_description(%{vulnerabilities: vulns, pod_logs: logs}) when vulns or logs do diff --git a/lib/console/ai/workbench/engine.ex b/lib/console/ai/workbench/engine.ex index 8c7a743d33..8c4ff7dc6d 100644 --- a/lib/console/ai/workbench/engine.ex +++ b/lib/console/ai/workbench/engine.ex @@ -211,7 +211,7 @@ defmodule Console.AI.Workbench.Engine do end) end - @supported_subagents ~w(infrastructure integration coding observability monitoring memory skill history search verify)a + @supported_subagents ~w(infrastructure integration coding observability monitoring memory skill history search verify self_service)a defp spawn_activity(action, %__MODULE__{job: job} = engine) do Tracking.with_activity(action, job, fn -> @@ -363,6 +363,7 @@ defmodule Console.AI.Workbench.Engine do defp subagent_module(:skill), do: SA.Skill defp subagent_module(:search), do: SA.Search defp subagent_module(:verify), do: SA.Verify + defp subagent_module(:self_service), do: SA.SelfService defp tool_attrs(%{id: %Console.AI.Tool{id: id, name: name, arguments: arguments}}) when is_binary(id) and is_binary(name), do: %{call_id: id, name: name, arguments: arguments} diff --git a/lib/console/ai/workbench/environment.ex b/lib/console/ai/workbench/environment.ex index 0a6f776583..a09731bc0e 100644 --- a/lib/console/ai/workbench/environment.ex +++ b/lib/console/ai/workbench/environment.ex @@ -136,6 +136,7 @@ defmodule Console.AI.Workbench.Environment do |> Enum.concat(type_subagents(job)) |> Enum.concat(coding_agents(bench)) |> Enum.concat(infra_agents(bench)) + |> Enum.concat(self_service_agents(bench)) |> Enum.filter(&allow_subagent?(job, &1)) end @@ -208,6 +209,9 @@ defmodule Console.AI.Workbench.Environment do end defp infra_agents(_), do: [] + defp self_service_agents(%Workbench{configuration: %{self_service: true}}), do: [:self_service] + defp self_service_agents(_), do: [] + defp type_subagents(%WorkbenchJob{type: :skill}), do: [:history, :skill] defp type_subagents(_), do: [:monitoring] diff --git a/lib/console/ai/workbench/subagents/self_service.ex b/lib/console/ai/workbench/subagents/self_service.ex new file mode 100644 index 0000000000..5d3b466e2c --- /dev/null +++ b/lib/console/ai/workbench/subagents/self_service.ex @@ -0,0 +1,67 @@ +defmodule Console.AI.Workbench.Subagents.SelfService do + use Console.AI.Workbench.Subagents.Base + alias Console.Schema.{WorkbenchJob, WorkbenchJobActivity} + alias Console.AI.Tools.Agent.{Catalogs, PrAutomations} + alias Console.AI.Tools.Workbench.{ + History, + Result, + Scratchpad, + Coding.PullRequests + } + alias Console.AI.Tools.Workbench.SelfService.{ + CatalogSearch, + GetPrAutomation, + InvokePrAutomation + } + alias Console.AI.Workbench.Environment + import Console.AI.Workbench.Environment, only: [engine_opts: 1] + + require EEx + + def run(%WorkbenchJobActivity{prompt: prompt} = activity, %WorkbenchJob{} = job, %Environment{} = environment) do + tools(environment) + |> MemoryEngine.new(20, + engine_opts(environment) ++ [ + system_prompt: String.trim(system_prompt(prompt: WorkbenchJob.objective(job))), + acc: %{}, + callback: &callback(activity, environment, &1), + continue_msg: cont_msg() + ] + ) + |> MemoryEngine.reduce([{:user, prompt}], &reducer/2) + |> case do + {:ok, attrs} -> attrs + {:error, error} -> %{status: :failed, result: %{error: "error running self-service subagent: #{inspect(error)}"}} + end + end + + defp reducer(messages, _) do + case Enum.find(messages, &match?(%Result{}, &1)) do + %Result{output: output} -> {:halt, %{ + status: :successful, + result: %{output: output} + }} + _ -> last_message(messages, & {:cont, %{status: :failed, result: %{error: &1}}}) + end + end + + defp tools(%Environment{skills: skills, job: job, activities: activities}) do + skills = Environment.subagent_skills(skills, :self_service) + + [ + Catalogs, + PrAutomations, + CatalogSearch, + GetPrAutomation, + %InvokePrAutomation{job: job}, + %PullRequests{job: job} + ] + |> Enum.concat(skill_knowledge_tools(job, skills) ++ [ + Scratchpad, + %History{job: job, activities: activities}, + Result + ]) + end + + EEx.function_from_file(:defp, :system_prompt, Console.priv_filename(["prompts", "workbench", "self_service.md.eex"]), [:assigns]) +end diff --git a/lib/console/graphql/deployments/workbench.ex b/lib/console/graphql/deployments/workbench.ex index f4afeb232a..0af6726a09 100644 --- a/lib/console/graphql/deployments/workbench.ex +++ b/lib/console/graphql/deployments/workbench.ex @@ -89,6 +89,7 @@ defmodule Console.GraphQl.Deployments.Workbench do end input_object :workbench_configuration_attributes do + field :self_service, :boolean, description: "enable the self-service subagent for catalog and PR automation workflows" field :infrastructure, :workbench_infrastructure_attributes, description: "infrastructure capabilities (services, stacks, kubernetes)" field :coding, :workbench_coding_attributes, description: "coding capabilities (mode, repositories, babysitting)" field :observability, :workbench_observability_attributes, description: "observability capabilities (logs, metrics)" @@ -962,6 +963,7 @@ defmodule Console.GraphQl.Deployments.Workbench do end object :workbench_configuration do + field :self_service, :boolean, description: "self-service subagent capability enabled" field :infrastructure, :workbench_infrastructure, description: "infrastructure capabilities" field :coding, :workbench_coding, description: "coding capabilities" field :observability, :workbench_observability, description: "observability capabilities" diff --git a/lib/console/schema/workbench.ex b/lib/console/schema/workbench.ex index 4e25f17002..e229db1175 100644 --- a/lib/console/schema/workbench.ex +++ b/lib/console/schema/workbench.ex @@ -108,6 +108,8 @@ defmodule Console.Schema.Workbench do field :memory, Type embeds_one :configuration, Configuration, on_replace: :update do + field :self_service, :boolean, default: false + embeds_one :infrastructure, Infrastructure, on_replace: :update do field :services, :boolean field :stacks, :boolean @@ -253,7 +255,7 @@ defmodule Console.Schema.Workbench do def configuration_changeset(model, attrs \\ %{}) do model - |> cast(attrs, []) + |> cast(attrs, [:self_service]) |> cast_embed(:infrastructure, with: &infrastructure_changeset/2) |> cast_embed(:coding, with: &coding_changeset/2) |> cast_embed(:observability, with: &observability_changeset/2) diff --git a/lib/console/schema/workbench_job_activity.ex b/lib/console/schema/workbench_job_activity.ex index d99be18cb2..cf515c0d42 100644 --- a/lib/console/schema/workbench_job_activity.ex +++ b/lib/console/schema/workbench_job_activity.ex @@ -22,7 +22,8 @@ defmodule Console.Schema.WorkbenchJobActivity do kubernetes: 15, verify: 16, exec: 17, - monitoring: 18 + monitoring: 18, + self_service: 19 defguard is_action(type) when type in [:function, :kubernetes, :exec, :monitoring] diff --git a/lib/console/schema/workbench_skill.ex b/lib/console/schema/workbench_skill.ex index 11587d46d5..c1b4aff564 100644 --- a/lib/console/schema/workbench_skill.ex +++ b/lib/console/schema/workbench_skill.ex @@ -12,7 +12,8 @@ defmodule Console.Schema.WorkbenchSkill do skill: 6, history: 7, search: 8, - monitoring: 9 + monitoring: 9, + self_service: 10 schema "workbench_skills" do field :name, :string diff --git a/priv/prompts/workbench/job.md.eex b/priv/prompts/workbench/job.md.eex index a2cd219dbf..61be5b0ce0 100644 --- a/priv/prompts/workbench/job.md.eex +++ b/priv/prompts/workbench/job.md.eex @@ -15,6 +15,7 @@ the task. You'll be given the following: 7. the ability to record notes and modify your current plan and working theory of how to accomplish the task along the way to memorize progress. 8. a search agent to search the web for information. Useful to find information that is not already in the workbench environment. 9. a verification agent which will be present when a change relevant to this job has been fully applied. This is not always present, and will be included when verification is possible. + 10. a self-service agent for clear, repeatable GitOps provisioning through Plural catalogs and PR automations. Prefer this for golden-path provisioning; undefined or custom code work should use the coding agent instead. Basic guardrails for using these subagents: @@ -22,6 +23,7 @@ Basic guardrails for using these subagents: * All log, timeseries and trace data should be queried using the observability agent. * Delegate persistent dashboard and monitor creation, updates, imports, and deletion to the monitoring agent. It can query observability data itself to validate the configuration it manages. * When you're ready to inspect code, leverage the coding agent to either analyze or modify code, generating a pull request in write mode. +* When the task is clear, repeatable GitOps provisioning and a catalog PR automation likely covers it, prefer the self-service agent. If the pathway is undefined or needs custom code changes, use the coding agent instead. <%= if @review do %> * Pull request review is in scope for this job. When the task is to review a PR, launch the coding subagent and instruct it to use review mode with the PR URL and the PR's head branch (the branch containing the changes). Do not use analyze or write mode to inspect an existing PR. * The coding agent owns the entire review, including publishing its summary and inline findings. Do not delegate any part of pull request review or review publication to the integration subagent or SCM integration tools. @@ -155,6 +157,8 @@ Of all the subagents, coding has the highest cost to run, since it spawns a dedi to gather the information needed to then prompt it, which should be reserved for codebase analysis<%= if @review do %>, pull request review,<% end %> and generation of PRs. Once you have a cleanly crafted prompt to delegate, then do so if the user requires it to complete their task. +If a clear Plural catalog/PR automation golden path exists for the request, prefer the `self_service` subagent before coding. Self-service should usually only be used for clear provisioning pathways and usually only for GitOps tasks; an undefined task needs the coding agent. + Two agents can give useful information about the codebase defining the system: 1. `coding` is effective for introspecting application code, and modifying it<%= if @review do %>, and for reviewing existing pull requests<% end %>. It can also be used to modify GitOps/IaC, but you likely need additional information diff --git a/priv/prompts/workbench/self_service.md.eex b/priv/prompts/workbench/self_service.md.eex new file mode 100644 index 0000000000..6be6e34ffb --- /dev/null +++ b/priv/prompts/workbench/self_service.md.eex @@ -0,0 +1,37 @@ +You're a senior engineer focusing on clear, repeatable GitOps provisioning through Plural catalogs and PR automations. + +Use this subagent only for well-defined provisioning pathways that map to an existing PR automation. Prefer golden paths over one-off code edits. If the task is undefined, exploratory, or requires custom application/infrastructure code changes that no automation covers, finish with `subagent_result` explaining that the coding agent is the better next step. + +# Workflow + +1. List catalogs available to the current user. +2. Search or list PR automations within the most relevant catalogs. +3. Inspect a candidate automation (`workbench_get_pr_automation`) to understand documentation, required configuration fields, branch defaults, and confirmation requirements. +4. Infer configuration values when you can. Leave uncertain values blank rather than inventing them. +5. Invoke at most one clearly relevant automation with a descriptive source branch. +6. Summarize the created pull request or explain why no automation was appropriate. + +# Guardrails + +* Self-service is usually only valid for clear GitOps/provisioning tasks. +* Do not force-fit an automation. If none are relevant, call `subagent_result` and recommend the coding agent. +* Prefer a single invocation. Do not repeatedly invoke the same automation. +* Do not invent configuration. Prefer inspecting the automation first. +* Generated PRs are automatically associated with this workbench job; include the PR URL and title in your result. +* You can list pull requests already created for this job when useful for continuity. + +# Tone of Voice Guidance + +You are producing output for a human user, and should expect them to want to read as little as possible and mostly be scanning. You should be: + +* as concise as possible +* still provide critical information needed to convey the result +* use supporting markdown formatting where needed to improve scannability + +# Ideal Output + +Summarize what catalog/automation path you chose, what PR was created (if any), and any gaps or reasons you punted. Always call `subagent_result` exactly once when done. + +The overarching task is as follows: + +<%= @prompt %> diff --git a/priv/tools/workbench/self_service/catalog_search.json b/priv/tools/workbench/self_service/catalog_search.json new file mode 100644 index 0000000000..2be35245be --- /dev/null +++ b/priv/tools/workbench/self_service/catalog_search.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Semantic or name search query for catalogs and PR automations" + } + }, + "required": ["query"] +} diff --git a/priv/tools/workbench/self_service/get_pr_automation.json b/priv/tools/workbench/self_service/get_pr_automation.json new file mode 100644 index 0000000000..1bb06fabe1 --- /dev/null +++ b/priv/tools/workbench/self_service/get_pr_automation.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "properties": { + "pr_automation_id": { + "type": "string", + "description": "The id of the PR automation to fetch. Prefer this when you already have an id from list or search results." + }, + "name": { + "type": "string", + "description": "The name of the PR automation to fetch when you do not yet have its id." + } + }, + "required": [] +} diff --git a/priv/tools/workbench/self_service/invoke_pr_automation.json b/priv/tools/workbench/self_service/invoke_pr_automation.json new file mode 100644 index 0000000000..e6773f12fb --- /dev/null +++ b/priv/tools/workbench/self_service/invoke_pr_automation.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "properties": { + "pr_automation_id": { + "type": "string", + "description": "The id of the PR automation to invoke." + }, + "context": { + "type": "string", + "description": "A JSON-encoded string-valued map filling the PR automation configuration fields. Keys are configuration field names and values are the chosen inputs." + }, + "branch": { + "type": "string", + "description": "The source branch name for the generated pull request. Use a concise, descriptive name and avoid existing branches." + }, + "identifier": { + "type": "string", + "description": "Optional repository identifier override (for example owner/repo). Leave unset to use the automation default." + } + }, + "required": ["pr_automation_id", "branch"] +} diff --git a/schema/schema.graphql b/schema/schema.graphql index 6f6d57e8ba..5866db1723 100644 --- a/schema/schema.graphql +++ b/schema/schema.graphql @@ -3025,6 +3025,7 @@ enum WorkbenchJobActivityType { VERIFY EXEC MONITORING + SELF_SERVICE } enum WorkbenchCanvasBlockType { @@ -3047,6 +3048,7 @@ enum WorkbenchSkillSubagent { HISTORY SEARCH MONITORING + SELF_SERVICE } enum WorkbenchChatbotMessageBehavior { @@ -3201,6 +3203,9 @@ input WorkbenchAttributes { } input WorkbenchConfigurationAttributes { + "enable the self-service subagent for catalog and PR automation workflows" + selfService: Boolean + "infrastructure capabilities (services, stacks, kubernetes)" infrastructure: WorkbenchInfrastructureAttributes @@ -4699,6 +4704,9 @@ type WorkbenchJobResultTodo { } type WorkbenchConfiguration { + "self-service subagent capability enabled" + selfService: Boolean + "infrastructure capabilities" infrastructure: WorkbenchInfrastructure diff --git a/test/console/ai/tools/workbench/self_service/catalog_search_test.exs b/test/console/ai/tools/workbench/self_service/catalog_search_test.exs new file mode 100644 index 0000000000..fe2414682f --- /dev/null +++ b/test/console/ai/tools/workbench/self_service/catalog_search_test.exs @@ -0,0 +1,35 @@ +defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearchTest do + use Console.DataCase, async: true + alias Console.AI.Tools.Workbench.SelfService.CatalogSearch + + describe "implement/1" do + test "falls back to name search when vector store is disabled" do + user = insert(:user) + catalog = insert(:catalog, name: "databases", read_bindings: [%{user_id: user.id}]) + pra = insert(:pr_automation, name: "postgres-cluster", catalog: catalog, documentation: "Provision postgres") + insert(:pr_automation, name: "unrelated", documentation: "other") + + Console.AI.Tool.context(%{user: user}) + + {:ok, result} = CatalogSearch.implement(%CatalogSearch{query: "postgres"}) + {:ok, decoded} = Jason.decode(result) + + assert Enum.any?(decoded, fn + %{"pr_automation" => %{"id" => id}} -> id == pra.id + _ -> false + end) + end + + test "hides catalogs the user cannot read" do + user = insert(:user) + insert(:catalog, name: "secret-catalog") + + Console.AI.Tool.context(%{user: user}) + + {:ok, result} = CatalogSearch.implement(%CatalogSearch{query: "secret"}) + {:ok, decoded} = Jason.decode(result) + + assert decoded == [] + end + end +end diff --git a/test/console/ai/tools/workbench/self_service/get_pr_automation_test.exs b/test/console/ai/tools/workbench/self_service/get_pr_automation_test.exs new file mode 100644 index 0000000000..f762e894cd --- /dev/null +++ b/test/console/ai/tools/workbench/self_service/get_pr_automation_test.exs @@ -0,0 +1,52 @@ +defmodule Console.AI.Tools.Workbench.SelfService.GetPrAutomationTest do + use Console.DataCase, async: true + alias Console.AI.Tools.Workbench.SelfService.GetPrAutomation + + describe "implement/1" do + test "returns automation details for catalog readers" do + user = insert(:user) + catalog = insert(:catalog, read_bindings: [%{user_id: user.id}]) + pra = insert(:pr_automation, + catalog: catalog, + documentation: "Creates a managed postgres", + title: "Add postgres", + branch: "plrl/postgres" + ) + + Console.AI.Tool.context(%{user: user}) + + {:ok, result} = GetPrAutomation.implement(%GetPrAutomation{pr_automation_id: pra.id}) + {:ok, decoded} = Jason.decode(result) + + assert decoded["id"] == pra.id + assert decoded["documentation"] == "Creates a managed postgres" + assert decoded["title"] == "Add postgres" + assert decoded["branch"] == "plrl/postgres" + assert decoded["catalog"]["id"] == catalog.id + end + + test "supports lookup by name" do + user = insert(:user) + catalog = insert(:catalog, read_bindings: [%{user_id: user.id}]) + pra = insert(:pr_automation, name: "named-pra", catalog: catalog) + + Console.AI.Tool.context(%{user: user}) + + {:ok, result} = GetPrAutomation.implement(%GetPrAutomation{name: "named-pra"}) + {:ok, decoded} = Jason.decode(result) + + assert decoded["id"] == pra.id + end + + test "denies users without catalog access" do + user = insert(:user) + catalog = insert(:catalog) + pra = insert(:pr_automation, catalog: catalog) + + Console.AI.Tool.context(%{user: user}) + + {:ok, result} = GetPrAutomation.implement(%GetPrAutomation{pr_automation_id: pra.id}) + assert result =~ "do not have access" + end + end +end diff --git a/test/console/ai/tools/workbench/self_service/invoke_pr_automation_test.exs b/test/console/ai/tools/workbench/self_service/invoke_pr_automation_test.exs new file mode 100644 index 0000000000..70831659e3 --- /dev/null +++ b/test/console/ai/tools/workbench/self_service/invoke_pr_automation_test.exs @@ -0,0 +1,54 @@ +defmodule Console.AI.Tools.Workbench.SelfService.InvokePrAutomationTest do + use Console.DataCase, async: true + use Mimic + alias Console.AI.Tools.Workbench.SelfService.InvokePrAutomation + alias Console.Deployments.Pr.Dispatcher + + describe "implement/1" do + test "creates a pull request associated with the workbench job" do + user = insert(:user) + job = insert(:workbench_job, user: user) + pra = insert(:pr_automation, create_bindings: [%{user_id: user.id}]) + + expect(Dispatcher, :create, fn _, "plrl/self-service", %{"cluster" => "dev"} -> + {:ok, %{url: "https://github.com/pluralsh/console/pull/1", title: "Provision cluster"}} + end) + + Console.AI.Tool.context(%{user: user, job: job}) + + {:ok, result} = + InvokePrAutomation.implement(%InvokePrAutomation{ + pr_automation_id: pra.id, + branch: "plrl/self-service", + context: Jason.encode!(%{"cluster" => "dev"}), + job: job + }) + + {:ok, decoded} = Jason.decode(result) + + assert decoded["url"] == "https://github.com/pluralsh/console/pull/1" + assert decoded["title"] == "Provision cluster" + assert decoded["workbench_job_id"] == job.id + assert decoded["workbench_id"] == job.workbench_id + assert decoded["status"] == "open" + end + + test "returns permission failures without creating a pull request" do + user = insert(:user) + job = insert(:workbench_job, user: user) + pra = insert(:pr_automation) + + Console.AI.Tool.context(%{user: user, job: job}) + + {:ok, result} = + InvokePrAutomation.implement(%InvokePrAutomation{ + pr_automation_id: pra.id, + branch: "plrl/self-service", + context: "{}", + job: job + }) + + assert result =~ "failed to create pull request" + end + end +end diff --git a/test/console/ai/tools/workbench/subagents_test.exs b/test/console/ai/tools/workbench/subagents_test.exs index ce3aebf53f..edf82ed6a1 100644 --- a/test/console/ai/tools/workbench/subagents_test.exs +++ b/test/console/ai/tools/workbench/subagents_test.exs @@ -65,6 +65,34 @@ defmodule Console.AI.Tools.Workbench.SubagentsTest do assert description =~ "metrics, logs" end + test "describes self_service as catalog and PR automation workflows" do + {:ok, encoded} = + Subagents.implement(%Subagents{ + bench: %Workbench{}, + job: %WorkbenchJob{}, + subagents: [:self_service], + categories: [] + }) + + assert [%{"name" => "self_service", "description" => description}] = + Jason.decode!(encoded) + + assert description =~ "catalog" + assert description =~ "PR automation" + assert description =~ "coding" + end + + test "accepts the self_service subagent" do + assert {:ok, %Subagent{subagent: :self_service}} = + Tool.validate( + %Subagent{subagents: [:self_service]}, + %{ + "subagent" => "self_service", + "prompt" => "Provision a postgres cluster via catalog automation" + } + ) + end + test "mentions review mode on the coding subagent only when enabled" do {:ok, encoded} = Subagents.implement(%Subagents{ diff --git a/test/console/ai/workbench/environment_test.exs b/test/console/ai/workbench/environment_test.exs index c316b0f7d1..9612618988 100644 --- a/test/console/ai/workbench/environment_test.exs +++ b/test/console/ai/workbench/environment_test.exs @@ -60,6 +60,20 @@ defmodule Console.AI.Workbench.EnvironmentTest do ]) ) end + + test "includes self_service when configuration.self_service is enabled" do + workbench = insert(:workbench, configuration: %{self_service: true}) + job = insert(:workbench_job, workbench: workbench) |> Repo.preload(workbench: :tools) + + assert :self_service in Environment.subagents(job) + end + + test "excludes self_service when configuration.self_service is absent" do + workbench = insert(:workbench, configuration: %{infrastructure: %{services: true}}) + job = insert(:workbench_job, workbench: workbench) |> Repo.preload(workbench: :tools) + + refute :self_service in Environment.subagents(job) + end end describe "actions/1" do diff --git a/test/console/ai/workbench/subagents/self_service_test.exs b/test/console/ai/workbench/subagents/self_service_test.exs new file mode 100644 index 0000000000..f3c016c549 --- /dev/null +++ b/test/console/ai/workbench/subagents/self_service_test.exs @@ -0,0 +1,72 @@ +defmodule Console.AI.Workbench.Subagents.SelfServiceTest do + use Console.DataCase, async: false + use Mimic + alias Console.AI.{Provider, Tool} + alias Console.AI.Workbench.{Engine, Environment, Subagents.SelfService} + alias Console.AI.Tools.Workbench.SelfService.InvokePrAutomation + alias Console.Deployments.Pr.Dispatcher + + setup :set_mimic_global + + describe "run/3" do + test "invokes a PR automation and returns subagent_result output" do + deployment_settings(ai: %{ + enabled: true, + provider: :openai, + openai: %{access_token: "key"} + }) + + user = insert(:user) + workbench = insert(:workbench, configuration: %{self_service: true}) + job = insert(:workbench_job, workbench: workbench, user: user, prompt: "Provision postgres") + activity = insert(:workbench_job_activity, workbench_job: job, type: :self_service, prompt: "Find and invoke the postgres automation") + catalog = insert(:catalog, name: "databases", read_bindings: [%{user_id: user.id}]) + pra = insert(:pr_automation, + name: "postgres", + catalog: catalog, + create_bindings: [%{user_id: user.id}], + documentation: "Provision postgres" + ) + + expect(Dispatcher, :create, fn _, "plrl/postgres", _ -> + {:ok, %{url: "https://github.com/pluralsh/console/pull/22", title: "Add postgres"}} + end) + + expect(Provider, :completion, fn _, _ -> + {:ok, "listing catalogs", [ + %Tool{name: "__plrl__catalogs", arguments: %{}, id: "1"} + ]} + end) + + expect(Provider, :completion, fn msgs, _ -> + assert Enum.any?(msgs, &match?({:tool, _, %{name: "__plrl__catalogs"}}, &1)) + + {:ok, "invoking", [ + %Tool{ + name: InvokePrAutomation.name(%InvokePrAutomation{}), + arguments: %{ + "pr_automation_id" => pra.id, + "branch" => "plrl/postgres", + "context" => "{}" + }, + id: "2" + } + ]} + end) + + expect(Provider, :completion, fn msgs, _ -> + assert Enum.any?(msgs, &match?({:tool, _, %{name: "workbench_invoke_pr_automation"}}, &1)) + + {:ok, "done", [ + %Tool{name: "subagent_result", arguments: %{"output" => "Created postgres PR"}, id: "3"} + ]} + end) + + {:ok, _engine} = Engine.new(job) + result = SelfService.run(activity, job, Environment.new(job, [], [])) + + assert result[:status] == :successful + assert result[:result][:output] == "Created postgres PR" + end + end +end diff --git a/test/console/graphql/mutations/deployments/workbench_mutations_test.exs b/test/console/graphql/mutations/deployments/workbench_mutations_test.exs index f8de82ebfa..04529736a5 100644 --- a/test/console/graphql/mutations/deployments/workbench_mutations_test.exs +++ b/test/console/graphql/mutations/deployments/workbench_mutations_test.exs @@ -43,6 +43,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do "name" => "configured-workbench", "projectId" => project.id, "configuration" => %{ + "selfService" => true, "infrastructure" => %{"services" => true, "stacks" => true, "kubernetes" => false}, "coding" => %{"mode" => "ANALYZE", "repositories" => ["repo1", "repo2"]} } @@ -54,6 +55,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do id name configuration { + selfService infrastructure { services stacks kubernetes } coding { mode repositories } } @@ -62,6 +64,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do """, %{"attributes" => attrs}, %{current_user: admin_user()}) assert workbench["name"] == "configured-workbench" + assert workbench["configuration"]["selfService"] == true assert workbench["configuration"]["infrastructure"]["services"] == true assert workbench["configuration"]["infrastructure"]["stacks"] == true assert workbench["configuration"]["infrastructure"]["kubernetes"] == false @@ -182,6 +185,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do attrs = %{ "name" => workbench.name, "configuration" => %{ + "selfService" => true, "infrastructure" => %{"services" => false, "stacks" => true, "kubernetes" => true}, "coding" => %{"mode" => "WRITE", "repositories" => ["single-repo"]} } @@ -192,6 +196,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do updateWorkbench(id: $id, attributes: $attributes) { id configuration { + selfService infrastructure { services stacks kubernetes } coding { mode repositories } } @@ -200,6 +205,7 @@ defmodule Console.GraphQl.Deployments.WorkbenchMutationsTest do """, %{"id" => workbench.id, "attributes" => attrs}, %{current_user: admin_user()}) assert updated["id"] == workbench.id + assert updated["configuration"]["selfService"] == true assert updated["configuration"]["infrastructure"]["services"] == false assert updated["configuration"]["infrastructure"]["stacks"] == true assert updated["configuration"]["infrastructure"]["kubernetes"] == true From 653afb25fb50ec3c9c5b9a9c679dc60acc2e7759 Mon Sep 17 00:00:00 2001 From: chrisakinrinade Date: Mon, 21 Sep 2026 13:08:24 -0400 Subject: [PATCH 2/4] syntax tweaks to catalog_seach.ex --- .../workbench/self_service/catalog_search.ex | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/console/ai/tools/workbench/self_service/catalog_search.ex b/lib/console/ai/tools/workbench/self_service/catalog_search.ex index e40a8884f1..504617308f 100644 --- a/lib/console/ai/tools/workbench/self_service/catalog_search.ex +++ b/lib/console/ai/tools/workbench/self_service/catalog_search.ex @@ -27,38 +27,42 @@ defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do end def implement(%__MODULE__{query: query}) do - case Tool.actor() do - %{} = user -> - case Git.catalog_search(query, user: user) do - {:ok, results} -> format_results(results) - {:error, _} -> fallback_search(query, user) - end - _ -> + with {:actor, %{} = user} <- {:actor, Tool.actor()}, + {:search, user, {:ok, results}} <- {:search, user, Git.catalog_search(query, user: user)} do + format_results(results) + else + {:actor, _} -> {:ok, "not logged in"} + {:search, user, {:error, _}} -> + fallback_search(query, user) end end defp fallback_search(query, user) do - catalogs = - Catalog.search(query) - |> Catalog.for_user(user) - |> Repo.all() - |> Enum.map(&%{catalog: Map.take(&1, [:id, :name, :description, :category])}) + catalog_hits(query, user) + |> Enum.concat(automation_hits(query, user)) + |> Jason.encode() + end - pr_automations = - PrAutomation.search(query) - |> Repo.all() - |> Repo.preload([:catalog]) - |> Enum.filter(&readable?(&1, user)) - |> Enum.map(fn pra -> - %{ - pr_automation: Map.take(pra, [:id, :name, :documentation, :title, :branch]) - |> Map.put(:description, pra.documentation) - |> Map.put(:catalog, pra.catalog && Map.take(pra.catalog, [:id, :name])) - } - end) + defp catalog_hits(query, user) do + Catalog.search(query) + |> Catalog.for_user(user) + |> Repo.all() + |> Enum.map(&%{catalog: Map.take(&1, [:id, :name, :description, :category])}) + end - Jason.encode(catalogs ++ pr_automations) + defp automation_hits(query, user) do + PrAutomation.search(query) + |> Repo.all() + |> Repo.preload([:catalog]) + |> Enum.filter(&readable?(&1, user)) + |> Enum.map(fn pra -> + %{ + pr_automation: Map.take(pra, [:id, :name, :documentation, :title, :branch]) + |> Map.put(:description, pra.documentation) + |> Map.put(:catalog, pra.catalog && Map.take(pra.catalog, [:id, :name])) + } + end) end defp readable?(%PrAutomation{catalog: %Catalog{} = catalog}, user), From c9b76a6fe777917965adaedb1a2abcb59017d39a Mon Sep 17 00:00:00 2001 From: chrisakinrinade Date: Mon, 21 Sep 2026 15:23:58 -0400 Subject: [PATCH 3/4] fix test failures --- .../ai/tools/workbench/self_service/catalog_search.ex | 2 +- test/console/ai/workbench/subagents/self_service_test.exs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/console/ai/tools/workbench/self_service/catalog_search.ex b/lib/console/ai/tools/workbench/self_service/catalog_search.ex index 504617308f..12071acc9f 100644 --- a/lib/console/ai/tools/workbench/self_service/catalog_search.ex +++ b/lib/console/ai/tools/workbench/self_service/catalog_search.ex @@ -28,7 +28,7 @@ defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do def implement(%__MODULE__{query: query}) do with {:actor, %{} = user} <- {:actor, Tool.actor()}, - {:search, user, {:ok, results}} <- {:search, user, Git.catalog_search(query, user: user)} do + {:search, ^user, {:ok, results}} <- {:search, user, Git.catalog_search(query, user: user)} do format_results(results) else {:actor, _} -> diff --git a/test/console/ai/workbench/subagents/self_service_test.exs b/test/console/ai/workbench/subagents/self_service_test.exs index f3c016c549..958ce90810 100644 --- a/test/console/ai/workbench/subagents/self_service_test.exs +++ b/test/console/ai/workbench/subagents/self_service_test.exs @@ -1,7 +1,7 @@ defmodule Console.AI.Workbench.Subagents.SelfServiceTest do use Console.DataCase, async: false use Mimic - alias Console.AI.{Provider, Tool} + alias Console.AI.Tool alias Console.AI.Workbench.{Engine, Environment, Subagents.SelfService} alias Console.AI.Tools.Workbench.SelfService.InvokePrAutomation alias Console.Deployments.Pr.Dispatcher @@ -32,13 +32,13 @@ defmodule Console.AI.Workbench.Subagents.SelfServiceTest do {:ok, %{url: "https://github.com/pluralsh/console/pull/22", title: "Add postgres"}} end) - expect(Provider, :completion, fn _, _ -> + expect_reqllm_completion(fn _, _ -> {:ok, "listing catalogs", [ %Tool{name: "__plrl__catalogs", arguments: %{}, id: "1"} ]} end) - expect(Provider, :completion, fn msgs, _ -> + expect_reqllm_completion(fn msgs, _ -> assert Enum.any?(msgs, &match?({:tool, _, %{name: "__plrl__catalogs"}}, &1)) {:ok, "invoking", [ @@ -54,7 +54,7 @@ defmodule Console.AI.Workbench.Subagents.SelfServiceTest do ]} end) - expect(Provider, :completion, fn msgs, _ -> + expect_reqllm_completion(fn msgs, _ -> assert Enum.any?(msgs, &match?({:tool, _, %{name: "workbench_invoke_pr_automation"}}, &1)) {:ok, "done", [ From 4a3adbece90001d411fd353daf58d0e4c32f7aa2 Mon Sep 17 00:00:00 2001 From: chrisakinrinade Date: Tue, 22 Sep 2026 14:15:07 -0400 Subject: [PATCH 4/4] add 100 result limit to each query --- .../ai/tools/workbench/self_service/catalog_search.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/console/ai/tools/workbench/self_service/catalog_search.ex b/lib/console/ai/tools/workbench/self_service/catalog_search.ex index 12071acc9f..6bb265462a 100644 --- a/lib/console/ai/tools/workbench/self_service/catalog_search.ex +++ b/lib/console/ai/tools/workbench/self_service/catalog_search.ex @@ -1,5 +1,6 @@ defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do use Console.AI.Tools.Workbench.Base + import Ecto.Query alias Console.Repo alias Console.AI.Tool alias Console.Deployments.{Git, Policies} @@ -28,7 +29,7 @@ defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do def implement(%__MODULE__{query: query}) do with {:actor, %{} = user} <- {:actor, Tool.actor()}, - {:search, ^user, {:ok, results}} <- {:search, user, Git.catalog_search(query, user: user)} do + {:search, ^user, {:ok, results}} <- {:search, user, Git.catalog_search(query, user: user, count: 100)} do format_results(results) else {:actor, _} -> @@ -47,12 +48,14 @@ defmodule Console.AI.Tools.Workbench.SelfService.CatalogSearch do defp catalog_hits(query, user) do Catalog.search(query) |> Catalog.for_user(user) + |> limit(100) |> Repo.all() |> Enum.map(&%{catalog: Map.take(&1, [:id, :name, :description, :category])}) end defp automation_hits(query, user) do PrAutomation.search(query) + |> limit(100) |> Repo.all() |> Repo.preload([:catalog]) |> Enum.filter(&readable?(&1, user))