feat(backend/copilot-bot): let users upload files to AutoPilot via Discord - #13427
Conversation
…scord Users can attach a file to a Discord message and AutoPilot reads it during the turn. Mirrors the web upload path: the file goes through the same WorkspaceManager.write_file machinery (ClamAV scan, storage, workspace file id) and is referenced by file_id — no content inlining. - adapter: download message attachments (bounded by size/count) into the context - bot_backend.upload_workspace_files: RPC per file to the linker - linker upload_chat_file: resolve the conversation owner, write_file (AV scan), map virus/scan/size failures to stable error codes - handler: upload attachments, pass file_ids to the turn, report rejected files, and process file-only messages (previously dropped as empty) - BotChatRequest.file_ids -> enqueue_copilot_turn (already consumed by executor) Uploads use a unique path so re-sending the same filename is a fresh file, not a path conflict.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📜 Recent review details⏰ Context from checks skipped due to timeout. (11)
WalkthroughAdds inbound attachment support from Discord download through workspace upload and file ID propagation into streamed chat turns. ChangesInbound Attachment Flow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 PR Overlap DetectionThis check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early. 🔴 Merge Conflicts DetectedThe following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.
Summary: 1 conflict(s), 0 medium risk, 0 low risk (out of 1 PRs with file overlap) Auto-generated on push. Ignores: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #13427 +/- ##
==========================================
+ Coverage 74.71% 74.74% +0.02%
==========================================
Files 2537 2534 -3
Lines 192322 192638 +316
Branches 18928 18929 +1
==========================================
+ Hits 143690 143978 +288
- Misses 44500 44534 +34
+ Partials 4132 4126 -6
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: 4
🧹 Nitpick comments (3)
autogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.py (1)
1425-1470: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a regression test for the attachment-count cap.
Please add a test with
MAX_INBOUND_ATTACHMENTS + 1attachments asserting only the firstMAX_INBOUND_ATTACHMENTSare returned/read. This locks the cap behavior and prevents accidental regressions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.py` around lines 1425 - 1470, Add a regression test in TestExtractAttachments for the attachment-count cap: construct a message with MAX_INBOUND_ATTACHMENTS + 1 attachments and assert that _extract_attachments only reads/returns the first MAX_INBOUND_ATTACHMENTS items. Reuse the existing _discord_attachment helper and verify the extra attachment is ignored so the cap behavior stays enforced.autogpt_platform/backend/backend/platform_linking/chat_test.py (2)
264-297: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the remaining stable error-code branches.
upload_chat_file()also returnsscan_unavailableandupload_failed, andhandler.pymaps both to user-facing attachment-failure text. Adding one test for each branch would lock down the full contract this PR introduces.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/platform_linking/chat_test.py` around lines 264 - 297, The current test coverage in upload_chat_file only exercises virus_detected, rejected, and the unlinked-user NotFoundError path; add two more async tests in chat_test.py to cover the remaining stable error-code branches, scan_unavailable and upload_failed. Use the same upload_chat_file entry point and the existing _patches/AsyncMock setup to force each branch, then assert the returned result.error matches the expected code so the handler.py mapping stays locked down.
224-225: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove these imports to module scope.
The new suite pulls
WorkspaceUploadRequest,upload_chat_file, andVirusDetectedErrorinside helpers/tests, but backend Python files here require top-level imports unless they're lazy imports for heavy optional dependencies.As per coding guidelines, "Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like
openpyxl".Also applies to: 255-289
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/platform_linking/chat_test.py` around lines 224 - 225, Move the `WorkspaceUploadRequest`, `upload_chat_file`, and `VirusDetectedError` imports out of the helper/test bodies and into module scope in `chat_test.py`. Update the affected helpers/tests (including `_req` and the cases around the referenced test block) to use those top-level imports directly, keeping local imports only for true lazy optional dependencies.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@autogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.py`:
- Line 479: The Discord attachment handling in the adapter currently truncates
attachments at MAX_INBOUND_ATTACHMENTS without any notice, so add explicit
logging and a user-facing note when message.attachments is capped. Update the
attachment processing path in the Discord adapter’s message handling logic to
detect when more than MAX_INBOUND_ATTACHMENTS are present, log that extra files
were dropped, and propagate a clear downstream note through the existing
user/message flow so users know some attachments were skipped.
In `@autogpt_platform/backend/backend/copilot/bot/bot_backend.py`:
- Around line 339-350: In bot_backend.py, the upload loop in
self._client.upload_workspace_file currently lets one exception abort all
remaining attachments. Update the attachment-processing path in the bot backend
to catch per-file upload failures inside the for attachment in attachments loop,
and convert each failure into a WorkspaceUploadResult with error="upload_failed"
while still appending successful results. Keep the existing
upload_workspace_file call and result accumulation intact, but ensure the
handler continues processing later attachments after a single failure.
In `@autogpt_platform/backend/backend/copilot/bot/handler.py`:
- Around line 111-117: The handler in bot/handler.py should not enqueue a turn
when the message is file-only and _upload_attachments returns no successful
file_ids. Add a guard around the _enqueue_and_process flow in the same block
that checks message_text, file_ids, and the file-only case so it exits early
after the rejection notice instead of sending a blank prompt. Keep the existing
fallback text only for the path where there is at least one uploaded attachment
and leave _upload_attachments, _message_text, and _enqueue_and_process as the
key symbols to update.
In `@autogpt_platform/backend/backend/platform_linking/chat.py`:
- Around line 82-88: The upload path in chat.py currently lets request.filename
influence the workspace path via the path argument passed to
WorkspaceManager.write_file. Update the upload flow so the path uses only a
sanitized basename or an opaque generated identifier, and keep request.filename
only for user-facing metadata/display fields; use the existing write_file call
site in the upload handler to make this change.
---
Nitpick comments:
In
`@autogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.py`:
- Around line 1425-1470: Add a regression test in TestExtractAttachments for the
attachment-count cap: construct a message with MAX_INBOUND_ATTACHMENTS + 1
attachments and assert that _extract_attachments only reads/returns the first
MAX_INBOUND_ATTACHMENTS items. Reuse the existing _discord_attachment helper and
verify the extra attachment is ignored so the cap behavior stays enforced.
In `@autogpt_platform/backend/backend/platform_linking/chat_test.py`:
- Around line 264-297: The current test coverage in upload_chat_file only
exercises virus_detected, rejected, and the unlinked-user NotFoundError path;
add two more async tests in chat_test.py to cover the remaining stable
error-code branches, scan_unavailable and upload_failed. Use the same
upload_chat_file entry point and the existing _patches/AsyncMock setup to force
each branch, then assert the returned result.error matches the expected code so
the handler.py mapping stays locked down.
- Around line 224-225: Move the `WorkspaceUploadRequest`, `upload_chat_file`,
and `VirusDetectedError` imports out of the helper/test bodies and into module
scope in `chat_test.py`. Update the affected helpers/tests (including `_req` and
the cases around the referenced test block) to use those top-level imports
directly, keeping local imports only for true lazy optional dependencies.
🪄 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: e12b72b9-b163-4510-9b6d-8a728284c4e7
📒 Files selected for processing (11)
autogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/platform_linking/models.py
📜 Review details
⏰ Context from checks skipped due to timeout. (10)
- GitHub Check: check API types
- GitHub Check: Seer Code Review
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: type-check (3.11)
- GitHub Check: type-check (3.13)
- GitHub Check: end-to-end tests
- GitHub Check: Check PR Status
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
🧠 Learnings (14)
📚 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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.
Applied to files:
autogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_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/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.
Applied to files:
autogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.
Applied to files:
autogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-06-11T19:39:10.493Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 13337
File: autogpt_platform/backend/backend/copilot/graphiti/reranker.py:0-0
Timestamp: 2026-06-11T19:39:10.493Z
Learning: In the Significant-Gravitas/AutoGPT Python backend, when calling the OpenAI Python client `chat.completions.create`, construct the `messages` payload using the concrete typed-dict variants from `openai.types.chat` (e.g., `ChatCompletionSystemMessageParam`, `ChatCompletionUserMessageParam`, etc.) rather than trying to instantiate `ChatCompletionMessageParam` directly. `ChatCompletionMessageParam` is a `Union` alias and is not constructible, so `ChatCompletionMessageParam(role=..., content=...)` should fail type checking. Build each message element with the appropriate concrete typed dict and then annotate the resulting list as `list[ChatCompletionMessageParam]` (e.g., `messages: list[ChatCompletionMessageParam] = [ChatCompletionSystemMessageParam(...), ...]`).
Applied to files:
autogpt_platform/backend/backend/platform_linking/manager.pyautogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/platform_linking/models.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).
Applied to files:
autogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
📚 Learning: 2026-06-06T12:22:37.648Z
Learnt from: anvyle
Repo: Significant-Gravitas/AutoGPT PR: 13302
File: autogpt_platform/backend/backend/copilot/tools/agent_search.py:579-583
Timestamp: 2026-06-06T12:22:37.648Z
Learning: When writing LLM-facing instruction strings that trigger tool calls in this AutoGPT codebase, use the exact registered tool name `view_agent_output` (as defined in `backend/copilot/tools/agent_output.py` via its `name` property and exported via `TOOL_REGISTRY`). Do not reference the bare name `agent_output`, since it is not a valid tool name and will cause tool invocation to fail.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/adapters/base.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.pyautogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend_test.pyautogpt_platform/backend/backend/copilot/bot/bot_backend.pyautogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
🔇 Additional comments (4)
autogpt_platform/backend/backend/copilot/bot/adapters/discord/adapter.py (1)
24-24: LGTM!Also applies to: 58-61, 465-467, 469-478, 480-500
autogpt_platform/backend/backend/copilot/bot/adapters/base.py (1)
63-73: LGTM!Also applies to: 124-126
autogpt_platform/backend/backend/copilot/bot/bot_backend_test.py (1)
412-452: LGTM!autogpt_platform/backend/backend/copilot/bot/handler_test.py (1)
9-49: LGTM!Also applies to: 80-80, 403-428, 930-1008
…path safety, edge cases) - bot_backend: isolate each upload so one transport/owner-resolution failure becomes an upload_failed result instead of crashing the handler (Sentry HIGH) - handler: don't enqueue a blank turn when a file-only message's every upload was rejected — the user already got the rejection note - chat: sanitize the filename's path component so it can't traverse the workspace path; keep the raw name only for display - adapter: log when attachments are dropped by the per-message cap - tests: per-file failure resilience, count cap, scan_unavailable/upload_failed error branches, path-traversal sanitization, file-only all-rejected
… just the path write_file passes filename through to the storage backend (GCS blob names), so the raw filename could still leak ../ and path separators there. Use safe_name for the filename arg too, matching the web upload endpoint.
Batching several file-heavy messages can accumulate more than BotChatRequest.file_ids allows (max_length=20), which would fail validation and surface a vague error. Cap the drained file_ids to MAX_TURN_FILE_IDS (with a log) so the turn still runs.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/copilot/bot/handler.py (1)
135-140: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winHandle upload RPC failures before continuing the turn.
Line 135 can raise before the rejection-result path runs, which drops the whole message without user feedback; for text + attachment messages it also prevents the text turn from being enqueued.
Suggested fix
- results = await self._api.upload_workspace_files( - platform=ctx.platform, - platform_user_id=ctx.user_id, - platform_server_id=ctx.server_id, - attachments=ctx.attachments, - ) + try: + results = await self._api.upload_workspace_files( + platform=ctx.platform, + platform_user_id=ctx.user_id, + platform_server_id=ctx.server_id, + attachments=ctx.attachments, + ) + except Exception: + logger.exception( + "Failed to upload inbound attachment(s) for target %s", target_id + ) + await adapter.send_message( + target_id, + "I couldn't upload the attached file(s). Please try again in a moment.", + ) + return []🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/copilot/bot/handler.py` around lines 135 - 140, The upload step in the bot turn flow can fail before the rejection-result path is reached, which causes the message to be dropped and can block the text turn from being enqueued. Update the handler logic around self._api.upload_workspace_files in the bot handler so upload RPC errors are caught and converted into the existing rejection handling before any further processing continues. Keep the fix localized to the turn-processing path that uses ctx.attachments so text-only continuation still proceeds when appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@autogpt_platform/backend/backend/copilot/bot/handler.py`:
- Around line 135-140: The upload step in the bot turn flow can fail before the
rejection-result path is reached, which causes the message to be dropped and can
block the text turn from being enqueued. Update the handler logic around
self._api.upload_workspace_files in the bot handler so upload RPC errors are
caught and converted into the existing rejection handling before any further
processing continues. Keep the fix localized to the turn-processing path that
uses ctx.attachments so text-only continuation still proceeds when appropriate.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b63cad0c-3870-492f-8b74-d43b0c3661b9
📒 Files selected for processing (2)
autogpt_platform/backend/backend/copilot/bot/handler.pyautogpt_platform/backend/backend/copilot/bot/handler_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
- autogpt_platform/backend/backend/copilot/bot/handler_test.py
📜 Review details
⏰ Context from checks skipped due to timeout. (13)
- GitHub Check: check API types
- GitHub Check: type-check (3.11)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: type-check (3.13)
- GitHub Check: lint
- GitHub Check: type-check (3.12)
- GitHub Check: test (3.12)
- GitHub Check: Seer Code Review
- GitHub Check: Check PR Status
- GitHub Check: Analyze (typescript)
- GitHub Check: end-to-end tests
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
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/copilot/bot/handler.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/bot/handler.py
🧠 Learnings (14)
📚 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/copilot/bot/handler.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.py
📚 Learning: 2026-06-06T12:22:37.648Z
Learnt from: anvyle
Repo: Significant-Gravitas/AutoGPT PR: 13302
File: autogpt_platform/backend/backend/copilot/tools/agent_search.py:579-583
Timestamp: 2026-06-06T12:22:37.648Z
Learning: When writing LLM-facing instruction strings that trigger tool calls in this AutoGPT codebase, use the exact registered tool name `view_agent_output` (as defined in `backend/copilot/tools/agent_output.py` via its `name` property and exported via `TOOL_REGISTRY`). Do not reference the bare name `agent_output`, since it is not a valid tool name and will cause tool invocation to fail.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.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/copilot/bot/handler.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/copilot/bot/handler.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/copilot/bot/handler.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/copilot/bot/handler.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.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/copilot/bot/handler.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/copilot/bot/handler.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.py
📚 Learning: 2026-06-11T19:39:10.493Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 13337
File: autogpt_platform/backend/backend/copilot/graphiti/reranker.py:0-0
Timestamp: 2026-06-11T19:39:10.493Z
Learning: In the Significant-Gravitas/AutoGPT Python backend, when calling the OpenAI Python client `chat.completions.create`, construct the `messages` payload using the concrete typed-dict variants from `openai.types.chat` (e.g., `ChatCompletionSystemMessageParam`, `ChatCompletionUserMessageParam`, etc.) rather than trying to instantiate `ChatCompletionMessageParam` directly. `ChatCompletionMessageParam` is a `Union` alias and is not constructible, so `ChatCompletionMessageParam(role=..., content=...)` should fail type checking. Build each message element with the appropriate concrete typed dict and then annotate the resulting list as `list[ChatCompletionMessageParam]` (e.g., `messages: list[ChatCompletionMessageParam] = [ChatCompletionSystemMessageParam(...), ...]`).
Applied to files:
autogpt_platform/backend/backend/copilot/bot/handler.py
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/bot/handler.py (1)
45-49: LGTM!Also applies to: 77-84, 116-126, 141-145, 220-225, 239-248, 375-381, 470-480
…channel message has no usable uploads A channel @mention creates and subscribes a thread before uploads run. If the message is file-only and every upload fails, we early-return without a turn — which left the thread subscribed (and lingering for 7 days). Unsubscribe the just-created thread on that path.
…load path errors bot_backend already turns per-file failures into results, but a total failure of the upload call (e.g. RPC transport down) could still raise and drop the whole message — including the text turn. Catch it in _upload_attachments, notify the user, and return no file_ids so any text still goes through.
… message text The 'nothing to enqueue' guard checked message_text, which includes thread history on the first @-into an unowned thread — so a file-only message whose uploads all failed slipped past and enqueued a turn that answered only old context. Gate on actual new input (ctx.text + file_ids) instead, and pass a file-only nudge as the current-message body via _message_text.
…' upload NotFoundError subclasses ValueError, so the broad 'except ValueError' (meant for write_file's size/quota errors) would mislabel a missing user/workspace as a storage rejection. Re-raise NotFoundError before the ValueError catch so it surfaces as a linking error.
…attachments Files that couldn't be ingested were silently dropped — the model assumed it had read them. Now both stages are surfaced: adapter-stage skips (too large, failed download, over the per-message cap) ride on MessageContext, and combined with upload-stage rejections (virus/quota) they produce a user note plus a '[Note: these attachments are unavailable to you — do not claim to have read them]' line injected into the turn so AutoPilot won't hallucinate having read a dropped file.
…space_file Align the chat.py helper name with the RPC method, models, and bot_backend wrapper (all already 'workspace'), so the manager method delegates to a same-named function — mirroring the existing start_chat_turn pattern. Addresses review feedback on the chat<->workspace naming swap.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/platform_linking/chat.py (1)
81-95: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winReject
"."and".."after basename stripping.
rsplit("/", 1)[-1]still yields"."or".."for inputs like".","..", or"dir/..". That value then flows intouploads/{uuid}/{safe_name}, which reintroduces a special path segment into the stored workspace path.Suggested fix
+import os + ... - safe_name = request.filename.replace("\\", "/").rsplit("/", 1)[-1] or "file" + safe_name = os.path.basename(request.filename.replace("\\", "/")) + if safe_name in {"", ".", ".."}: + safe_name = "file"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@autogpt_platform/backend/backend/platform_linking/chat.py` around lines 81 - 95, The filename sanitization in chat.py still allows "." and ".." to survive basename stripping, which can reintroduce special path segments into the stored upload path. Update the safe_name handling in the upload flow around request.filename and WorkspaceManager.write_file so that after the existing basename extraction, "." and ".." are rejected and replaced with the fallback name (or otherwise invalidated) before building uploads/{uuid4().hex}/{safe_name}. Keep the fix localized to the request.filename sanitization logic used for stored and uploaded filenames.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@autogpt_platform/backend/backend/platform_linking/chat.py`:
- Around line 81-95: The filename sanitization in chat.py still allows "." and
".." to survive basename stripping, which can reintroduce special path segments
into the stored upload path. Update the safe_name handling in the upload flow
around request.filename and WorkspaceManager.write_file so that after the
existing basename extraction, "." and ".." are rejected and replaced with the
fallback name (or otherwise invalidated) before building
uploads/{uuid4().hex}/{safe_name}. Keep the fix localized to the
request.filename sanitization logic used for stored and uploaded filenames.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2d1cb779-a9bb-4dc5-b089-264c12da5f23
📒 Files selected for processing (3)
autogpt_platform/backend/backend/platform_linking/chat.pyautogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/manager.py
🚧 Files skipped from review as they are similar to previous changes (1)
- autogpt_platform/backend/backend/platform_linking/manager.py
📜 Review details
⏰ Context from checks skipped due to timeout. (12)
- GitHub Check: check API types
- GitHub Check: Seer Code Review
- GitHub Check: type-check (3.11)
- GitHub Check: type-check (3.12)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: type-check (3.13)
- GitHub Check: Check PR Status
- GitHub Check: test (3.11)
- GitHub Check: Analyze (typescript)
- GitHub Check: Analyze (python)
- GitHub Check: end-to-end tests
🧰 Additional context used
📓 Path-based instructions (3)
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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
autogpt_platform/{backend,autogpt_libs}/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.py
🧠 Learnings (11)
📚 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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
📚 Learning: 2026-05-23T05:29:43.085Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13200
File: autogpt_platform/backend/backend/executor/scheduler.py:590-593
Timestamp: 2026-05-23T05:29:43.085Z
Learning: When reviewing Python code that uses Pydantic discriminated/tagged unions (e.g., `Annotated[Union[...], Field(discriminator="kind")]`), recognize that using `isinstance(x, SomeVariantInfo)` to narrow the union is an intentional and correct runtime guard and should also enable static type narrowing in tools like Pyright. Do not recommend replacing such `isinstance`-based narrowing with `cast(...)` when the check already proves the variant at runtime.
Applied to files:
autogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.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/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.
Applied to files:
autogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
📚 Learning: 2026-05-26T14:24:34.866Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 13217
File: autogpt_platform/backend/backend/api/features/search/service.py:137-137
Timestamp: 2026-05-26T14:24:34.866Z
Learning: In the Significant-Gravitas/AutoGPT backend, treat `user_id` (an opaque UUID used only for correlation/tracing) as non-PII. Do not flag direct logging of `user_id` in `logger.warning`/`logger.info` statements as a PII exposure issue, as the established convention is to log `user_id` for tracing while reserving PII for fields like email or display name.
Applied to files:
autogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
📚 Learning: 2026-06-11T19:39:10.493Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 13337
File: autogpt_platform/backend/backend/copilot/graphiti/reranker.py:0-0
Timestamp: 2026-06-11T19:39:10.493Z
Learning: In the Significant-Gravitas/AutoGPT Python backend, when calling the OpenAI Python client `chat.completions.create`, construct the `messages` payload using the concrete typed-dict variants from `openai.types.chat` (e.g., `ChatCompletionSystemMessageParam`, `ChatCompletionUserMessageParam`, etc.) rather than trying to instantiate `ChatCompletionMessageParam` directly. `ChatCompletionMessageParam` is a `Union` alias and is not constructible, so `ChatCompletionMessageParam(role=..., content=...)` should fail type checking. Build each message element with the appropriate concrete typed dict and then annotate the resulting list as `list[ChatCompletionMessageParam]` (e.g., `messages: list[ChatCompletionMessageParam] = [ChatCompletionSystemMessageParam(...), ...]`).
Applied to files:
autogpt_platform/backend/backend/platform_linking/chat_test.pyautogpt_platform/backend/backend/platform_linking/chat.py
🔇 Additional comments (2)
autogpt_platform/backend/backend/platform_linking/chat.py (1)
98-114: LGTM!Also applies to: 175-175
autogpt_platform/backend/backend/platform_linking/chat_test.py (1)
11-11: LGTM!Also applies to: 223-325
Basename stripping still let '.'/'..' through (e.g. filename '..' or 'dir/..'), reintroducing a special segment into uploads/<uuid>/<name> that resolves back out of the unique subdir. Use os.path.basename and fall back to 'file' for ''/'.'/'..'. Addresses CodeRabbit review.
… can read them (Significant-Gravitas#13464) ## Why The Discord CoPilot bot's file-upload feature ([Significant-Gravitas#13427](Significant-Gravitas#13427)) uploaded attachments with a **session-less** `WorkspaceManager`, storing them at `uploads/<uuid>/<name>`. The web UI (`api/features/workspace/routes.py`) uploads the same files **session-scoped** to `/sessions/<session_id>/<name>`. The copilot executor reads a turn's attachments through a **session-scoped** manager, so it never found the bot's file. Symptom in dev: AutoPilot saw the filename but couldn't read the contents ("saved but the path doesn't exist") — while an identical file uploaded via the web UI read fine (`Read test.txt from workspace:/sessions/<id>/test.txt`). Follow-up to Significant-Gravitas#13427, which shipped the upload pipeline but stored files session-less. ## What Store bot-uploaded attachments the same way the web endpoint does — session-scoped — so AutoPilot reads them identically. ## How - **`upload_workspace_file`** now uses `WorkspaceManager(owner, workspace, session_id)` + `write_file(content, filename, …)` — the exact documented pattern the web upload endpoint uses (`workspace/routes.py`). Dropped the ad-hoc `uploads/<uuid>/` path (and the `os.path` usage — workspace paths are POSIX). - The bot resolves its session only when a turn *starts* (after the upload), so a new **`ensure_chat_session`** RPC lets the handler resolve/create the session **before** uploading, thread it into the upload, and cache it so the turn reuses the same session. `start_chat_turn` shares the same `_resolve_or_create_session` helper. - Plumbing: `session_id` on `WorkspaceUploadRequest`; `ensure_chat_session` exposed on the manager + client; `bot_backend.ensure_session`; `sessions.get_session`. No new storage/DB API — only the documented `WorkspaceManager(session_id)` + `write_file`. ## Testing - `poetry run pytest backend/platform_linking/chat_test.py backend/copilot/bot/handler_test.py backend/copilot/bot/bot_backend_test.py` — green (incl. new tests: session-scoped upload, `ensure_chat_session` reuse/create/unlinked, handler ensure-session flow). - `ruff` / `black` / `isort` clean.
…e, not a host path (Significant-Gravitas#13470) ## Why Discord-bot file attachments upload correctly — AV-scanned (ClamAV via `WorkspaceManager.write_file`), stored session-scoped at `/sessions/<session_id>/<name>` ([Significant-Gravitas#13427](Significant-Gravitas#13427) + [Significant-Gravitas#13464](Significant-Gravitas#13464) + infra GCS perms) — but AutoPilot **still can't read them**. From the model's own trace in dev: > "The system says it was saved to `/tmp/copilot-804ca4e7-…/test.txt` but it doesn't exist." Root cause (`sdk/service.py::_prepare_file_attachments`): non-image attachments are written to the **executor host's** `sdk_cwd` and the model is told *"saved to `<host path>`. Use the Read tool."* Two problems: 1. **In E2B mode (the prod/dev default — `use_e2b_sandbox=True`)** the model's file tools operate on the **remote E2B sandbox filesystem** (`e2b_file_tools.py` deliberately routes non-tool-result paths to `sandbox.files.read`). The host-side `sdk_cwd` file doesn't exist there. 2. The built-in `Read` tool the hint names is **disallowed** in every mode (`SDK_DISALLOWED_TOOLS`). So the model is pointed at a path it cannot see, with a tool it cannot use. Web-uploaded files work because the model reads them via `read_workspace_file` (observed in production: `Read test.txt from workspace:/sessions/<id>/test.txt`) — which resolves by `file_id` from the workspace, independent of any local disk. ## What Point the model at `read_workspace_file` with the `file_id` for non-image attachments, instead of a host path + the disallowed `Read` tool. ## How In `_prepare_file_attachments`: - Non-image file descriptions now carry `file_id: <id>` instead of the host path. - The hint instructs `read_workspace_file` (with `save_to_path` mentioned for copying into the working directory) instead of "Use the Read tool". - The `sdk_cwd` copy is **still written**, preserving the working-directory affordance for non-E2B tooling — the model just isn't pointed at it. Blast radius: this function only runs for turns with attached `file_ids`. Web attachment turns get the same instruction that already matches their observed working behaviour; everything else is untouched. Completes the bot file-upload chain: Significant-Gravitas#13427 (upload pipeline) → infra#348 (GCS write perms) → Significant-Gravitas#13464 (session-scoped storage) → **this** (readable reference). ## Testing - TDD: updated/added tests in `sdk/service_test.py::TestPrepareFileAttachments` pinning the new hint (file_id present, host path absent, `read_workspace_file` named, no "Use the Read tool") — failed before the fix, 7/7 pass after. - No-regression check: full `service_test.py` run compared against clean `dev` — identical pre-existing failure set (env-dependent tests under `--noconftest`), zero new failures. - `ruff` / `black` / `isort` clean.
Why / What / How
Why: The Discord copilot bot could send files (workspace artifacts → Discord) but couldn't receive them. Users wanted to attach a file to a message and have AutoPilot read it — the natural counterpart to the download path, and a common ask once people started using the bot.
What: Attach a file to a Discord message (DM, @mention, or thread) and AutoPilot reads it during the turn. Works for images, PDFs, text, etc., and a file-only message (no text) is handled too.
How: It reuses the exact web-upload machinery — no new upload/scan/storage logic:
bot_backend.upload_workspace_filessends each file over RPC to the linker.upload_chat_fileresolves the conversation owner (same as a chat turn) and callsWorkspaceManager.write_file()— the same function the webPOST /files/uploadendpoint runs: ClamAV scan → quota/size checks → storage → a workspacefile_id. Virus/scan/size failures map to stable error codes.file_idsto the turn (BotChatRequest.file_ids → enqueue_copilot_turn, already consumed by the executor's attachment resolver), and tells the user about any rejected files.The file is referenced by
file_id, not inlined into the message — exactly how the web copilot does it. Storage backend is config-driven (MEDIA_GCS_BUCKET_NAME), so dev/prod write to the real GCS bucket automatically. Uploads are taggedorigin: "user-upload", so a Discord-uploaded file also appears in the user's Files/Artifacts page in their workspace.Uploads use a unique sub-path (
uploads/<uuid>/<filename>) so re-sending a file with the same name is a fresh file rather than a path conflict.Changes 🏗️
adapters/base.py:InboundAttachmentmodel +MessageContext.attachments.adapter.py:_extract_attachments— downloads attachment bytes (skips oversized / failed / over the count cap).bot_backend.py:upload_workspace_files(RPC per file → linker),stream_chatnow threadsfile_ids.platform_linking/chat.py:upload_chat_file(owner resolution +write_file+ error mapping);start_chat_turnpassesfile_idstoenqueue_copilot_turn.platform_linking/manager.py:@expose upload_workspace_file+ client method.platform_linking/models.py:WorkspaceUploadRequest/WorkspaceUploadResult+BotChatRequest.file_ids.handler.py: upload attachments →file_ids; surface rejected files; process file-only messages.Checklist 📋
For code changes:
test.txtin Discord, AutoPilot read the file's contents and replied accordingly; verified the file is stored in the workspace via the samewrite_file/ClamAV path the web usesFor configuration changes:
docker-compose.override.ymlis untracked)