More descriptive name since the tools are specifically for workspace
file operations (list, read, write, delete).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Add `InvalidInputError` for validation errors (search term too long,
invalid pagination) - returns 400 instead of 500
- Remove redundant try/catch blocks in library routes - global exception
handlers already handle `ValueError`→400 and `NotFoundError`→404
- Aggregate embedding backfill errors and log once at the end instead of
per content type to prevent Sentry issue spam
## Test plan
- [x] Verify validation errors (search term >100 chars) return 400 Bad
Request
- [x] Verify NotFoundError still returns 404
- [x] Verify embedding errors are logged once at the end with aggregated
counts
Fixes AUTOGPT-SERVER-7K5, BUILDER-6NC
---------
Co-authored-by: Swifty <craigswift13@gmail.com>
When downloading files from URLs without file extensions (like Unsplash
images), detect the extension from the Content-Type response header.
For example, `image/jpeg` becomes `.jpg`.
Also updates FileStoreBlock description to clarify behavior in both
CoPilot (saves to workspace) and graphs (outputs data URI).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change FileStoreBlock to use `for_block_output` return format instead
of `for_local_processing`. This makes files automatically save to the
user's workspace when running in CoPilot, while maintaining backward
compatibility for graph executions (returns data URI).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prevents session path prefix collisions where "/sessions/abc" could
incorrectly match "/sessions/abc123" when using startswith filtering.
Now uses "/sessions/abc/" to ensure exact session boundary matching.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Consolidate duplicated GCS code from workspace_storage.py and
cloud_storage.py into a new gcs_utils.py module. Extracts path parsing,
fresh-session downloads, and signed URL generation into reusable
utilities while preserving existing authorization and logging behavior.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Log exceptions with context (file.id, storagePath) when signed URL
generation fails, and wrap fallback streaming in try/except to avoid
silently swallowing errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add _get_effective_path() helper to deduplicate path resolution logic
between list_files and get_file_count methods
- Add broader exception handling in write_file to clean up storage files
when create_workspace_file fails with non-UniqueViolationError errors
- Fix test mock to include required graph_version attribute
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Return workspace:// format instead of calling get_download_url so
the frontend urlTransform can properly add the proxy prefix.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes test failures after changing ExecutionContext to use
graph_exec.graph_version instead of the parameter.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The RFC5987 encoding path was using the original filename instead of
the sanitized version, bypassing CR/LF/null byte removal.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The description was hardcoded to "100MB" but the actual limit is
configurable via Config().max_file_size_mb.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The graph_version parameter can be None, but graph_exec.graph_version
always has the concrete version from create_graph_execution.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same fix as FileStoreBlock - for_block_output can return workspace://
in CoPilot, for_external_api guarantees data URI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
for_block_output can return workspace:// in CoPilot, violating the
"Produce Base64 Output" promise. for_external_api guarantees data URI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Wrap overwrite retry in try/except to clean up storage file if retry fails
- Pass source_session_id to write_file in store_media_file for proper tracking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, write_file() generated a UUID for storage paths and let Prisma
auto-generate a separate UUID for the database record. This caused download
URLs to return 404 because the storage layer extracted the wrong ID.
Now the same UUID is used for both, fixing the download URL issue.
Also consolidates MAX_FILE_SIZE_BYTES into Config.max_file_size_mb setting
for consistent configuration across file.py, workspace.py, and workspace_tools.py.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Disable automatic onboarding redirects on signup/login while keeping the
checklist/wallet functional. Users now receive $5 (500 credits) on their
first visit to /copilot.
### Changes 🏗️
- **Frontend**: `shouldShowOnboarding()` now returns `false`, disabling
auto-redirects to `/onboarding`
- **Backend**: Added `VISIT_COPILOT` onboarding step with 500 credit
($5) reward
- **Frontend**: Copilot page automatically completes `VISIT_COPILOT`
step on mount
- **Database**: Migration to add `VISIT_COPILOT` to `OnboardingStep`
enum
NOTE: /onboarding/1-welcome -> /library now as shouldShowOnboardin is
always false
Users land directly on `/copilot` after signup/login and receive $5
invisibly (not shown in checklist UI).
### 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] New user signup (email/password) → lands on `/copilot`, wallet
shows 500 credits
- [x] Verified credits are only granted once (idempotent via onboarding
reward mechanism)
- [x] Existing user login (already granted flag set) → lands on
`/copilot`, no duplicate credits
- [x] Checklist/wallet remains functional
#### For configuration changes:
- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)
No configuration changes required.
---
OPEN-2967
🤖 Generated with [Claude Code](https://claude.ai/code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Introduces a new onboarding step and adjusts onboarding flow.
>
> - Adds `VISIT_COPILOT` onboarding step (+500 credits) with DB enum
migration and API/type updates
> - Copilot page auto-completes `VISIT_COPILOT` on mount to grant the
welcome bonus
> - Changes `/onboarding/enabled` to require user context and return
`false` when `CHAT` feature is enabled (skips legacy onboarding)
> - Wallet now refreshes credits on any onboarding `step_completed`
notification; confetti limited to visible tasks
> - Test flows updated to accept redirects to `copilot`/`library` and
verify authenticated state
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ec5a5a4dfd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Nicholas Tindle <ntindle@users.noreply.github.com>
- Fix invalid base64 padding in FalVideoGeneratorBlock test mock
- Add data URI mock to BannerbearTextOverlayBlock to avoid HTTP 404
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Block tests were failing because store_media_file tried to download
mock URLs that don't exist. Changed test mocks to return data URIs
instead, and updated test_output to use lambda validators that accept
either workspace:// refs or data: URIs.
Affected blocks:
- AIImageCustomizerBlock
- FluxKontextBlock
- FalVideoGeneratorBlock
- AIShortformVideoCreatorBlock
- AIProductAdvertCreatorBlock
- AIScreenshotToVideoBlock
- TalkingHeadBlock
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This function was defined but never imported or called anywhere.
Callers can use get_workspace_file_by_path() directly and check
if the result is None.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The isWorkspaceDownloadRequest function was checking for path.length >= 4,
but the pattern api/workspace/files/{id}/download has 5 segments. This
allowed malformed requests missing the file_id to be incorrectly routed
through the binary download handler.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update manual sections in Add Audio To Video and Loop Video blocks
to reflect that output format is now automatically determined
(workspace:// in CoPilot, data URIs in graph executions) instead
of requiring a manual output_return_type parameter.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Video workspace references (video_url, video_out, etc.) were being
incorrectly rendered as <img> tags. Added video keyword check to
exclude them before defaulting to image rendering.
TODO: Replace keyword matching with MIME type encoded in workspace ref
(e.g., workspace://abc123#video/mp4) for robust media type detection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Asserts can be stripped with -O flag. Use explicit ValueError for
graph_exec_id validation to ensure consistent error handling.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The output_return_type field was defined in Input but never wired up to
store_media_file. The code always used for_block_output. Removed the
misleading field from LoopVideoBlock and AddAudioToVideoBlock.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Wrap create_workspace_file in try/except for UniqueViolationError
- On conflict with overwrite=True, delete existing and retry
- Remove unused file_exists method (dead code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Concurrent first-time requests for the same user could both find no workspace
and attempt to create, causing unique constraint violation. Using upsert
handles this atomically.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Defer WorkspaceManager import to inside store_media_file() to break circular import
- Remove deprecated return_content and save_to_workspace parameters (no callers)
- Make return_format a required parameter
- Update tests to use return_format
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Defer sanitize_filename import to inside _build_file_path() to break
the circular import chain: workspace_storage → file → workspace → data → blocks → file
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- routes.py: Sanitize filename in Content-Disposition header to prevent
header injection (RFC5987 encoding for non-ASCII)
- http.py: Replace assert with explicit ValueError for graph_exec_id check
(asserts can be stripped with -O)
- workspace.py: Remove unused functions (get_workspace_by_id,
hard_delete_workspace_file, update_workspace_file)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add shutdown_workspace_storage() to properly close GCS aiohttp sessions
during application shutdown. Follows the same pattern as cloud_storage.py.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Sanitize filenames using sanitize_filename() before building paths
- Add is_relative_to() check after path resolution for defense in depth
- Replace string comparison with is_relative_to() in _parse_storage_path()
for robust path containment on case-insensitive filesystems
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use model_copy() instead of mutating shared ExecutionContext to prevent
race conditions when multiple nodes execute concurrently. Each node now
gets its own isolated copy with correct node_id and node_exec_id values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The empty string fallback was dead code since store_media_file() validates
graph_exec_id before these lines execute. Replace with explicit asserts
for clearer failure if assumptions are ever violated.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>