refactor(platform): migrate Ayrshare to standard managed-credential flow - #12883
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughSwitches Ayrshare flows from per-user stored profile keys to on-demand managed credentials: adds AyrshareManagedProvider and provisioning, updates SSO endpoint to ensure managed credential and extract profile key from managed APIKeyCredentials, replaces user-id profile-key lookups with credentials-based inputs across Ayrshare blocks, removes legacy setter, and adds GH CLI sandbox bootstrap. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as Integrations API
participant Manager as ManagedCredsManager
participant Store as CredStore
participant Ayr as Ayrshare API
Client->>API: GET /integrations/ayrshare/sso
API->>Manager: ensure_managed_credential(user_id, provider="ayrshare")
Manager->>Store: lock & has_managed_credential(user_id, "ayrshare")
alt credential exists
Store-->>Manager: APIKeyCredentials (managed)
else
Manager->>Manager: _provision_under_lock -> provider.provision(user_id)
Manager->>Store: add_managed_credential(user_id, credential)
Manager->>Manager: post_provision(user_id, store, credential)
end
Manager-->>API: APIKeyCredentials or failure
API->>API: extract api_key secret -> profile_key
API->>Client: 200 SSO URL (signed JWT with profile_key) / 502 on failure
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. 🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 0 conflict(s), 0 medium risk, 6 low risk (out of 6 PRs with file overlap) Auto-generated on push. Ignores: |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/tools/e2b_sandbox_test.py (1)
636-688: Solid coverage of the bootstrap contract.Tests cover the happy path, exception swallowing, non-zero exit swallowing, single-invocation on create, and skip-on-reconnect. Good alignment with the best-effort design.
Optional: consider an additional assertion that the pinned
_GH_CLI_VERSION(or thev{version}tag) actually appears in the emitted script — it would catch accidental bumps/typos in the f-string URL, which are otherwise only caught at runtime.🧪 Optional assertion
(cmd,), _ = sb.commands.run.call_args # The install script must at least detect gh and use the cli/cli repo. assert "command -v gh" in cmd assert "github.com/cli/cli" in cmd + from .e2b_sandbox import _GH_CLI_VERSION + assert f"/releases/download/v{_GH_CLI_VERSION}/" in cmd🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/tools/e2b_sandbox_test.py` around lines 636 - 688, Add an assertion in TestBootstrapSandbox.test_bootstrap_runs_install_script to verify the pinned GH CLI version appears in the generated install command: after extracting (cmd,), _ = sb.commands.run.call_args assert that either _GH_CLI_VERSION or the literal f"v{_GH_CLI_VERSION}" (depending on how the URL is composed in _bootstrap_sandbox) is present in cmd so accidental typos/bump in the f-string URL are caught; reference symbols: TestBootstrapSandbox.test_bootstrap_runs_install_script, _bootstrap_sandbox, _GH_CLI_VERSION, and the local variable cmd.autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py (1)
104-118: Remove redundantbash -cwrapper andshleximport in bootstrap invocation.The e2b SDK's
AsyncSandbox.commands.run()executes through a shell by default, so shell features likeset -e, pipes, globs, and$(...)work without an explicitbash -cwrapper. Pass the script directly as_SANDBOX_BOOTSTRAP_SCRIPT(eliminating line 160'sshlex.quote()wrapper) and remove theshleximport, which is only used here.Also worth noting: if bootstrap fails (e.g., transient CDN error),
_bootstrap_sandboxis only re-invoked on a fresh sandbox create, soghremains missing for the life of that sandbox. That matches the documented best-effort contract.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py` around lines 104 - 118, The bootstrap script is being needlessly wrapped with shlex.quote and a bash -c invocation; update the _bootstrap_sandbox call site to pass the _SANDBOX_BOOTSTRAP_SCRIPT string directly to AsyncSandbox.commands.run (or whatever invocation uses the script) so the shell runs it natively, and remove the now-unused shlex import from the file; ensure references to _SANDBOX_BOOTSTRAP_SCRIPT and _bootstrap_sandbox remain intact and that no extra quoting/wrapping is applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py`:
- Around line 49-57: provision currently returns a new APIKeyCredentials but
does not clear the legacy ayrshare_profile_key used by
_get_or_create_profile_key, so the legacy branch keeps winning; after creating
the APIKeyCredentials in provision, clear
UserIntegrations.managed_credentials.ayrshare_profile_key (e.g., set it to
None/remove it) and persist the change via your user integrations save/update
method so the legacy field is actually removed and subsequent calls use the
managed credential path instead of the legacy key.
---
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/tools/e2b_sandbox_test.py`:
- Around line 636-688: Add an assertion in
TestBootstrapSandbox.test_bootstrap_runs_install_script to verify the pinned GH
CLI version appears in the generated install command: after extracting (cmd,), _
= sb.commands.run.call_args assert that either _GH_CLI_VERSION or the literal
f"v{_GH_CLI_VERSION}" (depending on how the URL is composed in
_bootstrap_sandbox) is present in cmd so accidental typos/bump in the f-string
URL are caught; reference symbols:
TestBootstrapSandbox.test_bootstrap_runs_install_script, _bootstrap_sandbox,
_GH_CLI_VERSION, and the local variable cmd.
In `@autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py`:
- Around line 104-118: The bootstrap script is being needlessly wrapped with
shlex.quote and a bash -c invocation; update the _bootstrap_sandbox call site to
pass the _SANDBOX_BOOTSTRAP_SCRIPT string directly to AsyncSandbox.commands.run
(or whatever invocation uses the script) so the shell runs it natively, and
remove the now-unused shlex import from the file; ensure references to
_SANDBOX_BOOTSTRAP_SCRIPT and _bootstrap_sandbox remain intact and that no extra
quoting/wrapping is applied.
🪄 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: f8746368-7fcd-4e24-981f-5a164af366b6
📒 Files selected for processing (21)
autogpt_platform/backend/backend/api/features/integrations/router.pyautogpt_platform/backend/backend/blocks/ayrshare/_config.pyautogpt_platform/backend/backend/blocks/ayrshare/_util.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_bluesky.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_facebook.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_gmb.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_instagram.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_linkedin.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_reddit.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_snapchat.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_telegram.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_threads.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_tiktok.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_x.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_youtube.pyautogpt_platform/backend/backend/copilot/tools/e2b_sandbox.pyautogpt_platform/backend/backend/copilot/tools/e2b_sandbox_test.pyautogpt_platform/backend/backend/integrations/credentials_store.pyautogpt_platform/backend/backend/integrations/managed_providers/__init__.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
💤 Files with no reviewable changes (1)
- autogpt_platform/backend/backend/integrations/credentials_store.py
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #12883 +/- ##
==========================================
+ Coverage 68.00% 69.26% +1.26%
==========================================
Files 1913 1880 -33
Lines 148717 144517 -4200
Branches 15504 14836 -668
==========================================
- Hits 101129 100101 -1028
+ Misses 44573 41444 -3129
+ Partials 3015 2972 -43
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.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/integrations/managed_credentials.py`:
- Around line 132-137: The migration currently runs provider.provision (which
may clear the legacy ayrshare_profile_key) before persisting, risking loss if
store.add_managed_credential fails; change the flow in the async with
locks.locked(key) block so you first call provider.provision(user_id) but do not
allow it to clear the legacy Ayrshare key, or better: persist the returned
credential with store.add_managed_credential(user_id, credential) and only after
that succeeds clear the legacy ayrshare_profile_key (or perform both
persist+clear in a single durable transaction/post-persist hook). Update code
paths around locks.locked, store.has_managed_credential, provider.provision, and
store.add_managed_credential to ensure clearing of ayrshare_profile_key happens
after successful add, and add a unit/integration test that simulates
store.add_managed_credential raising/cancelling after provider.provision to
verify the legacy key is retained and the migration is idempotent.
🪄 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: 44208509-0ea4-4f17-867e-5e76d69994b7
📒 Files selected for processing (4)
autogpt_platform/backend/backend/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
- autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📜 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). (9)
- GitHub Check: check API types
- GitHub Check: end-to-end tests
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: type-check (3.11)
- GitHub Check: Seer Code Review
- GitHub Check: Analyze (python)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (5)
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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
autogpt_platform/backend/backend/api/features/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development
Files:
autogpt_platform/backend/backend/api/features/integrations/router.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
autogpt_platform/backend/**/api/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)
autogpt_platform/backend/**/api/**/*.py: UseSecurity()instead ofDepends()for authentication dependencies to get proper OpenAPI security specification
Follow SSE (Server-Sent Events) protocol: usedata:lines for frontend-parsed events (must match Zod schema) and: commentlines for heartbeats/status
Files:
autogpt_platform/backend/backend/api/features/integrations/router.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/integrations/managed_providers/ayrshare_test.py
🧠 Learnings (12)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12426
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-03-15T16:52:15.463Z
Learning: In Significant-Gravitas/AutoGPT (copilot backend), GitHub tokens (GH_TOKEN / GITHUB_TOKEN) for the `gh` CLI are injected lazily per-command in `autogpt_platform/backend/backend/copilot/tools/bash_exec._execute_on_e2b()` by calling `integration_creds.get_integration_env_vars(user_id)`, not on the global SDK subprocess environment in `sdk/service.py`. This scopes credentials to individual E2B sandbox command invocations and prevents token leakage into tool output streams or uploaded transcripts.
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.
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: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12566
File: autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts:968-974
Timestamp: 2026-03-26T00:32:06.673Z
Learning: In Significant-Gravitas/AutoGPT, the admin-facing methods in `autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts` (e.g., `addUserCredits`, `getUsersHistory`, `getUserRateLimit`, `resetUserRateLimit`) intentionally follow the legacy `BackendAPI` pattern with manually defined types in `autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts`. Migrating these admin endpoints to the generated OpenAPI hooks (`@/app/api/__generated__/endpoints/`) is a planned separate effort covering all admin endpoints together, not done piecemeal per PR. Do not flag individual admin type additions in `types.ts` as blocking issues.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12774
File: autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py:0-0
Timestamp: 2026-04-14T06:34:02.835Z
Learning: In `autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py`, the `asyncio.wait_for()` retry loop around `AsyncSandbox.create()` (introduced in PR `#12774`) can leak up to `_SANDBOX_CREATE_MAX_RETRIES - 1` (≤2) orphaned E2B sandboxes per hang incident because `wait_for` cancels only the client-side wait while E2B may complete server-side provisioning. With the default `on_timeout="pause"` lifecycle, leaked orphaned sandboxes are **paused** (not killed) when their original `end_at` is reached and persist indefinitely until explicitly killed — there is NO automatic E2B project-level cleanup. Operators must manage these manually or via their own cleanup jobs. The sandbox_id is not accessible from the timed-out coroutine, so recovery via `AsyncSandbox.connect(sandbox_id)` is not possible at timeout. This is an intentionally accepted trade-off; a proper fix is deferred to a follow-up PR. Do NOT flag the retry loop as a blocking issue.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
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:28.595Z
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: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
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.
📚 Learning: 2026-03-07T07:43:15.754Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/api/openapi.json:1116-1118
Timestamp: 2026-03-07T07:43:15.754Z
Learning: In Significant-Gravitas/AutoGPT, v2 chat endpoints often declare HTTPBearerJWT at the router level while using Depends(auth.get_user_id) that returns None for unauthenticated users; effective behavior is optional auth. Keep this convention unless doing a repo-wide OpenAPI update; prefer clarifying descriptions over per-operation security changes.
Applied to files:
autogpt_platform/backend/backend/api/features/integrations/router.py
📚 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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/api/features/integrations/router.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
📚 Learning: 2026-04-03T13:50:10.521Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12206
File: autogpt_platform/backend/backend/api/external/v2/integrations/helpers.py:25-46
Timestamp: 2026-04-03T13:50:10.521Z
Learning: In `autogpt_platform/backend/backend/api/external/v2/integrations/helpers.py`, `CredentialInfo.from_internal` is intentionally a read-only external API view that exposes only: id, type, provider, title, scopes, expires_at. It omits internal metadata and secret fields by design. Do not flag omitted fields in CredentialInfo as missing information — the limited field set is the correct external API contract.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_credentials.py
📚 Learning: 2026-01-19T07:20:23.494Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11795
File: autogpt_platform/backend/backend/api/features/chat/tools/utils.py:92-111
Timestamp: 2026-01-19T07:20:23.494Z
Learning: In autogpt_platform/backend/backend/api/features/chat/tools/utils.py, the _serialize_missing_credential function uses next(iter(field_info.provider)) for provider selection. The PR author confirmed this non-deterministic provider selection is acceptable because the function returns both "type" (single, for backward compatibility) and "types" (full array), which achieves the primary goal of deterministic credential type presentation.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_credentials.py
📚 Learning: 2026-04-08T17:28:23.439Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:23.439Z
Learning: Applies to autogpt_platform/backend/**/*_test.py : When creating snapshots in tests, use `poetry run pytest path/to/test.py --snapshot-update`; always review snapshot changes with `git diff` before committing
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py (1)
128-135: Drop theisinstancefallback —legacy_keyis typedOptional[SecretStr].
UserIntegrations.ManagedCredentials.ayrshare_profile_key: Optional[SecretStr](seebackend/data/model.py), and theif legacy_key:guard already filtersNone, so theelse str(legacy_key)branch is unreachable defensive code. As per coding guidelines ("Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead"), prefer calling the method directly on the declared type.♻️ Proposed simplification
user_integrations = await store._get_user_integrations(user_id) legacy_key = user_integrations.managed_credentials.ayrshare_profile_key if legacy_key: logger.debug("[ayrshare] Reusing legacy profile key for user %s", user_id) - return ( - legacy_key.get_secret_value() - if isinstance(legacy_key, SecretStr) - else str(legacy_key) - ) + return legacy_key.get_secret_value()As per coding guidelines: "Do not use duck typing — avoid
hasattr/getattr/isinstancefor type dispatch; use typed interfaces/unions/protocols instead".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py` around lines 128 - 135, legacy_key is declared as Optional[SecretStr] (user_integrations.managed_credentials.ayrshare_profile_key) and the preceding if legacy_key: already filters None, so remove the isinstance fallback and call legacy_key.get_secret_value() directly; update the block around legacy_key and the return in the function handling Ayrshare profile keys to simply log the reuse and return legacy_key.get_secret_value().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py`:
- Around line 20-24: Update the module docstring to remove the statement that
legacy migration "migrate ... and clear the legacy field in the same write" and
instead describe the current ordering: first persist the new managed credential
via add_managed_credential, then clear
UserIntegrations.managed_credentials.ayrshare_profile_key in a separate
edit_user_integrations write during post_provision; mention that this split is
intentional and covered by TestMigrationOrderingSafety to prevent data-loss.
---
Nitpick comments:
In `@autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py`:
- Around line 128-135: legacy_key is declared as Optional[SecretStr]
(user_integrations.managed_credentials.ayrshare_profile_key) and the preceding
if legacy_key: already filters None, so remove the isinstance fallback and call
legacy_key.get_secret_value() directly; update the block around legacy_key and
the return in the function handling Ayrshare profile keys to simply log the
reuse and return legacy_key.get_secret_value().
🪄 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: 554246aa-01cf-4c1d-80d0-5c18070dcb74
📒 Files selected for processing (7)
autogpt_platform/backend/backend/blocks/ayrshare/post_to_facebook.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_instagram.pyautogpt_platform/backend/backend/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_credentials.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.pyautogpt_platform/frontend/src/app/api/openapi.json
✅ Files skipped from review due to trivial changes (1)
- autogpt_platform/frontend/src/app/api/openapi.json
🚧 Files skipped from review as they are similar to previous changes (3)
- autogpt_platform/backend/backend/blocks/ayrshare/post_to_instagram.py
- autogpt_platform/backend/backend/integrations/managed_credentials.py
- autogpt_platform/backend/backend/blocks/ayrshare/post_to_facebook.py
📜 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). (14)
- GitHub Check: check API types
- GitHub Check: integration_test
- GitHub Check: test (3.11)
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.12)
- GitHub Check: test (3.12)
- GitHub Check: lint
- GitHub Check: test (3.13)
- GitHub Check: type-check (3.11)
- GitHub Check: end-to-end tests
- GitHub Check: Seer Code Review
- GitHub Check: Check PR Status
- GitHub Check: Analyze (typescript)
- GitHub Check: Analyze (python)
🧰 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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/ayrshare/post_to_pinterest.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/integrations/managed_providers/ayrshare_test.py
🧠 Learnings (40)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12426
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-03-15T16:52:15.463Z
Learning: In Significant-Gravitas/AutoGPT (copilot backend), GitHub tokens (GH_TOKEN / GITHUB_TOKEN) for the `gh` CLI are injected lazily per-command in `autogpt_platform/backend/backend/copilot/tools/bash_exec._execute_on_e2b()` by calling `integration_creds.get_integration_env_vars(user_id)`, not on the global SDK subprocess environment in `sdk/service.py`. This scopes credentials to individual E2B sandbox command invocations and prevents token leakage into tool output streams or uploaded transcripts.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12774
File: autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py:0-0
Timestamp: 2026-04-14T06:34:02.835Z
Learning: In `autogpt_platform/backend/backend/copilot/tools/e2b_sandbox.py`, the `asyncio.wait_for()` retry loop around `AsyncSandbox.create()` (introduced in PR `#12774`) can leak up to `_SANDBOX_CREATE_MAX_RETRIES - 1` (≤2) orphaned E2B sandboxes per hang incident because `wait_for` cancels only the client-side wait while E2B may complete server-side provisioning. With the default `on_timeout="pause"` lifecycle, leaked orphaned sandboxes are **paused** (not killed) when their original `end_at` is reached and persist indefinitely until explicitly killed — there is NO automatic E2B project-level cleanup. Operators must manage these manually or via their own cleanup jobs. The sandbox_id is not accessible from the timed-out coroutine, so recovery via `AsyncSandbox.connect(sandbox_id)` is not possible at timeout. This is an intentionally accepted trade-off; a proper fix is deferred to a follow-up PR. Do NOT flag the retry loop as a blocking issue.
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.
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: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
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: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
📚 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/ayrshare/post_to_pinterest.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/ayrshare/post_to_pinterest.py
📚 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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/ayrshare/post_to_pinterest.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/ayrshare/post_to_pinterest.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/ayrshare/post_to_pinterest.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.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/blocks/ayrshare/post_to_pinterest.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare.pyautogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
📚 Learning: 2026-04-03T13:50:10.521Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12206
File: autogpt_platform/backend/backend/api/external/v2/integrations/helpers.py:25-46
Timestamp: 2026-04-03T13:50:10.521Z
Learning: In `autogpt_platform/backend/backend/api/external/v2/integrations/helpers.py`, `CredentialInfo.from_internal` is intentionally a read-only external API view that exposes only: id, type, provider, title, scopes, expires_at. It omits internal metadata and secret fields by design. Do not flag omitted fields in CredentialInfo as missing information — the limited field set is the correct external API contract.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-01-19T07:20:23.494Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11795
File: autogpt_platform/backend/backend/api/features/chat/tools/utils.py:92-111
Timestamp: 2026-01-19T07:20:23.494Z
Learning: In autogpt_platform/backend/backend/api/features/chat/tools/utils.py, the _serialize_missing_credential function uses next(iter(field_info.provider)) for provider selection. The PR author confirmed this non-deterministic provider selection is acceptable because the function returns both "type" (single, for backward compatibility) and "types" (full array), which achieves the primary goal of deterministic credential type presentation.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-07T07:43:15.754Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 12328
File: autogpt_platform/frontend/src/app/api/openapi.json:1116-1118
Timestamp: 2026-03-07T07:43:15.754Z
Learning: In Significant-Gravitas/AutoGPT, v2 chat endpoints often declare HTTPBearerJWT at the router level while using Depends(auth.get_user_id) that returns None for unauthenticated users; effective behavior is optional auth. Keep this convention unless doing a repo-wide OpenAPI update; prefer clarifying descriptions over per-operation security changes.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-15T16:52:15.463Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12426
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-03-15T16:52:15.463Z
Learning: In Significant-Gravitas/AutoGPT (copilot backend), GitHub tokens (GH_TOKEN / GITHUB_TOKEN) for the `gh` CLI are injected lazily per-command in `autogpt_platform/backend/backend/copilot/tools/bash_exec._execute_on_e2b()` by calling `integration_creds.get_integration_env_vars(user_id)`, not on the global SDK subprocess environment in `sdk/service.py`. This scopes credentials to individual E2B sandbox command invocations and prevents token leakage into tool output streams or uploaded transcripts.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-10T11:22:18.867Z
Learnt from: Swiftyos
Repo: Significant-Gravitas/AutoGPT PR: 12347
File: autogpt_platform/backend/backend/data/invited_user.py:193-193
Timestamp: 2026-03-10T11:22:18.867Z
Learning: In Significant-Gravitas/AutoGPT, the admin data-layer functions in `autogpt_platform/backend/backend/data/invited_user.py` (`list_invited_users`, `create_invited_user`, `revoke_invited_user`, `retry_invited_user_tally`, `bulk_create_invited_users_from_file`) intentionally omit an acting-user/admin ID parameter. Authorization for these functions is enforced entirely at the FastAPI router layer via `Security(requires_admin_user)` in `user_admin_routes.py`. Do not flag the absence of a user_id/actor_id parameter in these functions as a missing data-access guardrail violation.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-10T08:39:22.025Z
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.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-09T16:20:43.788Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12727
File: autogpt_platform/backend/backend/data/credit.py:0-0
Timestamp: 2026-04-09T16:20:43.788Z
Learning: In Significant-Gravitas/AutoGPT, `get_user_by_id(user_id: str) -> User` in `autogpt_platform/backend/backend/data/user.py` raises `ValueError("User not found with ID: ...")` when the user row does not exist — it never returns `None`. Do not flag call sites that dereference the result without a None-check as potential null-pointer issues.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-04T23:58:18.476Z
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.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-21T04:35:34.710Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit.py:1584-1584
Timestamp: 2026-04-21T04:35:34.710Z
Learning: In Significant-Gravitas/AutoGPT, `get_user_by_id(user_id: str)` in `autogpt_platform/backend/backend/data/user.py` returns an application-layer Pydantic `User` model (defined in `autogpt_platform/backend/backend/data/model.py`), NOT the raw Prisma `User` object. This Pydantic model uses snake_case field names (e.g., `subscription_tier`, `stripe_customer_id`, `top_up_config`), which are mapped from camelCase Prisma fields (e.g., `subscriptionTier`, `stripeCustomerId`) inside `User.from_db()`. Do not flag `user.subscription_tier` as a wrong field name — it is correct on the app-layer model.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-01T07:59:02.311Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:10030-10037
Timestamp: 2026-03-01T07:59:02.311Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — For MCP manual token storage, backend model autogpt_platform/backend/backend/api/features/mcp/routes.py defines MCPStoreTokenRequest.token as Pydantic SecretStr with a min length constraint, which generates OpenAPI schema metadata (format: "password", writeOnly: true, minLength: 1) in autogpt_platform/frontend/src/app/api/openapi.json. Prefer SecretStr (with length constraints) for sensitive request fields so generated TS clients and docs treat them as secrets.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-09T08:47:32.750Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/graphiti/client.py:20-46
Timestamp: 2026-04-09T08:47:32.750Z
Learning: In Significant-Gravitas/AutoGPT, `user_id` values passed to `derive_group_id` in `autogpt_platform/backend/backend/copilot/graphiti/client.py` are always system-generated UUIDv4s (e.g. `883cc9da-fe37-4863-839b-acba022bf3ef`). The character set `[0-9a-f-]` is fully within `[a-zA-Z0-9_-]`, so the sanitization regex never strips any characters and no collision between two different user IDs is possible. Do not flag `derive_group_id` for collision-resistance issues.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-17T10:57:12.953Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — Backend/frontend OpenAPI codegen
Learning: For MCP schema models, required OpenAPI fields must have no defaults in Pydantic. Specifically, MCPToolInfo.input_schema must be required (no Field(default_factory=dict)) so openapi.json emits it in "required", ensuring generated TS types treat input_schema as non-optional.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-22T05:58:28.595Z
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:28.595Z
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).
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-18T14:03:32.534Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12473
File: autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py:86-195
Timestamp: 2026-03-18T14:03:32.534Z
Learning: In Significant-Gravitas/AutoGPT, the integration tests in `autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py` intentionally use real external URLs (example.com, httpbin.org). They are gated with `pytest.mark.skipif(shutil.which("agent-browser") is None, ...)`, so they are automatically skipped in standard CI where agent-browser is not installed. They are designed to be run explicitly inside the Docker environment to verify that system Chromium (AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium) actually launches and can fetch pages end-to-end. Do not flag the use of real network calls in these tests as a flakiness concern.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-24T21:27:22.326Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12536
File: autogpt_platform/frontend/src/app/api/openapi.json:5732-5752
Timestamp: 2026-03-24T21:27:22.326Z
Learning: Repo: Significant-Gravitas/AutoGPT — Preference: Do not add explicit 403/404 entries to FastAPI route decorators for admin endpoints just to influence OpenAPI. Keep openapi.json autogenerated and use route docstrings to document admin-only (403) and not-found (404) behavior; rely on tests for enforcement. File context: autogpt_platform/backend/backend/api/features/admin/store_admin_routes.py. PR `#12536`.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-24T21:25:15.983Z
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.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-08T17:26:41.549Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: classic/CLAUDE.md:0-0
Timestamp: 2026-04-08T17:26:41.549Z
Learning: Applies to classic/**/tests/**/*.py : Tests requiring API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY) will skip if not set
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-07T18:08:03.548Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12701
File: .claude/skills/orchestrate/scripts/verify-complete.sh:120-121
Timestamp: 2026-04-07T18:08:03.548Z
Learning: In Significant-Gravitas/AutoGPT, verify-complete.sh (`.claude/skills/orchestrate/scripts/verify-complete.sh`) uses `commits[-1].committedDate` (not `updatedAt`) to identify stale CHANGES_REQUESTED reviews. This is intentional: `updatedAt` changes on any PR activity (bot comments, label changes, description edits), which would falsely classify a reviewer's CHANGES_REQUESTED as stale — a silent false negative. The `committedDate` edge case (commit created locally before a review but pushed after) only causes a false positive (unnecessary re-brief), which is the safer failure mode. Do not suggest switching to `updatedAt` for this comparison.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-26T00:32:06.673Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12566
File: autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts:968-974
Timestamp: 2026-03-26T00:32:06.673Z
Learning: In Significant-Gravitas/AutoGPT, the admin-facing methods in `autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts` (e.g., `addUserCredits`, `getUsersHistory`, `getUserRateLimit`, `resetUserRateLimit`) intentionally follow the legacy `BackendAPI` pattern with manually defined types in `autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts`. Migrating these admin endpoints to the generated OpenAPI hooks (`@/app/api/__generated__/endpoints/`) is a planned separate effort covering all admin endpoints together, not done piecemeal per PR. Do not flag individual admin type additions in `types.ts` as blocking issues.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-10T08:38:36.655Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/tools/run_block.py:349-370
Timestamp: 2026-03-10T08:38:36.655Z
Learning: In the AutoGPT CoPilot HITL (Human-In-The-Loop) flow (`autogpt_platform/backend/backend/copilot/tools/run_block.py`), the review card presented to users sets `editable: false`, meaning reviewers cannot modify the input payload. Therefore, credentials resolved before `is_block_exec_need_review()` remain valid and do not need to be recomputed after the review step — the original `input_data` is unchanged through the review lifecycle.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-09T09:07:11.551Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/tools/graphiti_delete.py:63-69
Timestamp: 2026-04-09T09:07:11.551Z
Learning: In Significant-Gravitas/AutoGPT, gating `graphiti_delete_user_data` (and similar Graphiti memory tools) on the `is_enabled_for_user` / `graphiti-memory` LaunchDarkly flag in the delete path is intentional and acceptable. The scenario where a user has existing Graphiti data but the flag is later disabled (preventing deletion) is not a concern for the team. Do not flag this pattern as an issue.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-03-12T14:42:40.552Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:141-170
Timestamp: 2026-03-12T14:42:40.552Z
Learning: In Significant-Gravitas/AutoGPT, `check_rate_limit` in `autogpt_platform/backend/backend/copilot/rate_limit.py` is intentionally a "pre-turn soft check" (not a hard atomic reservation). Because LLM token counts are unknown before generation completes, a strict check-and-reserve is impractical. The TOCTOU race (two concurrent turns both passing the pre-check and both committing via `record_token_usage`) is an accepted trade-off. If stricter enforcement is ever needed, the approach is a Lua script doing GET+INCRBY atomically in Redis.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-03T13:50:29.037Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12206
File: autogpt_platform/backend/backend/api/external/v2/rate_limit.py:24-56
Timestamp: 2026-04-03T13:50:29.037Z
Learning: In `autogpt_platform/backend/backend/api/external/v2/rate_limit.py`, the `RateLimiter` class uses in-process (per-worker) memory for sliding-window rate limiting. This is intentionally documented as a known limitation via WARNING comments in the module and class docstrings. A full Redis-backed migration (using ZADD/ZREMRANGEBYSCORE/ZCARD with TTL/Lua for atomic multi-replica enforcement) is deferred to a later PR. Do not re-flag the in-memory implementation as a blocking bug — the limitation is documented and accepted for the initial v2 external API release.
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py
📚 Learning: 2026-04-08T17:28:23.439Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:23.439Z
Learning: Applies to autogpt_platform/backend/**/*_test.py : When creating snapshots in tests, use `poetry run pytest path/to/test.py --snapshot-update`; always review snapshot changes with `git diff` before committing
Applied to files:
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py
🔇 Additional comments (3)
autogpt_platform/backend/backend/integrations/managed_providers/ayrshare.py (1)
49-109: LGTM on the provider contract and ordering fix.
is_available() → Falsecleanly opts Ayrshare out of the startup sweep (per the prior review thread), and splitting legacy-clearing intopost_provisionis the right ordering — ifadd_managed_credentialfails,_provision_under_locknever callspost_provision, so the legacy key survives for a retry. The regression test inayrshare_test.py::TestMigrationOrderingSafetypins this behavior.One small observation (non-blocking):
post_provisionalways entersstore.edit_user_integrations(...)even on fresh users where the legacy field is alreadyNone, so the write-lock path is taken on every provision just to no-op. Ifedit_user_integrationsends up persisting on exit unconditionally, consider a read-only pre-check (e.g.store._get_user_integrations(user_id)) and only open the edit CM when the legacy field is actually populated. Minor, fine to defer.autogpt_platform/backend/backend/blocks/ayrshare/post_to_pinterest.py (1)
92-205: LGTM — consistent with the shared Ayrshare credential pattern.Signature and
profile_key=get_profile_key(credentials)call match the established pattern from the otherpost_to_*blocks (e.g.post_to_instagram.py). Error handling continues to rely on the block executor converting exceptions into theerroroutput, matching the repo convention for blockrun()methods.autogpt_platform/backend/backend/integrations/managed_providers/ayrshare_test.py (1)
1-257: LGTM — solid coverage including the migration-ordering regression.Coverage lines up well with the prior review asks:
is_availableopt-out,_settings_availablevariants, legacy-reuse-without-eager-clear, fresh profile creation,post_provisionclear + idempotent, managedAPIKeyCredentialsshape, and critically the retry-safety regression (TestMigrationOrderingSafety) that pins the ordering fix against future reorderings.Minor nit (non-blocking) on
test_post_provision_skips_when_legacy_already_clear(lines 182-184): the comment says "no write attempted", butpost_provisionunconditionally entersedit_user_integrations, so the CM is still invoked — the test only asserts the final field state. If you actually want to pin "no mutation path taken", consider asserting on the mock (e.g. that no write occurs) or soften the comment to match what's verified.
…r-scoped list
Providers with ``auto_provision=False`` (e.g. Ayrshare) opt out of the
startup sweep because provisioning burns per-user quota. Consequently,
``GET /{provider}/credentials`` returned an empty list for a brand-new
user — the block UI fell back to "Add API key" instead of showing the
managed cred, defeating the "managed" part of the flow.
The provider-scoped lookup is the right on-demand trigger: the user only
hits it after opening a block that needs that provider, so provisioning
is still bounded. Inline-provision managed creds for opted-out providers
whose org-level config is available, before returning the list.
…corators The auto_provision=False opt-out meant Ayrshare was never provisioned from the global credential-list sweep (which is what the builder uses to populate block credential dropdowns), so dropping a Post-to-X block showed an empty dropdown + "Add API key" instead of the managed cred. The on-demand provider-scoped trigger only fires for the settings page, not the builder. Simplest fix: drop the opt-out and let Ayrshare ride the sweep like AgentMail. Profile creation is at Ayrshare plan quota, not per-post, so the cost is bounded at the plan level. Users who never touch a social block will have an unused profile slot, which we can accept. Also restores the @cost(*AYRSHARE_POST_COSTS) decorator + _cost imports on all 13 Ayrshare blocks — those were stripped during the rebase onto dev (which had merged #12893 adding the billing hooks).
…d sees it The `/credentials` endpoint kicked the sweep off as `asyncio.create_task`, so the endpoint returned BEFORE provisioning finished. Fine under the old design (AgentMail was the only provisioning path and it was idempotent across page loads), but with Ayrshare now riding the same sweep the first-time user sees an empty dropdown + "Add API key" until they refresh. Block on `ensure_managed_credentials`. It fans providers out via `asyncio.gather`, so the latency is max(provider_times), not sum. Once a user is in `_provisioned_users` (in-memory per process) subsequent calls short-circuit, so this is a one-shot cost per user per pod.
Dev surfaced a Pydantic ValidationError during the sweep: Ayrshare's GET /profiles returns entries without ``profileKey`` for incomplete / freshly-created profiles (Sentry predicted this earlier and I got the fix wrong — was a required str). Make ``profileKey`` optional and skip matches that lack it in the recovery lookup so we fall through to ``create_profile`` instead of returning ``None`` as a key. Added a regression test for the missing- key case.
…builder The block credential picker let the user delete a managed credential (e.g. "Ayrshare (managed by AutoGPT)"), which would orphan them from the auto-provisioned cred until the sweep re-ran. The /profile/integrations page already gates on ``!cred.is_managed``; match that in the builder's CredentialsInput/FlatView by dropping ``onDelete`` for managed entries so the row's context menu never offers the action.
…rror handling Three items from the latest Sentry review: 1. PostToYouTubeBlock / PostToSnapchatBlock lost their ``is_video=True`` override during the rebase onto dev. Without it both blocks inherited ``is_video=False``, so the @cost(*AYRSHARE_POST_COSTS) filter matched the image tier (2 credits) instead of the video tier (5 credits) — under-charging on every run. Restore the explicit True override on both blocks. 2. AyrshareClient was constructing ``Requests`` with the default ``raise_for_status=True``, which would raise a generic HTTPError before any of our methods could read the JSON body and raise a contextual AyrshareAPIException. Pass ``raise_for_status=False`` so the custom error-surfacing path runs as intended. 3. ``list_profiles`` ``payload.get("status")`` check — Sentry self- resolved this in the previous commit; thread-resolve only.
… fallback Sentry flagged that the await on ensure_managed_credentials has no timeout, so if Ayrshare (or any managed provider's upstream) hangs, the whole /credentials endpoint would block up to the default 5-minute aiohttp timeout on a first-time user. Wrap the sweep in asyncio.wait_for with a 10s budget. On timeout we log a warning and kick off a background task so provisioning still completes — the user simply won't see the managed cred until their next refresh, which is the same guarantee as the old fire-and-forget path but only kicks in as a fallback when upstream is actually slow.
…share client
Sentry flagged that all four error-handling sites in AyrshareClient
call ``.get("message")`` directly on ``response.json()``, which raises
AttributeError when the body decodes to a list or a bare string. The
outer handler still surfaces an error, but the user sees a generic
"Provisioning failed" instead of Ayrshare's actual detail.
Extract ``_extract_error_message`` which isinstance-checks the decoded
body and falls back to ``response.text()`` for non-dict shapes, then
use it at all four sites. Removes the repeated try/except boilerplate
as a side benefit.
Every login auto-provisioning Ayrshare burned a subscription-quota slot for every user, even those who never used a social-media block. Switch to opt-out of the startup sweep and provision only when the user explicitly clicks the "Connect Social Media Accounts" button — the SSO URL endpoint already calls ``ensure_managed_credential`` under the hood, so the provisioning moment moves from "first /credentials call" to "user signals intent to connect". To surface the newly-provisioned managed cred without a page refresh, expose ``CredentialsActionsContext`` with a ``reload`` method and have AyrshareConnectButton call it after the SSO URL fetch succeeds. The dropdown stays empty until the user clicks Connect — which is now the explicit trigger for both profile creation and the social-linking SSO popup, matching the user's mental model.
…eload Adds coverage for the two frontend changes in this PR: - CredentialsFlatView: asserts that rendering a credential with ``is_managed: true`` does NOT offer a delete action (the row's overflow menu trigger is suppressed via the ``onDelete`` gate). - AyrshareConnectButton: asserts that a successful SSO URL fetch triggers ``credentialsActions.reload()`` on the actions context and that a failed fetch does NOT. Brings codecov/patch on the frontend diff above the 80% threshold.
…ete+create Ayrshare's GET /profiles never returns ``profileKey`` for any profile — we verified 0/30 profiles in the dev subscription have one. That means there's no supported way to retrieve an existing profile's key via any Ayrshare endpoint. When our DB drops the managed credential while the upstream profile persists, the old idempotency recovery path would silently skip the match (no key to return) and fall through to ``create_profile``, which then 400s with code 146 "Profile title already exists" — a 502 for the user with no recovery. Switch the recovery strategy: when ``list_profiles`` reveals a profile titled the same as ours, treat it as an orphan and ``delete_profile`` before ``create_profile``. Destructive — any socials linked to the orphan are lost — but the orphan is already unusable without the key (we can't post through it, can't JWT through it), so the alternatives are either a permanently-stuck user or a shadow profile forever eating a quota slot. Adds ``AyrshareClient.delete_profile`` to back the recovery.
Ayrshare is provisioned exclusively via the server-side SSO flow (the "Connect Social Media Accounts" button on the block). Showing "Add API key" / "Use a new API key" next to the credential dropdown mis-suggests that a user-pasted key is a valid setup path — Toran and Zamil both tripped over this in dev testing. Suppress the manual credential-add button in CredentialsFlatView when the provider is a managed-only one (currently just "ayrshare"). When the user has no managed credential yet, render a small hint pointing at the Connect button instead of leaving an empty space.
d94aaa5 to
cfeb2db
Compare
|
Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly. |
…orphan delete Sentry flagged that Ayrshare's DELETE /profiles requires a Profile-Key header — which we don't have for orphaned profiles. So the "delete + recreate" orphan recovery from the previous commit never actually worked; the delete would 400 and we'd still hit the duplicate- title error on create. Drop the whole recovery codepath. Append a ``secrets.token_hex(3)`` suffix to every profile title we create — the title is cosmetic in Ayrshare's dashboard, but unique per provision attempt means we never collide with an orphaned upstream profile from a prior session. Orphans stick around in Ayrshare until cleaned up manually; that's the accepted tradeoff for unblocking users without an endpoint to retrieve their existing profileKey. Also removes the now-unused ProfileSummary model, list_profiles method, delete_profile method, and their tests. Adds a test asserting titles are unique across provision attempts.
Two connected UX bugs when an Ayrshare block fails:
1. Ayrshare's POST /post returns HTTP 400 with the actionable reason
nested at ``posts[0].errors[0].message`` ("Twitter is not linked...")
— our ``_extract_error_message`` only looked at the root-level
``message`` field, so users saw "Unknown error" instead. Now we dig
into the ``posts[].errors[]`` structure first and fall back to the
root message / text.
2. BlockError wrapped every exception with "raised by X with message:
Y. block_id: Z", which turned the already-wrapped Ayrshare error
into "raised by PostToXBlock with message: Ayrshare API request
failed (400): Unknown error. block_id: ..." — double-framed and
illegible. Drop the framing; keep ``block_name`` / ``block_id`` as
attributes for structured logging. Callers yielding ``str(exc)`` now
surface just the underlying message.
Combined effect: a failed Post To X now yields "Twitter is not linked.
Please confirm the linkage on the Social Accounts page..." instead of
"raised by PostToXBlock with message: Ayrshare API request failed (400):
Unknown error. block_id: ...".
Previous fix only dug into ``posts[].errors[].message`` — but Ayrshare
also returns request-level rejects with the error directly on the post
object:
{"posts": [{"action": "post", "code": 162,
"message": "Missing or incorrect post parameter..."}]}
That shape fires when validation rejects the request before trying any
platform (e.g. empty post field on LinkedIn). Extract both shapes so
the user sees the real reason instead of "Unknown error".
Introduced by #12883 — module-level `import secrets` sat after class/function definitions, tripping ruff E402 on our branch post-merge. One-line hoist unblocks lint.
…cept AgentInputBlock subclasses (#12880) ### Why / What / How **Why:** Resolves #12875. CoPilot's agent-builder was hardcoding Google Drive file IDs into consuming blocks' `input_default` instead of wiring an `AgentGoogleDriveFileInputBlock`. A beta user hit this across **13 saved versions** of one agent. Root causes: 1. `validate_io_blocks` only accepted the literal base `AgentInputBlock` / `AgentOutputBlock` IDs, so even when CoPilot used a specialized subclass like `AgentGoogleDriveFileInputBlock` as the only input, the validator forced it to keep a throwaway base alongside — entrenching the anti-pattern. 2. Running a Drive consumer directly via CoPilot's `run_block` silently failed because the auto-credentials flow (picker attaches `_credentials_id`) existed only in the graph executor, never in CoPilot's direct-execution path. 3. Drive picker guidance lived in `agent_generation_guide.md` instead of on the blocks themselves, so it duplicated and drifted from the code. 4. Observed in a live session: when asked to read a private sheet, CoPilot refused with "share publicly or use the builder" instead of calling `run_block` and letting the picker render — the prompt rule was buried and the fallback path (omitted required picker field) returned a generic schema preview. **What:** Four coordinated platform + CoPilot improvements. No block-specific validator rules, no Drive-specific code in UI or prompt. **How:** #### 1. `validate_io_blocks` subclass support Accepts any block with `uiType == "Input"` / `"Output"` (populated from `Block.block_type` at registration). `AgentGoogleDriveFileInputBlock`, `AgentDropdownInputBlock`, `AgentTableInputBlock`, etc. stand alone. Base-ID fallback preserved for call sites that pass a minimal blocks list. #### 2. Inline picker via `run_block` - Extracted `_acquire_auto_credentials` from `backend/executor/manager.py` into shared `backend/executor/auto_credentials.py` (exports `acquire_auto_credentials` + `MissingAutoCredentialsError`). - Wired it into `backend/copilot/tools/helpers.py::execute_block`. When `_credentials_id` is present, the block executes with creds injected (chained flows work). When missing/null, `execute_block` returns the existing `SetupRequirementsResponse` — frontend's `FormRenderer` renders the picker inline via the existing `GoogleDrivePickerField`/`GoogleDrivePickerInput`. On pick, the LLM re-invokes `run_block` with the populated input — same continuation pattern as OAuth-missing-credentials. No new response types, no new continuation tool, no new frontend component. - `run_block` now short-circuits to `SetupRequirementsResponse` when missing required fields include a picker-backed field, skipping the schema-preview round trip the LLM would otherwise take. - `get_inputs_from_schema` spreads the full property schema (`**schema`) instead of whitelisting — any `format` / `json_schema_extra` / custom widget config flows through to the generic custom-field dispatch on the frontend. Future picker formats (date pickers, file pickers, etc.) work without backend changes. - Frontend `SetupRequirementsCard/helpers.ts` uses index-signature passthrough for arbitrary schema keys — no widget-specific code in that layer. #### 3. `validate_only` parameter on `run_block` `run_block(id, {})` is not always a safe probe — for blocks with zero required inputs, it executes. New `validate_only: true` parameter returns `BlockDetailsResponse` (schema + missing-input list) without executing, rendering picker cards, or charging credits. Same response shape as the existing schema preview — no new branch, just an extra condition on the existing one. LLM uses this for pre-flight when it's unsure whether a block has required inputs. #### 4. Block-local picker guidance Agent-generation picker guidance relocated from the guide onto the blocks themselves — surfaced at `find_block` time, exactly when the LLM decides to wire a picker-backed consumer: - `GoogleDriveFileField` (shared factory for every Drive field on Sheets/Docs/etc.) appends a standard hint to the caller's description covering: feed from the specialized input block, never hardcode (even one parsed from a URL), picker is the only credential source. - `AgentGoogleDriveFileInputBlock`'s block description now covers when it's required, the `allowed_views` mapping, wiring direction, and a concrete link-shape example. - `agent_generation_guide.md` loses the dedicated 71-line Drive section. The IO-blocks section now tells the LLM specialized subclasses satisfy the requirement and carry their own usage guidance in block/field descriptions — read them when `find_block` surfaces a match. - New "Picker-backed inputs via `run_block`" section in the CoPilot prompt, written generically (picker fields detected via `format` / `auto_credentials` schema hints, no provider names hardcoded) — covers: don't ask the user for URLs/IDs, don't refuse private-resource asks, chained picker objects pass through as-is. - Sharpened `MissingAutoCredentialsError` message so when a bare ID reaches execution, the error explicitly tells the LLM the picker renders inline (not "ask the user for something"). ### Changes 🏗️ - `backend/copilot/tools/agent_generator/validator.py` — `_collect_io_block_ids` + subclass-aware `validate_io_blocks`. - `backend/executor/auto_credentials.py` (new) — shared `acquire_auto_credentials` + `MissingAutoCredentialsError`. - `backend/executor/manager.py` — imports from the shared module, drops the local copy. - `backend/copilot/tools/helpers.py` — `execute_block` calls `acquire_auto_credentials`, merges kwargs, releases locks in `finally`, returns `SetupRequirementsResponse` on missing creds. `get_inputs_from_schema` spreads the full property schema. - `backend/copilot/tools/run_block.py` — picker-field short-circuit + `validate_only` parameter. - `backend/copilot/prompting.py` — "Picker-backed inputs via `run_block`" + "Pre-flight with `validate_only`" sections. - `backend/blocks/google/_drive.py` — `GoogleDriveFileField` appends the agent-builder hint to every Drive consumer's description. - `backend/blocks/io.py` — `AgentGoogleDriveFileInputBlock` description expanded. - `backend/copilot/sdk/agent_generation_guide.md` — Drive section removed, IO-blocks subclass note expanded. - `frontend/.../SetupRequirementsCard/helpers.ts` — index-signature passthrough for arbitrary schema keys; schema fields propagate into the generated RJSF schema. - Tests: new `TestExecuteBlockAutoCredentials` (4 cases) + `validate_only` + picker-short-circuit cases in `run_block_test.py`; `manager_auto_credentials_test.py` moved to new import path; 6 new frontend cases in `SetupRequirementsCard/__tests__/helpers.test.ts` covering schema passthrough. - Also: one-line hoist of `import secrets` in `backend/integrations/managed_providers/ayrshare.py` — ruff E402 introduced by #12883 was blocking our lint post-merge. ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Backend unit suites: validator_test (48), helpers_test (40), run_block_test (19), manager_auto_credentials_test (15) — **all green** - [x] Frontend `SetupRequirementsCard` helpers — **75/75 pass** (including 6 new passthrough cases) - [x] `poetry run format` (ruff + isort + black) clean on touched files (pre-existing pyright errors in unrelated `graphiti_core` / `StreamEvent` / etc. files not introduced by this PR) - [x] Live CoPilot chat on dev-builder confirmed the setup card renders `custom/google_drive_picker_field` for a Drive consumer block called via `run_block` - [x] Live agent-generation confirmed CoPilot creates a subclass-only agent (`AgentGoogleDriveFileInputBlock` → `GoogleSheetsReadBlock` → `AgentOutputBlock`) with no throwaway base `AgentInputBlock` #### For configuration changes: - [x] N/A — no config changes --------- Co-authored-by: majdyz <zamil.majdy@agpt.co>
…d sweep (#12908) ## Why Review comments on #12883 (thanks @Pwuts) surfaced a few spots where the managed-credential plumbing's names and docstrings didn't match what the code actually does: - `_read_or_create_profile_key` suggests "read from any source or create new", but only migrates the legacy `managed_credentials.ayrshare_profile_key` side-channel — it doesn't read an existing managed credential. (That check lives in the outer `_provision_under_lock`.) - Docstrings refer to "the startup sweep" in several places — there's no startup hook; the sweep runs on `/credentials` fetches. - `is_available` / `auto_provision` relationship wasn't explicit; readers couldn't tell whether `is_available` was a config check or a liveness check, or which of the two gates the sweep checks first. ## What Naming + docstring cleanup. **Zero behavior changes.** - Rename `_read_or_create_profile_key` → `_migrate_legacy_or_create_profile_key` with docstring explaining why it doesn't re-check the managed cred. - Replace "startup sweep" → "credentials sweep" everywhere. - `ManagedCredentialProvider` class docstring now names the two gates: 1. `auto_provision` — does this provider participate in the sweep at all? 2. `is_available` — are the required env vars / secrets set? - `is_available` docstring now spells out: what it checks (env vars), what it does NOT check (upstream health), and that it's only consulted when `auto_provision=True`. - `ensure_managed_credentials` docstring defines "credentials sweep", when it fires, how the per-user in-memory cache works. - Module-level docstring drops the stale "non-blocking background task" wording (#12883 made the sweep bounded-await). ## How 4 files, all backend: - `backend/integrations/managed_credentials.py` - `backend/integrations/managed_providers/ayrshare.py` - `backend/integrations/managed_providers/ayrshare_test.py` - `backend/api/features/integrations/router.py` Tests: 13/13 Ayrshare tests pass against the rename. ## Checklist - [x] Follows style guide - [x] Existing tests still pass (no functional change) - [x] No new tests needed — pure rename + docstring change
Why
Beta user report: AutoPilot told them to sign up for Ayrshare themselves — which AutoGPT actually manages — because AutoPilot inferred the requirement from the block description string rather than any structured schema. Root cause: Ayrshare was the only block family whose "credential" lived in a bespoke
UserIntegrations.managed_credentials.ayrshare_profile_keyside channel and whose blocks declared nocredentialsfield.find_block/resolve_block_credentialshad nothing to show the LLM, so the LLM guessed.(An initial commit added a runtime
ghCLI bootstrap for a separate "gh isn't installed in the sandbox" report — that work was empirically verified unnecessary and reverted; see the commit history for the bench results.)What
Ayrshare now goes through the standard managed-credential flow:
AyrshareManagedProvideralongside the existingAgentMailManagedProvider. Provisions the per-user profile asAPIKeyCredentials(provider="ayrshare", is_managed=True)via the sharedadd_managed_credentialpath. Reuses any legacymanaged_credentials.ayrshare_profile_keyvalue on first provision so existing users keep their linked social accounts.AyrshareManagedProvider.is_available()returnsFalseso theensure_managed_credentialsstartup sweep never auto-provisions Ayrshare (profile quota is a real per-user subscription cost). New publicensure_managed_credential(user_id, store, provider)helper lets the/api/integrations/ayrshare/sso_urlroute provision on demand, reusing the same distributed Redis lock + upsert path as AgentMail.ProviderBuilder.with_managed_api_key()method registersapi_keyas a supported auth type without the env-var-backed default credential thatwith_api_key()creates — so the org-level Ayrshare admin key cannot leak to blocks as a "profile key".BaseAyrshareInputgains a sharedcredentialsfield; all 13 social blocks inherit it. Eachrun()now takescredentials: APIKeyCredentials; the inlineget_profile_keyguard + "please link a social account" error is gone. Standardresolve_block_credentialspre-run check owns the "not connected" path, returning a normalSetupRequirementsResponse.post_provisionhook onManagedCredentialProviderclears the legacyayrshare_profile_keyfield only afteradd_managed_credentialhas durably stored the managed credential. If persistence fails, the legacy key stays intact so a retry can reuse it — covered byTestMigrationOrderingSafety.IntegrationCredentialsStore.get_user_integrations()— reads no longer have to reach past the_get_user_integrationsprivacy fence or abuseedit_user_integrationsas a pseudo-read./api/integrations/ayrshare/sso_urlcollapses from a 60-line provision-then-sign dance to: pre-flightsettings_available(),ensure_managed_credential, fetch the credential, sign a JWT.IntegrationCredentialsStore.set_ayrshare_profile_keyremoved — the managed credential is now the only write path.UserIntegrations.ManagedCredentials.ayrshare_profile_keyfield is retained so the managed provider can migrate existing users on first provision; removing the field is a follow-up once rollout has propagated.How
After this PR,
find_blockreturns Ayrshare blocks with a structuredcredentials_provider: ['ayrshare']. AutoPilot sees the credential requirement the same way it sees GitHub's or AgentMail's, callsrun_block, and gets a plainSetupRequirementsResponsewhen the managed credential has not been provisioned yet. No more description-string speculation; the whole Ayrshare flow is the normal flow.The Builder's
AyrshareConnectButton(BlockType.AYRSHARE) still works — it hits the same endpoint, now a thin wrapper over the managed provider — so users still get the "Connect Social Accounts" popup for OAuth'ing individual social networks.Test plan
poetry run pytest backend/blocks/test/test_block.py -k "ayrshare or PostTo"— 26/26 pass.poetry run pytest backend/integrations/managed_providers/ayrshare_test.py— 10/10 pass.poetry run pytest backend/api/features/integrations/router_test.py— 21/21 pass.poetry run pyrighton all touched backend files — 0 errors.find_blockonPostToXBlocklistscredentials_provider: ['ayrshare']in the JSON schema.managed_credentials.ayrshare_profile_keycontinue to work without re-linking.