feat(sandbox): THREAD_SANDBOX_START creates the chat when threadId is omitted; --agent becomes --project
Merged
PE
8h ago+190+190+012345678901234567890123456789-131-131-012345678901234567890123456789
Includes and supersedes #8432 (the decocms-studio skill section for sandboxes).
What is this contribution about?
THREAD_SANDBOX_START required a threadId, so an agent calling the tool directly had to create a chat first with COLLECTION_THREADS_CREATE, and deco sandbox new made two calls plus an org lookup. Now:
threadIdis optional. Without it the tool creates a sandbox chat (runtime: "sandbox") onvirtualMcpId, or the org's default agent, then bindsrepositoryif given and starts the sandbox. The output gainsthreadId. Passing boththreadIdandvirtualMcpIdis rejected. Asking for a repository on a project fails before any chat is created, so the refusal leaves no orphan chat.deco sandbox newis one tool call, and no longer looks up the org id through/api/_me/organizations(that helper is removed).--agentis now--project. "Project" is what people call an agent that has its own repository. This breaks--agent, which shipped indecocms4.564.0 a few hours ago.- Skill: adds the sandbox section from #8432, uses
--project, tells agents to use the commands rather than callingTHREAD_SANDBOX_START(execandchatreach routes no tool covers), and defines project in the vocabulary. Plugin 1.1.0.
The chat is created through COLLECTION_THREADS_CREATE.execute, so the caller also needs that tool's permission, and the create-time prewarm and chat_started event behave as for any other new chat.
How did you verify your code works?
apps/api/src/tools/sandbox/thread-start.test.ts: without athreadIdthe handler creates the chat on the default agent withruntime: "sandbox", starts the sandbox on it, and returnsthreadId; a repository with a projectvirtualMcpIdis refused before the create is called; the input schema rejectsthreadId+virtualMcpId. The create and start are spied, not run.apps/api/src/cli/commands/sandbox.test.ts:new,new --repoandnew --projecteach make exactly oneTHREAD_SANDBOX_STARTcall with the right body;--repowith--projectexits 1 without a request.registry.test.ts,decopilot/routes.test.tspass.oxlint,knip,biome format, andtsc --noEmitfor api, shared and web are clean.- Not run: an end-to-end boot against a running Studio.
How to Test
deco sandbox new --org <slug> --repo <owner>/<name>→ prints a chat id; the chat shows in Studio with the repository loaded.deco sandbox exec <id> --org <slug> -- git remote get-url origin→ that repository.deco tools call THREAD_SANDBOX_START --org <slug> -d '{"wait":false}'→ returns a newthreadId.deco sandbox new --org <slug> --project <id> --repo <owner>/<name>→ exits 1.
Review Checklist
- PR title is clear and descriptive
- Changes are tested and working
- Documentation is updated (if needed)
- No breaking changes —
--agentis renamed to--project
Summary by cubic
THREAD_SANDBOX_START now creates a sandbox chat when threadId is omitted, so agents and deco sandbox new no longer need a separate create call. The CLI flag --agent is renamed to --project — a breaking change shipped in decocms 4.564.0 hours ago.
Behavior changes
- Omitted
threadIdcreates a chat withruntime: "sandbox"onvirtualMcpId(or the org's default agent), bindsrepositoryif given, and starts the sandbox; the output now includesthreadId. - Passing both
threadIdandvirtualMcpIdis rejected, and a repository request on a project fails before any chat is created, so the refusal leaves no orphan chat. deco sandbox newmakes one tool call and no longer resolves the org id via/api/_me/organizations; that helper was removed.- The tool creates the chat through
COLLECTION_THREADS_CREATE, so the caller needs that tool's permission. - The decocms-studio skill (now 1.1.1) teaches agents to use
deco sandbox new|exec|chatinstead of callingTHREAD_SANDBOX_STARTdirectly, and defines project in its vocabulary.
Migration
- Replace
--agent <id>with--project <id>ondeco sandbox new.