calm-artist-7206
02/14/2026, 5:10 PMnutritious-balloon-65098
02/14/2026, 5:11 PMnutritious-balloon-65098
02/14/2026, 5:38 PMConfirmStopModal (frontend/src/components/features/conversation-panel/confirm-stop-modal.tsx)
2. On confirm, calls useUnifiedPauseConversationSandbox() (frontend/src/hooks/mutation/use-unified-stop-conversation.ts)
That “unified” hook routes to either V1 pause-sandbox or V0 stop-conversation depending on the conversation version.
---
## 2) If your conversation is V1 (Cloud): it pauses the sandbox (it does not delete it)
### The exact steps
For V1 conversations, useUnifiedPauseConversationSandbox ultimately does:
1. Fetch conversation data to find the sandbox_id via:
- V1ConversationService.batchGetAppConversations([conversationId])
- (in frontend/src/hooks/mutation/conversation-mutation-utils.ts, function fetchV1ConversationData)
2. Call:
- POST /api/v1/sandboxes/{sandbox_id}/pause
- via SandboxService.pauseSandbox() (frontend/src/api/sandbox-service/sandbox-service.api.ts)
### What “pause” means on the backend
That endpoint maps to pause_sandbox in:
- openhands/app_server/sandbox/sandbox_router.py → sandbox_service.pause_sandbox(...)
And the behavior depends on sandbox backend:
- Docker sandbox: calls Docker “pause” (freezes processes), does not remove container/volume:
- `openhands/app_server/sandbox/docker_sandbox_service.py`:
- container.pause() if running
- Remote sandbox: calls the runtime service “pause” endpoint:
- `openhands/app_server/sandbox/remote_sandbox_service.py`:
- sends POST /pause with {runtime_id: ...}
### Why it doesn’t “fully reinitialize” when you reopen
Because it’s just a pause, reopening the conversation typically just resumes the same sandbox (same filesystem, same agent server process state, etc.), rather than creating a brand new one.
In fact, the “start” behavior for V1 in the frontend is also “resume sandbox”, not “create fresh sandbox”:
- useUnifiedResumeConversationSandbox (frontend/src/hooks/mutation/use-unified-start-conversation.ts)
- for V1 it calls resumeV1ConversationSandbox() → POST /api/v1/sandboxes/{id}/resume
So: Close → Pause, Open → Resume. No full reset is implied.
### What would cause a true “fresh start” in V1?
A “fresh start” (the behavior you described where setup.sh runs again and LLM changes get picked up) happens when the underlying sandbox is actually stopped/removed somewhere else.
There’s an explicit comment in the backend acknowledging that idle sandboxes get stopped by the runtime platform and may return 503:
- `openhands/app_server/app_conversation/live_status_app_conversation_service.py`:
- “The runtime API stops idle sandboxes all the time and they return a 503.”
When that happens, resuming can no longer resume the old sandbox (it’s gone), so the system ends up starting a new sandbox, and that is when you see setup.sh again and updated settings taking effect.
---
## 3) If your conversation is V0 (legacy): it stops the agent loop, and runtime shutdown is not “waited for”
For V0 conversations, the frontend calls:
- POST /api/conversations/{conversation_id}/stop
- via ConversationService.stopConversation() (frontend/src/api/conversation-service/conversation-service.api.ts)
Backend handler:
- openhands/server/routes/manage_conversations.py → stop_conversation (marked deprecated=True)
What it does:
1. Checks if the conversation agent loop is RUNNING/STARTING
2. Calls:
- conversation_manager.close_session(conversation_id)nutritious-balloon-65098
02/14/2026, 5:38 PMAgentSession.close() submits runtime.close to a thread executor without awaiting it:
- `openhands/server/session/agent_session.py`:
- EXECUTOR.submit(self.runtime.close)
So even in V0, “stop” can return before the runtime is fully down, and if you reopen quickly you can get “it wasn’t completely stopped” type behavior.
---
## 4) “How long does it take to stop?”
### V1 (pause sandbox)
The UI waits only for the HTTP request to return success. There is no additional polling to confirm “everything is fully paused and torn down” (and it’s not torn down anyway).
- Docker pause is usually fast (seconds / sub-second), but it’s a “freeze”, not a teardown.
- Remote pause depends on the runtime provider; again it’s typically fast to acknowledge, but it’s not a guarantee of full reinitialization next time.
### V0 (close session)
The stop endpoint awaits close_session, but runtime shutdown is kicked off in the background (not awaited). Also, if the session is still initializing, AgentSession.close() may wait for initialization to finish (up to the internal wait limit) before closing.
---
## 5) The key mismatch with your expectation
Your expectation is: “Close Conversation” should make the next open behave like a brand new runtime (rerun setup.sh, pick up new Settings/LLM).
But in current code, for V1 it’s explicitly: pause + resume, which is designed to preserve state, not reset it.
If you want, I can also point to what the backend itself considers the “full stop and remove” operation for V1 (it’s referenced in the V0 stop endpoint docstring), and we can discuss what UI/UX would be needed to expose that as a “Reset runtime” or “Stop & delete runtime” action.calm-artist-7206
02/14/2026, 5:53 PMnutritious-balloon-65098
02/14/2026, 5:53 PMnutritious-balloon-65098
02/14/2026, 5:57 PMuseUnifiedPauseConversationSandbox() → pauseV1ConversationSandbox() → POST /api/v1/sandboxes/{sandbox_id}/pause.
- Reopening/starting later is typically resume the same sandbox: POST /api/v1/sandboxes/{sandbox_id}/resume.
- So it’s expected that reopening may not fully reinitialize (no guaranteed rerun of setup.sh, no guaranteed pickup of changed Settings/LLM), because the sandbox was only paused.
- In V0 (legacy): it calls POST /api/conversations/{conversation_id}/stop, which ends up in conversation_manager.close_session(...).
- The runtime shutdown is not necessarily “fully awaited” all the way down (runtime close is kicked off asynchronously), so you can see cases where “stop” returns before everything is completely torn down.
### What UI/UX would be needed to have a “Stop/reset sandbox”
Because “pause” and “hard reset” are different user intents, the UI should expose distinct actions, for example:
1) Pause runtime (current behavior; just pause/freeze)
2) Restart runtime (re-run setup) (keep workspace, but force a fresh runtime process and re-apply Settings)
3) Reset runtime (new sandbox / wipe runtime) (hard reset; may be destructive unless designed to preserve workspace)
To implement these cleanly for V1, you likely need either:
- a dedicated restart endpoint (e.g. /restart or /stop + /start semantics), or
- a supported flow to delete the sandbox (DELETE /api/v1/sandboxes/{sandbox_id} exists server-side) and then create a new sandbox and reattach it to the same conversation (otherwise you risk leaving the conversation pointing to a missing sandbox).
### What you can do right now to reliably get “full reinit” behavior
Since the UI currently only pauses/resumes for V1, the most reliable practical options today are:
1) Start a new conversation after changing Settings/LLM (most reliable “fresh init”).
2) Delete the conversation and recreate (destructive to the conversation record, but does trigger sandbox deletion in the delete path).
3) Wait for idle cleanup to fully stop the sandbox (timing-dependent; you already observed ~30–60 minutes ballpark behavior).
No git diff exists because no repository files were modified.nutritious-balloon-65098
02/14/2026, 6:05 PMsetup.sh again + picks up latest Settings (LLM model) on next open” behavior, you typically want an explicit restart or reset concept.
A clean UI/UX would usually split this into two distinct actions, because users mean two different things by “reset”:
### A. “Restart runtime” (keep workspace, but restart the agent server process)
User intent: “Same conversation + same files, but I want a clean runtime process and fresh settings applied.”
UX proposal
- Rename current menu item to something like:
- Pause runtime (current behavior), not “Close (Stop Runtime)”
- Add a new menu item:
- Restart runtime (re-run setup)
Modal copy should explicitly say
- This will restart the runtime and may take N seconds
- It will re-run setup (and therefore pick up updated Settings like model/provider)
- It should say whether it preserves workspace state (ideally yes)
Status / feedback
- Show progress states: Stopping… → Starting… → Running
- Disable Start/Stop while restart is in-flight
- If restart fails, show a recoverable error (“Try again”, “Create new conversation”, etc.)
Backend/API capability needed
- Today V1 has pause and resume, and it has delete (full removal). It does not have a clear “stop but keep the sandbox identity/workspace” API exposed to the frontend.
- So implementing “Restart runtime” cleanly likely needs a new V1 endpoint along the lines of:
- POST /api/v1/sandboxes/{sandbox_id}/stop (stop, but keep resources)
- or POST /api/v1/sandboxes/{sandbox_id}/restart
- Then the existing “Start” behavior could call resume/start after a stop.
### B. “Reset runtime” (fresh sandbox; may discard workspace)
User intent: “I want a totally clean machine.”
UX proposal
- Add a separate menu item:
- Reset runtime (new sandbox) or Reset runtime (wipe and reinitialize)
- Very explicit warning:
- “This will discard runtime state and may remove files unless they’re committed/pushed.”
Backend/API capability needed
- This maps most closely to deleting the sandbox and then creating a new one and reattaching it to the conversation.
- The codebase already describes the “full stop and remove” concept for V1 as:
- DELETE /api/v1/sandboxes/{sandbox_id} (mentioned in openhands/server/routes/manage_conversations.py)
- However, doing only a delete can leave the conversation pointing at a missing sandbox unless there’s a supported “create new sandbox for this existing conversation” path. So a proper “Reset runtime” UX usually also requires server support to:
- create a new sandbox and associate it with the same conversation, or
- create a replacement conversation automatically and redirect you.
---
## 2) What you can do now (without “Restart/Reset runtime” in the UI) to be reasonably sure you’ll get full reinit
Given how it’s currently implemented, “Close Conversation (Stop Runtime)” for V1 is a pause, which is specifically the case that does not reliably trigger the “fresh init” behavior you want.
Here are the practical options today, from most reliable to least:
### Option 1 (most reliable): Create a new conversation after changing Settings
If your goal is “definitely apply the new LLM/model/provider and rerun init”:
1. Commit/push any important workspace changes (or otherwise export/save them)
2. Start a new conversation with the repo/branch you want
This is the only option that reliably forces a brand new runtime lifecycle without relying on idle cleanup timing.
### Option 2: Delete the conversation (forces V1 sandbox deletion), then recreate
If you’re okay losing the conversation record itself:nutritious-balloon-65098
02/14/2026, 6:05 PMdelete_conversation in openhands/server/routes/manage_conversations.py, which calls sandbox_service.delete_sandbox(...)).
- Practically: use Delete Conversation, then start a new one.
This is closer to a “hard reset,” but it’s destructive to the conversation.
### Option 3: Wait for the platform’s idle cleanup to stop it, then reopen
You already observed this: after ~30–60 minutes (ballpark), the runtime platform may stop idle sandboxes. The code even anticipates idle sandboxes disappearing/going 503 in the live-status path.
This can produce the “reinit” effect, but it’s:
- slow
- timing-dependent
- not something you can force on demand from the UI today
### Option 4 (advanced / only if you’re comfortable): Call internal APIs manually
If you can identify the sandbox_id (it’s fetched in the frontend via the V1 “batch get app conversations” call), you could theoretically call a sandbox delete/stop endpoint directly.
Caveat: unless the product/server has a supported flow to recreate and reattach a sandbox to that same conversation, manually deleting the sandbox can leave the conversation in a broken “can’t resume” state until some other mechanism provisions a replacement.
---
If you tell me which environment you mean (OpenHands Cloud vs self-hosted, and whether your conversations are V1—which they almost certainly are on Cloud), I can be more concrete about which of the above is safest for you and what the exact wording/placement should be for the new UI action(s).