feat(app): archive a workspace from its sidebar row and undo it

Hovering a workspace row on desktop shows an archive button next to the
row menu. Archiving shows a toast with Undo and a Cmd/Ctrl+Z shortcut so a
mis-click costs one keystroke instead of a trip through History.

The daemon serializes archive and undo per workspace across every
connection. An undo that lands while an archive is still tearing down
aborts it at the next checkpoint and restores only the agents that archive
took down; deleting the worktree directory is the point of no return, after
which undo recreates the worktree.
This commit is contained in:
Mohamed Boudra
2026-11-06 05:37:41 +01:00
parent 5abfdf7107
commit 5d8a78112c
50 changed files with 2314 additions and 161 deletions
+26
View File
@@ -1105,6 +1105,14 @@ export const WorkspaceRecoveryRestoreRequestSchema = z.object({
requestId: z.string(),
});
// Undoes the latest archive of a workspace: cancels it if it is still running, otherwise
// restores the workspace, then unarchives the agents that archive took down.
export const WorkspaceArchiveUndoRequestSchema = z.object({
type: z.literal("workspace.archive.undo.request"),
workspaceId: z.string(),
requestId: z.string(),
});
export const SetVoiceModeMessageSchema = z.object({
type: z.literal("set_voice_mode"),
enabled: z.boolean(),
@@ -2249,6 +2257,16 @@ export const WorkspaceRecoveryRestoreResponseSchema = z.object({
}),
});
export const WorkspaceArchiveUndoResponseSchema = z.object({
type: z.literal("workspace.archive.undo.response"),
payload: z.object({
requestId: z.string(),
workspaceId: z.string(),
accepted: z.boolean(),
error: z.string().nullable(),
}),
});
export const SetVoiceModeResponseMessageSchema = z.object({
type: z.literal("set_voice_mode_response"),
payload: z.object({
@@ -3355,6 +3373,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
WorkspaceLabelDeleteInspectRequestSchema,
WorkspaceRecoveryInspectRequestSchema,
WorkspaceRecoveryRestoreRequestSchema,
WorkspaceArchiveUndoRequestSchema,
SetVoiceModeMessageSchema,
SendAgentMessageRequestSchema,
WaitForFinishRequestSchema,
@@ -3820,6 +3839,8 @@ export const ServerInfoStatusPayloadSchema = z
worktreeRestore: z.boolean().optional(),
// COMPAT(workspaceRecovery): added in v0.1.105, remove after 2027-01-11 once daemon floor >= v0.1.105.
workspaceRecovery: z.boolean().optional(),
// COMPAT(workspaceArchiveUndo): added in v0.15.3, remove gate after 2027-05-06.
workspaceArchiveUndo: z.boolean().optional(),
// COMPAT(workspaceFileEditing): added in v0.2.0, remove after 2027-01-18 once daemon floor >= v0.2.0.
workspaceFileEditing: z.boolean().optional(),
// COMPAT(fileByteWrites): added in v0.13.0, remove gate after 2027-04-20.
@@ -4796,6 +4817,8 @@ export const ArchiveWorkspaceResponseMessageSchema = z.object({
workspaceId: z.string(),
archivedAt: z.string().nullable(),
error: z.string().nullable(),
// An undo stopped this archive before it finished; `error` carries the reason.
canceled: z.boolean().optional(),
}),
});
@@ -7231,6 +7254,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
WorkspaceBaseBranchSetResponseSchema,
WorkspaceRecoveryInspectResponseSchema,
WorkspaceRecoveryRestoreResponseSchema,
WorkspaceArchiveUndoResponseSchema,
WaitForFinishResponseMessageSchema,
AgentPermissionRequestMessageSchema,
AgentPermissionResolvedMessageSchema,
@@ -7444,6 +7468,7 @@ export type WorkspaceRecoveryInspectResponse = z.infer<
export type WorkspaceRecoveryRestoreResponse = z.infer<
typeof WorkspaceRecoveryRestoreResponseSchema
>;
export type WorkspaceArchiveUndoResponse = z.infer<typeof WorkspaceArchiveUndoResponseSchema>;
export type WorkspaceCreateRequest = z.infer<typeof WorkspaceCreateRequestSchema>;
export type WorkspaceCreateResponse = z.infer<typeof WorkspaceCreateResponseSchema>;
export type ProjectRenameResponsePayload = z.infer<typeof ProjectRenameResponsePayloadSchema>;
@@ -7593,6 +7618,7 @@ export type WorkspacePinSetRequest = z.infer<typeof WorkspacePinSetRequestSchema
export type WorkspaceBaseBranchSetRequest = z.infer<typeof WorkspaceBaseBranchSetRequestSchema>;
export type WorkspaceRecoveryInspectRequest = z.infer<typeof WorkspaceRecoveryInspectRequestSchema>;
export type WorkspaceRecoveryRestoreRequest = z.infer<typeof WorkspaceRecoveryRestoreRequestSchema>;
export type WorkspaceArchiveUndoRequest = z.infer<typeof WorkspaceArchiveUndoRequestSchema>;
export type SetAgentModeRequestMessage = z.infer<typeof SetAgentModeRequestMessageSchema>;
export type SetAgentModelRequestMessage = z.infer<typeof SetAgentModelRequestMessageSchema>;
export type SetAgentThinkingRequestMessage = z.infer<typeof SetAgentThinkingRequestMessageSchema>;