Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/agents/definitions/backlog-manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ triggers:
- event: internal:auto-chain
label: Auto-chain after Splitting
description: When splitting completes on a card with the auto label, immediately chain to backlog manager
defaultEnabled: true
defaultEnabled: false
contextPipeline: [pipelineSnapshot]

strategies: {}
Expand Down
4 changes: 2 additions & 2 deletions src/agents/definitions/implementation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ triggers:
- event: pm:status-changed
label: Status Changed to Todo
description: Trigger when work item status changes to Todo
defaultEnabled: true
defaultEnabled: false
parameters:
- name: targetStatus
type: select
Expand All @@ -39,7 +39,7 @@ triggers:
- event: pm:label-added
label: Ready to Process Label
description: Trigger when Ready to Process label added to a card in the Todo list
defaultEnabled: true
defaultEnabled: false
parameters:
- name: listKey
type: select
Expand Down
6 changes: 3 additions & 3 deletions src/agents/definitions/planning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ triggers:
- event: pm:status-changed
label: Status Changed to Planning
description: Trigger when work item status changes to Planning
defaultEnabled: true
defaultEnabled: false
parameters:
- name: targetStatus
type: select
Expand All @@ -36,7 +36,7 @@ triggers:
- event: pm:label-added
label: Ready to Process Label
description: Trigger when Ready to Process label added to a card in Planning list
defaultEnabled: true
defaultEnabled: false
parameters:
- name: listKey
type: select
Expand All @@ -47,7 +47,7 @@ triggers:
- event: pm:comment-mention
label: Comment @mention
description: Trigger when bot is @mentioned in a card/issue comment
defaultEnabled: true
defaultEnabled: false
contextPipeline: [directoryListing, contextFiles, squint, workItem]

strategies: {}
Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/resolve-conflicts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ triggers:
- event: scm:pr-conflict-detected
label: PR Conflict Detected
description: Trigger when a PR has merge conflicts with the base branch
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: [prContext, directoryListing, contextFiles, squint, workItem]

Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/respond-to-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ triggers:
- event: scm:check-suite-failure
label: Check Suite Failure
description: Trigger when CI checks fail
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: [prContext, directoryListing, contextFiles, squint, workItem]

Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/respond-to-planning-comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ triggers:
- event: pm:comment-mention
label: Comment @mention
description: Trigger when bot is @mentioned in a card/issue comment
defaultEnabled: true
defaultEnabled: false
contextPipeline: [directoryListing, contextFiles, squint, workItem]

strategies: {}
Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/respond-to-pr-comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ triggers:
- event: scm:pr-comment-mention
label: PR Comment @mention
description: Trigger when the implementer bot is @mentioned in a PR comment
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: [prContext, prConversation, directoryListing, contextFiles, squint]

Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/respond-to-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ triggers:
- event: scm:pr-review-submitted
label: PR Review Submitted
description: Trigger when a review with changes requested or comments is submitted
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: [prContext, prConversation, directoryListing, contextFiles, squint]

Expand Down
4 changes: 2 additions & 2 deletions src/agents/definitions/review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ triggers:
- event: scm:pr-ready-to-merge
label: PR Ready to Merge
description: Move work item to DONE when PR is approved and all checks pass
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: []
- event: scm:pr-merged
label: PR Merged
description: Move work item to MERGED status when PR is merged
defaultEnabled: true
defaultEnabled: false
providers: [github]
contextPipeline: []

Expand Down
2 changes: 1 addition & 1 deletion src/agents/definitions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const SupportedTriggerSchema = z.object({
/** Optional description for help text */
description: z.string().optional(),
/** Whether the trigger is enabled by default */
defaultEnabled: z.boolean().default(true),
defaultEnabled: z.boolean().default(false),
/** Configurable parameters for this trigger */
parameters: z.array(TriggerParameterSchema).default([]),
/** Provider filter - only applies to these providers (e.g., ['trello']) */
Expand Down
4 changes: 2 additions & 2 deletions src/agents/definitions/splitting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ triggers:
- event: pm:status-changed
label: Status Changed to Splitting
description: Trigger when work item status changes to Splitting
defaultEnabled: true
defaultEnabled: false
parameters:
- name: targetStatus
type: select
Expand All @@ -37,7 +37,7 @@ triggers:
- event: pm:label-added
label: Ready to Process Label
description: Trigger when Ready to Process label added to a card in Splitting list
defaultEnabled: true
defaultEnabled: false
parameters:
- name: listKey
type: select
Expand Down
15 changes: 15 additions & 0 deletions src/agents/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
import { Eta } from 'eta';

import { resolveKnownAgentTypes } from '../definitions/index.js';
import { loadAgentDefinition } from '../definitions/loader.js';

const __dirname = dirname(fileURLToPath(import.meta.url));
const templatesDir = join(__dirname, 'templates');
Expand Down Expand Up @@ -231,6 +232,20 @@ export function renderInlineTaskPrompt(
return taskEta.renderString(expanded, context);
}

/**
* Returns the YAML-defined taskPrompt for an agent type (the factory default).
* Does not require initPrompts() — reads directly from YAML.
* Returns null if the agent type is unknown or has no taskPrompt defined.
*/
export function getDefaultTaskPrompt(agentType: string): string | null {
try {
const definition = loadAgentDefinition(agentType);
return definition.prompts.taskPrompt ?? null;
} catch {
return null;
}
}

/** Returns the raw .eta template source from disk (before rendering). */
export function getRawTemplate(agentType: string): string {
requireInitialized('getRawTemplate');
Expand Down
10 changes: 10 additions & 0 deletions src/api/routers/_shared/triggerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,19 @@ export interface ProjectIntegrationsMap {
/**
* Complete triggers view for a project.
* Response type for getProjectTriggersView.
*
* `enabledAgents` — agents that have an explicit agent_configs row (opt-in enabled).
* `availableAgents` — agents that exist in definitions but are NOT yet configured.
*
* The legacy `agents` field equals `enabledAgents` for backwards compatibility.
*/
export interface ProjectTriggersView {
/** @deprecated Use enabledAgents instead */
agents: AgentTriggersView[];
/** Agents with an explicit agent_configs row — actively configured for this project */
enabledAgents: AgentTriggersView[];
/** Agent types defined in YAML/DB but not yet configured for this project */
availableAgents: string[];
integrations: ProjectIntegrationsMap;
}

Expand Down
10 changes: 9 additions & 1 deletion src/api/routers/agentConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { TRPCError } from '@trpc/server';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
import { resolveAgentDefinition } from '../../agents/definitions/index.js';
import { getRawTemplate, validateTemplate } from '../../agents/prompts/index.js';
import {
getDefaultTaskPrompt,
getRawTemplate,
validateTemplate,
} from '../../agents/prompts/index.js';
import { getEngineCatalog, registerBuiltInEngines } from '../../backends/index.js';
import { EngineSettingsSchema } from '../../config/engineSettings.js';
import { getDb } from '../../db/client.js';
Expand Down Expand Up @@ -178,12 +182,16 @@ export const agentConfigsRouter = router({
// No .eta template on disk — skip gracefully
}

// 4. YAML-defined task prompt (factory default)
const defaultTaskPrompt = getDefaultTaskPrompt(input.agentType);

return {
projectSystemPrompt,
projectTaskPrompt,
globalSystemPrompt,
globalTaskPrompt,
defaultSystemPrompt,
defaultTaskPrompt,
};
}),
});
37 changes: 25 additions & 12 deletions src/api/routers/agentTriggerConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
SupportedTrigger,
TriggerParameter,
} from '../../agents/definitions/schema.js';
import { listAgentConfigs } from '../../db/repositories/agentConfigsRepository.js';
import { listAgentDefinitions } from '../../db/repositories/agentDefinitionsRepository.js';
import {
deleteTriggerConfig,
Expand Down Expand Up @@ -204,16 +205,20 @@ export const agentTriggerConfigsRouter = router({
.query(async ({ ctx, input }): Promise<ProjectTriggersView> => {
await verifyProjectOrgAccess(input.projectId, ctx.effectiveOrgId);

// Fetch DB definitions and configs in parallel
const [dbDefinitions, configs, integrations] = await Promise.all([
// Fetch DB definitions, trigger configs, agent configs (for enabled check), and integrations
const [dbDefinitions, configs, projectAgentConfigs, integrations] = await Promise.all([
listAgentDefinitions().catch((err) => {
logger.warn('Failed to fetch agent definitions from DB', { error: err });
return [];
}),
getTriggerConfigsByProject(input.projectId),
listAgentConfigs({ projectId: input.projectId }),
listProjectIntegrations(input.projectId),
]);

// Build set of explicitly enabled agent types for this project
const enabledAgentTypes = new Set(projectAgentConfigs.map((c) => c.agentType));

// Build a combined list of definitions (DB + YAML)
const yamlTypes = getKnownAgentTypes();
const definitions: Array<{ agentType: string; definition: AgentDefinition }> = [];
Expand Down Expand Up @@ -280,12 +285,12 @@ export const agentTriggerConfigsRouter = router({
};
}

// Build the agents array with merged trigger data
const agents = definitions.map((def) => {
const agentConfigs = configMap.get(def.agentType);
// Build merged trigger data for a definition
function buildAgentTriggersView(def: { agentType: string; definition: AgentDefinition }) {
const agentTriggerConfigs = configMap.get(def.agentType);
const triggers: ResolvedTrigger[] = (def.definition.triggers ?? []).map(
(trigger: SupportedTrigger) => {
const config = agentConfigs?.get(trigger.event);
const config = agentTriggerConfigs?.get(trigger.event);
return {
event: trigger.event,
label: trigger.label,
Expand All @@ -301,12 +306,18 @@ export const agentTriggerConfigsRouter = router({
};
},
);
return { agentType: def.agentType, triggers };
}

return {
agentType: def.agentType,
triggers,
};
});
// Split definitions into enabled (have agent_configs row) and available (no row)
// The debug agent is always shown as enabled (internal infrastructure)
const enabledAgents = definitions
.filter((def) => enabledAgentTypes.has(def.agentType) || def.agentType === 'debug')
.map(buildAgentTriggersView);

const availableAgents = definitions
.filter((def) => !enabledAgentTypes.has(def.agentType) && def.agentType !== 'debug')
.map((def) => def.agentType);

// Build integrations map with single pass
const integrationsMap = {
Expand All @@ -321,7 +332,9 @@ export const agentTriggerConfigsRouter = router({
}

return {
agents,
agents: enabledAgents, // backwards compat: same as enabledAgents
enabledAgents,
availableAgents,
integrations: integrationsMap,
};
}),
Expand Down
10 changes: 10 additions & 0 deletions src/api/routers/runs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TRPCError } from '@trpc/server';
import { z } from 'zod';
import { loadProjectConfigById } from '../../config/provider.js';
import { isAgentEnabledForProject } from '../../db/repositories/agentConfigsRepository.js';
import {
DEFAULT_STALE_RUN_THRESHOLD_MS,
cancelRunById,
Expand Down Expand Up @@ -274,6 +275,15 @@ export const runsRouter = router({
});
}

// Check agent is explicitly enabled for this project
const agentEnabled = await isAgentEnabledForProject(input.projectId, input.agentType);
if (!agentEnabled) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: `Agent '${input.agentType}' is not enabled for this project. Add an agent config in Project Settings > Agent Configs to enable it.`,
});
}

if (useQueue) {
const { submitDashboardJob } = await import('../../queue/client.js');
await submitDashboardJob({
Expand Down
8 changes: 7 additions & 1 deletion src/cli/dashboard/agents/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Flags } from '@oclif/core';
import { DashboardCommand } from '../_shared/base.js';

export default class AgentsList extends DashboardCommand {
static override description = 'List agent configurations for a project.';
static override description =
'List enabled agent configurations for a project. Only agents with an explicit config row are shown (opt-in required).';

static override flags = {
...DashboardCommand.baseFlags,
Expand All @@ -22,6 +23,11 @@ export default class AgentsList extends DashboardCommand {
return;
}

if (configs.length === 0) {
this.log('No agents enabled for this project. Use `cascade agents create` to enable one.');
return;
}

this.outputTable(configs as unknown as Record<string, unknown>[], [
{ key: 'id', header: 'ID' },
{ key: 'agentType', header: 'Agent Type' },
Expand Down
Loading