refactor(sandbox): tidy up service, API, and test code; truncate shell output - #173
Draft
Simpleyyt wants to merge 2 commits into
Draft
refactor(sandbox): tidy up service, API, and test code; truncate shell output#173Simpleyyt wants to merge 2 commits into
Simpleyyt wants to merge 2 commits into
Conversation
- shell service: replace untyped session dicts with a ShellSession dataclass, add _get_session/_terminate_process helpers, remove dead ShellTask model and shell_tasks store, fix console-variable shadowing in view_shell, drop unused imports, and guard the output reader against writing into a replaced process - file service: remove unused imports, rename misleading *_async inner functions, deduplicate exception re-raising, simplify ensure_file - supervisor service: connect to supervisord lazily (ServerProxy connects on first RPC call) so importing the app no longer requires a running supervisord; deduplicate activate/extend into _schedule_shutdown; fix the import-time timer to fall back to threading.Timer only when no event loop is running; unify auto_expand/auto_extend naming; centralize timer cancellation - API layer: move TimeoutRequest into app/schemas/supervisor.py, serialize all supervisor responses with model_dump for consistency, add response_model to the upload route, drop redundant empty-session-ID checks handled by the service layer, use os.path.basename for download filename - main.py: remove duplicated log-level setup; config.py: migrate deprecated class Config to SettingsConfigDict - tests: upload to /tmp instead of the mounted repo directory (removes the accidentally committed sandbox/resource/test_upload_unique.txt), clean up unused imports, reuse the client fixture in conftest Co-authored-by: Simpleyyt <simpleyyt@gmail.com>
- view_shell / get_console_records now truncate output to max_length (default 10000 chars), keeping the most recent tail with a '(truncated)' prefix; exec_command results get the same default - add optional max_length to the shell/view request, mirroring file/read - cap stored session output at 1MB per session (with 64KB slack to avoid copying on every append), discarding the oldest output Co-authored-by: Simpleyyt <simpleyyt@gmail.com>
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
Cleans up the sandbox service without changing its API contract (verified against the routes the backend's
docker_sandbox.pycalls), and adds output truncation to the shell API.Shell output truncation (new)
shell/viewand console records now truncate output tomax_length(default 10000 chars), keeping the most recent tail with a(truncated)prefix;shell/execresults get the same defaultmax_lengthfield on theshell/viewrequest, mirroringfile/readapp/services/shell.pyDict[str, Dict[str, Any]]session store with a typedShellSessiondataclass_get_session(deduplicates 5 copies of the "session not found" check) and_terminate_process(deduplicates graceful-terminate/force-kill logic used by bothexec_commandandkill_process)ShellTaskmodel,shell_tasksstore, unusedsubprocess/Tupleimports, and an unusedget_console_recordscall inexec_commandconsoleparameter being shadowed by a local variable inview_shellexec_commandcontrol flow (single create-process path, no nested try/except swallowing) and guard the output reader so a replaced process can't write into the new command's outputapp/services/supervisor.pyServerProxyconnects on first RPC call), so importing the app no longer requires a running supervisordactivate_timeout/extend_timeoutinto a shared_schedule_shutdownget_running_loop()and falls back tothreading.Timerwhen no loop is running_cancel_timer, remove bareexcept:blocks, unifyauto_expand/auto_extendnamingapp/services/file.pysubprocess,mimetypes,BinaryIO), rename misleading*_asyncinner functions (they are sync functions run viaasyncio.to_thread), simplifyensure_fileand exception re-raisingAPI layer
TimeoutRequestout of the route file intoapp/schemas/supervisor.pymodel_dump()for consistency; addresponse_model=Responseto the upload routeos.path.basenamefor the download filenameMisc
main.py: remove duplicated log-level setupconfig.py: migrate deprecatedclass ConfigtoSettingsConfigDict/tmpinstead of the mounted repo directory — this removessandbox/resource/test_upload_unique.txt, which a previous test run had accidentally committed into the repo; clean up unused imports and reuse theclientfixtureTesting
cd sandbox && uv run pytest— 3 passed against the running dev sandbox containershell/exec(fast + long-running + session reuse),shell/view(with console records),shell/wait(timeout 400),shell/write(interactivecat),shell/kill,file/read|write|replace|search|find|upload|download(incl. sudo read, invalid regex 400, missing file 404),supervisor/status,supervisor/timeout/activate|extend|cancel|statusseqoutput returns 10011 chars starting with(truncated)and keeping the last line;max_length=50honored onshell/view; console records truncated too; 5MByesoutput stored at ~1MB in memory with the tail (DONE_MARKER) preservedlocalhost:5173; the mock-LLM-driven agent flow completed — plan, notify message, web search, threefile/writes,shell/exec(ls -a), browser navigation, completion with attachments. Sandbox access logs show allfile/read,file/write,shell/exec,shell/viewcalls returning 200, and the files exist in the sandbox with the expected content.e2e_agent_flow_shell_file_tools.mp4
Shell tool view showing ls -a output with the created files
Completed task with attachment cards
To show artifacts inline, enable in settings.