feat(platform): set up webhook triggers from AutoPilot#13298
Conversation
AutoPilot could not help users set up webhook-triggered agents: it had no tool to create a triggered preset and no way to retrieve the webhook ingress URL, so it invented incorrect steps (wrong host, nonexistent Builder panel). - add the `setup_agent_webhook_trigger` copilot tool: creates a triggered preset and returns the exact ingress URL for manual webhooks; requires explicit credential selection for provider webhooks (never auto-picks the account a webhook is registered under) - expose `webhook_url` (+ provider) on `list_agent_triggers` - extract `setup_triggered_preset()` shared by the setup-trigger route and the tool; expose it as a DatabaseManager RPC endpoint (reached via a new `triggers_db()` accessor) since the copilot worker has no connected Prisma - document the webhook-trigger flow in the agent-building guide Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/review |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughCentralizes webhook-triggered preset creation into a new helper, exposes it via the presets API and RPC, surfaces webhook metadata in copilot tools and listings, standardizes provider webhook error handling, adds a conversational copilot tool for trigger setup with credential flows, updates validator/docs/UI, and adds tests across backend and frontend. ChangesWebhook-triggered agent preset setup
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 PR Overlap DetectionThis check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early. 🔴 Merge Conflicts DetectedThe following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.
🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 4 conflict(s), 0 medium risk, 4 low risk (out of 8 PRs with file overlap) Auto-generated on push. Ignores: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #13298 +/- ##
==========================================
+ Coverage 72.95% 73.14% +0.19%
==========================================
Files 2373 2386 +13
Lines 177395 178432 +1037
Branches 17936 18041 +105
==========================================
+ Hits 129419 130521 +1102
+ Misses 44152 44056 -96
- Partials 3824 3855 +31
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
📋 Automated Review — PR #13298
PR #13298 — feat(backend/copilot): add setup_agent_webhook_trigger tool
Author: Pwuts | Files: 13
🎯 Verdict: REQUEST_CHANGES
PR Description Quality
✅ Has Why + What + How — Clear description of the problem (AutoPilot inventing wrong webhook URLs), the solution (new copilot tool + shared extraction), and implementation approach (RPC exposure, explicit credential selection).
What This PR Does
AutoPilot (the AI copilot) was fabricating incorrect webhook ingress URLs when users asked it to set up webhook triggers for their agents. This PR adds a setup_agent_webhook_trigger copilot tool that properly creates webhook presets and returns the real ingress URL. It extracts the webhook-preset logic from the existing REST route into a shared setup_triggered_preset() function used by both the route and the new tool, exposes it via RPC, and adds a deliberate two-step credential flow that never auto-picks accounts.
Specialist Findings
🛡️ Security ✅ — Auth scoping is correct throughout: all graph/library lookups include user_id, credentials require explicit user choice (never auto-picked), and the requires_auth = True guard is properly enforced.
- 🟡
trigger_configis an unvalidateddict[str, Any]passed tosetup_triggered_preset()(setup_agent_webhook_trigger.py:219), adding a second unvalidated entry point alongside the HTTP route. Downstream validation insetup_webhook_for_blocklikely catches issues, but schema validation at the tool boundary would be stronger.
🏗️ Architecture ✅ — Clean extract-and-delegate refactor. setup_triggered_preset() is a proper shared function, the RPC exposure via triggers_db() follows the exact db_accessors.py pattern, and the tool structure matches sibling tools.
- 🟠 After refactoring, the
setup_triggerroute (presets.py:158) no longer catches exceptions explicitly. The old code raisedHTTPException(410)for missing graphs andHTTPException(400)for invalid input. NowNotFoundErrorandInvalidInputErrorpropagate to global handlers — ifNotFoundErrormaps to 404 (common default) instead of 410, this is a silent API-breaking change. (Flagged by: architect, testing — 2)
⚡ Performance ✅ — No hot-path concerns. One minor optimization opportunity: get_or_create_library_agent() is called (setup_agent_webhook_trigger.py:194) before checking whether credentials are still needed, wasting a DB call (and potential write) when the response is TriggerCredentialsRequiredResponse.
- 🟡 Double graph fetch (
_resolve_graph+setup_triggered_preset) is documented as intentional for RPC compatibility. Adds ~50-200ms per call; acceptable for interactive copilot use.
🧪 Testing setup_triggered_preset() function — the shared core logic now used by both the REST route and the copilot tool — has zero direct unit tests. The copilot tool tests mock it away entirely, so regressions in the core webhook-setup logic would go undetected.
- 🔴 No unit tests for
setup_triggered_preset()intriggers.py— 3 error paths and 1 success path untested directly. - 🟠 No test verifying the route refactor preserves HTTP status codes (old: explicit
HTTPException(410/400), new: domain exceptions via global handlers). (Flagged by: architect, testing — 2) - 🟠
list_agent_triggers_test.pyhas no test forpreset.webhook = None, leaving thep.webhook.url if p.webhook else Noneguard (list_agent_triggers.py:169) unverified.
📖 Quality ✅ — Clean, well-documented code. Good docstrings, consistent patterns, clear helper method separation (_resolve_graph, _resolve_credentials, _build_success_response).
- 🔵 Non-deterministic
next(iter(field_info.provider))on afrozenset(setup_agent_webhook_trigger.py:280) — usesorted()for deterministic output. - 🔵 Minor duplication:
p.webhook.url if p.webhook else None/p.webhook.provider if p.webhook else Noneinlist_agent_triggers.py:169-170.
📦 Product ✅ — Directly solves the reported user problem. The explicit credential selection design is correct, the "never reconstruct the URL" messaging is strong, and the guide documentation is thorough.
- 🟡 No duplicate-prevention guard — calling the tool twice creates duplicate presets. Guide tells the LLM to check first, but this isn't enforced. Pre-existing behavior from the REST route.
📬 Discussion PLATFORM_TOOL_NAMES Literal is missing the new tool name. E2E failure is an infra flake (Docker 504).
- 🔴
setup_agent_webhook_triggermust be added toPLATFORM_TOOL_NAMESinpermissions.py—autopilot_permissions_testandpermissions_testfail across Python 3.11/3.12/3.13. (Flagged by: discussion, CI — 2) - 🟠 Merge conflict with PR #12206 in
copilot/tools/__init__.py(~148 lines overlap). Needs coordination.
🔎 QA ✅ — OpenAPI spec updated with both new ResponseType values, copilot recognizes the tool, REST endpoint works with refactored logic, 178 unit tests pass, negative cases (unauthenticated access, non-existent graph) return proper errors. Full E2E webhook creation couldn't be tested due to missing webhook-trigger agents and LLM keys in the sandbox.
🔴 Blockers
PLATFORM_TOOL_NAMESLiteral not updated (backend/copilot/permissions.py) — CI fails on all Python versions. Add"setup_agent_webhook_trigger"to thePLATFORM_TOOL_NAMESLiteral type. (Flagged by: discussion, CI — 2)- No unit tests for
setup_triggered_preset()(backend/api/features/library/triggers.py:24) — This is the shared core logic used by both the REST route and the new copilot tool. It has 3 error branches and 1 success path with zero direct tests. The copilot tool tests mock it away entirely. Add atriggers_test.pycovering: graph not found →NotFoundError, no webhook node →InvalidInputError, webhook rejection →InvalidInputError, happy path → returns preset with webhook. (Flagged by: testing)
🟠 Should Fix
- Route error mapping regression risk (
presets.py:158) — The old route explicitly raisedHTTPException(410)/HTTPException(400). After refactoring,NotFoundErrorandInvalidInputErrorpropagate to global handlers. Verify global handlers mapNotFoundError→ 410 (not 404), or add an explicittry/exceptin the route. Add a route-level test asserting the status codes. (Flagged by: architect, testing — 2) - Missing
webhook=Nonetest (list_agent_triggers_test.py:29) —_make_webhook_preset()usesMagicMock()which auto-createspreset.webhook. Add a test withpreset.webhook = Noneto verify the None-guard atlist_agent_triggers.py:169. (Flagged by: testing) - Wasted DB call when credentials required (
setup_agent_webhook_trigger.py:194) —get_or_create_library_agent()is called before checking if credentials are still needed (line 196). Move it after theif required:early-return. (Flagged by: performance) - Merge conflict with #12206 (
copilot/tools/__init__.py) — Coordinate merge order with the overlapping PR. (Flagged by: discussion)
🟡 Nice to Have
- Extract response models (
setup_agent_webhook_trigger.py:34-77) — File is 361 lines, slightly over the ~300 line guideline. MovingCredentialChoice,RequiredTriggerCredential, etc. tomodels.pywould bring it under budget. (architect, quality) - Duplicate-prevention guard (
setup_agent_webhook_trigger.py:213) — Check for existing active presets before creating new ones, rather than relying on LLM compliance with guide instructions. (security, product, architect) - Deterministic provider selection (
setup_agent_webhook_trigger.py:280) — Usesorted(field_info.provider)[0]instead ofnext(iter(...))on a frozenset. (quality)
🔵 Nits
- Minor duplication (
list_agent_triggers.py:169-170) — Extractwh = p.webhookto avoid repeating the None-check. - Variable shadows function name (
db_accessors.py:56) — Localtriggers_dbshadows the enclosing function. Matches existing pattern but is a mild smell.
QA Screenshots
| Screenshot | Description |
|---|---|
![]() |
Copilot page loads and recognizes the new tool ✅ |
Human Review Needed
YES — This is a multi-layer refactor (REST route → shared function → RPC → copilot tool) touching auth-adjacent code (credentials handling, webhook registration), with 13 files changed and a new RPC endpoint. The error mapping regression risk in the route refactor and the credential flow design warrant human review from a second maintainer.
Risk Assessment
Merge risk: MEDIUM | Rollback: EASY
CI is currently broken (fixable with a one-line Literal update), and the route refactor has an unverified error-mapping change. The merge conflict with #12206 adds coordination overhead. Rollback is easy — no migrations, no schema changes, feature is additive.
CI Status
❌ 4/6 checks failed — Backend lint ✅, Frontend lint ✅, Frontend typecheck ❌ (pre-existing), Backend tests ❌ (missing PLATFORM_TOOL_NAMES entry), Frontend tests ❌ (pre-existing), Frontend build ❌ (pre-existing). 2 failures are PR-related (backend tests due to missing tool name registration).
UI Testing — Variant Results
✅ local: All testable scenarios pass: OpenAPI updated, copilot recognizes tool, REST endpoint works with refactored shared logic, 178 unit tests pass, negative cases return proper errors.
✅ hosted: All 178 unit tests pass, API negative tests confirm proper error handling, OpenAPI updated correctly; copilot E2E blocked by missing LLM API keys in test env (environment limitation, not code issue).
The copilot AgentValidator required every agent to have at least one input block, so building a webhook-triggered agent forced an extra user-facing input node onto it — itself invalid, since a triggered agent is started by an external event and takes no user input. validate_io_blocks now satisfies the input requirement with EITHER at least one input block OR a webhook trigger block (uiType "Webhook" / "Webhook (manual)"). The output-block requirement is unchanged. The agent-building guide is updated to tell the model to use an input block or a trigger block, never both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nagers Webhook registration failures now raise WebhookRegistrationError (defined in backend.util.exceptions) instead of provider-specific HTTP/Runtime/ValueError types, so the reason survives reconstruction across the DatabaseManager RPC boundary (e.g. when the copilot worker sets up a trigger). The base _create_webhook wraps any non-WebhookRegistrationError from a provider's _register_webhook as a safety net. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in chat Adds the SetupTrigger tool UI: renders the credentials setup card (explicit account selection for the trigger, carried back to the tool), the trigger_config_required prompt, and a success card with the manual webhook ingress URL + copy. Dispatches it from MessagePartRenderer and exempts it (and connect_integration) from CollapsedToolGroup so the cards persist. AgentDetailsCard now shows an informational message (no action button) for webhook-trigger agents redirected from run_agent, so nothing goes stale after setup succeeds. Includes the regenerated openapi.json (ResponseType.trigger_config_required, AgentInfo.trigger_info). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…in webhook trigger setup setup_agent_webhook_trigger now: (1) returns trigger_config_required so the LLM collects the trigger block's config (e.g. GitHub repo/events) from the user instead of guessing; (2) surfaces the trigger node's own credential for explicit account selection in the setup card (never auto-picked), while other agent credentials mirror run_agent; (3) tells the user the account connects in that card so the LLM does not fire a duplicate connect_integration. list_agent_triggers exposes each webhook's ingress URL + provider via the RPC-safe library_db accessor. Expose setup_triggered_preset/list_presets/list_trigger_agents on DatabaseManager. models: ResponseType.trigger_config_required + AgentInfo.trigger_info. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@autogpt_platform/backend/backend/integrations/webhooks/_base.py`:
- Around line 214-223: The except block around the call to _register_webhook
currently wraps every exception into WebhookRegistrationError, hiding provider
domain errors (e.g., provider-raised
ValueError/NotFoundError/NotAuthorizedError). Change the handler so that after
checking for WebhookRegistrationError it re-raises provider domain ValueError
instances (or their subclasses) unchanged (i.e., if isinstance(e, ValueError):
raise) and only wrap other unexpected exceptions into WebhookRegistrationError;
keep the original exception as the __cause__ when wrapping. This targets the
try/except around _register_webhook and ensures callers that branch on provider
ValueError types still receive them.
In `@autogpt_platform/backend/backend/integrations/webhooks/github.py`:
- Around line 125-136: The new GitHub 401/403 branch raises NotAuthorizedError
but the webhook setup handler (setup_agent_webhook_trigger.py) only catches
NotFoundError and InvalidInputError, so NotAuthorizedError will escape and
bypass trigger_setup_failed; update the handler to also catch NotAuthorizedError
(or map it to the same failure flow) so that when GitHubRepoWebhook.create
raises NotAuthorizedError the code invokes trigger_setup_failed and returns the
same user-facing failure path as for NotFoundError/InvalidInputError.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b578b9f9-a14e-4f3e-9ecb-79dc0146a871
📒 Files selected for processing (10)
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/sdk/agent_generation_guide.mdautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/integrations/webhooks/telegram.pyautogpt_platform/backend/backend/util/exceptions.py
✅ Files skipped from review due to trivial changes (2)
- autogpt_platform/backend/backend/integrations/webhooks/telegram.py
- autogpt_platform/backend/backend/copilot/sdk/agent_generation_guide.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: integration_test
- GitHub Check: lint
- GitHub Check: check API types
- GitHub Check: end-to-end tests
- GitHub Check: Check PR Status
- GitHub Check: type-check (3.13)
- GitHub Check: Analyze (python)
- GitHub Check: test (3.13)
- GitHub Check: Analyze (typescript)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development
autogpt_platform/backend/**/*.py: Usepoetry run ...command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies likeopenpyxl
Use absolute imports withfrom backend.module import ...for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoidhasattr/getattr/isinstancefor type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no# type: ignore,# noqa,# pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use%sfor deferred interpolation indebuglog statements for efficiency; use f-strings elsewhere for readability (e.g.,logger.debug("Processing %s items", count)vslogger.info(f"Processing {count} items"))
Sanitize error paths by usingos.path.basename()in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Usetransaction=Truefor Redis pipelines to ensure atomicity on multi-step operations
Usemax(0, value)guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...
Files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
autogpt_platform/backend/backend/blocks/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/backend/backend/blocks/**/*.py: Inherit from 'Block' base class with input/output schemas when adding new blocks in backend
Implement 'run' method with proper error handling in backend blocks
Generate block UUID using 'uuid.uuid4()' when creating new blocks in backend
Write tests alongside block implementation when adding new blocks in backend
autogpt_platform/backend/backend/blocks/**/*.py: For blocks handling files, usestore_media_file()withreturn_format="for_local_processing"when processing with local tools (ffmpeg, MoviePy, PIL)
For blocks handling files, usestore_media_file()withreturn_format="for_external_api"when sending content to external APIs (Replicate, OpenAI)
For blocks returning files, usestore_media_file()withreturn_format="for_block_output"to enable auto-adaptation to execution context (workspace:// in CoPilot, data URI in graphs)
When creating new blocks, inherit fromBlockbase class, define input/output schemas usingBlockSchema, implement asyncrunmethod, and generate unique block ID usinguuid.uuid4()
Files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
autogpt_platform/backend/**/*_test.py
📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)
autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using*_test.pynaming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
UseAsyncMockfromunittest.mockfor async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with@pytest.mark.xfailbefore implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, usepoetry run pytest path/to/test.py --snapshot-update; always review snapshot changes withgit diffbefore committing
Files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.py
🧠 Learnings (29)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:31.684Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12536
File: autogpt_platform/frontend/src/app/api/openapi.json:5770-5790
Timestamp: 2026-03-24T21:25:15.983Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12536`
File: autogpt_platform/frontend/src/app/api/openapi.json
Learning: The OpenAPI spec file is auto-generated; per established convention, endpoints generally declare only 200/201, 401, and 422 responses. Do not suggest adding explicit 403/404 response entries for single operations unless planning a repo-wide spec update. Prefer clarifying such behaviors in endpoint descriptions/docstrings instead of altering response maps.
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/slant3d.pyautogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/util/exceptions.pyautogpt_platform/backend/backend/integrations/webhooks/github.pyautogpt_platform/backend/backend/blocks/exa/_webhook.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-02-05T04:11:00.596Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11796
File: autogpt_platform/backend/backend/blocks/video/concat.py:3-4
Timestamp: 2026-02-05T04:11:00.596Z
Learning: In autogpt_platform/backend/backend/blocks/**/*.py, when creating a new block, generate a UUID once with uuid.uuid4() and hard-code the resulting string as the block's id parameter. Do not call uuid.uuid4() at runtime; IDs must be constant across all imports and runs to ensure stability.
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-03-16T16:32:21.686Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/pods.py:62-74
Timestamp: 2026-03-16T16:32:21.686Z
Learning: In autogpt_platform/backend/backend/blocks/, the Block base class execute() already wraps run() in a try/except to convert uncaught exceptions into BlockExecutionError/BlockUnknownError. Do not add per-block try/except in individual block run() methods, as this is not the established pattern (e.g., Gmail, Slack, Todoist blocks omit it). Only use explicit try/except within blocks that need to distinguish between success and error yield paths inside a generator (e.g., attachment blocks). This guidance applies to all Python files under autogpt_platform/backend/backend/blocks/ and similar block implementations; avoid duplicating error handling in run() unless a block requires generator-based branching.
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-04-23T12:55:26.122Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12893
File: autogpt_platform/backend/backend/blocks/ayrshare/post_to_tiktok.py:24-24
Timestamp: 2026-04-23T12:55:26.122Z
Learning: Cost billing via the cost(*costs) decorator is applied at input-evaluation time (before a block’s run() executes). Therefore, mutating input_data inside run() will not change billing. When a block’s billing depends on a field plus URL/sniff-derived signals, treat the explicitly declared billing field (e.g., is_video) as the only billing source—set it correctly before run() (or in the code path that occurs before the decorator evaluates input_data). This should be checked for all blocks under autogpt_platform/backend/backend/blocks/ so billing signals are not mistakenly assumed to update during run().
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-03-16T16:30:11.452Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/threads.py:80-102
Timestamp: 2026-03-16T16:30:11.452Z
Learning: In autogpt_platform/backend/backend/blocks/ (and related blocks under autogpt_platform/backend/backend/blocks/), do not add try/except blocks around a block's run() method for standard error propagation. The block executor framework (backend/executor/manager.py) catches uncaught exceptions from run() and emits them on the 'error' output. Only add explicit try/except blocks when you need to control partial outputs in failure cases (e.g., certain outputs must not be yielded on error, as in attachment blocks). This is the standard pattern across the codebase; apply it broadly to blocks' run() implementations.
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-03-16T16:30:23.196Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/pods.py:62-74
Timestamp: 2026-03-16T16:30:23.196Z
Learning: In any Python file under autogpt_platform/backend/backend/blocks, do not add a try/except around run() solely for standard error handling. The block framework’s _execute() in _base.py already catches unhandled exceptions and re-raises as BlockExecutionError or BlockUnknownError. If you yield ("error", message), _execute() raises BlockExecutionError immediately, so the error port will not propagate downstream. Reserve explicit try/except for scenarios where you must control partial output (e.g., attachment blocks that must skip yielding content_base64 on failure).
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-03-16T16:30:11.452Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/threads.py:80-102
Timestamp: 2026-03-16T16:30:11.452Z
Learning: Do not wrap synchronous AgentMail SDK calls with asyncio.to_thread() in blocks under autogpt_platform/backend/backend/blocks (and across the codebase). The block executor runs node execution in dedicated threads via asyncio.run_coroutine_threadsafe (see manager.py around lines ~745-752 and ~1079). The existing pattern avoids using asyncio.to_thread for SDK calls inside async run() methods, so maintain that approach and do not add to_thread usage in these code paths.
Applied to files:
autogpt_platform/backend/backend/blocks/airtable/_webhook.pyautogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-05-17T06:58:54.665Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 13135
File: autogpt_platform/backend/backend/blocks/generic_webhook/_webhook.py:0-0
Timestamp: 2026-05-17T06:58:54.665Z
Learning: In `autogpt_platform/backend/backend/blocks/generic_webhook/_webhook.py`, when `GenericWebhooksManager._configured_secret()` finds multiple distinct `secret_token` values across attached `triggered_nodes` and `triggered_presets`, the intended behavior is to log a warning (not fail closed / raise HTTP 403). This is because a webhook is scoped per `(user_id, graph_id)`, so multiple `GenericWebhookTriggerBlock` instances in the same graph share one webhook endpoint and may legitimately have different tokens configured by the user. Failing closed would lock users out of their own webhook, which is worse UX than logging the conflict. The first token found wins.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/_base.py
📚 Learning: 2026-06-03T14:22:14.714Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12206
File: autogpt_platform/backend/backend/api/external/v2/runs.py:130-145
Timestamp: 2026-06-03T14:22:14.714Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `add_exception_handlers(app)` from `autogpt_platform/backend/backend/api/utils/exceptions.py` is registered on every API app instance, including `v2_app`. It automatically maps `NotFoundError` and `PrismaRecordNotFoundError` to HTTP 404. Therefore, v2 external API route handlers (e.g., in `autogpt_platform/backend/backend/api/external/v2/runs.py`) do NOT need explicit try/except blocks to catch `NotFoundError` from downstream helpers like `execution_utils.stop_graph_execution`; the top-level handler translates those to 404 without leaking a 500. Do NOT flag missing per-endpoint NotFoundError handling as a bug in v2 external API routes.
Applied to files:
autogpt_platform/backend/backend/integrations/webhooks/_base.pyautogpt_platform/backend/backend/integrations/webhooks/github.py
📚 Learning: 2025-11-03T17:41:48.242Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11185
File: autogpt_platform/backend/backend/blocks/exa/webhook_blocks.py:133-161
Timestamp: 2025-11-03T17:41:48.242Z
Learning: The ExaWebsetWebhookBlock in autogpt_platform/backend/backend/blocks/exa/webhook_blocks.py is currently disabled (disabled=True in __init__) and not active in production.
Applied to files:
autogpt_platform/backend/backend/blocks/exa/_webhook.py
📚 Learning: 2026-04-30T14:10:26.644Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12960
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/helpers.test.ts:3-3
Timestamp: 2026-04-30T14:10:26.644Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/helpers.test.ts`, the import path change from `./helpers` to `../helpers` was purely a file relocation into a `__tests__/` subdirectory — the helper module's contract did not change. `buildInputSchema({})` correctly returns `null` (empty properties check), `extractDefaults` correctly falls back to `examples[0]`, and `isFormValid(schema, formData)` has the correct argument order. Do not flag these assertions as mismatched after the path adjustment.
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/blocks/**/*.py : Write tests alongside block implementation when adding new blocks in backend
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/**/test/**/*.py : Use snapshot testing with '--snapshot-update' flag in backend tests when output changes; always review with 'git diff'
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-03-04T12:19:39.243Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12279
File: autogpt_platform/backend/backend/copilot/tools/base.py:184-188
Timestamp: 2026-03-04T12:19:39.243Z
Learning: In autogpt_platform/backend/backend/copilot/tools/, ensure that anonymous users always pass user_id=None to tool execution methods. The anon_ prefix (e.g., anon_123) is used only for PostHog/analytics distinct_id and must not be used as an actual user_id. Use a simple truthiness check on user_id (e.g., if user_id: ... else: ... or a dedicated is_authenticated flag) to distinguish anonymous from authenticated users, and review all tool execution call sites within this directory to prevent accidentally forwarding an anon_ user_id to tools.
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-03-31T14:22:26.566Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12622
File: autogpt_platform/backend/backend/copilot/tools/agent_search.py:223-236
Timestamp: 2026-03-31T14:22:26.566Z
Learning: In files under autogpt_platform/backend/backend/copilot/tools/, ensure agent graph enrichment uses the typed Pydantic model `backend.data.graph.Graph` for `AgentInfo.graph` (i.e., `Graph | None`), not `dict[str, Any]`. When enriching with graph data (e.g., `_enrich_agents_with_graph`), prefer calling `graph_db().get_graph(graph_id, version=None, user_id=user_id)` directly to retrieve the typed `Graph` object rather than routing through JSON conversions like `get_agent_as_json()` / `graph_to_json()`.
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.pyautogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/blocks/**/*.py : Inherit from 'Block' base class with input/output schemas when adding new blocks in backend
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
📚 Learning: 2026-04-08T17:26:12.102Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/CLAUDE.md:0-0
Timestamp: 2026-04-08T17:26:12.102Z
Learning: Agents must validate inputs before processing
Applied to files:
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py
🔇 Additional comments (2)
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator.py (1)
8-8: LGTM!Also applies to: 24-26, 663-694, 696-756
autogpt_platform/backend/backend/copilot/tools/agent_generator/validator_test.py (1)
586-649: LGTM!
…gent find_library_agent returns each agent's trigger_setup_info (provider + config_schema) so AutoPilot configures webhook triggers without fetching/parsing the full graph, and its response steers to setup_agent_webhook_trigger. list_library_agents gains an include_nodes flag (default False keeps the main library page fast); the copilot search opts in, since has_external_trigger / trigger_setup_info are False/None without graph nodes loaded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
edit_agent rejects edits that change a webhook trigger node's config fields (e.g. repo/events) — those belong on a per-trigger preset via setup_agent_webhook_trigger, not the agent's global graph — with an actionable error. run_agent detects webhook-trigger agents (which can't be run/scheduled directly) and returns the agent details pointing to setup_agent_webhook_trigger instead of attempting execution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/cli/chat.py (1)
1-305:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMove local imports to module top level.
Lines 13, 42, 44-45, 95-98, 138, 158, 184, 212, 242, 265, 294 import standard library and application modules locally within functions and helpers. Per coding guidelines, use top-level imports only—local imports are reserved for heavy optional dependencies like
openpyxl.Move these imports to the top of the file:
asyncio(lines 42, 95)json(lines 212, 242, 294)re(line 265)logging(line 13 — already correctly placed at function scope for one-time setup; can stay)click(line 138 — used for exception; should be top-level)prisma.models(lines 44, 97, 158, 184)prisma.types(line 98)backend.data.db(lines 46, 100)♻️ Refactor to use top-level imports
+import asyncio +import json +import re + import click +import prisma.models +import prisma.types + +from backend.data.db import connect, disconnect `@click.group`() def chat(): """ Group for CoPilot chat inspection commands """ # Suppress INFO/DEBUG logs (Prisma connection chatter, service-client # setup) so they don't interleave with the rendered transcript. Use # logging.disable so it can't be undone by configure_logging() running # later during connect(). import logging logging.disable(logging.INFO)Then remove the local
importlines from within functions.As per coding guidelines: "Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like
openpyxl"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/cli/chat.py` around lines 1 - 305, The file contains many local imports; move these to module top-level: asyncio, json, re, click, prisma.models, prisma.types, and from backend.data.db import connect, disconnect (leave logging.disable call in chat() as-is). Remove the corresponding in-function local import statements found in chat_view (asyncio, prisma.models, backend.data.db), chat_list (asyncio, prisma.models, prisma.types, backend.data.db), and helper functions (_render_session, _render_message, _assistant_lines, _tool_lines, _strip_context_blocks, _render_session_json) so their references use the top-level imports; keep behavior and names unchanged (functions: chat_view, chat_list, _render_session, _render_message, _assistant_lines, _tool_lines, _user_lines, _strip_context_blocks, _render_session_json).
🧹 Nitpick comments (5)
autogpt_platform/backend/backend/cli/main.py (2)
69-86: 💤 Low valueConsider removing PID file after process termination.
Lines 79-83 remove the PID file before terminating the process. If termination fails or raises an exception, the PID file is already gone, making it harder to retry. Consider moving
os.remove(get_pid_path())to after the termination logic or into afinallyblock.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/cli/main.py` around lines 69 - 86, The PID file is removed before attempting to terminate the process in stop(), which can leave no record if termination fails; update stop() to only remove the PID file after the termination logic succeeds or always remove it in a finally block: locate stop(), get_pid(), get_pid_path(), psutil.Process(...) and the loop over process.children(...). Move the os.remove(get_pid_path()) call so it runs after calling child.terminate() and process.terminate(), or wrap the termination code in try/finally and call os.remove(get_pid_path()) from the finally to ensure the PID file is cleaned up reliably only after attempting termination.
40-45: ⚡ Quick winDocument why
os._exit(0)is used instead ofsys.exit(0).Line 45 uses
os._exit(0), which bypasses Python cleanup (finally blocks, atexit handlers, etc.). If this is intentional to avoid interfering with the backgrounded child process, add a comment explaining the reasoning.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/cli/main.py` around lines 40 - 45, The code calls os._exit(0) after starting the background server (MainApp().start) and writing the PID (write_pid); add an inline comment just above the os._exit(0) call that documents why os._exit is intentionally used instead of sys.exit (it immediately terminates the parent process without running Python cleanup handlers, finally blocks, or atexit hooks) and state that this avoids running cleanup code that could interfere with or hang the background child process and double-shutdowns; keep write_pid(pid) before the exit and do not change the exit status—just add the explanatory comment referencing MainApp.start and write_pid to make the intent explicit.autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/AgentDetailsCard.test.tsx (1)
15-18: 💤 Low valueConsider removing redundant
cleanup()call.Per the learnings,
testing-library/reactcleanup is handled globally viasrc/tests/integrations/vitest.setup.tsx. Thecleanup()call on line 16 may be redundant. TheonSend.mockReset()on line 17 should remain to reset the mock between tests.Based on learnings: "In the AutoGPT frontend,
testing-library/reactcleanup is already handled globally after each test viasrc/tests/integrations/vitest.setup.tsx. Therefore, for integration test files under__tests__/, do NOT add redundantafterEach(() => cleanup())."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/AgentDetailsCard.test.tsx around lines 15 - 18, Remove the redundant cleanup() call from the afterEach block and keep only the mock reset call: replace the current afterEach(() => { cleanup(); onSend.mockReset(); }) with an afterEach that calls onSend.mockReset() so that testing-library/react's global cleanup (from vitest.setup.tsx) is not duplicated; locate the afterEach in the AgentDetailsCard.test.tsx file and update the block that references cleanup() and onSend.mockReset().autogpt_platform/frontend/src/app/(platform)/copilot/tools/SetupTrigger/__tests__/SetupTrigger.test.tsx (1)
23-29: 💤 Low valueConsider removing redundant
cleanup()call.The
cleanup()call on line 24 may be redundant since cleanup is handled globally. TheuseConnectedProvidersStore.setState()call (lines 25-28) should remain as it resets state specific to this test suite.Based on learnings: "In the AutoGPT frontend,
testing-library/reactcleanup is already handled globally after each test viasrc/tests/integrations/vitest.setup.tsx."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/SetupTrigger/__tests__/SetupTrigger.test.tsx around lines 23 - 29, Remove the redundant cleanup() call from the afterEach block and keep the state reset for useConnectedProvidersStore; specifically, edit the afterEach to only call useConnectedProvidersStore.setState({ connected: new Set(), autoDismissedKeys: new Set() }) because testing-library/react cleanup is already handled globally and the explicit cleanup() is unnecessary.autogpt_platform/frontend/src/app/(platform)/copilot/tools/SetupTrigger/SetupTrigger.tsx (1)
106-111: ⚡ Quick winAdd cleanup for setTimeout to prevent memory leak.
If the component unmounts during the 2-second delay, the timeout will still fire and attempt to call
setCopied, which can cause a React warning or memory leak. Store the timeout ID and clear it in a cleanup function.🧹 Proposed fix to add cleanup
function handleCopy() { if (!output.webhook_url) return; navigator.clipboard.writeText(output.webhook_url); setCopied(true); - setTimeout(() => setCopied(false), 2000); + const timeoutId = setTimeout(() => setCopied(false), 2000); + return () => clearTimeout(timeoutId); }Or wrap in a useEffect if you want to ensure cleanup runs:
+ import { useEffect } from "react"; + function handleCopy() { if (!output.webhook_url) return; navigator.clipboard.writeText(output.webhook_url); setCopied(true); } + useEffect(() => { + if (!copied) return; + const timeoutId = setTimeout(() => setCopied(false), 2000); + return () => clearTimeout(timeoutId); + }, [copied]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/SetupTrigger/SetupTrigger.tsx around lines 106 - 111, handleCopy currently starts a 2s setTimeout to reset setCopied without clearing it, which can fire after unmount and cause warnings; change handleCopy to save the timeout ID (e.g., via a ref) when calling setTimeout and then clear that timeout in a cleanup (useEffect return) to avoid leaks. Ensure you still check output.webhook_url and call navigator.clipboard.writeText, but store the timer id and call clearTimeout(timerId) in the component cleanup so setCopied is not invoked after unmount.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@autogpt_platform/backend/backend/cli/main.py`:
- Around line 55-67: get_pid() currently creates parent directories
(os.makedirs) even though it's a read-only accessor; remove the directory
creation call from get_pid() so it only reads the PID file (use get_pid_path()
and open/read + int conversion as-is), and ensure the complementary write_pid()
(or wherever the PID is written) is responsible for creating the parent
directory (call os.makedirs(file_path.parent, exist_ok=True) inside write_pid()
before opening/writing the file).
- Around line 69-86: The stop() command calls psutil.Process(int(pid)) without
handling a missing PID; wrap the psutil.Process(...) lookup and subsequent
termination calls in a try/except that catches psutil.NoSuchProcess (and
optionally psutil.AccessDenied) to handle already-exited PIDs gracefully, remove
the PID file only when appropriate using get_pid_path(), and log/print a clear
message when the process does not exist; ensure you still attempt to remove the
PID file if it’s stale and that process.children(recursive=True) and
process.terminate() are only called when the Process object was successfully
obtained.
In `@autogpt_platform/backend/backend/copilot/tools/agent_search_test.py`:
- Around line 181-184: The local import of GraphTriggerInfo inside the helper
method _github_trigger_info should be moved to the module top-level: remove the
inner "from backend.data.graph import GraphTriggerInfo" from
_github_trigger_info and add a top-level import "from backend.data.graph import
GraphTriggerInfo" at the top of the test module so the helper uses the
module-scoped symbol.
In `@autogpt_platform/backend/backend/copilot/tools/models.py`:
- Around line 170-179: Replace the untyped dict field trigger_info with a proper
Pydantic model: define a TriggerInfo (or similarly named) subclass of
pydantic.BaseModel with the documented attributes (e.g., provider: str,
config_schema: dict[str, Any] or a more specific model if appropriate,
credentials_input_name: str | None) and any field descriptions/validators
needed, then change the field declaration from trigger_info: dict[str, Any] |
None = Field(...) to trigger_info: TriggerInfo | None = Field(...). Update any
imports and usages that construct or access trigger_info to use the new model.
In
`@autogpt_platform/backend/backend/copilot/tools/setup_agent_webhook_trigger_test.py`:
- Around line 55-60: The helper _github_meta currently uses inline "# type:
ignore" comments for the provider and type fields when constructing a
CredentialsMetaInput; remove those suppressors and instead construct the object
via a proper typed conversion (e.g., build a typed dict/Mapping with
provider="github" and type="api_key" and pass it through CredentialsMetaInput's
validated constructor or parsing method) so the returned CredentialsMetaInput is
created with correct types and no linter ignores; update the _github_meta
signature and body accordingly to return a fully typed CredentialsMetaInput
without using "# type: ignore".
---
Outside diff comments:
In `@autogpt_platform/backend/backend/cli/chat.py`:
- Around line 1-305: The file contains many local imports; move these to module
top-level: asyncio, json, re, click, prisma.models, prisma.types, and from
backend.data.db import connect, disconnect (leave logging.disable call in chat()
as-is). Remove the corresponding in-function local import statements found in
chat_view (asyncio, prisma.models, backend.data.db), chat_list (asyncio,
prisma.models, prisma.types, backend.data.db), and helper functions
(_render_session, _render_message, _assistant_lines, _tool_lines,
_strip_context_blocks, _render_session_json) so their references use the
top-level imports; keep behavior and names unchanged (functions: chat_view,
chat_list, _render_session, _render_message, _assistant_lines, _tool_lines,
_user_lines, _strip_context_blocks, _render_session_json).
---
Nitpick comments:
In `@autogpt_platform/backend/backend/cli/main.py`:
- Around line 69-86: The PID file is removed before attempting to terminate the
process in stop(), which can leave no record if termination fails; update stop()
to only remove the PID file after the termination logic succeeds or always
remove it in a finally block: locate stop(), get_pid(), get_pid_path(),
psutil.Process(...) and the loop over process.children(...). Move the
os.remove(get_pid_path()) call so it runs after calling child.terminate() and
process.terminate(), or wrap the termination code in try/finally and call
os.remove(get_pid_path()) from the finally to ensure the PID file is cleaned up
reliably only after attempting termination.
- Around line 40-45: The code calls os._exit(0) after starting the background
server (MainApp().start) and writing the PID (write_pid); add an inline comment
just above the os._exit(0) call that documents why os._exit is intentionally
used instead of sys.exit (it immediately terminates the parent process without
running Python cleanup handlers, finally blocks, or atexit hooks) and state that
this avoids running cleanup code that could interfere with or hang the
background child process and double-shutdowns; keep write_pid(pid) before the
exit and do not change the exit status—just add the explanatory comment
referencing MainApp.start and write_pid to make the intent explicit.
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/AgentDetailsCard.test.tsx:
- Around line 15-18: Remove the redundant cleanup() call from the afterEach
block and keep only the mock reset call: replace the current afterEach(() => {
cleanup(); onSend.mockReset(); }) with an afterEach that calls
onSend.mockReset() so that testing-library/react's global cleanup (from
vitest.setup.tsx) is not duplicated; locate the afterEach in the
AgentDetailsCard.test.tsx file and update the block that references cleanup()
and onSend.mockReset().
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/SetupTrigger/__tests__/SetupTrigger.test.tsx:
- Around line 23-29: Remove the redundant cleanup() call from the afterEach
block and keep the state reset for useConnectedProvidersStore; specifically,
edit the afterEach to only call useConnectedProvidersStore.setState({ connected:
new Set(), autoDismissedKeys: new Set() }) because testing-library/react cleanup
is already handled globally and the explicit cleanup() is unnecessary.
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/tools/SetupTrigger/SetupTrigger.tsx:
- Around line 106-111: handleCopy currently starts a 2s setTimeout to reset
setCopied without clearing it, which can fire after unmount and cause warnings;
change handleCopy to save the timeout ID (e.g., via a ref) when calling
setTimeout and then clear that timeout in a cleanup (useEffect return) to avoid
leaks. Ensure you still check output.webhook_url and call
navigator.clipboard.writeText, but store the timer id and call
clearTimeout(timerId) in the component cleanup so setCopied is not invoked after
unmount.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b968c59c-f38c-4dd2-b7a4-a3a977381523
📒 Files selected for processing (32)
autogpt_platform/backend/backend/api/features/library/db.pyautogpt_platform/backend/backend/api/features/library/routes/presets.pyautogpt_platform/backend/backend/cli/__init__.pyautogpt_platform/backend/backend/cli/chat.pyautogpt_platform/backend/backend/cli/main.pyautogpt_platform/backend/backend/cli/render_test.pyautogpt_platform/backend/backend/cli/test.pyautogpt_platform/backend/backend/copilot/sdk/agent_generation_guide.mdautogpt_platform/backend/backend/copilot/tools/agent_search.pyautogpt_platform/backend/backend/copilot/tools/agent_search_test.pyautogpt_platform/backend/backend/copilot/tools/edit_agent.pyautogpt_platform/backend/backend/copilot/tools/edit_agent_test.pyautogpt_platform/backend/backend/copilot/tools/find_library_agent_test.pyautogpt_platform/backend/backend/copilot/tools/list_agent_triggers.pyautogpt_platform/backend/backend/copilot/tools/list_agent_triggers_test.pyautogpt_platform/backend/backend/copilot/tools/models.pyautogpt_platform/backend/backend/copilot/tools/run_agent.pyautogpt_platform/backend/backend/copilot/tools/run_agent_test.pyautogpt_platform/backend/backend/copilot/tools/setup_agent_webhook_trigger.pyautogpt_platform/backend/backend/copilot/tools/setup_agent_webhook_trigger_test.pyautogpt_platform/backend/backend/data/db_manager.pyautogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/MessagePartRenderer.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/components/SetupRequirementsCard/SetupRequirementsCard.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/SetupRequirementsCard/helpers.tsautogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/AgentDetailsCard.tsxautogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/components/AgentDetailsCard/__tests__/AgentDetailsCard.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/tools/RunAgent/helpers.tsxautogpt_platform/frontend/src/app/(platform)/copilot/tools/SetupTrigger/SetupTrigger.tsxautogpt_platform/frontend/src/app/(platform)/copilot/tools/SetupTrigger/__tests__/SetupTrigger.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/tools/SetupTrigger/helpers.tsxautogpt_platform/frontend/src/app/api/openapi.json
✅ Files skipped from review due to trivial changes (2)
- autogpt_platform/backend/backend/copilot/tools/find_library_agent_test.py
- autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/helpers.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- autogpt_platform/backend/backend/copilot/tools/list_agent_triggers.py
- autogpt_platform/backend/backend/api/features/library/routes/presets.py
- autogpt_platform/backend/backend/data/db_manager.py
- autogpt_platform/backend/backend/copilot/tools/list_agent_triggers_test.py
- autogpt_platform/backend/backend/copilot/sdk/agent_generation_guide.md
|
/review |
…ions The tool was added to TOOL_REGISTRY but not to the ToolName Literal / PLATFORM_TOOL_NAMES in permissions.py, so the registry-sync assertions in permissions_test.py and autopilot_permissions_test.py failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-run test mocks The webhook-redirect guard in run_agent reads graph.has_external_trigger; these mocks left it an unset (truthy) MagicMock, so the redirect fired and short-circuited execution (KeyError: 'dry_run', add_graph_execution not called). Non-webhook agents have has_external_trigger=False. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
📋 Automated Review — PR #13298
PR #13298 — feat(platform): set up webhook triggers from AutoPilot
Author: Pwuts | Files: 45
🎯 Verdict: REQUEST_CHANGES
PR Description Quality
✅ Has Why + What + How — PR describes the full copilot webhook trigger setup flow, lists all new tools and guardrails, and explains the credential selection UX.
What This PR Does
Adds an end-to-end webhook trigger setup flow to the AutoPilot copilot: a new setup_agent_webhook_trigger tool discovers the agent's trigger block, collects config conversationally, lets the user pick a registration account via a credential card, creates the preset, and returns the ingress URL. Supporting changes include shared business logic extraction (setup_triggered_preset), guardrails preventing run_agent/edit_agent from silently mishandling webhook agents, trigger discovery in find_library_agent, a CLI for inspecting copilot chat sessions, and new Airtable/Exa webhook block support.
Specialist Findings
🛡️ Security ✅ — Auth checks are consistent across all new tools (user_id null checks, access control via get_graph). No SQL injection, no hardcoded secrets. Credential validation in _resolve_selection is sound.
- 🟡
trigger_configvalues are not validated against the block's config schema at thetriggers.py:58layer — only presence is checked. Defense-in-depth improvement. - 🟡 Error messages in
triggers.py:74,airtable/_webhook.py:130,exa/_webhook.py:109may leak raw third-party API responses to users.
🏗️ Architecture ✅ — Clean extraction of shared logic into setup_triggered_preset, proper RPC accessor pattern via triggers_db(), effective guardrails in edit_agent against trigger-config mutation, and correct webhook error boundary with WebhookRegistrationError.
- 🟠 Error handling inconsistency:
_resolve_graphusesif not library_agentguard (setup_agent_webhook_trigger.py:257) whilelist_agent_triggers.py:117catchesNotFoundError— one path may be dead code. - 🟠 Dual error path (
InvalidInputErrorfrom None-return vsWebhookRegistrationErrorfrom webhook manager) is correct but undocumented — future maintainers will be confused.
⚡ Performance ✅ — No critical perf issues. Three efficiency concerns, all bounded:
- 🟡 Double graph fetch:
_resolve_graphloads the full graph, thensetup_triggered_presetloads it again (setup_agent_webhook_trigger.py:210→triggers.py:50). One extra DB round-trip per trigger setup. - 🟡
include_nodes=Truein copilot search (agent_search.py:153) loads full node trees for up to 50 agents. Bounded by pagination but worth monitoring. - 🔵 N+1 query in CLI
chat_list(chat.py:118) — dev-only tool, default limit 20.
🧪 Testing setup_agent_webhook_trigger has strong multi-path coverage. edit_agent trigger guard, validator, and CLI render tests are solid. However:
- 🔴
setup_triggered_preset()(triggers.py:24) — the shared core logic — has zero unit tests. The route delegates to it; the copilot tool mocks it out. Three error branches and a success path are completely untested in isolation. (Flagged by: testing, discussion — 2) - 🟠
WebhookRegistrationError— added in this PR for RPC-safe error propagation — is never exercised in any test (setup_agent_webhook_trigger_test.py:219). (Flagged by: testing — 1) - 🟠
list_agent_triggers_test.pyhas only 2 tests (auth + one happy path). The"agent"trigger kind, empty results,NotFoundErrorpath (line 121), andasyncio.gatherconcurrency are all untested. (Flagged by: testing — 1) - 🟠
library_agent_idresolution path (setup_agent_webhook_trigger.py:253-264) andbuilder_graph_idfallback (lines 242-251) have zero test coverage. (Flagged by: testing — 1) - 🟡 Frontend
AgentDetailsCard.test.tsx— no regression test for non-webhook agents to ensure buttons still render normally.
📖 Quality
- 🔴 Exception handling gap in
presets.py:168: After refactoring tosetup_triggered_preset, the route only catchesWebhookRegistrationError. But the shared function raisesNotFoundErrorandInvalidInputError— these now bubble as unhandled 500 errors instead of proper 4xx responses. The previous route returned HTTP 410 for missing graphs; this is a silent API contract break. (Flagged by: quality, discussion, architect — 3) - 🟡 Dead imports in
presets.py:14-15:get_webhook_managerandsetup_webhook_for_blockare no longer used after the refactor.
📦 Product ✅ — Feature delivers on all claims. Explicit credential selection, config collection without guessing, and stale-CTA prevention are well-designed.
- 🟡
AgentDetailsCard.tsx:60— webhook-trigger agent card tells the user it can't be run but offers no visible forward instruction (the hint goes only to the LLM). - 🟡
navigator.clipboard.writeTextinSetupTrigger.tsx:109has no try/catch — may silently fail in non-HTTPS contexts.
📬 Discussion
- 🟠
NotAuthorizedErrorfromgithub.py:136(401/403 GitHub responses) is not caught bysetup_agent_webhook_trigger.py, which only handlesInvalidInputErrorandWebhookRegistrationError. A GitHub account without repo-admin rights produces an unhandled exception. (Flagged by: discussion — 1) - 🟠
_base.py:223broadexcept ValueErrorwraps all provider errors intoWebhookRegistrationError, hiding domain-specificNotFoundError/NotAuthorizedError. (Flagged by: discussion — 1)
🔎 QA ✅ — All endpoint validations pass: auth enforcement returns 401, invalid graph IDs return clear errors, copilot correctly reasons about webhook triggers and guides users through setup. Frontend loads without errors. OpenAPI schema includes new trigger_config_required, trigger_setup, trigger_info types.
🔴 Blockers
-
Missing exception handlers in
presets.pyroute after refactor (presets.py:155-172) —setup_triggered_preset()raisesNotFoundError(graph gone → should be 410) andInvalidInputError(no webhook node, setup failed → should be 400), but the route only catchesWebhookRegistrationError. These exceptions now produce 500 Internal Server Errors, silently breaking the existing API contract (previously returned 410 for missing graphs). (Flagged by: quality, discussion, architect — 3) -
setup_triggered_preset()has zero unit tests (triggers.py:24) — This is the extracted shared core logic used by both the REST route and copilot tool. It has three error branches (NotFoundError, twoInvalidInputError) and a success path involving credential merging, webhook setup, and preset creation — none are directly tested. The copilot tool tests mock this function entirely; the route test delegates to it. Regressions in this function would go completely undetected. (Flagged by: testing, discussion — 2)
🟠 Should Fix
-
NotAuthorizedErrorescapes webhook trigger setup (github.py:136→setup_agent_webhook_trigger.py:219) — A GitHub 401/403 raisesNotAuthorizedError, which is neitherInvalidInputErrornorWebhookRegistrationError. The tool's error handler doesn't catch it, producing an unhandled exception. Either catch it in the tool or ensure_base.py's wrapper converts it. (Flagged by: discussion — 1) -
WebhookRegistrationErrornever exercised in tests (setup_agent_webhook_trigger_test.py) — This error type was added specifically in this PR for RPC-safe error propagation and is caught on line 219, but no test triggers it. Add a test wheresetup_mock.side_effect = WebhookRegistrationError(...). (Flagged by: testing — 1) -
list_agent_triggerstest coverage is skeletal (list_agent_triggers_test.py) — Only 2 tests: auth check and one webhook happy path. The"agent"trigger kind, empty results, andNotFoundErrorpath (line 121) are all untested. (Flagged by: testing — 1) -
library_agent_idandbuilder_graph_idresolution paths untested (setup_agent_webhook_trigger.py:242-264) — All tests usegraph_id=directly. The library-agent lookup and session-metadata fallback are completely uncovered. (Flagged by: testing — 1) -
Broad
except ValueErrorhides domain exceptions (_base.py:223) — The webhook manager wrapper catches allValueErrorsubclasses and wraps them asWebhookRegistrationError, hidingNotFoundError/NotAuthorizedErrorfrom callers that branch on those types. Let domain-specific exceptions propagate before the generic catch. (Flagged by: discussion — 1)
🟡 Nice to Have
- Validate
trigger_configvalues against block schema (triggers.py:58) — Currently only checks presence via_is_filled, not types/constraints. Defense-in-depth improvement. (security) - Sanitize error messages (
triggers.py:74,airtable/_webhook.py:130,exa/_webhook.py:109) — Raw third-party API responses could leak internal details. Log full errors server-side, return generic messages. (security) - Eliminate double graph fetch (
setup_agent_webhook_trigger.py:210) — Pass already-loaded graph tosetup_triggered_preset()via optional parameter. Saves one DB round-trip per trigger setup. (performance) - Frontend
AgentDetailsCardregression test — Add a test for non-webhook agents to ensure action buttons still render. (testing) - Add ARIA live region for copy confirmation (
SetupTrigger.tsx:108) — Screen reader users get no feedback when URL is copied. (product) - Split LLM-facing and user-facing messages (
setup_agent_webhook_trigger.py:297, 439) —trigger_config_requiredand success messages contain tool-calling instructions that read oddly if rendered to users. (product)
🔵 Nits
- Dead imports (
presets.py:14-15) —get_webhook_managerandsetup_webhook_for_blockare unused after the refactor. - Magic number (
chat.py:280) — Truncation length500should be a named constant. - Redundant inner import (
chat.py:138) —import clickis already at top level. - Untyped CLI render functions (
chat.py:157,183,207,293) — Type hints would improve maintainability. _patchesdead parameter (setup_agent_webhook_trigger_test.py:99) —missingparameter is accepted but never applied to any mock.
QA Screenshots
| Screenshot | Description |
|---|---|
![]() |
Copilot chat UI loads correctly with new webhook trigger support ✅ |
Human Review Needed
YES — Large refactor (45 files), new RPC boundary for webhook triggers, API contract change in presets.py route, credential handling, and zero human reviews to date.
Risk Assessment
Merge risk: MEDIUM | Rollback: EASY (feature-additive, no DB migrations)
CI Status
❌ 2/6 checks pass, 4 failing: typecheck (pnpm types), backend tests (poetry run test), frontend tests (pnpm test:unit), frontend build (pnpm build). Additionally, check-docs-sync failing on GitHub and 5 checks still pending (backend tests 3.11/3.12/3.13, E2E, PR status).
UI Testing — Variant Results
✅ local: All endpoint validations, auth checks, copilot webhook guidance, and frontend rendering pass; no regressions detected
⚠️ hosted: API endpoints are properly guarded with auth/validation/error handling, but full happy path could not be verified due to subscription gate and lack of seeded webhook-trigger agents
- medium: setup_trigger catches WebhookRegistrationError but the shared setup_triggered_preset function raises InvalidInputError for graph-not-found and no-webhook-node cases. The route handler does not catch InvalidInputError, so those will surface as 500s instead of 400/410.
- low: NotFoundError is raised for missing graph but the route handler in presets.py only catches WebhookRegistrationError — NotFoundError will bubble up as an unhandled 500.
setup_agent_webhook_trigger now catches NotFoundError from setup_triggered_preset, whose graph re-fetch (it fetches by id to run as an RPC endpoint) can find the graph deleted/inaccessible between the tool's resolve and preset creation. Returns the clean trigger_setup_failed message instead of the generic fallback. Provider registration failures remain WebhookRegistrationError (wrapped in _base by design) and are already caught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CLI: drop makedirs from read-only get_pid(); handle psutil.NoSuchProcess in stop(); hoist logging/click imports to module scope; name the truncation length constant. Tests: move GraphTriggerInfo import to module scope; build CredentialsMetaInput with ProviderName.GITHUB instead of suppressing the type with # type: ignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
navigator.clipboard.writeText can reject in non-secure/unsupported contexts; wrap it in try/catch so the copy button no-ops gracefully (the URL stays visible for manual copy) instead of throwing an unhandled rejection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request. |
…lot-doesnt-know-how-to-help-user-set-up-webhook
|
Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly. |
…as GraphTriggerInfo trigger_info had a defined schema (provider, config_schema, credentials_input_name) but was typed dict[str, Any]; type it as GraphTriggerInfo so the OpenAPI/generated client expose a proper model. run_agent and find_library_agent pass the GraphTriggerInfo instance through instead of model_dump()-ing it. Regenerates openapi.json (AgentInfo/AgentDetails trigger_info -> ref GraphTriggerInfo). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-setup failure paths _resolve_graph uses try/except NotFoundError for the library_agent lookup (get_library_agent raises NotFoundError, it doesn't return None). Adds tests covering the tool's failure paths: WebhookRegistrationError (provider refusal) and NotFoundError (graph deleted between resolve and preset creation) both surface a clean trigger_setup_failed message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Direct unit test for setup_triggered_preset (happy + NotFoundError + no-webhook-node + webhook-rejected), setup_agent_webhook_trigger library_agent_id resolution (found + NotFoundError), and a list_agent_triggers 'agent'-kind test. Lifts backend patch coverage past 80% and closes the corresponding review threads. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… for patch coverage Adds/extends Vitest unit tests for the webhook-trigger frontend: SetupTrigger (all output branches + copy button + helpers), RunAgent helpers (trigger_info accordion/animation branches), MessagePartRenderer dispatch (incl. tool-setup_agent_webhook_trigger), SetupRequirementsCard trigger-mode credential carry-back, AgentDetailsCard run paths, and ChatMessagesContainer helpers. Lifts platform-frontend patch coverage well past the 70% target (changed files now 97-100%). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>


Why / What / How
Why: Resolves OPEN-3152. AutoPilot (the copilot) couldn't help users set up webhook-triggered agents — it had no way to create a triggered preset, collect the trigger's config, choose which account the webhook registers under, or hand back the ingress URL. In practice it invented incorrect steps (wrong host
platform.agpt.coinstead ofbackend.agpt.co, a nonexistent Builder side-panel) or tried to "configure" the trigger by editing the agent graph.What: AutoPilot can now set up a webhook trigger end-to-end, with the full inline chat UI: discover the agent, collect the trigger block's config conversationally, pick the registration account explicitly, create the preset, and return the exact ingress URL. Plus guardrails that keep it on the trigger-setup path (rather than editing the graph or trying to "run" a triggered agent), and a small dev CLI for inspecting copilot chat sessions (used to validate all of the above).
How:
setup_triggered_preset()backs bothPOST /presets/setup-triggerand thesetup_agent_webhook_triggertool; it's exposed as aDatabaseManagerRPC and reached from the copilot worker via atriggers_db()accessor (the worker has no connected Prisma client).repo+events), the tool returnstrigger_config_requiredwith the schema so the LLM asks the user instead of inventing values.run_agent(auto-matched, shown only when missing). The card also tells the user the account connects right there, so the LLM doesn't fire a duplicateconnect_integration.find_library_agentreturns each agent'strigger_setup_info(provider + config schema), so AutoPilot recognizes/configures triggers without fetching and reverse-engineering the full graph. This required loading graph nodes in the listing —list_library_agentsgains an opt-ininclude_nodesflag (the main library page stays fast and unchanged).edit_agentrejects edits that change a trigger node's config fields (those belong on the per-trigger preset, not the agent's global graph);run_agentdetects webhook-trigger agents (which can't be run/scheduled directly) and points tosetup_agent_webhook_trigger, rendered as an informational card (no stale "set up" button after setup already succeeded).WebhookRegistrationErrorso the failure reason survives the RPC boundary into the copilot worker.Changes 🏗️
Backend
setup_agent_webhook_triggertool:trigger_config_requiredconfig collection, explicit credential selection via the setup card, preset creation, and the manual-setup ingress URL.list_agent_triggersreturnswebhook_url+provider(via the RPC-safelibrary_dbaccessor).find_library_agentsurfacestrigger_info;list_library_agentsgains an opt-ininclude_nodesflag.edit_agentguard against in-graph trigger-config edits;run_agentredirect for webhook-trigger agents.WebhookRegistrationErrorraised consistently across webhook managers (github / slant3d / telegram / airtable / exa, plus a base-class wrapper).ResponseType.trigger_config_required+AgentInfo.trigger_info(regeneratedopenapi.json).Frontend
SetupTriggertool UI: credentials setup card (explicit account selection, carried back to the tool),trigger_config_requiredprompt, and a success card with the ingress URL + copy.AgentDetailsCardshows an informational card (no action button) for webhook-trigger agents redirected fromrun_agent, so nothing goes stale after setup succeeds.connect_integrationcards exempt fromCollapsedToolGroupso they persist.DX
poetry run cli chat view <id> [range]/chat listto inspect copilot chat sessions (--full/--json; suppresses INFO log noise). Also fixes a pre-existingcli.pyvscli/packaging clash that leftpoetry run clibroken.Checklist 📋
For code changes:
setup_agent_webhook_trigger_test.py,list_agent_triggers_test.py,agent_search_test.py,find_library_agent_test.py,edit_agent_test.py,run_agent_test.py(webhook redirect),tool_schema_test.py(schema char budget),cli/render_test.py.SetupTrigger(credentials card + config-required + success URL),AgentDetailsCard(webhook agent → informational card, no button).connect_integrationcard. Generic webhook (TypeForm) — preset created and correctbackend.agpt.co/.../ingressURL returned; verified the run-redirect card no longer shows a stale "Set up trigger" button.For configuration changes:
.env.defaultis updated or already compatible with my changesdocker-compose.ymlis updated or already compatible with my changesN/A — no configuration changes.