MCPcopy
hub / github.com/coder/mux / remove

Method remove

src/node/services/workspaceService.ts:3721–4194  ·  view source on GitHub ↗
(workspaceId: string, force = false)

Source from the content-addressed store, hash-verified

3719 }
3720
3721 async remove(workspaceId: string, force = false): Promise<Result<void>> {
3722 // Idempotent: if already removing, return success to prevent race conditions
3723 if (this.removingWorkspaces.has(workspaceId)) {
3724 return Ok(undefined);
3725 }
3726 this.removingWorkspaces.add(workspaceId);
3727
3728 // If this workspace is mid-init, cancel the fire-and-forget init work (postCreateSetup,
3729 // sync/checkout, .mux/init hook, etc.) so removal doesn't leave orphaned background work.
3730 const initAbortController = this.initAbortControllers.get(workspaceId);
3731 if (initAbortController) {
3732 initAbortController.abort();
3733 this.initAbortControllers.delete(workspaceId);
3734 }
3735
3736 const persistedWorkspace = this.config.findWorkspace(workspaceId);
3737
3738 // Try to remove from runtime (filesystem)
3739 try {
3740 if (!force) {
3741 const config = this.config.loadConfigOrDefault();
3742 const taskSettings = normalizeTaskSettings(config.taskSettings);
3743 if (
3744 taskSettings.preserveSubagentsUntilArchive &&
3745 this.taskService?.hasCompletedDescendants?.(workspaceId)
3746 ) {
3747 const persistedWorkspaceEntry = findWorkspaceEntry(config, workspaceId);
3748 const isArchived =
3749 persistedWorkspaceEntry != null &&
3750 isWorkspaceArchived(
3751 persistedWorkspaceEntry.workspace.archivedAt,
3752 persistedWorkspaceEntry.workspace.unarchivedAt
3753 );
3754
3755 // Keep the whole parentWorkspaceId chain intact while completed descendants still exist.
3756 // Unarchived ancestors must be archived first so descendant cleanup can safely walk that lineage.
3757 if (!isArchived) {
3758 return Err(
3759 "This workspace has preserved completed sub-agent workspaces. Archive the workspace first to trigger cleanup, then try removing it."
3760 );
3761 }
3762
3763 // Archived parents can still retain completed descendants while cleanup waits on
3764 // prerequisites like pending patch artifacts. Keep removal blocked until that cleanup
3765 // finishes so descendants do not lose the archived ancestor that makes them eligible.
3766 return Err(
3767 "This workspace still has completed sub-agent workspaces pending cleanup. Wait for cleanup to finish, or force-remove the workspace."
3768 );
3769 }
3770 }
3771
3772 // Stop any active stream before deleting metadata/config to avoid tool calls racing with removal.
3773 //
3774 // IMPORTANT: AIService forwards "stream-abort" asynchronously after partial cleanup. If we roll up
3775 // session timing (or delete session files) immediately after stopStream(), we can race the final
3776 // abort timing write.
3777 const wasStreaming = this.aiService.isStreaming(workspaceId);
3778 const streamStoppedEvent: Promise<"abort" | "end" | undefined> | undefined = wasStreaming

Callers

nothing calls this directly

Calls 15

removeContainerMethod · 0.95
disposeSessionMethod · 0.95
emitMethod · 0.95
OkFunction · 0.90
normalizeTaskSettingsFunction · 0.90
findWorkspaceEntryFunction · 0.90
isWorkspaceArchivedFunction · 0.90
ErrFunction · 0.90
isMultiProjectFunction · 0.90
getProjectsFunction · 0.90
createRuntimeFunction · 0.90

Tested by

no test coverage detected