diff --git a/frontend/src/hooks/mutation/use-save-settings.ts b/frontend/src/hooks/mutation/use-save-settings.ts index 8c6a1f6b46..dd14b86e90 100644 --- a/frontend/src/hooks/mutation/use-save-settings.ts +++ b/frontend/src/hooks/mutation/use-save-settings.ts @@ -27,6 +27,10 @@ const saveSettingsMutationFn = async (settings: Partial) => { settings.ENABLE_PROACTIVE_CONVERSATION_STARTERS, search_api_key: settings.SEARCH_API_KEY?.trim() || "", max_budget_per_task: settings.MAX_BUDGET_PER_TASK, + git_user_name: + settings.GIT_USER_NAME?.trim() || DEFAULT_SETTINGS.GIT_USER_NAME, + git_user_email: + settings.GIT_USER_EMAIL?.trim() || DEFAULT_SETTINGS.GIT_USER_EMAIL, }; await OpenHands.saveSettings(apiSettings); diff --git a/frontend/src/hooks/query/use-settings.ts b/frontend/src/hooks/query/use-settings.ts index 9ac38e04d8..d73f1a0978 100644 --- a/frontend/src/hooks/query/use-settings.ts +++ b/frontend/src/hooks/query/use-settings.ts @@ -31,6 +31,9 @@ const getSettingsQueryFn = async (): Promise => { EMAIL: apiSettings.email || "", EMAIL_VERIFIED: apiSettings.email_verified, MCP_CONFIG: apiSettings.mcp_config, + GIT_USER_NAME: apiSettings.git_user_name || DEFAULT_SETTINGS.GIT_USER_NAME, + GIT_USER_EMAIL: + apiSettings.git_user_email || DEFAULT_SETTINGS.GIT_USER_EMAIL, IS_NEW_USER: false, }; }; diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index 22943610dc..5425477fe4 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -739,6 +739,8 @@ export enum I18nKey { MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO", MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO", MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION", + SETTINGS$GIT_USERNAME = "SETTINGS$GIT_USERNAME", + SETTINGS$GIT_EMAIL = "SETTINGS$GIT_EMAIL", PROJECT_MANAGEMENT$TITLE = "PROJECT_MANAGEMENT$TITLE", PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR = "PROJECT_MANAGEMENT$VALIDATE_INTEGRATION_ERROR", PROJECT_MANAGEMENT$LINK_BUTTON_LABEL = "PROJECT_MANAGEMENT$LINK_BUTTON_LABEL", diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 18270254d6..aec50265f3 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -11823,6 +11823,38 @@ "de": "OpenHands aktualisiert den Microagenten basierend auf Ihren Anweisungen.", "uk": "OpenHands оновить мікроагента відповідно до ваших інструкцій." }, + "SETTINGS$GIT_USERNAME": { + "en": "Git Username", + "ja": "Gitユーザー名", + "zh-CN": "Git用户名", + "zh-TW": "Git使用者名稱", + "ko-KR": "Git 사용자 이름", + "no": "Git-brukernavn", + "it": "Nome utente Git", + "pt": "Nome de usuário Git", + "es": "Nombre de usuario Git", + "ar": "اسم مستخدم Git", + "fr": "Nom d'utilisateur Git", + "tr": "Git Kullanıcı Adı", + "de": "Git-Benutzername", + "uk": "Ім'я користувача Git" + }, + "SETTINGS$GIT_EMAIL": { + "en": "Git Email", + "ja": "Gitメールアドレス", + "zh-CN": "Git电子邮件", + "zh-TW": "Git電子郵件", + "ko-KR": "Git 이메일", + "no": "Git-e-post", + "it": "Email Git", + "pt": "Email Git", + "es": "Correo electrónico Git", + "ar": "بريد Git الإلكتروني", + "fr": "Email Git", + "tr": "Git E-posta", + "de": "Git-E-Mail", + "uk": "Електронна пошта Git" + }, "PROJECT_MANAGEMENT$TITLE": { "en": "Project Management", "ja": "プロジェクト管理", diff --git a/frontend/src/routes/app-settings.tsx b/frontend/src/routes/app-settings.tsx index 5e3c832dc8..f673a5254d 100644 --- a/frontend/src/routes/app-settings.tsx +++ b/frontend/src/routes/app-settings.tsx @@ -40,6 +40,10 @@ function AppSettingsScreen() { ] = React.useState(false); const [maxBudgetPerTaskHasChanged, setMaxBudgetPerTaskHasChanged] = React.useState(false); + const [gitUserNameHasChanged, setGitUserNameHasChanged] = + React.useState(false); + const [gitUserEmailHasChanged, setGitUserEmailHasChanged] = + React.useState(false); const formAction = (formData: FormData) => { const languageLabel = formData.get("language-input")?.toString(); @@ -62,6 +66,13 @@ function AppSettingsScreen() { ?.toString(); const maxBudgetPerTask = parseMaxBudgetPerTask(maxBudgetPerTaskValue || ""); + const gitUserName = + formData.get("git-user-name-input")?.toString() || + DEFAULT_SETTINGS.GIT_USER_NAME; + const gitUserEmail = + formData.get("git-user-email-input")?.toString() || + DEFAULT_SETTINGS.GIT_USER_EMAIL; + saveSettings( { LANGUAGE: language, @@ -69,6 +80,8 @@ function AppSettingsScreen() { ENABLE_SOUND_NOTIFICATIONS: enableSoundNotifications, ENABLE_PROACTIVE_CONVERSATION_STARTERS: enableProactiveConversations, MAX_BUDGET_PER_TASK: maxBudgetPerTask, + GIT_USER_NAME: gitUserName, + GIT_USER_EMAIL: gitUserEmail, }, { onSuccess: () => { @@ -85,6 +98,8 @@ function AppSettingsScreen() { setSoundNotificationsSwitchHasChanged(false); setProactiveConversationsSwitchHasChanged(false); setMaxBudgetPerTaskHasChanged(false); + setGitUserNameHasChanged(false); + setGitUserEmailHasChanged(false); }, }, ); @@ -127,12 +142,24 @@ function AppSettingsScreen() { setMaxBudgetPerTaskHasChanged(newValue !== currentValue); }; + const checkIfGitUserNameHasChanged = (value: string) => { + const currentValue = settings?.GIT_USER_NAME; + setGitUserNameHasChanged(value !== currentValue); + }; + + const checkIfGitUserEmailHasChanged = (value: string) => { + const currentValue = settings?.GIT_USER_EMAIL; + setGitUserEmailHasChanged(value !== currentValue); + }; + const formIsClean = !languageInputHasChanged && !analyticsSwitchHasChanged && !soundNotificationsSwitchHasChanged && !proactiveConversationsSwitchHasChanged && - !maxBudgetPerTaskHasChanged; + !maxBudgetPerTaskHasChanged && + !gitUserNameHasChanged && + !gitUserEmailHasChanged; const shouldBeLoading = !settings || isLoading || isPending; @@ -194,6 +221,34 @@ function AppSettingsScreen() { step={1} className="w-full max-w-[680px]" // Match the width of the language field /> + +
+

+ {t(I18nKey.SETTINGS$GIT_SETTINGS)} +

+
+ + +
+
)} diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index c1b46b46f1..0ec0e01ced 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -26,6 +26,8 @@ export const DEFAULT_SETTINGS: Settings = { sse_servers: [], stdio_servers: [], }, + GIT_USER_NAME: "openhands", + GIT_USER_EMAIL: "openhands@all-hands.dev", }; /** diff --git a/frontend/src/types/settings.ts b/frontend/src/types/settings.ts index 03f72a4a85..a7613b251a 100644 --- a/frontend/src/types/settings.ts +++ b/frontend/src/types/settings.ts @@ -50,6 +50,8 @@ export type Settings = { MAX_BUDGET_PER_TASK: number | null; EMAIL?: string; EMAIL_VERIFIED?: boolean; + GIT_USER_NAME?: string; + GIT_USER_EMAIL?: string; }; export type ApiSettings = { @@ -76,6 +78,8 @@ export type ApiSettings = { }; email?: string; email_verified?: boolean; + git_user_name?: string; + git_user_email?: string; }; export type PostSettings = Settings & { diff --git a/openhands/server/conversation_manager/standalone_conversation_manager.py b/openhands/server/conversation_manager/standalone_conversation_manager.py index cb0b1d5e25..ad8dc5b0b5 100644 --- a/openhands/server/conversation_manager/standalone_conversation_manager.py +++ b/openhands/server/conversation_manager/standalone_conversation_manager.py @@ -331,10 +331,12 @@ class StandaloneConversationManager(ConversationManager): ) await self.close_session(oldest_conversation_id) + config = self.config.model_copy(deep=True) + session = Session( sid=sid, file_store=self.file_store, - config=self.config, + config=config, sio=self.sio, user_id=user_id, ) diff --git a/openhands/server/session/session.py b/openhands/server/session/session.py index 9ad93f2360..7bde68f413 100644 --- a/openhands/server/session/session.py +++ b/openhands/server/session/session.py @@ -122,6 +122,12 @@ class Session: or settings.sandbox_runtime_container_image else self.config.sandbox.runtime_container_image ) + + # Set Git user configuration if provided in settings + if hasattr(settings, 'git_user_name') and settings.git_user_name: + self.config.git_user_name = settings.git_user_name + if hasattr(settings, 'git_user_email') and settings.git_user_email: + self.config.git_user_email = settings.git_user_email max_iterations = settings.max_iterations or self.config.max_iterations # Prioritize settings over config for max_budget_per_task diff --git a/openhands/storage/data_models/settings.py b/openhands/storage/data_models/settings.py index c3029dfcdf..fee80713be 100644 --- a/openhands/storage/data_models/settings.py +++ b/openhands/storage/data_models/settings.py @@ -45,6 +45,8 @@ class Settings(BaseModel): max_budget_per_task: float | None = None email: str | None = None email_verified: bool | None = None + git_user_name: str | None = None + git_user_email: str | None = None model_config = ConfigDict( validate_assignment=True,