Skip to content

Merge dev to main#615

Merged
zbigniewsobiecki merged 73 commits intomainfrom
dev
Mar 6, 2026
Merged

Merge dev to main#615
zbigniewsobiecki merged 73 commits intomainfrom
dev

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

Merges dev branch to main, including:

  • fix(gadgets): add logging when progress comment update fails - Adds WARN level logging when cascade-tools pm post-comment fails to update an existing progress comment, matching the pattern used in PMProgressPoster

Test plan

  • All CI checks passed on dev branch
  • Unit tests pass
  • Integration tests pass
  • Docker builds validated

🤖 Generated with Claude Code

Cascade Bot and others added 30 commits February 25, 2026 17:58
When the Claude Code backend passes checklist items as JSON objects
(e.g. --item '{"name":"...","description":"..."}'), the raw JSON string
was being used as the JIRA subtask title instead of the name field.

Add a parseItem() helper that tries JSON.parse on each --item value and
returns a ChecklistItemInput object when the result has a string `name`
property. Plain strings and unrecognised JSON fall back to the raw string,
preserving full backward compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lists

When a card in IN PROGRESS, IN REVIEW, DONE or any other non-trigger list
received the READY TO PROCESS label, the handler fell through to a hardcoded
default of 'splitting', firing an unintended splitting run.

Replace the fallback with an early null return so the trigger is silently
skipped for any list that isn't splitting/planning/todo. Update the unit test
to assert null instead of the previous incorrect expectation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…son-parsing

fix(cli): parse JSON --item strings in add-checklist CLI command
Remove unused `eq`, `getDb`, and `organizations` imports from
repositories-edge-cases.test.ts. Extract duplicated `assertFound<T>()`
helper from 3 test files into tests/integration/helpers/assert.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…recognized-list

fix(triggers): skip READY TO PROCESS label for cards in non-eligible lists
Remove ~70% duplicated test surface from webhook-logging.test.ts and
multi-provider-credentials.test.ts that overlapped with existing
webhookLogsRepository.test.ts, credentialsRepository.test.ts, and
credentialResolution.test.ts. Kept only the unique value-add tests:
- webhook-logging: per-source GitHub/JIRA logs, bodyRaw, project resolution
- multi-provider-credentials: multi-project isolation, PM/SCM isolation,
  dual-persona tokens, 3-project cross-contamination checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ercise createPMProvider

- Replace if/else conditional assertions in github-personas and
  pm-provider-switching tests with direct expect() calls that fail
  if behavior changes (addresses review observation #3)
- Replace manual pm.type ?? 'trello' reimplementation with actual
  createPMProvider() call in "defaults to Trello" test (addresses
  review observation #2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…support

fix(jira): replace hand-rolled markdownToAdf with marklassian library for proper table support
…ts-699f2e4

feat(tests): add integration tests for triggers, webhooks, credentials, and personas
…r-from-messages

feat(progress): remove visual progress bars and iteration counters from messages
Implement comprehensive email integration for CASCADE agents:

Core features:
- IMAP client for searching, reading emails (imapflow)
- SMTP client for sending emails, replying to threads (nodemailer)
- AsyncLocalStorage-based credential scoping (withEmailIntegration)
- Database migration for email integration credentials

Gadgets (SearchEmails, ReadEmail, SendEmail, ReplyToEmail):
- Full schema validation (RFC 2822 subject length, date formats, UID validation)
- Error logging for all gadget operations
- HTML body fallback when text body unavailable
- Proper email threading support (In-Reply-To, References headers)

Agent execution integration:
- Email credential scoping in manual-runner, GitHub webhook handler, PM webhook handler
- Integration role definitions for email provider credentials
- canAccessEmail capability flag in agent definitions

Improvements from code review:
- IMAP connection timeouts (30s connect, 15s greeting, 60s socket)
- Awaited transport.close() to prevent connection leaks
- Credential resolution error logging
- Reduced cognitive complexity via helper function extraction

Tests:
- Email integration tests (credential resolution, withEmailIntegration)
- Email client credential scoping tests
- Gadget core function tests (27 tests total)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat(email): add email client tools for agents
…561)

## Email-Joke Agent
- New agent type for responding to emails with humorous replies
- Agent definition in YAML with prompts and gadget configuration
- Reads unread emails, sends joke replies, marks as read

## Email Integration
- Gmail OAuth support with browser-based authentication flow
- IMAP/SMTP support for any email provider
- Credential management per integration role (imap_host, smtp_port, etc.)
- Email wizard UI for setup with multi-step flow
- CLI commands: email integration-set, joke-config, oauth, verify

## Database Schema
- Migration 0018: Gmail OAuth token storage
- Migration 0019: Make repo column optional (for email-only projects)
- Updated ProjectConfigRaw and ProjectRow types

## UI Improvements
- Email wizard component with Gmail OAuth and IMAP configuration
- OAuth callback route for Gmail authentication
- Project forms now support optional repo field
- EmailJokeConfig component for sender email filtering

## Code Quality Fixes
- Added error handling to markEmailAsSeen() in email client
- Fixed duplicate comment in email/client.ts
- Added parseEmailJokeTriggers() helper with proper type safety
- Updated triggerConfig schema to allow null senderEmail
- Added guards for email-only projects in setup-webhooks tool
- Refactored CLI joke-config to reduce cognitive complexity

## Tests
- Added tests for EmailJokeTriggerConfigSchema
- Added tests for parseEmailJokeTriggers and resolveEmailJokeTriggerConfig
- Added tests for markEmailAsSeen gadget function

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…#562)

Add pre-flight validation of project integrations before agent execution.
This catches misconfiguration early with clear, actionable error messages
pointing users to the dashboard settings.

Key changes:
- Add validateIntegrations() to check PM, SCM, and email integrations
- Add persona-aware SCM validation (implementer vs reviewer tokens)
- Add hasEmailIntegration(), hasPmIntegration(), hasScmIntegration() checks
- Add persona field to agent definitions for SCM token requirements
- Wire validation into agent-execution and manual-runner entry points

Agent requirements by type:
- implementation, splitting, planning: PM + SCM (implementer)
- respond-to-review, respond-to-ci, respond-to-pr-comment: SCM (implementer)
- review: SCM (reviewer)
- debug: PM only
- email-joke: email only

Test improvements:
- Add comprehensive integration tests (45 tests)
- Add unit tests for validation logic (10 tests)
- Refactor implementer agent tests using it.each()
- Standardize seedGitHubIntegration helper options to skip* pattern
- Add Gmail credential tests and empty value edge case
- Add consistent array length checks before element access

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…crash (#563)

`cascade email verify` was crashing with a Zod `invalid_string` error
because the CLI passed the masked credential value (`****xyz`) returned
by `credentials.list` directly as the `email: z.string().email()` field
on the `verifyGmail` mutation.

Fix: replace `email: z.string().email()` with `gmailEmailCredentialId:
z.number()` in the `verifyGmail` input schema, resolve the email
server-side via `resolveCredentialValue()` (consistent with `verifyImap`
and every other credential in this router), and remove the now-redundant
client-side credential value lookup from the CLI.

Also adds tests for `verifyGmail` (auth, success, schema guard, token
refresh failure, IMAP failure) and `verifyImap` (auth, success, invalid
port, IMAP failure) — neither procedure had test coverage before.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…isting Gmail integration (#564)

When opening the email wizard in edit mode for an existing Gmail
integration, INIT_EDIT set oauthComplete=true but left gmailEmail and
verificationEmail as null. This caused two problems:

1. Clicking "Confirm" dispatched SET_VERIFICATION with email: null
   (state.gmailEmail), so verificationEmail stayed null and the
   "Update Integration" button remained disabled.

2. The verify step showed "Gmail connection verified for " (empty name).

Fix: in the INIT_EDIT useEffect, wait for orgCredentials to load then
look up the gmail_email credential (stored as "Gmail: user@example.com")
and pre-populate both gmailEmail and verificationEmail. This auto-confirms
the verify step for existing integrations so "Update Integration" is
immediately enabled, consistent with how IMAP edit mode works.

Uses a useRef guard to ensure INIT_EDIT only fires once even though
orgCredentials is now a dependency.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The EmailJokeConfig component (sender filter for the email-joke agent)
was exported from email-wizard.tsx but never mounted anywhere in the UI.
Add it below the EmailWizard in the email integration tab so users can
configure the sender email filter from the dashboard.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…567)

The EmailJokeConfig sender filter widget was placed in the Email
integration tab, but it is agent-specific trigger configuration and
belongs in the Agent Configs tab alongside PM and SCM triggers.

Changes:
- Add 'email-joke' to ALL_AGENT_TYPES so it appears as a section in
  Agent Configs
- Move AGENT_LABELS to trigger-agent-mapping.ts and type it as
  Record<KnownAgentType, string> — adding a new agent type without a
  label is now a compile error rather than a silent fallback
- Export EMAIL_TRIGGER_AGENTS (Set<KnownAgentType>) from
  trigger-agent-mapping.ts — replaces the magic string 'email-joke'
  that was hard-coded in the generic AgentSection component
- Add 'email-joke': [] to AGENT_TRIGGER_MAP for consistency with all
  other known agent types
- Render EmailJokeConfig inside the email-joke AgentSection under an
  "Email Triggers" heading when expanded
- Remove EmailJokeConfig from the integration-form email tab
- Remove "Add Custom Agent Config" button — only supported agent types
  defined in ALL_AGENT_TYPES are valid; per-section "Add Config"
  buttons pre-seed the type so it is always read-only in the dialog
- Suppress pre-existing noExcessiveCognitiveComplexity biome warning
  in EmailWizard's multi-provider initialization useEffect
- Add tests for AGENT_LABELS, EMAIL_TRIGGER_AGENTS, ALL_AGENT_TYPES
  completeness, and email-joke trigger definitions

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ds (#569)

The three CLI integration-credential commands (list, set, remove)
hardcoded `options: ['pm', 'scm']`, actively rejecting `--category email`
even though the tRPC API already accepts all three categories. Users
with email integrations had no CLI path to inspect or manage linked
credentials.

Changes:
- `projects/overrides.ts`: add 'email' to category options; default
  sweep now includes pm + scm + email
- `projects/override-set.ts`: add 'email' to category options and type cast
- `projects/override-rm.ts`: add 'email' to category options and type cast
- `email/integration-set.ts`: fix IMAP guidance note to include
  `--category email` so users land on the right command

Also fix pre-existing `noExcessiveCognitiveComplexity` lint warning in
`agent-execution.ts` by extracting the validation failure handling block
into a `notifyValidationFailure` helper (complexity 17 → 12).

Tests:
- New `tests/unit/cli/dashboard/projects/integration-credentials.test.ts`
  covers all three categories for list/set/remove, plus rejection of
  unknown values and JSON output
- New `tests/unit/cli/dashboard/email/integration-set.test.ts` covers
  gmail/imap upsert, custom config JSON, and the IMAP credential guidance
- Extended `tests/unit/api/routers/projects.test.ts` with email category
  cases for all three integrationCredentials router procedures

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
SMTP port 465 is blocked in the worker container, causing all
ReplyToEmail/SendEmail gadget calls on Gmail OAuth accounts to silently
hang for 30 s before the gadget timeout fires — confirmed in the
car-dealership email-joke run (three ReplyToEmail calls, each 30 000 ms).

Root cause: nodemailer's createTransport() has no connection-level
timeout, so the gadget outer timer was the only kill-switch, yielding a
cryptic "Gadget exceeded timeout" error with no actionable detail.

Fix: for OAuth accounts (authMethod === 'oauth'), route sendEmail() and
replyToEmail() through the Gmail REST API (messages.send) instead of
SMTP. IMAP operations (SearchEmails, ReadEmail, MarkEmailAsSeen) are
unchanged — port 993 is not blocked.

Implementation:
- src/email/gmail/send.ts (new): sendViaGmailApi() and replyViaGmailApi()
  use nodemailer streamTransport (in-process, no network) to build
  RFC 822 bytes, then POST to gmail.users.messages.send via
  @googleapis/gmail. OAuth2Client passes the existing access token
  directly — no re-auth needed (token refresh is already handled in
  oauth.ts before this point).
- src/email/client.ts: branch on creds.authMethod at the top of both
  send functions; password/SMTP accounts are completely unaffected.
- @googleapis/gmail added as a production dependency (~1 MB, pulls in
  google-auth-library transitively).

Tests: 8 new unit tests in tests/unit/email/gmail/send.test.ts covering
success paths, base64url encoding, reply-to-sender, reply-all (self
excluded), Re: prefix deduplication, and error propagation.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… fixes (#573)

* refactor(email): replace monolithic client with registry-pattern provider architecture

Decomposes EmailClient into clean, extensible layers:

- EmailProvider interface — runtime ops (search/read/send/reply/mark)
- EmailIntegration interface — credential resolution + AsyncLocalStorage scoping
- EmailIntegrationRegistry — singleton, populated at import time (mirrors pm/index.ts)
- ImapEmailProvider — password-auth via imapflow + nodemailer SMTP
- GmailEmailProvider — OAuth-auth via imapflow IMAP + Gmail REST API send
- ImapIntegration / GmailIntegration — per-provider DB credential resolution

Code-review fixes applied to gadget core files:
- Hoist const message before logger.error (was extracted twice in all 5 files)
- Add (no body) fallback in readEmail for attachment-only emails
- Guard empty accepted list in sendEmail + replyToEmail (misleading success message)
- Remove embedded \n from searchEmails header (caused double blank line in output)
- Log logger.warn in ImapIntegration when IMAP/SMTP port parses as NaN

New and expanded test coverage:
- context.test.ts: AsyncLocalStorage scoping + getEmailProvider throws outside scope
- imap/adapter.test.ts: readEmail (success + not-found) + replyToEmail (threading, cleanup)
- gmail/adapter.test.ts: searchEmails + readEmail tests; mocks migrated to vi.hoisted()
- integration.test.ts: registry delegation, credential fallback, warn path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(email): import from email/index.js to trigger provider registration

Production callers of withEmailIntegration / hasEmailIntegration were importing
directly from email/integration.js, which bypasses the side-effect registration in
email/index.ts that populates the emailRegistry with ImapIntegration and
GmailIntegration.

Without the registry populated, emailRegistry.getOrNull() always returns null,
causing withEmailIntegration to run without scoping a provider (email gadgets fail)
and hasEmailIntegration to always return false.

Fix: update the four production callers and the integration test to import from
email/index.js (same pattern as PM callers importing from pm/index.js).

Files changed:
- src/triggers/shared/integration-validation.ts
- src/triggers/shared/manual-runner.ts
- src/triggers/github/webhook-handler.ts
- src/pm/webhook-handler.ts
- tests/integration/integration-validation.test.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion (#574)

Before this change the email-joke agent always started regardless of whether
any matching emails existed — if the INBOX was empty the agent would search,
find nothing, and call Finish immediately, wasting a full agent run.

This PR adds a pre-check phase: before the agent starts, the manual runner
searches for unread emails (filtered by senderEmail when configured). If none
are found the run is skipped cleanly; if emails are found they are injected
into the agent's initial context via the new `prefetchedEmails` pipeline step,
so the agent can skip the redundant SearchEmails call and go straight to
reading and replying.

Changes:
- src/types/index.ts: add `preFoundEmails?: EmailSummary[]` to AgentInput
- src/triggers/shared/manual-runner.ts: add `prefetchEmailsForJokeAgent`
  pre-check inside withEmailIntegration; fix no-provider path to abort
  (return false) instead of silently proceeding with a broken prompt
- src/agents/definitions/contextSteps.ts: add `fetchEmailsFromInputStep`
  that injects pre-fetched emails as a synthetic SearchEmails tool result
- src/agents/definitions/schema.ts: register `prefetchedEmails` enum value
- src/agents/definitions/strategies.ts: wire `prefetchedEmails` registry key
- src/agents/definitions/email-joke.yaml: use `prefetchedEmails` pipeline step
- src/agents/prompts/task-templates/emailJoke.eta: update task prompt to
  reference the pre-fetched results and drop the now-redundant search step

Code-review fixes applied in the same commit:
- Consolidate EmailSearchCriteria import to email barrel (email/index.js)
- Remove redundant `as EmailSummary[]` and `as string` casts now that
  AgentInput has properly typed named fields
- Registry key follows the existing noun-phrase convention (`prefetchedEmails`
  not `fetchEmailsFromInput`)

Tests:
- tests/unit/triggers/manual-runner.test.ts: new `email-joke pre-check`
  describe block (5 cases: no provider, zero emails, emails found, non-email
  agent skips pre-check, senderEmail → criteria.from filter)
- tests/unit/agents/definitions/contextSteps.test.ts: new file with 6 focused
  tests for fetchEmailsFromInputStep (undefined/empty, injection shape,
  senderEmail filter, multi-email numbering, UTC date extraction)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…rganisation (#575)

Three robustness fixes for the email-joke scheduler:

1. **Double-start guard** — `startEmailScheduler()` now mirrors
   `startWorkerProcessor()`: a second call logs a warning and returns
   early instead of creating a second concurrent `setInterval` loop.

2. **Deduplicating jobId** — `submitDashboardJob` accepts an optional
   `jobId` parameter (existing callers unchanged).  The scheduler passes
   a deterministic `email-joke-{projectId}-{windowId}` ID so BullMQ
   silently rejects a duplicate job that arrives within the same interval
   window (prevents double-sends when a prior run is still in flight).

3. **Repository organisation** — `getAllProjectIdsWithEmailIntegration`
   moved into the existing `// Project Integrations` section of
   `settingsRepository.ts` (after `deleteProjectIntegration`, before
   `// Integration Credentials`), removing the stray
   `// Email Integration Queries` section it had been placed in.

Also fixes a trailing-newline lint error in `settingsRepository.ts`.

Tests: 13 new tests in `email-scheduler.test.ts`; 2 new cases in
`settingsRepository.test.ts`; 1 new case in `config.test.ts`.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds end-to-end SMS support via Twilio, following the same
registry/AsyncLocalStorage/credential-resolution patterns as the
email integration.

## Backend

- `src/sms/` — provider interface, registry, AsyncLocalStorage scoping,
  integration credential resolution, and Twilio adapter
- `src/gadgets/sms/SendSms` — LLMist gadget that agents call to send SMS
- `src/router/adapters/twilio.ts` — inbound webhook handler with
  Twilio signature validation (`POST /twilio/webhook/:projectId`)
- SMS provider scoped in all three agent execution paths:
  pm/webhook-handler, github/webhook-handler, manual-runner
- Pre-execution integration validation for agents that declare `sms`
  as a required integration

## Database

- Migration 0020: extends `chk_integration_category_provider` to
  include `sms/twilio`, and `chk_integration_credential_role` to
  include `account_sid`, `auth_token`, `phone_number`
- `getAllProjectIdsWithSmsIntegration()` in settingsRepository
  (mirrors email equivalent, ready for future SMS scheduler)

## API / CLI

- `integrationsDiscovery.verifyTwilio` tRPC mutation — validates
  Account SID + Auth Token against the Twilio API
- `projects.integrations.*` and `integrationCredentials.*` routers
  updated to accept `sms` category
- CLI `integration-credentials`, `integration-credential-set`,
  `integration-credential-rm` commands updated to include `sms`

## Dashboard UI

- `TwilioWizard` — 3-step accordion (credentials → verify → save)
  with inline credential creator, copy-to-clipboard webhook URL
  panel, and edit-mode pre-verification
- `IntegrationForm` — new SMS tab wired to TwilioWizard

## Tests

- Unit tests for SMS context, TwilioSmsProvider, TwilioIntegration,
  webhook handler, SendSms gadget, settingsRepository, and CLI commands
- 206 test files, all passing

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
aaight and others added 29 commits March 1, 2026 13:25
The `npm run build` script was missing a step to copy system prompt
templates, causing blank System Prompt tabs in the Agent Definition
Editor for non-Docker deployments.

Changes:
- Add `build:copy-system-prompts` script that recursively copies
  `src/agents/prompts/templates/` to `dist/agents/prompts/templates/`
- Use `cp -r` for robustness (matches Docker COPY behavior)
- Also fixes 11 lint warnings (noExplicitAny) in test files by:
  - Adding proper `mockAgentDefinition()` helper in modelResolution.test.ts
  - Using `Object.hasOwn()` instead of `as any` for property checks

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…592)

The dashboard API was not calling initPrompts() at startup, causing
the System Prompt tab in the Agent Definition Editor to show blank
content. The prompts tRPC router calls getRawTemplate() which requires
initPrompts() to be called first.

Changes:
- Add initPrompts() call in async startDashboard() function
- Add proper error handling with Sentry capture and flush before exit
- Match the startup pattern used in src/router/index.ts

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…d modules (#590)

Co-authored-by: Cascade Bot <bot@cascade.dev>
#593)

Replace the ad-hoc capability system with a unified capability-centric
architecture where integrations provide capabilities and capabilities
provide tools.

Key changes:

**New capability system (`src/agents/capabilities/`):**
- Add capability registry mapping capabilities to gadgets and SDK tools
- Add resolver functions for deriving integrations from capabilities
- Add `resolveEffectiveCapabilities()` for runtime optional capability filtering
- Add `createIntegrationChecker()` factory for project integration lookups
- Add `generateUnavailableCapabilitiesNote()` for system prompt injection

**Schema changes (`src/agents/definitions/schema.ts`):**
- Replace boolean capability flags with typed capability arrays
- `capabilities: { required: Capability[], optional: Capability[] }`
- Remove separate `integrations` and `tools` sections (now derived)
- Remove `gadgetBuilder` strategy (replaced by capability-based building)

**Agent definition updates (all YAML files):**
- Migrate all 10 agent definitions to new capability format
- debug.yaml: Remove fs:write (read-only analysis agent)
- respond-to-ci.yaml: Add pm:checklist to optional capabilities

**Runtime improvements:**
- Wire `IntegrationChecker` through profiles.ts and llmist backend
- Optional capabilities filtered by actual project integration availability
- Fix: Throw on missing gadget constructors (was silently skipped)
- Fix: Only fall back to full access for "not found" errors in legacy shim
- Fix: Pass correct agentType to preExecute hooks
- Add validation warning for missing tools in filterToolManifests

**Test coverage:**
- Add comprehensive resolver.test.ts for capability resolution functions
- Update all affected tests for new capability format
- Add mock for createIntegrationChecker in llmist tests

This refactoring:
- Eliminates redundancy between schema and runtime capability logic
- Makes integration requirements derivable from capability declarations
- Provides architectural safety (tools don't exist if cap not enabled)
- Enables graceful degradation for optional integrations

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
#594)

* refactor(agents): implement integration-driven capability architecture

Replace the ad-hoc capability system with a unified capability-centric
architecture where integrations provide capabilities and capabilities
provide tools.

Key changes:

**New capability system (`src/agents/capabilities/`):**
- Add capability registry mapping capabilities to gadgets and SDK tools
- Add resolver functions for deriving integrations from capabilities
- Add `resolveEffectiveCapabilities()` for runtime optional capability filtering
- Add `createIntegrationChecker()` factory for project integration lookups
- Add `generateUnavailableCapabilitiesNote()` for system prompt injection

**Schema changes (`src/agents/definitions/schema.ts`):**
- Replace boolean capability flags with typed capability arrays
- `capabilities: { required: Capability[], optional: Capability[] }`
- Remove separate `integrations` and `tools` sections (now derived)
- Remove `gadgetBuilder` strategy (replaced by capability-based building)

**Agent definition updates (all YAML files):**
- Migrate all 10 agent definitions to new capability format
- debug.yaml: Remove fs:write (read-only analysis agent)
- respond-to-ci.yaml: Add pm:checklist to optional capabilities

**Runtime improvements:**
- Wire `IntegrationChecker` through profiles.ts and llmist backend
- Optional capabilities filtered by actual project integration availability
- Fix: Throw on missing gadget constructors (was silently skipped)
- Fix: Only fall back to full access for "not found" errors in legacy shim
- Fix: Pass correct agentType to preExecute hooks
- Add validation warning for missing tools in filterToolManifests

**Test coverage:**
- Add comprehensive resolver.test.ts for capability resolution functions
- Update all affected tests for new capability format
- Add mock for createIntegrationChecker in llmist tests

This refactoring:
- Eliminates redundancy between schema and runtime capability logic
- Makes integration requirements derivable from capability declarations
- Provides architectural safety (tools don't exist if cap not enabled)
- Enables graceful degradation for optional integrations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(claude-code): add context offloading for large context injections

When context injections exceed the inline threshold (8k tokens), offload
them to files under .cascade/context/ and instruct Claude to read them
on-demand using its Read tool. This prevents prompt size issues with
large PR diffs or file contents.

Key changes:
- Add contextFiles.ts module with offloadLargeContext, cleanupContextFiles
- Add CONTEXT_OFFLOAD_CONFIG with inlineThreshold, contextDir, enabled
- Update buildTaskPrompt to be async and handle context offloading
- Auto-cleanup context files after agent execution via try/finally
- Slugify filenames with index suffix to prevent collisions
- Use POSIX paths for cross-platform consistency in instructions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 - Critical Backend Fixes:
- Add missing AgentInput fields (triggerCommentBody, triggerCommentPath) for PR comment triggers
- Create unified buildTaskPromptContext() in prompts/index.ts with consistent null handling
- Fix modelResolution.ts to use renderInlineTaskPrompt() instead of renderCustomPrompt()
- Add schema validation requiring prompts.taskPrompt (PromptsSchema no longer optional)
- Add early Eta syntax validation in profile builder to catch errors at build time

Phase 2 - Dashboard Fixes:
- Fix save logic to always send both prompts (prevents losing inactive section)
- Fix reset button: rename to "Reset All Prompts" and add confirmation dialog
- Show variable descriptions in ReferencePanel
- Add loading/error states in PromptsPanel
- Refactor PromptsPanel to reduce cognitive complexity (extract helper components)

Phase 3 - Consistency Cleanup:
- Standardize YAML taskPrompt format to pipe style (debug, planning, splitting, implementation)
- Remove unnecessary dbPartials size check in getSystemPrompt() and renderInlineTaskPrompt()
- Add placeholder text for system prompt textarea

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…YAML)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-review

fix(agents): address inline task prompts code review findings
…chitecture

Schema validation improvements:
- Add TriggerEventSchema with regex validation for {category}:{event-name} format
- Add KnownProviderSchema enum (trello, jira, github, imap, gmail, twilio)
- Add defaultValue type consistency refinement (must match parameter type)
- Add refinement preventing required: true with defaultValue
- Add IntegrationRequirementsSchema overlap refinement

Repository fixes:
- Fix bulkUpsertTriggerConfigs bug (was using first config's values for all conflicts)
- Add getTriggerConfigById function for ownership verification
- Use individual upserts in transaction for correct per-config values

Router refactoring:
- Replace direct DB access with repository functions in update/delete procedures
- Clean up imports (remove unused getDb, eq)

YAML definition fixes:
- review.yaml: consolidate duplicate scm:check-suite-success triggers into single
  trigger with authorMode select parameter (own/external/all)
- debug.yaml: remove undeclared pm:attachment-added trigger (agent is manually triggered)
- implementation.yaml, planning.yaml, splitting.yaml: remove contradictory
  required: true from parameters that have defaultValue

Tests:
- Add comprehensive tests for TriggerParameterSchema validation
- Add tests for SupportedTriggerSchema event format validation
- Add tests for KnownProviderSchema
- Add tests for IntegrationRequirementsSchema overlap
- Add tests for AgentDefinitionSchema with triggers
- Add full test suite for agentTriggerConfigs router (51 new tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add `triggers: []` to EMPTY_DEFINITION in agent-definition-editor.tsx
to satisfy the TypeScript type now that triggers is a required field
in AgentDefinition.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-architecture

fix(triggers): address code review findings for integration-driven architecture
The previous fix (7d45347) only updated package.json but missed
Dockerfile.worker. Task templates are now inline in YAML definitions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same fix as Dockerfile.worker - task templates are now inline in YAML.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… migration (#597)

Introduces a definition-driven trigger configuration system that replaces
the legacy per-integration trigger config approach with a unified model.

Unified Trigger CLI Commands:
- Add `cascade projects trigger-set` - replaces pm-trigger-set and review-trigger-set
- Add `cascade projects trigger-list` - list configured triggers for a project
- Add `cascade projects trigger-discover` - discover triggers from agent definitions
- Remove deprecated `pm-trigger-set` and `review-trigger-set` commands

Definition-Based Trigger System:
- Add `triggers` array to agent YAML definitions
- Add `SupportedTrigger` schema with event, label, parameters, providers
- Add `TriggerParameter` schema (string, email, boolean, select types)
- Use category-prefixed format: `{category}:{event-name}`

Trigger Config Resolution:
- Rewrite `config-resolver.ts` to merge definition defaults with DB overrides
- Add `isTriggerEnabled()`, `getTriggerParameters()`, `resolveTriggerConfigs()`
- Resolution order: DB config > definition default

Dashboard Integration:
- Add `getProjectTriggersView` tRPC endpoint for composite trigger data
- Add `DefinitionTriggerToggles` component for triggers with parameters
- Add `TriggerParameterInput` component for parameter editing
- Simplify `trigger-agent-mapping.ts` to re-export from shared types

Auto-Deploy Migration:
- Add `tools/migrate-triggers.ts` for migrating legacy triggers
- Add `tool:migrate-triggers` npm script
- Add trigger migration step to deploy.yml and deploy-dev.yml workflows
- Migration runs after drizzle schema migrations, is idempotent

Tests & Documentation:
- Add tests for shared trigger types (`triggerTypes.test.ts`)
- Expand tests for trigger utility functions (`triggerAgentMapping.test.ts`)
- Update CLAUDE.md with new CLI commands and trigger format docs

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The trigger migration script needs to be available in the builder image.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…egistry (#598)

- Add missing GitHub triggers (scm:pr-merged, scm:pr-ready-to-merge) to TRIGGER_REGISTRY
- Use proper literal types (ContextStepName[], KnownProvider[]) instead of string[]
- Remove duplicate TRIGGER_CATEGORY_LABELS from frontend (import from shared types)
- Remove duplicate type definitions from CLI triggers.ts (import from shared modules)
- Standardize trigger descriptions to consistent past tense verb phrases
- Add comprehensive unit tests for TRIGGER_REGISTRY
- Export ContextStepName and KnownProvider types from triggerTypes.ts for consumers

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
)

This refactoring moves context pipeline configuration from a global
default in `strategies.contextPipeline` to trigger-specific definitions.
This provides more explicit control over what context each trigger fetches.

## Changes

### Schema (`schema.ts`)
- Remove `contextPipeline` from `StrategiesSchema`
- Update comment to clarify context step names are only used by triggers
- Update JSDoc for `contextPipeline` in `SupportedTriggerSchema`

### Profile Resolution (`profiles.ts`)
- Simplify `resolveContextPipeline()` to only use trigger-defined pipelines
- Return empty array when no trigger matches (no default fallback)
- Add comprehensive JSDoc documenting edge cases

### Agent Definitions (YAML)
- Move `contextPipeline` from `strategies` to each trigger definition
- `strategies` now only contains `gadgetOptions` (or empty object)
- Fix respond-to-review.yaml taskPrompt (was copy-pasted from respond-to-pr-comment)
- Add missing `trailingMessage` to respond-to-pr-comment.yaml for consistency

### API Router
- Remove `contextStepNames` from schema endpoint (no longer needed by frontend)

### Dashboard UI
- Remove context pipeline editor from strategies section
- Strategies section now only shows gadgetOptions when present
- Clean up unused schema data

### Tests
- Update loader tests to use trigger-defined pipelines
- Update schema tests to remove strategies.contextPipeline assertions
- Add edge case tests for resolveContextPipeline:
  - Returns empty array when triggerType is undefined
  - Returns empty array when triggerType matches no trigger
  - Returns empty array for agents with no triggers (debug)
  - Returns empty array when triggerType is empty string

## Benefits

1. **Explicit context per trigger**: Each trigger clearly defines what
   context it needs, making the configuration more transparent
2. **No hidden defaults**: Removed the fallback behavior that could mask
   misconfiguration
3. **Simpler mental model**: One place to look for context configuration
4. **Better for review agents**: SCM-triggered agents don't accidentally
   fetch PM context they don't need

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
… in agent definition editor (#602)

* feat(dashboard): extract Capabilities and Triggers into separate tabs in agent definition editor

Moves CapabilitiesSection and TriggersSection out of the monolithic
Definition tab into their own dedicated tabs. Updates handleTabChange
to sync JSON ↔ all structured tabs (definition, capabilities, triggers).

Tab order: Definition | Capabilities | Triggers | Prompts (edit only) | Raw JSON

Closes: https://trello.com/c/69a56e72a79db92b36d7a9e3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(dashboard): extract Capabilities and Triggers into separate tabs in agent definition editor

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…onfig (#603)

Co-authored-by: Cascade Bot <bot@cascade.dev>
…coped infrastructure (#606)

Co-authored-by: Cascade Bot <bot@cascade.dev>
* feat(triggers): unify PM trigger naming to pm:status-changed

Consolidates the provider-specific `pm:card-moved` (Trello) and
`pm:issue-transitioned` (Jira) events into a single `pm:status-changed`
trigger that works across all PM integrations at the category level.

Key changes:
- TRIGGER_REGISTRY: replace two PM entries with unified pm:status-changed
- Agent YAMLs: merge two triggers into single pm:status-changed per agent
- New handlers: TrelloStatusChangedTrigger, JiraStatusChangedTrigger
- New Trello types: moved TrelloWebhookPayload to src/triggers/trello/types.ts
- Config schema: add statusChanged key (replaces cardMovedTo*/issueTransitioned)
- Backward compat: legacy config keys still parsed and resolved
- Migration tool: maps old event names to pm:status-changed
- All tests updated/rewritten for new naming

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update CLAUDE.md trigger references to pm:status-changed

Replace stale pm:card-moved and pm:issue-transitioned references with
the unified pm:status-changed event name throughout the Agent Trigger
Configuration section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(triggers): remove dead card-moved and issue-transitioned files

Delete old trigger handlers and their orphaned tests that were replaced
by the unified status-changed triggers but left on disk.

Removed files:
- src/triggers/trello/card-moved.ts
- src/triggers/jira/issue-transitioned.ts
- tests/unit/triggers/card-moved.test.ts
- tests/unit/triggers/jira-issue-transitioned.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(triggers): add per-agent granularity and legacy fallback for Trello status-changed

Address review feedback on PR #605:

Issue 1: Trello triggers now support per-agent granularity
- Changed TrelloTriggerConfigSchema.statusChanged from z.boolean() to StatusChangedSchema
- This allows statusChanged to accept both boolean and per-agent objects like Jira
- Config examples: statusChanged: false (all agents) or
  { splitting: true, planning: false, implementation: true }

Issue 2: Legacy Trello config keys now work via fallback
- Added resolveTrelloStatusChangedEnabled() function with fallback pattern like
  Jira's resolveStatusChangedEnabled()
- Falls back to cardMovedToSplitting/cardMovedToPlanning/cardMovedToTodo when
  statusChanged not set
- Trigger handlers now use resolveTrelloStatusChangedEnabled() instead of
  resolveTrelloTriggerEnabled()
- Legacy keys are no longer silently dead - they work when statusChanged is absent

Added comprehensive tests for new per-agent and fallback behavior.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…to ack infrastructure (#607)

* refactor(agents): remove preExecute hook - move postInitialPRComment to ack infrastructure

* fix(ci): remove remaining preExecute references from test and frontend editor

The loader test still had an assertion checking def.backend.preExecute,
and the agent definition editor still rendered a Pre-Execute Hook select
dropdown, both referencing the removed preExecute schema property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Cascade Bot <bot@cascade.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ion unconditional (#608)

Co-authored-by: Cascade Bot <bot@cascade.dev>
Previously, when `cascade-tools pm post-comment` attempted to update an
existing progress comment and failed, the error was silently swallowed
with no logging, making debugging production failures impossible.

This adds WARN level logging to the catch block in postComment.ts,
matching the pattern used in PMProgressPoster (pmPoster.ts). The log
includes workItemId, commentId, and error message for debugging.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit e0d5e6f into main Mar 6, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants