feat(files): create folder in File Manager (#37) - #898
Merged
Conversation
Adds a "New Folder" capability to the per-agent File Manager (Files tab).
Three surfaces, mirroring the existing update/delete file path:
- Agent-server: POST /api/files/mkdir — workspace-confined, rejects
edit-protected paths (.trinity/.git/etc.), 409 if target exists,
creates intermediate parents.
- Backend: POST /api/agents/{name}/files/mkdir → create_agent_folder_logic
(access check + _is_user_writable_path deny-list + container-running
guard, proxies to agent-server). Propagates 403/409 from the agent.
- Frontend: New Folder button + modal in FilesPanel.vue; creates inside
the selected directory when one is selected, else workspace root;
supports nested paths via "/". New createAgentFolder store action.
Tests: TestCreateFolder in tests/test_agent_files.py (create+list,
duplicate→409, protected→403, unknown agent→404, auth→401).
Docs: requirements.md §13.1 + architecture.md endpoint tables.
Verification: all changed Python py_compile clean; frontend vite build
clean; new tests collect (5). Integration tests require a live stack
(self-skip on 503) and run in CI.
Related to #37
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion)
CI CodeQL flagged "uncontrolled data used in path expression" (CWE-022)
on create_folder. The str(requested_path).startswith(str(allowed_base))
guard is not a recognized CodeQL barrier and is genuinely weak: it has
a sibling-prefix bypass ("/home/developer-x".startswith("/home/developer")
is True).
Replace with Path.is_relative_to() against a resolved base. .resolve()
collapses any "../" before the check, so this is both a correct
containment check and the path-traversal barrier. Verified against
traversal, sibling-prefix, and absolute-escape cases.
Scope: only the new create_folder endpoint. update_file/delete_file in
the same file use the same legacy startswith pattern but are unchanged
here (not in #37's scope, not flagged on this PR's diff) — noted for a
separate follow-up.
Related to #37
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vybe
approved these changes
May 20, 2026
vybe
left a comment
Contributor
There was a problem hiding this comment.
Approving. Clean implementation; nice CodeQL response in commit 2 (the sibling-prefix bypass fix shows you understood the finding, not just silenced it).
Two non-blocking follow-ups:
- Update
docs/memory/feature-flows/file-browser.mdwith the mkdir endpoint alongside the existing delete/update API sections — keeps the flow doc as the single source of truth for the file-manager surface. - Open a follow-up
type-bug priority-p2issue to apply the samePath.resolve()+is_relative_to()containment toupdate_fileanddelete_fileindocker/base-image/agent_server/routers/files.py. The PR body acknowledges this as intentional scope-limitation; let's track the debt explicitly so it doesn't get lost.
Optional polish: two regression tests to lock in the containment fix:
- sibling-prefix:
POST .../mkdir {"path": "/home/developer-x/foo"}→ 403 - traversal:
POST .../mkdir {"path": "/home/developer/../etc/foo"}→ 403
9 tasks
5 tasks
vybe
pushed a commit
that referenced
this pull request
May 25, 2026
Several integration tests were asserting pre-change behaviour: - tests/test_agent_timeout.py — default execution_timeout_seconds was raised from 900 → 3600 in #665. Rename test_get_timeout_default_is_900 and update the expected value. - tests/integration/test_monitoring_service.py — /health-specific override in monitoring_service.py treats ReadTimeout, ReadError, and PoolTimeout as agent liveness signals and DOES record_failure() on the per-agent circuit. Rename the three `_does_not_record_` tests to `_records_failure` and flip the assertion from 0 → 1. The docstrings now point at the explicit /health-specific handlers rather than the unrelated TRANSIENT_TRANSPORT_EXCEPTIONS contract. - tests/test_agent_files.py — POST /api/agents/{name}/files/mkdir (#898) is the first test in the file that requires the agent container to be visible to the backend Docker SDK before the request, not just the row marked status=running. Add a small _wait_for_container_ready helper that polls /files until the container is visible (probe returns anything other than 404) and call it in the two TestCreateFolder tests that actually hit the endpoint (test_create_folder_and_list, test_create_folder_duplicate_returns_409). The other three tests in the class fail-fast before the container check (deny-list 403, nonexistent-agent 404, unauth 401) and don't need the wait. No production code changes — tests only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #37 — create new directories in the agent workspace via the File Manager UI (per-agent Files tab).
Follows the existing three-surface file-op pattern (mirrors
update/deletefile logic exactly):POST /api/files/mkdir— workspace-confined to/home/developer, rejects edit-protected paths (.trinity/.git/credential files, parent-walked),409if target exists, creates intermediate parentsPOST /api/agents/{name}/files/mkdir→create_agent_folder_logic— access check +_is_user_writable_pathdeny-list (AISEC-C2/#590 boundary) + container-running guard, proxies to agent-server, propagates403/409FilesPanel.vue; creates inside the selected directory when one is selected, else workspace root; nested paths via/. NewcreateAgentFolderstore actionScope notes
FilesPanel.vueis THE file manager (mounted inAgentDetail);views/FileManager.vueis unused/unrouted (requirements.md §13.2 deprecated) — intentionally untouched.CreateFolderRequestdefined inline in the router, following the existing localFileUpdateRequestconvention inagent_files.py.Tests
tests/test_agent_files.py::TestCreateFolder— create+list, duplicate→409, protected-path→403, unknown-agent→404, unauth→401. Mirrors the existing integration-test style (self-skips on503when stack is down).Verification
py_compileclean (agent-server, backend service/router/init, test)vite buildclean (AgentDetail bundle includes FilesPanel)Docs
requirements.md§13.1 — feature listed, dated, issue taggedarchitecture.md— Agents endpoint table + agent-server internal API listRelated to #37
🤖 Generated with Claude Code