From c68a3ab2a87e737b15d40904fab46346ae10dcd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Sat, 23 May 2026 01:21:46 +0800 Subject: [PATCH 1/9] feat: make action budget configurable --- .env.example | 2 + .../settings/panels/ActionBudgetPanel.tsx | 167 ++++++++++++++++++ .../settings/panels/DeveloperOptionsPanel.tsx | 16 ++ .../__tests__/ActionBudgetPanel.test.tsx | 90 ++++++++++ app/src/lib/i18n/chunks/ar-5.ts | 13 ++ app/src/lib/i18n/chunks/bn-5.ts | 13 ++ app/src/lib/i18n/chunks/de-3.ts | 2 - app/src/lib/i18n/chunks/de-5.ts | 35 ++-- app/src/lib/i18n/chunks/en-5.ts | 13 ++ app/src/lib/i18n/chunks/es-5.ts | 13 ++ app/src/lib/i18n/chunks/fr-5.ts | 13 ++ app/src/lib/i18n/chunks/hi-5.ts | 13 ++ app/src/lib/i18n/chunks/id-5.ts | 13 ++ app/src/lib/i18n/chunks/it-5.ts | 13 ++ app/src/lib/i18n/chunks/ko-5.ts | 13 ++ app/src/lib/i18n/chunks/pt-5.ts | 13 ++ app/src/lib/i18n/chunks/ru-5.ts | 13 ++ app/src/lib/i18n/chunks/zh-CN-5.ts | 13 ++ app/src/lib/i18n/en.ts | 13 ++ app/src/pages/Settings.tsx | 2 + app/src/services/rpcMethods.ts | 2 + app/src/utils/tauriCommands/config.ts | 16 ++ .../developing/architecture/agent-harness.md | 2 + src/core/legacy_aliases.rs | 4 + src/openhuman/config/ops.rs | 32 ++++ src/openhuman/config/ops_tests.rs | 26 +++ src/openhuman/config/schema/load.rs | 13 ++ src/openhuman/config/schema/load_tests.rs | 21 +++ src/openhuman/config/schemas.rs | 54 ++++++ src/openhuman/security/ops.rs | 40 ++++- src/openhuman/security/policy.rs | 5 +- src/openhuman/security/policy_tests.rs | 16 ++ src/openhuman/security/schemas.rs | 14 +- 33 files changed, 695 insertions(+), 33 deletions(-) create mode 100644 app/src/components/settings/panels/ActionBudgetPanel.tsx create mode 100644 app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx diff --git a/.env.example b/.env.example index 698664938e..67e7cfa1c7 100644 --- a/.env.example +++ b/.env.example @@ -72,6 +72,8 @@ OPENHUMAN_MODEL= OPENHUMAN_WORKSPACE= # [optional] Default: 0.7 OPENHUMAN_TEMPERATURE=0.7 +# [optional] Local safety cap for side-effecting tool actions in a rolling hour (default 20) +OPENHUMAN_MAX_ACTIONS_PER_HOUR=20 # [optional] Skill + agent tool execution timeout in seconds (default 120, max 3600) # OPENHUMAN_TOOL_TIMEOUT_SECS= # [optional] Headless update restart contract: self_replace | supervisor diff --git a/app/src/components/settings/panels/ActionBudgetPanel.tsx b/app/src/components/settings/panels/ActionBudgetPanel.tsx new file mode 100644 index 0000000000..dc85790c7f --- /dev/null +++ b/app/src/components/settings/panels/ActionBudgetPanel.tsx @@ -0,0 +1,167 @@ +import { useCallback, useEffect, useState } from 'react'; + +import { useT } from '../../../lib/i18n/I18nContext'; +import { + type ConfigSnapshot, + isTauri, + openhumanGetConfig, + openhumanUpdateAutonomySettings, +} from '../../../utils/tauriCommands'; +import SettingsHeader from '../components/SettingsHeader'; +import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; + +const DEFAULT_ACTION_BUDGET = 20; +const MIN_ACTION_BUDGET = 1; +const MAX_ACTION_BUDGET = 1000; + +function actionBudgetFromSnapshot(snapshot: ConfigSnapshot): number { + const autonomy = snapshot.config.autonomy; + if (autonomy && typeof autonomy === 'object' && 'max_actions_per_hour' in autonomy) { + const value = (autonomy as { max_actions_per_hour?: unknown }).max_actions_per_hour; + if (typeof value === 'number' && Number.isFinite(value)) { + return Math.trunc(value); + } + } + return DEFAULT_ACTION_BUDGET; +} + +function parseBudgetInput(value: string): number | null { + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < MIN_ACTION_BUDGET || parsed > MAX_ACTION_BUDGET) { + return null; + } + return parsed; +} + +const ActionBudgetPanel = () => { + const { t } = useT(); + const tauriRuntime = isTauri(); + const { navigateBack, breadcrumbs } = useSettingsNavigation(); + const [value, setValue] = useState(String(DEFAULT_ACTION_BUDGET)); + const [activeLimit, setActiveLimit] = useState(DEFAULT_ACTION_BUDGET); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); + const [status, setStatus] = useState(null); + + const loadConfig = useCallback(async () => { + if (!tauriRuntime) { + setIsLoading(false); + setError(t('settings.actionBudget.desktopOnly')); + return; + } + + setIsLoading(true); + setError(null); + try { + const response = await openhumanGetConfig(); + const nextLimit = actionBudgetFromSnapshot(response.result); + setActiveLimit(nextLimit); + setValue(String(nextLimit)); + } catch (err) { + setError(err instanceof Error ? err.message : t('settings.actionBudget.loadFailed')); + } finally { + setIsLoading(false); + } + }, [t, tauriRuntime]); + + useEffect(() => { + void loadConfig(); + }, [loadConfig]); + + const save = async () => { + if (!tauriRuntime) { + setError(t('settings.actionBudget.desktopOnly')); + return; + } + + const parsed = parseBudgetInput(value); + setStatus(null); + if (parsed === null) { + setError(t('settings.actionBudget.validation')); + return; + } + + setIsSaving(true); + setError(null); + try { + const response = await openhumanUpdateAutonomySettings({ max_actions_per_hour: parsed }); + const savedLimit = actionBudgetFromSnapshot(response.result); + setActiveLimit(savedLimit); + setValue(String(savedLimit)); + setStatus(t('settings.actionBudget.saved')); + } catch (err) { + setError(err instanceof Error ? err.message : t('settings.actionBudget.saveFailed')); + } finally { + setIsSaving(false); + } + }; + + return ( +
+ + +
+
+
+
+

+ {t('settings.actionBudget.activeLimit')} +

+
+ {t('settings.actionBudget.sideEffectingActions')} +
+
+
+ {activeLimit} {t('settings.actionBudget.actionsPerHour')} +
+
+ + + +
+
+ {isLoading && ( + {t('common.loading')} + )} + {status && {status}} + {error && {error}} +
+ +
+
+
+
+ ); +}; + +export default ActionBudgetPanel; diff --git a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx index aef4091340..4a8236b863 100644 --- a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx +++ b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx @@ -87,6 +87,22 @@ const developerItems = [ ), }, + { + id: 'action-budget', + titleKey: 'settings.developerMenu.actionBudget.title', + descriptionKey: 'settings.developerMenu.actionBudget.desc', + route: 'action-budget', + icon: ( + + + + ), + }, { id: 'agent-chat', titleKey: 'settings.developerMenu.agentChat.title', diff --git a/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx b/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx new file mode 100644 index 0000000000..dcfd08d3a7 --- /dev/null +++ b/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx @@ -0,0 +1,90 @@ +import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { renderWithProviders } from '../../../../test/test-utils'; +import ActionBudgetPanel from '../ActionBudgetPanel'; + +const hoisted = vi.hoisted(() => ({ + mockGetConfig: vi.fn(), + mockUpdateAutonomySettings: vi.fn(), + mockIsTauri: vi.fn(() => true), +})); + +vi.mock('../../../../utils/tauriCommands', () => ({ + isTauri: hoisted.mockIsTauri, + openhumanGetConfig: hoisted.mockGetConfig, + openhumanUpdateAutonomySettings: hoisted.mockUpdateAutonomySettings, +})); + +vi.mock('../../hooks/useSettingsNavigation', () => ({ + useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), +})); + +const configResponse = (max_actions_per_hour: number) => ({ + result: { + config: { autonomy: { max_actions_per_hour } }, + workspace_dir: '/tmp/openhuman/workspace', + config_path: '/tmp/openhuman/config.toml', + }, + logs: [], +}); + +describe('ActionBudgetPanel', () => { + beforeEach(() => { + hoisted.mockGetConfig.mockReset(); + hoisted.mockUpdateAutonomySettings.mockReset(); + hoisted.mockIsTauri.mockReturnValue(true); + }); + + it('loads and displays the active action budget', async () => { + hoisted.mockGetConfig.mockResolvedValue(configResponse(64)); + + renderWithProviders(); + + await waitFor(() => { + expect(screen.getByDisplayValue('64')).toBeInTheDocument(); + }); + expect(screen.getByText('64 actions/hour')).toBeInTheDocument(); + }); + + it('saves a changed action budget through config RPC', async () => { + hoisted.mockGetConfig.mockResolvedValue(configResponse(20)); + hoisted.mockUpdateAutonomySettings.mockResolvedValue(configResponse(48)); + + renderWithProviders(); + + const input = await screen.findByLabelText(/actions per hour/i); + fireEvent.change(input, { target: { value: '48' } }); + fireEvent.click(screen.getByRole('button', { name: /save/i })); + + await waitFor(() => { + expect(hoisted.mockUpdateAutonomySettings).toHaveBeenCalledWith({ max_actions_per_hour: 48 }); + }); + expect(await screen.findByText(/saved/i)).toBeInTheDocument(); + expect(screen.getByText('48 actions/hour')).toBeInTheDocument(); + }); + + it('rejects values below one before calling RPC', async () => { + hoisted.mockGetConfig.mockResolvedValue(configResponse(20)); + + renderWithProviders(); + + const input = await screen.findByLabelText(/actions per hour/i); + fireEvent.change(input, { target: { value: '0' } }); + fireEvent.click(screen.getByRole('button', { name: /save/i })); + + expect(await screen.findByText(/enter a whole number from 1 to 1000/i)).toBeInTheDocument(); + expect(hoisted.mockUpdateAutonomySettings).not.toHaveBeenCalled(); + }); + + it('disables save outside the desktop runtime', async () => { + hoisted.mockIsTauri.mockReturnValue(false); + + renderWithProviders(); + + expect(await screen.findByText(/available in the desktop app/i)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /save/i })).toBeDisabled(); + expect(hoisted.mockGetConfig).not.toHaveBeenCalled(); + expect(hoisted.mockUpdateAutonomySettings).not.toHaveBeenCalled(); + }); +}); diff --git a/app/src/lib/i18n/chunks/ar-5.ts b/app/src/lib/i18n/chunks/ar-5.ts index 4967ddf89b..f155485321 100644 --- a/app/src/lib/i18n/chunks/ar-5.ts +++ b/app/src/lib/i18n/chunks/ar-5.ts @@ -172,6 +172,8 @@ const ar5: TranslationMap = { 'settings.developerMenu.tools.title': 'الأدوات', 'settings.developerMenu.tools.desc': 'تفعيل أو تعطيل الإمكانات التي يمكن لـ OpenHuman استخدامها نيابةً عنك', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'دردشة الوكيل', 'settings.developerMenu.agentChat.desc': 'اختبار محادثة الوكيل مع تجاوزات النموذج ودرجة الحرارة', 'settings.developerMenu.cronJobs.title': 'مهام Cron', @@ -196,6 +198,17 @@ const ar5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'مشغلات التكامل', 'settings.developerMenu.integrationTriggers.desc': 'تكوين إعدادات فرز الذكاء الاصطناعي لمشغلات تكامل Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'اختر الفاتح أو الداكن أو مطابقة سمة النظام', 'settings.mascot.active': 'نشط', 'settings.mascot.characterDesc': 'وصف الشخصية', diff --git a/app/src/lib/i18n/chunks/bn-5.ts b/app/src/lib/i18n/chunks/bn-5.ts index 9d8c80e93a..da27d1f9a8 100644 --- a/app/src/lib/i18n/chunks/bn-5.ts +++ b/app/src/lib/i18n/chunks/bn-5.ts @@ -176,6 +176,8 @@ const bn5: TranslationMap = { 'settings.developerMenu.tools.title': 'টুলস', 'settings.developerMenu.tools.desc': 'OpenHuman আপনার পক্ষ থেকে যে সক্ষমতাগুলি ব্যবহার করতে পারে সেগুলি চালু বা বন্ধ করুন', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'এজেন্ট চ্যাট', 'settings.developerMenu.agentChat.desc': 'মডেল এবং টেম্পারেচার ওভাররাইডসহ এজেন্ট কথোপকথন পরীক্ষা করুন', @@ -202,6 +204,17 @@ const bn5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'ইন্টিগ্রেশন ট্রিগার', 'settings.developerMenu.integrationTriggers.desc': 'Composio ইন্টিগ্রেশন ট্রিগারের জন্য AI ট্রায়াজ সেটিংস কনফিগার করুন', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'লাইট, ডার্ক বা সিস্টেম থিমের সাথে মিল বেছে নিন', 'settings.mascot.active': 'সক্রিয়', 'settings.mascot.characterDesc': 'চরিত্রের বিবরণ', diff --git a/app/src/lib/i18n/chunks/de-3.ts b/app/src/lib/i18n/chunks/de-3.ts index b5857a82bd..996a81855a 100644 --- a/app/src/lib/i18n/chunks/de-3.ts +++ b/app/src/lib/i18n/chunks/de-3.ts @@ -123,8 +123,6 @@ const de3: TranslationMap = { 'subconscious.decision.failed': 'Fehlgeschlagen', 'subconscious.decision.cancelled': 'Abgesagt', 'subconscious.decision.skipped': 'Übersprungen', - 'subconscious.providerUnavailableTitle': 'Unterbewusstsein pausiert', - 'subconscious.providerSettings': 'KI-Einstellungen', 'actionable.complete': 'Komplett', 'actionable.dismiss': 'Entlassen', 'actionable.snooze': 'Schlummern', diff --git a/app/src/lib/i18n/chunks/de-5.ts b/app/src/lib/i18n/chunks/de-5.ts index 0e77924028..9e8f751c2e 100644 --- a/app/src/lib/i18n/chunks/de-5.ts +++ b/app/src/lib/i18n/chunks/de-5.ts @@ -184,6 +184,8 @@ const de5: TranslationMap = { 'settings.developerMenu.tools.title': 'Werkzeuge', 'settings.developerMenu.tools.desc': 'Aktiviere oder deaktiviere Funktionen, die OpenHuman in deinem Namen nutzen kann', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agenten-Chat', 'settings.developerMenu.agentChat.desc': 'Test-Agent-Konversation mit Modell- und Temperaturüberschreibungen', @@ -233,6 +235,17 @@ const de5: TranslationMap = { 'settings.mcpServer.clientZed': 'Zed', 'settings.mcpServer.configFilePath': 'Konfigurationsdatei', 'settings.mcpServer.clientSelectorAriaLabel': 'MCP-Client-Auswahl', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Wähle hell, dunkel oder passend zu deinem Systemthema', 'settings.mascot.active': 'Aktiv', 'settings.mascot.characterDesc': 'Charakterbeschreibung', @@ -523,28 +536,6 @@ const de5: TranslationMap = { 'settings.mascot.colorYellow': 'Gelb', 'settings.mascot.libraryUnavailable': 'OpenHuman Bibliothek nicht verfügbar', 'settings.mascot.title': 'OpenHuman', - 'settings.developerMenu.mcpServer.title': 'MCP-Server', - 'settings.developerMenu.mcpServer.desc': - 'Externe MCP-Clients für die Verbindung zu OpenHuman konfigurieren', - 'settings.mcpServer.title': 'MCP-Server', - 'settings.mcpServer.toolsSectionTitle': 'Verfügbare Tools', - 'settings.mcpServer.toolsSectionDesc': - 'Tools, die über den MCP-stdio-Server bereitgestellt werden, wenn openhuman-core mcp läuft', - 'settings.mcpServer.configSectionTitle': 'Client-Konfiguration', - 'settings.mcpServer.configSectionDesc': - 'Wähle deinen MCP-Client, um den passenden Konfigurations-Snippet zu erzeugen', - 'settings.mcpServer.copySnippet': 'In Zwischenablage kopieren', - 'settings.mcpServer.copied': 'Kopiert!', - 'settings.mcpServer.openConfigFile': 'Konfigurationsdatei öffnen', - 'settings.mcpServer.binaryPathNotFound': - 'OpenHuman-Binary nicht gefunden. Bei Quellbau bitte mit `cargo build --bin openhuman-core` bauen.', - 'settings.mcpServer.openConfigError': 'Konfigurationsdatei konnte nicht geöffnet werden', - 'settings.mcpServer.clientClaudeDesktop': 'Claude Desktop', - 'settings.mcpServer.clientCursor': 'Cursor', - 'settings.mcpServer.clientCodex': 'Codex', - 'settings.mcpServer.clientZed': 'Zed', - 'settings.mcpServer.configFilePath': 'Konfigurationsdatei', - 'settings.mcpServer.clientSelectorAriaLabel': 'MCP-Client-Auswahl', }; export default de5; diff --git a/app/src/lib/i18n/chunks/en-5.ts b/app/src/lib/i18n/chunks/en-5.ts index f1470083d2..f129439af5 100644 --- a/app/src/lib/i18n/chunks/en-5.ts +++ b/app/src/lib/i18n/chunks/en-5.ts @@ -175,6 +175,8 @@ const en5: TranslationMap = { 'settings.developerMenu.tools.title': 'Tools', 'settings.developerMenu.tools.desc': 'Enable or disable capabilities OpenHuman can use on your behalf', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agent Chat', 'settings.developerMenu.agentChat.desc': 'Test agent conversation with model and temperature overrides', @@ -200,6 +202,17 @@ const en5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Integration Triggers', 'settings.developerMenu.integrationTriggers.desc': 'Configure AI triage settings for Composio integration triggers', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Pick light, dark, or match your system theme', 'settings.mascot.active': 'Active', 'settings.mascot.characterDesc': 'Character desc', diff --git a/app/src/lib/i18n/chunks/es-5.ts b/app/src/lib/i18n/chunks/es-5.ts index 85a2e41f15..8f2be5d715 100644 --- a/app/src/lib/i18n/chunks/es-5.ts +++ b/app/src/lib/i18n/chunks/es-5.ts @@ -179,6 +179,8 @@ const es5: TranslationMap = { 'settings.developerMenu.tools.title': 'Herramientas', 'settings.developerMenu.tools.desc': 'Activa o desactiva las capacidades que OpenHuman puede usar en tu nombre', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat del agente', 'settings.developerMenu.agentChat.desc': 'Prueba conversaciones del agente con ajustes de modelo y temperatura', @@ -206,6 +208,17 @@ const es5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Disparadores de integración', 'settings.developerMenu.integrationTriggers.desc': 'Configura los ajustes de triaje de IA para disparadores de integración de Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Elige claro, oscuro o igualar el tema del sistema', 'settings.mascot.active': 'Activo', 'settings.mascot.characterDesc': 'Descripción del personaje', diff --git a/app/src/lib/i18n/chunks/fr-5.ts b/app/src/lib/i18n/chunks/fr-5.ts index ff051e8ecc..42271ec2c5 100644 --- a/app/src/lib/i18n/chunks/fr-5.ts +++ b/app/src/lib/i18n/chunks/fr-5.ts @@ -181,6 +181,8 @@ const fr5: TranslationMap = { 'settings.developerMenu.tools.title': 'Outils', 'settings.developerMenu.tools.desc': "Active ou désactive les capacités qu'OpenHuman peut utiliser en ton nom", + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat agent', 'settings.developerMenu.agentChat.desc': 'Teste une conversation agent avec des remplacements de modèle et de température', @@ -208,6 +210,17 @@ const fr5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': "Déclencheurs d'intégration", 'settings.developerMenu.integrationTriggers.desc': "Configurer les paramètres de triage IA pour les déclencheurs d'intégration Composio", + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Choisis clair, sombre ou le thème du système', 'settings.mascot.active': 'Actif', 'settings.mascot.characterDesc': 'Description du personnage', diff --git a/app/src/lib/i18n/chunks/hi-5.ts b/app/src/lib/i18n/chunks/hi-5.ts index 06b3db21f8..4ca1873e8d 100644 --- a/app/src/lib/i18n/chunks/hi-5.ts +++ b/app/src/lib/i18n/chunks/hi-5.ts @@ -176,6 +176,8 @@ const hi5: TranslationMap = { 'settings.developerMenu.tools.title': 'टूल्स', 'settings.developerMenu.tools.desc': 'OpenHuman आपकी ओर से जिन क्षमताओं का उपयोग कर सकता है, उन्हें सक्षम या अक्षम करें', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'एजेंट चैट', 'settings.developerMenu.agentChat.desc': 'मॉडल और तापमान ओवरराइड के साथ एजेंट वार्तालाप का परीक्षण करें', @@ -202,6 +204,17 @@ const hi5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'इंटीग्रेशन ट्रिगर्स', 'settings.developerMenu.integrationTriggers.desc': 'Composio इंटीग्रेशन ट्रिगर्स के लिए AI ट्रायेज सेटिंग्स कॉन्फ़िगर करें', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'लाइट, डार्क, या सिस्टम थीम से मिलान चुनें', 'settings.mascot.active': 'एक्टिव', 'settings.mascot.characterDesc': 'कैरेक्टर विवरण', diff --git a/app/src/lib/i18n/chunks/id-5.ts b/app/src/lib/i18n/chunks/id-5.ts index 36aefb885c..d10e589106 100644 --- a/app/src/lib/i18n/chunks/id-5.ts +++ b/app/src/lib/i18n/chunks/id-5.ts @@ -177,6 +177,8 @@ const id5: TranslationMap = { 'settings.developerMenu.tools.title': 'Alat', 'settings.developerMenu.tools.desc': 'Aktifkan atau nonaktifkan kemampuan yang bisa digunakan OpenHuman atas nama Anda', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat Agen', 'settings.developerMenu.agentChat.desc': 'Uji percakapan agen dengan override model dan temperatur', @@ -202,6 +204,17 @@ const id5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Pemicu Integrasi', 'settings.developerMenu.integrationTriggers.desc': 'Atur pengaturan triase AI untuk pemicu integrasi Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Pilih terang, gelap, atau ikuti tema sistem', 'settings.mascot.active': 'Aktif', 'settings.mascot.characterDesc': 'Deskripsi karakter', diff --git a/app/src/lib/i18n/chunks/it-5.ts b/app/src/lib/i18n/chunks/it-5.ts index efd5f22a25..6c24ef42ff 100644 --- a/app/src/lib/i18n/chunks/it-5.ts +++ b/app/src/lib/i18n/chunks/it-5.ts @@ -179,6 +179,8 @@ const it5: TranslationMap = { 'settings.developerMenu.tools.title': 'Strumenti', 'settings.developerMenu.tools.desc': 'Abilita o disabilita le capacità che OpenHuman può usare per tuo conto', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat agente', 'settings.developerMenu.agentChat.desc': "Testa conversazioni dell'agente con override di modello e temperatura", @@ -206,6 +208,17 @@ const it5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Trigger di integrazione', 'settings.developerMenu.integrationTriggers.desc': 'Configura le impostazioni di triage IA per i trigger di integrazione Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Scegli chiaro, scuro o il tema del sistema', 'settings.mascot.active': 'Attivo', 'settings.mascot.characterDesc': 'Descrizione personaggio', diff --git a/app/src/lib/i18n/chunks/ko-5.ts b/app/src/lib/i18n/chunks/ko-5.ts index c4d6be9b1a..75825c6db3 100644 --- a/app/src/lib/i18n/chunks/ko-5.ts +++ b/app/src/lib/i18n/chunks/ko-5.ts @@ -443,6 +443,19 @@ const ko5: TranslationMap = { 'settings.mascot.colorYellow': '노랑', 'settings.mascot.libraryUnavailable': 'OpenHuman 라이브러리를 사용할 수 없음', 'settings.mascot.title': 'OpenHuman', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.developerMenu.mcpServer.title': 'MCP Server', 'settings.developerMenu.mcpServer.desc': 'Configure external MCP clients to connect to OpenHuman', 'settings.mcpServer.title': 'MCP Server', diff --git a/app/src/lib/i18n/chunks/pt-5.ts b/app/src/lib/i18n/chunks/pt-5.ts index be3abfe5bb..24347812ba 100644 --- a/app/src/lib/i18n/chunks/pt-5.ts +++ b/app/src/lib/i18n/chunks/pt-5.ts @@ -180,6 +180,8 @@ const pt5: TranslationMap = { 'settings.developerMenu.tools.title': 'Ferramentas', 'settings.developerMenu.tools.desc': 'Ative ou desative capacidades que o OpenHuman pode usar em seu nome', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat do agente', 'settings.developerMenu.agentChat.desc': 'Teste conversas do agente com substituições de modelo e temperatura', @@ -207,6 +209,17 @@ const pt5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Gatilhos de integração', 'settings.developerMenu.integrationTriggers.desc': 'Configure as opções de triagem por IA para gatilhos de integração Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Escolha claro, escuro ou o tema do sistema', 'settings.mascot.active': 'Ativo', 'settings.mascot.characterDesc': 'Descrição do personagem', diff --git a/app/src/lib/i18n/chunks/ru-5.ts b/app/src/lib/i18n/chunks/ru-5.ts index acb2459500..4ff85e951c 100644 --- a/app/src/lib/i18n/chunks/ru-5.ts +++ b/app/src/lib/i18n/chunks/ru-5.ts @@ -177,6 +177,8 @@ const ru5: TranslationMap = { 'settings.developerMenu.tools.title': 'Инструменты', 'settings.developerMenu.tools.desc': 'Включайте или отключайте возможности, которые OpenHuman может использовать от вашего имени', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Чат агента', 'settings.developerMenu.agentChat.desc': 'Тестируйте разговор агента с переопределениями модели и температуры', @@ -203,6 +205,17 @@ const ru5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Триггеры интеграций', 'settings.developerMenu.integrationTriggers.desc': 'Настройка параметров AI-триажа для триггеров интеграций Composio', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Выберите светлую, темную или системную тему', 'settings.mascot.active': 'Активно', 'settings.mascot.characterDesc': 'Описание персонажа', diff --git a/app/src/lib/i18n/chunks/zh-CN-5.ts b/app/src/lib/i18n/chunks/zh-CN-5.ts index 8c6a4f0689..75506697c7 100644 --- a/app/src/lib/i18n/chunks/zh-CN-5.ts +++ b/app/src/lib/i18n/chunks/zh-CN-5.ts @@ -167,6 +167,8 @@ const zhCN5: TranslationMap = { 'settings.developerMenu.messagingChannels.desc': '配置 Telegram/Discord 认证模式和默认渠道路由', 'settings.developerMenu.tools.title': '工具', 'settings.developerMenu.tools.desc': '启用或停用 OpenHuman 可代表你使用的能力', + 'settings.developerMenu.actionBudget.title': '行动预算', + 'settings.developerMenu.actionBudget.desc': '设置工具行动的本地每小时上限', 'settings.developerMenu.agentChat.title': '智能体聊天', 'settings.developerMenu.agentChat.desc': '使用模型和温度覆盖测试智能体对话', 'settings.developerMenu.cronJobs.title': '定时任务', @@ -186,6 +188,17 @@ const zhCN5: TranslationMap = { '使用你自己的 Composio API 密钥并将调用直接路由到 backend.composio.dev', 'settings.developerMenu.integrationTriggers.title': '集成触发器', 'settings.developerMenu.integrationTriggers.desc': '配置 Composio 集成触发器的 AI 分流设置', + 'settings.actionBudget.title': '行动预算', + 'settings.actionBudget.activeLimit': '当前上限', + 'settings.actionBudget.sideEffectingActions': '会产生副作用的工具行动', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': '每小时行动数', + 'settings.actionBudget.desktopOnly': '行动预算仅在桌面应用中可用。', + 'settings.actionBudget.loadFailed': '无法加载行动预算。', + 'settings.actionBudget.saveFailed': '无法保存行动预算。', + 'settings.actionBudget.validation': '请输入 1 到 1000 的整数。', + 'settings.actionBudget.saved': '已保存', + 'settings.actionBudget.saving': '保存中...', 'settings.appearance.menuDesc': '选择浅色、深色或跟随系统主题', 'settings.mascot.active': '活跃', 'settings.mascot.characterDesc': '选择你的 OpenHuman 角色', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 9ac18e4cf8..a0a2c3f0cb 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -1931,6 +1931,8 @@ const en: TranslationMap = { 'settings.developerMenu.tools.title': 'Tools', 'settings.developerMenu.tools.desc': 'Enable or disable capabilities OpenHuman can use on your behalf', + 'settings.developerMenu.actionBudget.title': 'Action Budget', + 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agent Chat', 'settings.developerMenu.agentChat.desc': 'Test agent conversation with model and temperature overrides', @@ -1958,6 +1960,17 @@ const en: TranslationMap = { 'Configure AI triage settings for Composio integration triggers', 'settings.developerMenu.mcpServer.title': 'MCP Server', 'settings.developerMenu.mcpServer.desc': 'Configure external MCP clients to connect to OpenHuman', + 'settings.actionBudget.title': 'Action Budget', + 'settings.actionBudget.activeLimit': 'Active limit', + 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', + 'settings.actionBudget.actionsPerHour': 'actions/hour', + 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', + 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', + 'settings.actionBudget.loadFailed': 'Failed to load action budget.', + 'settings.actionBudget.saveFailed': 'Failed to save action budget.', + 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', + 'settings.actionBudget.saved': 'Saved', + 'settings.actionBudget.saving': 'Saving...', 'settings.mcpServer.title': 'MCP Server', 'settings.mcpServer.toolsSectionTitle': 'Available Tools', 'settings.mcpServer.toolsSectionDesc': diff --git a/app/src/pages/Settings.tsx b/app/src/pages/Settings.tsx index 20db4f13e9..cb774fb432 100644 --- a/app/src/pages/Settings.tsx +++ b/app/src/pages/Settings.tsx @@ -2,6 +2,7 @@ import type { ReactNode } from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; import AboutPanel from '../components/settings/panels/AboutPanel'; +import ActionBudgetPanel from '../components/settings/panels/ActionBudgetPanel'; import AgentChatPanel from '../components/settings/panels/AgentChatPanel'; import AIPanel from '../components/settings/panels/AIPanel'; import AppearancePanel from '../components/settings/panels/AppearancePanel'; @@ -353,6 +354,7 @@ const Settings = () => { )} /> {/* Developer Options */} )} /> + )} /> )} /> = { 'openhuman.ping': CORE_RPC_METHODS.corePing, 'openhuman.set_browser_allow_all': CORE_RPC_METHODS.configSetBrowserAllowAll, 'openhuman.update_analytics_settings': CORE_RPC_METHODS.configUpdateAnalyticsSettings, + 'openhuman.update_autonomy_settings': CORE_RPC_METHODS.configUpdateAutonomySettings, 'openhuman.update_browser_settings': CORE_RPC_METHODS.configUpdateBrowserSettings, 'openhuman.update_composio_trigger_settings': CORE_RPC_METHODS.configUpdateComposioTriggerSettings, diff --git a/app/src/utils/tauriCommands/config.ts b/app/src/utils/tauriCommands/config.ts index 1faa5e9162..b1e4eaa8ae 100644 --- a/app/src/utils/tauriCommands/config.ts +++ b/app/src/utils/tauriCommands/config.ts @@ -114,6 +114,10 @@ export interface MemorySettingsUpdate { memory_window?: MemoryContextWindow | null; } +export interface AutonomySettingsUpdate { + max_actions_per_hour?: number | null; +} + export interface RuntimeSettingsUpdate { kind?: string | null; reasoning_enabled?: boolean | null; @@ -261,6 +265,18 @@ export async function openhumanUpdateMemorySettings( }); } +export async function openhumanUpdateAutonomySettings( + update: AutonomySettingsUpdate +): Promise> { + if (!isTauri()) { + throw new Error('Not running in Tauri'); + } + return await callCoreRpc>({ + method: CORE_RPC_METHODS.configUpdateAutonomySettings, + params: update, + }); +} + export async function openhumanUpdateRuntimeSettings( update: RuntimeSettingsUpdate ): Promise> { diff --git a/gitbooks/developing/architecture/agent-harness.md b/gitbooks/developing/architecture/agent-harness.md index 1ef70c85e5..d7d972a84c 100644 --- a/gitbooks/developing/architecture/agent-harness.md +++ b/gitbooks/developing/architecture/agent-harness.md @@ -61,6 +61,7 @@ A **session** is the live conversation an `Agent` instance is running. The `Agen * The tool registry visible to the model. * A memory loader that hydrates relevant memories before each user message. * Per-turn budgets - max tool iterations, max payload size, max USD cost. +* Local action budget - a rolling hourly cap for side-effecting tool actions, read from `config.autonomy.max_actions_per_hour`. `Agent::turn(user_message)` is the hot path. In one turn it: @@ -234,6 +235,7 @@ Stop hooks fire **between** iterations of the tool-call loop. They're the policy * **Budget stop hook** - caps cumulative turn cost in USD using the per-iteration cost accumulator. * **Max-iterations stop hook** - caps iteration count from outside the agent's persistent config. +* **Action budget policy** - `SecurityPolicy` enforces `config.autonomy.max_actions_per_hour` for side-effecting tool operations. Users can tune it in Settings -> Advanced -> Action Budget, or operators can override it with `OPENHUMAN_MAX_ACTIONS_PER_HOUR`. A hook returning `Stop` aborts the loop with a clear reason the caller can surface to the user. Stop hooks are distinct from interrupts (next section): they're policy-driven, not user-driven. diff --git a/src/core/legacy_aliases.rs b/src/core/legacy_aliases.rs index 32fbe5e400..4c6a475b95 100644 --- a/src/core/legacy_aliases.rs +++ b/src/core/legacy_aliases.rs @@ -43,6 +43,10 @@ const LEGACY_ALIASES: &[(&str, &str)] = &[ "openhuman.update_analytics_settings", "openhuman.config_update_analytics_settings", ), + ( + "openhuman.update_autonomy_settings", + "openhuman.config_update_autonomy_settings", + ), ( "openhuman.update_browser_settings", "openhuman.config_update_browser_settings", diff --git a/src/openhuman/config/ops.rs b/src/openhuman/config/ops.rs index 2bb2b727da..e02c887f38 100644 --- a/src/openhuman/config/ops.rs +++ b/src/openhuman/config/ops.rs @@ -335,6 +335,11 @@ pub struct ModelSettingsPatch { pub subconscious_provider: Option, } +#[derive(Debug, Clone, Default)] +pub struct AutonomySettingsPatch { + pub max_actions_per_hour: Option, +} + #[derive(Debug, Clone, Default)] pub struct MemorySettingsPatch { pub backend: Option, @@ -603,6 +608,25 @@ pub async fn apply_memory_settings( )) } +/// Updates local autonomy and action budget settings in the configuration. +pub async fn apply_autonomy_settings( + config: &mut Config, + update: AutonomySettingsPatch, +) -> Result, String> { + if let Some(max_actions_per_hour) = update.max_actions_per_hour { + config.autonomy.max_actions_per_hour = max_actions_per_hour; + } + config.save().await.map_err(|e| e.to_string())?; + let snapshot = snapshot_config_json(config)?; + Ok(RpcOutcome::new( + snapshot, + vec![format!( + "autonomy settings saved to {}", + config.config_path.display() + )], + )) +} + /// Updates the screen intelligence settings in the configuration. pub async fn apply_screen_intelligence_settings( config: &mut Config, @@ -717,6 +741,14 @@ pub async fn load_and_apply_memory_settings( apply_memory_settings(&mut config, update).await } +/// Loads the configuration, applies autonomy settings updates, and saves it. +pub async fn load_and_apply_autonomy_settings( + update: AutonomySettingsPatch, +) -> Result, String> { + let mut config = load_config_with_timeout().await?; + apply_autonomy_settings(&mut config, update).await +} + /// Loads the configuration, applies screen intelligence settings updates, and saves it. pub async fn load_and_apply_screen_intelligence_settings( update: ScreenIntelligenceSettingsPatch, diff --git a/src/openhuman/config/ops_tests.rs b/src/openhuman/config/ops_tests.rs index 2d1bdc069b..726e1a66d6 100644 --- a/src/openhuman/config/ops_tests.rs +++ b/src/openhuman/config/ops_tests.rs @@ -495,6 +495,32 @@ async fn apply_memory_settings_updates_all_provided_fields() { ); } +#[tokio::test] +async fn apply_autonomy_settings_updates_action_budget() { + let tmp = tempdir().unwrap(); + let mut cfg = tmp_config(&tmp); + cfg.autonomy.max_actions_per_hour = 20; + + let outcome = apply_autonomy_settings( + &mut cfg, + AutonomySettingsPatch { + max_actions_per_hour: Some(64), + }, + ) + .await + .expect("apply autonomy settings"); + + assert_eq!(cfg.autonomy.max_actions_per_hour, 64); + assert_eq!( + outcome.value["config"]["autonomy"]["max_actions_per_hour"], + serde_json::json!(64) + ); + assert!(outcome + .logs + .iter() + .any(|l| l.contains("autonomy settings saved to"))); +} + #[tokio::test] async fn apply_memory_settings_ignores_unknown_memory_window_label() { let tmp = tempdir().unwrap(); diff --git a/src/openhuman/config/schema/load.rs b/src/openhuman/config/schema/load.rs index 11dc8e39e2..3e1b430ec5 100644 --- a/src/openhuman/config/schema/load.rs +++ b/src/openhuman/config/schema/load.rs @@ -1336,6 +1336,19 @@ impl Config { } } + if let Some(raw) = env.get("OPENHUMAN_MAX_ACTIONS_PER_HOUR") { + let trimmed = raw.trim(); + if !trimmed.is_empty() { + match trimmed.parse::() { + Ok(limit) => self.autonomy.max_actions_per_hour = limit, + Err(_) => tracing::warn!( + value = %raw, + "invalid OPENHUMAN_MAX_ACTIONS_PER_HOUR ignored; expected an unsigned integer" + ), + } + } + } + if let Some(flag) = env.get_any(&["OPENHUMAN_REASONING_ENABLED", "REASONING_ENABLED"]) { let normalized = flag.trim().to_ascii_lowercase(); match normalized.as_str() { diff --git a/src/openhuman/config/schema/load_tests.rs b/src/openhuman/config/schema/load_tests.rs index 0f64ec8564..098d4bd8b2 100644 --- a/src/openhuman/config/schema/load_tests.rs +++ b/src/openhuman/config/schema/load_tests.rs @@ -546,6 +546,27 @@ fn env_overlay_temperature_accepts_valid_and_ignores_out_of_range_or_garbage() { assert_eq!(cfg.default_temperature, 2.0); } +#[test] +fn env_overlay_autonomy_max_actions_per_hour_accepts_valid_u32() { + let mut cfg = Config::default(); + cfg.autonomy.max_actions_per_hour = 20; + + cfg.apply_env_overlay_with(&HashMapEnv::new().with("OPENHUMAN_MAX_ACTIONS_PER_HOUR", "64")); + assert_eq!(cfg.autonomy.max_actions_per_hour, 64); + + cfg.apply_env_overlay_with(&HashMapEnv::new().with("OPENHUMAN_MAX_ACTIONS_PER_HOUR", " ")); + assert_eq!( + cfg.autonomy.max_actions_per_hour, 64, + "blank env value must leave the configured limit unchanged" + ); + + cfg.apply_env_overlay_with(&HashMapEnv::new().with("OPENHUMAN_MAX_ACTIONS_PER_HOUR", "NaN")); + assert_eq!( + cfg.autonomy.max_actions_per_hour, 64, + "invalid env value must leave the configured limit unchanged" + ); +} + #[test] fn env_overlay_reasoning_enabled_recognises_truthy_falsy_and_ignores_garbage() { let mut cfg = Config::default(); diff --git a/src/openhuman/config/schemas.rs b/src/openhuman/config/schemas.rs index d8aa47085b..4ab73d8550 100644 --- a/src/openhuman/config/schemas.rs +++ b/src/openhuman/config/schemas.rs @@ -86,6 +86,11 @@ struct MemorySettingsUpdate { memory_window: Option, } +#[derive(Debug, Deserialize)] +struct AutonomySettingsUpdate { + max_actions_per_hour: Option, +} + #[derive(Debug, Deserialize)] struct RuntimeSettingsUpdate { kind: Option, @@ -193,6 +198,7 @@ pub fn all_controller_schemas() -> Vec { schemas("get_client_config"), schemas("update_model_settings"), schemas("update_memory_settings"), + schemas("update_autonomy_settings"), schemas("update_screen_intelligence_settings"), schemas("update_runtime_settings"), schemas("update_browser_settings"), @@ -238,6 +244,10 @@ pub fn all_registered_controllers() -> Vec { schema: schemas("update_memory_settings"), handler: handle_update_memory_settings, }, + RegisteredController { + schema: schemas("update_autonomy_settings"), + handler: handle_update_autonomy_settings, + }, RegisteredController { schema: schemas("update_screen_intelligence_settings"), handler: handle_update_screen_intelligence_settings, @@ -462,6 +472,18 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("snapshot", "Updated config snapshot.")], }, + "update_autonomy_settings" => ControllerSchema { + namespace: "config", + function: "update_autonomy_settings", + description: "Update autonomy and local action-budget settings.", + inputs: vec![FieldSchema { + name: "max_actions_per_hour", + ty: TypeSchema::Option(Box::new(TypeSchema::U64)), + comment: "Maximum tool actions allowed in the rolling one-hour local safety window.", + required: false, + }], + outputs: vec![json_output("snapshot", "Updated config snapshot.")], + }, "update_screen_intelligence_settings" => ControllerSchema { namespace: "config", function: "update_screen_intelligence_settings", @@ -997,6 +1019,38 @@ fn handle_update_memory_settings(params: Map) -> ControllerFuture }) } +fn handle_update_autonomy_settings(params: Map) -> ControllerFuture { + Box::pin(async move { + log::debug!("[config][rpc] update_autonomy_settings enter"); + let update = match deserialize_params::(params) { + Ok(update) => update, + Err(err) => { + log::warn!("[config][rpc] update_autonomy_settings invalid params: {err}"); + return Err(err); + } + }; + let patch = config_rpc::AutonomySettingsPatch { + max_actions_per_hour: update.max_actions_per_hour, + }; + let max_actions_per_hour = patch.max_actions_per_hour; + log::debug!( + "[config][rpc] update_autonomy_settings apply max_actions_per_hour={max_actions_per_hour:?}" + ); + match config_rpc::load_and_apply_autonomy_settings(patch).await { + Ok(outcome) => { + log::debug!( + "[config][rpc] update_autonomy_settings ok max_actions_per_hour={max_actions_per_hour:?}" + ); + to_json(outcome) + } + Err(err) => { + log::warn!("[config][rpc] update_autonomy_settings failed: {err}"); + Err(err) + } + } + }) +} + fn handle_update_screen_intelligence_settings(params: Map) -> ControllerFuture { Box::pin(async move { let update = deserialize_params::(params)?; diff --git a/src/openhuman/security/ops.rs b/src/openhuman/security/ops.rs index fc05d36a32..e59c3206a7 100644 --- a/src/openhuman/security/ops.rs +++ b/src/openhuman/security/ops.rs @@ -2,20 +2,35 @@ use serde_json::json; +use crate::openhuman::config::Config; use crate::openhuman::security::SecurityPolicy; use crate::rpc::RpcOutcome; -pub fn security_policy_info() -> RpcOutcome { - let policy = SecurityPolicy::default(); - let payload = json!({ +fn policy_info_payload(policy: SecurityPolicy) -> serde_json::Value { + json!({ "autonomy": policy.autonomy, "workspace_only": policy.workspace_only, "allowed_commands": policy.allowed_commands, "max_actions_per_hour": policy.max_actions_per_hour, "require_approval_for_medium_risk": policy.require_approval_for_medium_risk, "block_high_risk_commands": policy.block_high_risk_commands, - }); - RpcOutcome::single_log(payload, "security_policy_info computed") + }) +} + +pub fn security_policy_info_for_config(config: &Config) -> RpcOutcome { + let policy = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); + let payload = policy_info_payload(policy); + RpcOutcome::single_log(payload, "security_policy_info computed from active config") +} + +pub fn security_policy_info() -> RpcOutcome { + let config = Config::default(); + security_policy_info_for_config(&config) +} + +pub async fn load_and_get_security_policy_info() -> Result, String> { + let config = crate::openhuman::config::ops::load_config_with_timeout().await?; + Ok(security_policy_info_for_config(&config)) } #[cfg(test)] @@ -48,9 +63,10 @@ mod tests { } #[test] - fn security_policy_info_matches_default_policy_values() { + fn security_policy_info_matches_default_config_policy_values() { let outcome = security_policy_info(); - let default = SecurityPolicy::default(); + let config = Config::default(); + let default = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); assert_eq!(outcome.value["autonomy"], json!(default.autonomy)); assert_eq!( outcome.value["allowed_commands"], @@ -73,4 +89,14 @@ mod tests { json!(default.require_approval_for_medium_risk) ); } + + #[test] + fn security_policy_info_reflects_configured_action_budget() { + let mut config = crate::openhuman::config::Config::default(); + config.autonomy.max_actions_per_hour = 77; + + let outcome = security_policy_info_for_config(&config); + + assert_eq!(outcome.value["max_actions_per_hour"], json!(77)); + } } diff --git a/src/openhuman/security/policy.rs b/src/openhuman/security/policy.rs index 3431a548e7..160b9794b0 100644 --- a/src/openhuman/security/policy.rs +++ b/src/openhuman/security/policy.rs @@ -1036,7 +1036,10 @@ impl SecurityPolicy { "[openhuman:policy] Operation '{}' blocked: rate limit exceeded", operation_name ); - return Err("Rate limit exceeded: action budget exhausted".to_string()); + return Err(format!( + "Rate limit exceeded: action budget exhausted ({} actions/hour). Increase the limit in Settings -> Advanced -> Action Budget or wait for the rolling one-hour window to refill.", + self.max_actions_per_hour + )); } log::debug!( diff --git a/src/openhuman/security/policy_tests.rs b/src/openhuman/security/policy_tests.rs index 690338287c..d254171492 100644 --- a/src/openhuman/security/policy_tests.rs +++ b/src/openhuman/security/policy_tests.rs @@ -79,6 +79,22 @@ fn enforce_tool_operation_act_uses_rate_budget() { assert!(err.contains("Rate limit exceeded")); } +#[test] +fn action_budget_error_mentions_limit_and_settings() { + let p = SecurityPolicy { + max_actions_per_hour: 0, + ..default_policy() + }; + + let err = p + .enforce_tool_operation(ToolOperation::Act, "write_file") + .unwrap_err(); + + assert!(err.contains("Rate limit exceeded: action budget exhausted")); + assert!(err.contains("0 actions/hour")); + assert!(err.contains("Settings -> Advanced -> Action Budget")); +} + // -- is_command_allowed ------------------------------------------- #[test] diff --git a/src/openhuman/security/schemas.rs b/src/openhuman/security/schemas.rs index 446ff74acb..6047779c45 100644 --- a/src/openhuman/security/schemas.rs +++ b/src/openhuman/security/schemas.rs @@ -40,7 +40,19 @@ pub fn schemas(function: &str) -> ControllerSchema { } fn handle_policy_info(_params: Map) -> ControllerFuture { - Box::pin(async { to_json(crate::openhuman::security::rpc::security_policy_info()) }) + Box::pin(async { + log::debug!("[security][rpc] policy_info enter"); + match crate::openhuman::security::rpc::load_and_get_security_policy_info().await { + Ok(outcome) => { + log::debug!("[security][rpc] policy_info ok"); + to_json(outcome) + } + Err(err) => { + log::warn!("[security][rpc] policy_info failed: {err}"); + Err(err) + } + } + }) } fn to_json(outcome: RpcOutcome) -> Result { From 937fb4bbed877c631a4d1bf548895bce70489fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Sat, 23 May 2026 01:55:02 +0800 Subject: [PATCH 2/9] chore: order action budget env override --- .env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 67e7cfa1c7..21896e9e42 100644 --- a/.env.example +++ b/.env.example @@ -66,14 +66,14 @@ OPENHUMAN_CORE_BIN= # --------------------------------------------------------------------------- # Config overrides (override config.toml values at runtime) # --------------------------------------------------------------------------- +# [optional] Local safety cap for side-effecting tool actions in a rolling hour (default 20) +OPENHUMAN_MAX_ACTIONS_PER_HOUR=20 # [optional] Default model to use OPENHUMAN_MODEL= -# [optional] Workspace directory (default: ~/.openhuman or ~/.openhuman-staging when OPENHUMAN_APP_ENV=staging) -OPENHUMAN_WORKSPACE= # [optional] Default: 0.7 OPENHUMAN_TEMPERATURE=0.7 -# [optional] Local safety cap for side-effecting tool actions in a rolling hour (default 20) -OPENHUMAN_MAX_ACTIONS_PER_HOUR=20 +# [optional] Workspace directory (default: ~/.openhuman or ~/.openhuman-staging when OPENHUMAN_APP_ENV=staging) +OPENHUMAN_WORKSPACE= # [optional] Skill + agent tool execution timeout in seconds (default 120, max 3600) # OPENHUMAN_TOOL_TIMEOUT_SECS= # [optional] Headless update restart contract: self_replace | supervisor From cb92e1c805fcbd2bf9a3b091cafb6262d174e1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Sat, 23 May 2026 10:14:32 +0800 Subject: [PATCH 3/9] chore: rerun cancelled CI From ec55001d95020e2b97689a3e658eb0554f47903e Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sat, 23 May 2026 01:40:37 -0700 Subject: [PATCH 4/9] fix(autonomy): dedupe action-budget vs autonomy panel after #2500 landed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #2500 merged into main introduced an `AutonomyPanel` plus matching `update_autonomy_settings` RPC, `AutonomySettingsPatch` type, and `apply_autonomy_settings` ops that overlap with this PR's `ActionBudgetPanel`. The post-merge tree had: - two `openhumanUpdateAutonomySettings` TS functions (TS2323), - two `AutonomySettingsPatch` structs and two `apply_autonomy_settings` / `load_and_apply_autonomy_settings` fns in core (E0428/E0119), - two `AutonomySettingsUpdate` deserialize structs, two schema arms, two registered controllers, and two `handle_update_autonomy_settings` handlers in `config/schemas.rs`. Keep main's versions (they add `max_actions_per_hour` range validation and ship a sibling `get_autonomy_settings` RPC) and drop the PR's duplicates. Both UI panels (`AutonomyPanel`, `ActionBudgetPanel`) now share the same backend. Note: `AutonomyPanel` and `ActionBudgetPanel` still both render in DeveloperOptions for the same setting — flagging for human decision in a follow-up PR comment. --- app/src-tauri/Cargo.lock | 286 +++----------------------- app/src/utils/tauriCommands/config.ts | 16 -- src/openhuman/config/ops.rs | 31 --- src/openhuman/config/schemas.rs | 54 ----- 4 files changed, 24 insertions(+), 363 deletions(-) diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 3165838308..2dbd6bdb6b 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -703,15 +703,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "block2" version = "0.5.1" @@ -868,15 +859,6 @@ dependencies = [ "toml 0.9.12+spec-1.1.0", ] -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.62" @@ -1712,24 +1694,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "dbus-secret-service" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" -dependencies = [ - "aes", - "block-padding", - "cbc", - "dbus", - "fastrand", - "hkdf", - "num", - "once_cell", - "sha2 0.10.9", - "zeroize", -] - [[package]] name = "debugid" version = "0.8.0" @@ -3246,15 +3210,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - [[package]] name = "hmac" version = "0.12.1" @@ -3714,7 +3669,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "block-padding", "generic-array", ] @@ -3949,9 +3903,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ "byteorder", - "dbus-secret-service", + "linux-keyutils", "log", - "secret-service", "security-framework 2.11.1", "security-framework 3.7.0", "windows-sys 0.60.2", @@ -4138,6 +4091,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-keyutils" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" +dependencies = [ + "bitflags 2.11.1", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -4515,7 +4478,6 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", - "memoffset", ] [[package]] @@ -4567,7 +4529,7 @@ dependencies = [ "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus 5.15.0", + "zbus", ] [[package]] @@ -4598,39 +4560,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.2.1" @@ -4648,37 +4577,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -6980,25 +6878,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secret-service" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4d35ad99a181be0a60ffcbe85d680d98f87bdc4d7644ade319b87076b9dbfd4" -dependencies = [ - "aes", - "cbc", - "futures-util", - "generic-array", - "hkdf", - "num", - "once_cell", - "rand 0.8.6", - "serde", - "sha2 0.10.9", - "zbus 4.4.0", -] - [[package]] name = "security-framework" version = "2.11.1" @@ -8199,7 +8078,7 @@ dependencies = [ "thiserror 2.0.18", "url", "windows 0.61.3", - "zbus 5.15.0", + "zbus", ] [[package]] @@ -8214,7 +8093,7 @@ dependencies = [ "thiserror 2.0.18", "tracing", "windows-sys 0.60.2", - "zbus 5.15.0", + "zbus", ] [[package]] @@ -10633,16 +10512,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon" version = "0.8.0" @@ -10698,38 +10567,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" -dependencies = [ - "async-broadcast", - "async-process", - "async-recursion", - "async-trait", - "enumflags2", - "event-listener 5.4.1", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.29.0", - "ordered-stream", - "rand 0.8.6", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", -] - [[package]] name = "zbus" version = "5.15.0" @@ -10760,22 +10597,9 @@ dependencies = [ "uuid 1.23.1", "windows-sys 0.61.2", "winnow 1.0.2", - "zbus_macros 5.15.0", - "zbus_names 4.3.2", - "zvariant 5.11.0", -] - -[[package]] -name = "zbus_macros" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils 2.1.0", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] @@ -10788,20 +10612,9 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "zbus_names 4.3.2", - "zvariant 5.11.0", - "zvariant_utils 3.3.1", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant 4.2.0", + "zbus_names", + "zvariant", + "zvariant_utils", ] [[package]] @@ -10812,7 +10625,7 @@ checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", "winnow 1.0.2", - "zvariant 5.11.0", + "zvariant", ] [[package]] @@ -10861,20 +10674,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] [[package]] name = "zerotrie" @@ -10971,19 +10770,6 @@ dependencies = [ "zune-core", ] -[[package]] -name = "zvariant" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "zvariant_derive 4.2.0", -] - [[package]] name = "zvariant" version = "5.11.0" @@ -10994,21 +10780,8 @@ dependencies = [ "enumflags2", "serde", "winnow 1.0.2", - "zvariant_derive 5.11.0", - "zvariant_utils 3.3.1", -] - -[[package]] -name = "zvariant_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils 2.1.0", + "zvariant_derive", + "zvariant_utils", ] [[package]] @@ -11021,18 +10794,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "zvariant_utils 3.3.1", -] - -[[package]] -name = "zvariant_utils" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "zvariant_utils", ] [[package]] diff --git a/app/src/utils/tauriCommands/config.ts b/app/src/utils/tauriCommands/config.ts index 01adb20734..06bbe100bc 100644 --- a/app/src/utils/tauriCommands/config.ts +++ b/app/src/utils/tauriCommands/config.ts @@ -114,10 +114,6 @@ export interface MemorySettingsUpdate { memory_window?: MemoryContextWindow | null; } -export interface AutonomySettingsUpdate { - max_actions_per_hour?: number | null; -} - export interface RuntimeSettingsUpdate { kind?: string | null; reasoning_enabled?: boolean | null; @@ -265,18 +261,6 @@ export async function openhumanUpdateMemorySettings( }); } -export async function openhumanUpdateAutonomySettings( - update: AutonomySettingsUpdate -): Promise> { - if (!isTauri()) { - throw new Error('Not running in Tauri'); - } - return await callCoreRpc>({ - method: CORE_RPC_METHODS.configUpdateAutonomySettings, - params: update, - }); -} - export async function openhumanUpdateRuntimeSettings( update: RuntimeSettingsUpdate ): Promise> { diff --git a/src/openhuman/config/ops.rs b/src/openhuman/config/ops.rs index 90469915bc..3a77f81e50 100644 --- a/src/openhuman/config/ops.rs +++ b/src/openhuman/config/ops.rs @@ -379,11 +379,6 @@ pub struct MeetSettingsPatch { pub auto_orchestrator_handoff: Option, } -#[derive(Debug, Clone, Default)] -pub struct AutonomySettingsPatch { - pub max_actions_per_hour: Option, -} - #[derive(Debug, Clone, Default)] pub struct LocalAiSettingsPatch { pub runtime_enabled: Option, @@ -640,24 +635,6 @@ pub async fn apply_memory_settings( } /// Updates local autonomy and action budget settings in the configuration. -pub async fn apply_autonomy_settings( - config: &mut Config, - update: AutonomySettingsPatch, -) -> Result, String> { - if let Some(max_actions_per_hour) = update.max_actions_per_hour { - config.autonomy.max_actions_per_hour = max_actions_per_hour; - } - config.save().await.map_err(|e| e.to_string())?; - let snapshot = snapshot_config_json(config)?; - Ok(RpcOutcome::new( - snapshot, - vec![format!( - "autonomy settings saved to {}", - config.config_path.display() - )], - )) -} - /// Updates the screen intelligence settings in the configuration. pub async fn apply_screen_intelligence_settings( config: &mut Config, @@ -772,14 +749,6 @@ pub async fn load_and_apply_memory_settings( apply_memory_settings(&mut config, update).await } -/// Loads the configuration, applies autonomy settings updates, and saves it. -pub async fn load_and_apply_autonomy_settings( - update: AutonomySettingsPatch, -) -> Result, String> { - let mut config = load_config_with_timeout().await?; - apply_autonomy_settings(&mut config, update).await -} - /// Loads the configuration, applies screen intelligence settings updates, and saves it. pub async fn load_and_apply_screen_intelligence_settings( update: ScreenIntelligenceSettingsPatch, diff --git a/src/openhuman/config/schemas.rs b/src/openhuman/config/schemas.rs index bc61cd13e8..526138c4fe 100644 --- a/src/openhuman/config/schemas.rs +++ b/src/openhuman/config/schemas.rs @@ -86,11 +86,6 @@ struct MemorySettingsUpdate { memory_window: Option, } -#[derive(Debug, Deserialize)] -struct AutonomySettingsUpdate { - max_actions_per_hour: Option, -} - #[derive(Debug, Deserialize)] struct RuntimeSettingsUpdate { kind: Option, @@ -203,7 +198,6 @@ pub fn all_controller_schemas() -> Vec { schemas("get_client_config"), schemas("update_model_settings"), schemas("update_memory_settings"), - schemas("update_autonomy_settings"), schemas("update_screen_intelligence_settings"), schemas("update_runtime_settings"), schemas("update_browser_settings"), @@ -251,10 +245,6 @@ pub fn all_registered_controllers() -> Vec { schema: schemas("update_memory_settings"), handler: handle_update_memory_settings, }, - RegisteredController { - schema: schemas("update_autonomy_settings"), - handler: handle_update_autonomy_settings, - }, RegisteredController { schema: schemas("update_screen_intelligence_settings"), handler: handle_update_screen_intelligence_settings, @@ -487,18 +477,6 @@ pub fn schemas(function: &str) -> ControllerSchema { ], outputs: vec![json_output("snapshot", "Updated config snapshot.")], }, - "update_autonomy_settings" => ControllerSchema { - namespace: "config", - function: "update_autonomy_settings", - description: "Update autonomy and local action-budget settings.", - inputs: vec![FieldSchema { - name: "max_actions_per_hour", - ty: TypeSchema::Option(Box::new(TypeSchema::U64)), - comment: "Maximum tool actions allowed in the rolling one-hour local safety window.", - required: false, - }], - outputs: vec![json_output("snapshot", "Updated config snapshot.")], - }, "update_screen_intelligence_settings" => ControllerSchema { namespace: "config", function: "update_screen_intelligence_settings", @@ -1079,38 +1057,6 @@ fn handle_update_memory_settings(params: Map) -> ControllerFuture }) } -fn handle_update_autonomy_settings(params: Map) -> ControllerFuture { - Box::pin(async move { - log::debug!("[config][rpc] update_autonomy_settings enter"); - let update = match deserialize_params::(params) { - Ok(update) => update, - Err(err) => { - log::warn!("[config][rpc] update_autonomy_settings invalid params: {err}"); - return Err(err); - } - }; - let patch = config_rpc::AutonomySettingsPatch { - max_actions_per_hour: update.max_actions_per_hour, - }; - let max_actions_per_hour = patch.max_actions_per_hour; - log::debug!( - "[config][rpc] update_autonomy_settings apply max_actions_per_hour={max_actions_per_hour:?}" - ); - match config_rpc::load_and_apply_autonomy_settings(patch).await { - Ok(outcome) => { - log::debug!( - "[config][rpc] update_autonomy_settings ok max_actions_per_hour={max_actions_per_hour:?}" - ); - to_json(outcome) - } - Err(err) => { - log::warn!("[config][rpc] update_autonomy_settings failed: {err}"); - Err(err) - } - } - }) -} - fn handle_update_screen_intelligence_settings(params: Map) -> ControllerFuture { Box::pin(async move { let update = deserialize_params::(params)?; From 472008bf2b39af96c1059fc48918a919773afbdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Sat, 23 May 2026 20:05:39 +0800 Subject: [PATCH 5/9] fix(settings): remove duplicate action budget panel --- .../settings/panels/ActionBudgetPanel.tsx | 167 ------------------ .../settings/panels/DeveloperOptionsPanel.tsx | 16 -- .../__tests__/ActionBudgetPanel.test.tsx | 90 ---------- app/src/lib/i18n/chunks/ar-5.ts | 13 -- app/src/lib/i18n/chunks/bn-5.ts | 13 -- app/src/lib/i18n/chunks/de-5.ts | 13 -- app/src/lib/i18n/chunks/en-5.ts | 13 -- app/src/lib/i18n/chunks/es-5.ts | 13 -- app/src/lib/i18n/chunks/fr-5.ts | 13 -- app/src/lib/i18n/chunks/hi-5.ts | 13 -- app/src/lib/i18n/chunks/id-5.ts | 13 -- app/src/lib/i18n/chunks/it-5.ts | 13 -- app/src/lib/i18n/chunks/ko-5.ts | 13 -- app/src/lib/i18n/chunks/pt-5.ts | 13 -- app/src/lib/i18n/chunks/ru-5.ts | 13 -- app/src/lib/i18n/chunks/zh-CN-5.ts | 13 -- app/src/lib/i18n/en.ts | 13 -- app/src/pages/Settings.tsx | 2 - .../developing/architecture/agent-harness.md | 2 +- src/openhuman/config/ops.rs | 11 +- src/openhuman/security/policy.rs | 2 +- src/openhuman/security/policy_tests.rs | 2 +- 22 files changed, 8 insertions(+), 466 deletions(-) delete mode 100644 app/src/components/settings/panels/ActionBudgetPanel.tsx delete mode 100644 app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx diff --git a/app/src/components/settings/panels/ActionBudgetPanel.tsx b/app/src/components/settings/panels/ActionBudgetPanel.tsx deleted file mode 100644 index dc85790c7f..0000000000 --- a/app/src/components/settings/panels/ActionBudgetPanel.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; - -import { useT } from '../../../lib/i18n/I18nContext'; -import { - type ConfigSnapshot, - isTauri, - openhumanGetConfig, - openhumanUpdateAutonomySettings, -} from '../../../utils/tauriCommands'; -import SettingsHeader from '../components/SettingsHeader'; -import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; - -const DEFAULT_ACTION_BUDGET = 20; -const MIN_ACTION_BUDGET = 1; -const MAX_ACTION_BUDGET = 1000; - -function actionBudgetFromSnapshot(snapshot: ConfigSnapshot): number { - const autonomy = snapshot.config.autonomy; - if (autonomy && typeof autonomy === 'object' && 'max_actions_per_hour' in autonomy) { - const value = (autonomy as { max_actions_per_hour?: unknown }).max_actions_per_hour; - if (typeof value === 'number' && Number.isFinite(value)) { - return Math.trunc(value); - } - } - return DEFAULT_ACTION_BUDGET; -} - -function parseBudgetInput(value: string): number | null { - const parsed = Number(value); - if (!Number.isInteger(parsed) || parsed < MIN_ACTION_BUDGET || parsed > MAX_ACTION_BUDGET) { - return null; - } - return parsed; -} - -const ActionBudgetPanel = () => { - const { t } = useT(); - const tauriRuntime = isTauri(); - const { navigateBack, breadcrumbs } = useSettingsNavigation(); - const [value, setValue] = useState(String(DEFAULT_ACTION_BUDGET)); - const [activeLimit, setActiveLimit] = useState(DEFAULT_ACTION_BUDGET); - const [isLoading, setIsLoading] = useState(true); - const [isSaving, setIsSaving] = useState(false); - const [error, setError] = useState(null); - const [status, setStatus] = useState(null); - - const loadConfig = useCallback(async () => { - if (!tauriRuntime) { - setIsLoading(false); - setError(t('settings.actionBudget.desktopOnly')); - return; - } - - setIsLoading(true); - setError(null); - try { - const response = await openhumanGetConfig(); - const nextLimit = actionBudgetFromSnapshot(response.result); - setActiveLimit(nextLimit); - setValue(String(nextLimit)); - } catch (err) { - setError(err instanceof Error ? err.message : t('settings.actionBudget.loadFailed')); - } finally { - setIsLoading(false); - } - }, [t, tauriRuntime]); - - useEffect(() => { - void loadConfig(); - }, [loadConfig]); - - const save = async () => { - if (!tauriRuntime) { - setError(t('settings.actionBudget.desktopOnly')); - return; - } - - const parsed = parseBudgetInput(value); - setStatus(null); - if (parsed === null) { - setError(t('settings.actionBudget.validation')); - return; - } - - setIsSaving(true); - setError(null); - try { - const response = await openhumanUpdateAutonomySettings({ max_actions_per_hour: parsed }); - const savedLimit = actionBudgetFromSnapshot(response.result); - setActiveLimit(savedLimit); - setValue(String(savedLimit)); - setStatus(t('settings.actionBudget.saved')); - } catch (err) { - setError(err instanceof Error ? err.message : t('settings.actionBudget.saveFailed')); - } finally { - setIsSaving(false); - } - }; - - return ( -
- - -
-
-
-
-

- {t('settings.actionBudget.activeLimit')} -

-
- {t('settings.actionBudget.sideEffectingActions')} -
-
-
- {activeLimit} {t('settings.actionBudget.actionsPerHour')} -
-
- - - -
-
- {isLoading && ( - {t('common.loading')} - )} - {status && {status}} - {error && {error}} -
- -
-
-
-
- ); -}; - -export default ActionBudgetPanel; diff --git a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx index 11cdf93d15..b2c15b7d90 100644 --- a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx +++ b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx @@ -87,22 +87,6 @@ const developerItems = [ ), }, - { - id: 'action-budget', - titleKey: 'settings.developerMenu.actionBudget.title', - descriptionKey: 'settings.developerMenu.actionBudget.desc', - route: 'action-budget', - icon: ( - - - - ), - }, { id: 'agent-chat', titleKey: 'settings.developerMenu.agentChat.title', diff --git a/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx b/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx deleted file mode 100644 index dcfd08d3a7..0000000000 --- a/app/src/components/settings/panels/__tests__/ActionBudgetPanel.test.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { renderWithProviders } from '../../../../test/test-utils'; -import ActionBudgetPanel from '../ActionBudgetPanel'; - -const hoisted = vi.hoisted(() => ({ - mockGetConfig: vi.fn(), - mockUpdateAutonomySettings: vi.fn(), - mockIsTauri: vi.fn(() => true), -})); - -vi.mock('../../../../utils/tauriCommands', () => ({ - isTauri: hoisted.mockIsTauri, - openhumanGetConfig: hoisted.mockGetConfig, - openhumanUpdateAutonomySettings: hoisted.mockUpdateAutonomySettings, -})); - -vi.mock('../../hooks/useSettingsNavigation', () => ({ - useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }), -})); - -const configResponse = (max_actions_per_hour: number) => ({ - result: { - config: { autonomy: { max_actions_per_hour } }, - workspace_dir: '/tmp/openhuman/workspace', - config_path: '/tmp/openhuman/config.toml', - }, - logs: [], -}); - -describe('ActionBudgetPanel', () => { - beforeEach(() => { - hoisted.mockGetConfig.mockReset(); - hoisted.mockUpdateAutonomySettings.mockReset(); - hoisted.mockIsTauri.mockReturnValue(true); - }); - - it('loads and displays the active action budget', async () => { - hoisted.mockGetConfig.mockResolvedValue(configResponse(64)); - - renderWithProviders(); - - await waitFor(() => { - expect(screen.getByDisplayValue('64')).toBeInTheDocument(); - }); - expect(screen.getByText('64 actions/hour')).toBeInTheDocument(); - }); - - it('saves a changed action budget through config RPC', async () => { - hoisted.mockGetConfig.mockResolvedValue(configResponse(20)); - hoisted.mockUpdateAutonomySettings.mockResolvedValue(configResponse(48)); - - renderWithProviders(); - - const input = await screen.findByLabelText(/actions per hour/i); - fireEvent.change(input, { target: { value: '48' } }); - fireEvent.click(screen.getByRole('button', { name: /save/i })); - - await waitFor(() => { - expect(hoisted.mockUpdateAutonomySettings).toHaveBeenCalledWith({ max_actions_per_hour: 48 }); - }); - expect(await screen.findByText(/saved/i)).toBeInTheDocument(); - expect(screen.getByText('48 actions/hour')).toBeInTheDocument(); - }); - - it('rejects values below one before calling RPC', async () => { - hoisted.mockGetConfig.mockResolvedValue(configResponse(20)); - - renderWithProviders(); - - const input = await screen.findByLabelText(/actions per hour/i); - fireEvent.change(input, { target: { value: '0' } }); - fireEvent.click(screen.getByRole('button', { name: /save/i })); - - expect(await screen.findByText(/enter a whole number from 1 to 1000/i)).toBeInTheDocument(); - expect(hoisted.mockUpdateAutonomySettings).not.toHaveBeenCalled(); - }); - - it('disables save outside the desktop runtime', async () => { - hoisted.mockIsTauri.mockReturnValue(false); - - renderWithProviders(); - - expect(await screen.findByText(/available in the desktop app/i)).toBeInTheDocument(); - expect(screen.getByRole('button', { name: /save/i })).toBeDisabled(); - expect(hoisted.mockGetConfig).not.toHaveBeenCalled(); - expect(hoisted.mockUpdateAutonomySettings).not.toHaveBeenCalled(); - }); -}); diff --git a/app/src/lib/i18n/chunks/ar-5.ts b/app/src/lib/i18n/chunks/ar-5.ts index 7fba8a4378..b3637ed29c 100644 --- a/app/src/lib/i18n/chunks/ar-5.ts +++ b/app/src/lib/i18n/chunks/ar-5.ts @@ -172,8 +172,6 @@ const ar5: TranslationMap = { 'settings.developerMenu.tools.title': 'الأدوات', 'settings.developerMenu.tools.desc': 'تفعيل أو تعطيل الإمكانات التي يمكن لـ OpenHuman استخدامها نيابةً عنك', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'دردشة الوكيل', 'settings.developerMenu.agentChat.desc': 'اختبار محادثة الوكيل مع تجاوزات النموذج ودرجة الحرارة', 'settings.developerMenu.cronJobs.title': 'مهام Cron', @@ -198,17 +196,6 @@ const ar5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'مشغلات التكامل', 'settings.developerMenu.integrationTriggers.desc': 'تكوين إعدادات فرز الذكاء الاصطناعي لمشغلات تكامل Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'اختر الفاتح أو الداكن أو مطابقة سمة النظام', 'settings.mascot.active': 'نشط', 'settings.mascot.characterDesc': 'وصف الشخصية', diff --git a/app/src/lib/i18n/chunks/bn-5.ts b/app/src/lib/i18n/chunks/bn-5.ts index 141761feb7..77b2378087 100644 --- a/app/src/lib/i18n/chunks/bn-5.ts +++ b/app/src/lib/i18n/chunks/bn-5.ts @@ -176,8 +176,6 @@ const bn5: TranslationMap = { 'settings.developerMenu.tools.title': 'টুলস', 'settings.developerMenu.tools.desc': 'OpenHuman আপনার পক্ষ থেকে যে সক্ষমতাগুলি ব্যবহার করতে পারে সেগুলি চালু বা বন্ধ করুন', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'এজেন্ট চ্যাট', 'settings.developerMenu.agentChat.desc': 'মডেল এবং টেম্পারেচার ওভাররাইডসহ এজেন্ট কথোপকথন পরীক্ষা করুন', @@ -204,17 +202,6 @@ const bn5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'ইন্টিগ্রেশন ট্রিগার', 'settings.developerMenu.integrationTriggers.desc': 'Composio ইন্টিগ্রেশন ট্রিগারের জন্য AI ট্রায়াজ সেটিংস কনফিগার করুন', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'লাইট, ডার্ক বা সিস্টেম থিমের সাথে মিল বেছে নিন', 'settings.mascot.active': 'সক্রিয়', 'settings.mascot.characterDesc': 'চরিত্রের বিবরণ', diff --git a/app/src/lib/i18n/chunks/de-5.ts b/app/src/lib/i18n/chunks/de-5.ts index 6df8f02811..3b6e325510 100644 --- a/app/src/lib/i18n/chunks/de-5.ts +++ b/app/src/lib/i18n/chunks/de-5.ts @@ -184,8 +184,6 @@ const de5: TranslationMap = { 'settings.developerMenu.tools.title': 'Werkzeuge', 'settings.developerMenu.tools.desc': 'Aktiviere oder deaktiviere Funktionen, die OpenHuman in deinem Namen nutzen kann', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agenten-Chat', 'settings.developerMenu.agentChat.desc': 'Test-Agent-Konversation mit Modell- und Temperaturüberschreibungen', @@ -238,17 +236,6 @@ const de5: TranslationMap = { 'settings.mcpServer.clientZed': 'Zed', 'settings.mcpServer.configFilePath': 'Konfigurationsdatei', 'settings.mcpServer.clientSelectorAriaLabel': 'MCP-Client-Auswahl', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Wähle hell, dunkel oder passend zu deinem Systemthema', 'settings.mascot.active': 'Aktiv', 'settings.mascot.characterDesc': 'Charakterbeschreibung', diff --git a/app/src/lib/i18n/chunks/en-5.ts b/app/src/lib/i18n/chunks/en-5.ts index 8f53276f9d..ae4288bb5b 100644 --- a/app/src/lib/i18n/chunks/en-5.ts +++ b/app/src/lib/i18n/chunks/en-5.ts @@ -175,8 +175,6 @@ const en5: TranslationMap = { 'settings.developerMenu.tools.title': 'Tools', 'settings.developerMenu.tools.desc': 'Enable or disable capabilities OpenHuman can use on your behalf', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agent Chat', 'settings.developerMenu.agentChat.desc': 'Test agent conversation with model and temperature overrides', @@ -202,17 +200,6 @@ const en5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Integration Triggers', 'settings.developerMenu.integrationTriggers.desc': 'Configure AI triage settings for Composio integration triggers', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Pick light, dark, or match your system theme', 'settings.mascot.active': 'Active', 'settings.mascot.characterDesc': 'Character desc', diff --git a/app/src/lib/i18n/chunks/es-5.ts b/app/src/lib/i18n/chunks/es-5.ts index 3a16d05557..eeea2351bb 100644 --- a/app/src/lib/i18n/chunks/es-5.ts +++ b/app/src/lib/i18n/chunks/es-5.ts @@ -179,8 +179,6 @@ const es5: TranslationMap = { 'settings.developerMenu.tools.title': 'Herramientas', 'settings.developerMenu.tools.desc': 'Activa o desactiva las capacidades que OpenHuman puede usar en tu nombre', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat del agente', 'settings.developerMenu.agentChat.desc': 'Prueba conversaciones del agente con ajustes de modelo y temperatura', @@ -208,17 +206,6 @@ const es5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Disparadores de integración', 'settings.developerMenu.integrationTriggers.desc': 'Configura los ajustes de triaje de IA para disparadores de integración de Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Elige claro, oscuro o igualar el tema del sistema', 'settings.mascot.active': 'Activo', 'settings.mascot.characterDesc': 'Descripción del personaje', diff --git a/app/src/lib/i18n/chunks/fr-5.ts b/app/src/lib/i18n/chunks/fr-5.ts index 6f2be1e436..4b90263bbc 100644 --- a/app/src/lib/i18n/chunks/fr-5.ts +++ b/app/src/lib/i18n/chunks/fr-5.ts @@ -181,8 +181,6 @@ const fr5: TranslationMap = { 'settings.developerMenu.tools.title': 'Outils', 'settings.developerMenu.tools.desc': "Active ou désactive les capacités qu'OpenHuman peut utiliser en ton nom", - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat agent', 'settings.developerMenu.agentChat.desc': 'Teste une conversation agent avec des remplacements de modèle et de température', @@ -210,17 +208,6 @@ const fr5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': "Déclencheurs d'intégration", 'settings.developerMenu.integrationTriggers.desc': "Configurer les paramètres de triage IA pour les déclencheurs d'intégration Composio", - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Choisis clair, sombre ou le thème du système', 'settings.mascot.active': 'Actif', 'settings.mascot.characterDesc': 'Description du personnage', diff --git a/app/src/lib/i18n/chunks/hi-5.ts b/app/src/lib/i18n/chunks/hi-5.ts index 51cdf35cfe..aeb928ceec 100644 --- a/app/src/lib/i18n/chunks/hi-5.ts +++ b/app/src/lib/i18n/chunks/hi-5.ts @@ -176,8 +176,6 @@ const hi5: TranslationMap = { 'settings.developerMenu.tools.title': 'टूल्स', 'settings.developerMenu.tools.desc': 'OpenHuman आपकी ओर से जिन क्षमताओं का उपयोग कर सकता है, उन्हें सक्षम या अक्षम करें', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'एजेंट चैट', 'settings.developerMenu.agentChat.desc': 'मॉडल और तापमान ओवरराइड के साथ एजेंट वार्तालाप का परीक्षण करें', @@ -204,17 +202,6 @@ const hi5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'इंटीग्रेशन ट्रिगर्स', 'settings.developerMenu.integrationTriggers.desc': 'Composio इंटीग्रेशन ट्रिगर्स के लिए AI ट्रायेज सेटिंग्स कॉन्फ़िगर करें', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'लाइट, डार्क, या सिस्टम थीम से मिलान चुनें', 'settings.mascot.active': 'एक्टिव', 'settings.mascot.characterDesc': 'कैरेक्टर विवरण', diff --git a/app/src/lib/i18n/chunks/id-5.ts b/app/src/lib/i18n/chunks/id-5.ts index b95488e29d..a55b6b860c 100644 --- a/app/src/lib/i18n/chunks/id-5.ts +++ b/app/src/lib/i18n/chunks/id-5.ts @@ -177,8 +177,6 @@ const id5: TranslationMap = { 'settings.developerMenu.tools.title': 'Alat', 'settings.developerMenu.tools.desc': 'Aktifkan atau nonaktifkan kemampuan yang bisa digunakan OpenHuman atas nama Anda', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat Agen', 'settings.developerMenu.agentChat.desc': 'Uji percakapan agen dengan override model dan temperatur', @@ -204,17 +202,6 @@ const id5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Pemicu Integrasi', 'settings.developerMenu.integrationTriggers.desc': 'Atur pengaturan triase AI untuk pemicu integrasi Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Pilih terang, gelap, atau ikuti tema sistem', 'settings.mascot.active': 'Aktif', 'settings.mascot.characterDesc': 'Deskripsi karakter', diff --git a/app/src/lib/i18n/chunks/it-5.ts b/app/src/lib/i18n/chunks/it-5.ts index 2cfb7c991f..d38c4934df 100644 --- a/app/src/lib/i18n/chunks/it-5.ts +++ b/app/src/lib/i18n/chunks/it-5.ts @@ -179,8 +179,6 @@ const it5: TranslationMap = { 'settings.developerMenu.tools.title': 'Strumenti', 'settings.developerMenu.tools.desc': 'Abilita o disabilita le capacità che OpenHuman può usare per tuo conto', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat agente', 'settings.developerMenu.agentChat.desc': "Testa conversazioni dell'agente con override di modello e temperatura", @@ -208,17 +206,6 @@ const it5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Trigger di integrazione', 'settings.developerMenu.integrationTriggers.desc': 'Configura le impostazioni di triage IA per i trigger di integrazione Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Scegli chiaro, scuro o il tema del sistema', 'settings.mascot.active': 'Attivo', 'settings.mascot.characterDesc': 'Descrizione personaggio', diff --git a/app/src/lib/i18n/chunks/ko-5.ts b/app/src/lib/i18n/chunks/ko-5.ts index b6ec5cc599..249688d2ee 100644 --- a/app/src/lib/i18n/chunks/ko-5.ts +++ b/app/src/lib/i18n/chunks/ko-5.ts @@ -450,19 +450,6 @@ const ko5: TranslationMap = { 'settings.mascot.colorYellow': '노랑', 'settings.mascot.libraryUnavailable': 'OpenHuman 라이브러리를 사용할 수 없음', 'settings.mascot.title': 'OpenHuman', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.developerMenu.mcpServer.title': 'MCP Server', 'settings.developerMenu.mcpServer.desc': 'Configure external MCP clients to connect to OpenHuman', 'settings.developerMenu.autonomy.title': '에이전트 자율성', diff --git a/app/src/lib/i18n/chunks/pt-5.ts b/app/src/lib/i18n/chunks/pt-5.ts index 0944fa9e13..50dacf095a 100644 --- a/app/src/lib/i18n/chunks/pt-5.ts +++ b/app/src/lib/i18n/chunks/pt-5.ts @@ -180,8 +180,6 @@ const pt5: TranslationMap = { 'settings.developerMenu.tools.title': 'Ferramentas', 'settings.developerMenu.tools.desc': 'Ative ou desative capacidades que o OpenHuman pode usar em seu nome', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Chat do agente', 'settings.developerMenu.agentChat.desc': 'Teste conversas do agente com substituições de modelo e temperatura', @@ -209,17 +207,6 @@ const pt5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Gatilhos de integração', 'settings.developerMenu.integrationTriggers.desc': 'Configure as opções de triagem por IA para gatilhos de integração Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Escolha claro, escuro ou o tema do sistema', 'settings.mascot.active': 'Ativo', 'settings.mascot.characterDesc': 'Descrição do personagem', diff --git a/app/src/lib/i18n/chunks/ru-5.ts b/app/src/lib/i18n/chunks/ru-5.ts index 17be24806a..65cd652842 100644 --- a/app/src/lib/i18n/chunks/ru-5.ts +++ b/app/src/lib/i18n/chunks/ru-5.ts @@ -177,8 +177,6 @@ const ru5: TranslationMap = { 'settings.developerMenu.tools.title': 'Инструменты', 'settings.developerMenu.tools.desc': 'Включайте или отключайте возможности, которые OpenHuman может использовать от вашего имени', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Чат агента', 'settings.developerMenu.agentChat.desc': 'Тестируйте разговор агента с переопределениями модели и температуры', @@ -205,17 +203,6 @@ const ru5: TranslationMap = { 'settings.developerMenu.integrationTriggers.title': 'Триггеры интеграций', 'settings.developerMenu.integrationTriggers.desc': 'Настройка параметров AI-триажа для триггеров интеграций Composio', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.appearance.menuDesc': 'Выберите светлую, темную или системную тему', 'settings.mascot.active': 'Активно', 'settings.mascot.characterDesc': 'Описание персонажа', diff --git a/app/src/lib/i18n/chunks/zh-CN-5.ts b/app/src/lib/i18n/chunks/zh-CN-5.ts index 378b8e4f54..36de298f41 100644 --- a/app/src/lib/i18n/chunks/zh-CN-5.ts +++ b/app/src/lib/i18n/chunks/zh-CN-5.ts @@ -167,8 +167,6 @@ const zhCN5: TranslationMap = { 'settings.developerMenu.messagingChannels.desc': '配置 Telegram/Discord 认证模式和默认渠道路由', 'settings.developerMenu.tools.title': '工具', 'settings.developerMenu.tools.desc': '启用或停用 OpenHuman 可代表你使用的能力', - 'settings.developerMenu.actionBudget.title': '行动预算', - 'settings.developerMenu.actionBudget.desc': '设置工具行动的本地每小时上限', 'settings.developerMenu.agentChat.title': '智能体聊天', 'settings.developerMenu.agentChat.desc': '使用模型和温度覆盖测试智能体对话', 'settings.developerMenu.cronJobs.title': '定时任务', @@ -188,17 +186,6 @@ const zhCN5: TranslationMap = { '使用你自己的 Composio API 密钥并将调用直接路由到 backend.composio.dev', 'settings.developerMenu.integrationTriggers.title': '集成触发器', 'settings.developerMenu.integrationTriggers.desc': '配置 Composio 集成触发器的 AI 分流设置', - 'settings.actionBudget.title': '行动预算', - 'settings.actionBudget.activeLimit': '当前上限', - 'settings.actionBudget.sideEffectingActions': '会产生副作用的工具行动', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': '每小时行动数', - 'settings.actionBudget.desktopOnly': '行动预算仅在桌面应用中可用。', - 'settings.actionBudget.loadFailed': '无法加载行动预算。', - 'settings.actionBudget.saveFailed': '无法保存行动预算。', - 'settings.actionBudget.validation': '请输入 1 到 1000 的整数。', - 'settings.actionBudget.saved': '已保存', - 'settings.actionBudget.saving': '保存中...', 'settings.appearance.menuDesc': '选择浅色、深色或跟随系统主题', 'settings.mascot.active': '活跃', 'settings.mascot.characterDesc': '选择你的 OpenHuman 角色', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 4e459bf68d..22932e474d 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -1936,8 +1936,6 @@ const en: TranslationMap = { 'settings.developerMenu.tools.title': 'Tools', 'settings.developerMenu.tools.desc': 'Enable or disable capabilities OpenHuman can use on your behalf', - 'settings.developerMenu.actionBudget.title': 'Action Budget', - 'settings.developerMenu.actionBudget.desc': 'Set the local hourly cap for tool actions', 'settings.developerMenu.agentChat.title': 'Agent Chat', 'settings.developerMenu.agentChat.desc': 'Test agent conversation with model and temperature overrides', @@ -1965,17 +1963,6 @@ const en: TranslationMap = { 'Configure AI triage settings for Composio integration triggers', 'settings.developerMenu.mcpServer.title': 'MCP Server', 'settings.developerMenu.mcpServer.desc': 'Configure external MCP clients to connect to OpenHuman', - 'settings.actionBudget.title': 'Action Budget', - 'settings.actionBudget.activeLimit': 'Active limit', - 'settings.actionBudget.sideEffectingActions': 'Side-effecting tool actions', - 'settings.actionBudget.actionsPerHour': 'actions/hour', - 'settings.actionBudget.actionsPerHourLabel': 'Actions per hour', - 'settings.actionBudget.desktopOnly': 'Action Budget is available in the desktop app.', - 'settings.actionBudget.loadFailed': 'Failed to load action budget.', - 'settings.actionBudget.saveFailed': 'Failed to save action budget.', - 'settings.actionBudget.validation': 'Enter a whole number from 1 to 1000.', - 'settings.actionBudget.saved': 'Saved', - 'settings.actionBudget.saving': 'Saving...', 'settings.developerMenu.autonomy.title': 'Agent autonomy', 'settings.developerMenu.autonomy.desc': 'Tool action rate limits and safety thresholds', 'settings.mcpServer.title': 'MCP Server', diff --git a/app/src/pages/Settings.tsx b/app/src/pages/Settings.tsx index 5c80a067cc..1af00cef2d 100644 --- a/app/src/pages/Settings.tsx +++ b/app/src/pages/Settings.tsx @@ -2,7 +2,6 @@ import type { ReactNode } from 'react'; import { Navigate, Route, Routes } from 'react-router-dom'; import AboutPanel from '../components/settings/panels/AboutPanel'; -import ActionBudgetPanel from '../components/settings/panels/ActionBudgetPanel'; import AgentChatPanel from '../components/settings/panels/AgentChatPanel'; import AIPanel from '../components/settings/panels/AIPanel'; import AppearancePanel from '../components/settings/panels/AppearancePanel'; @@ -356,7 +355,6 @@ const Settings = () => { )} /> {/* Developer Options */} )} /> - )} /> )} /> )} /> Advanced -> Action Budget, or operators can override it with `OPENHUMAN_MAX_ACTIONS_PER_HOUR`. +* **Action budget policy** - `SecurityPolicy` enforces `config.autonomy.max_actions_per_hour` for side-effecting tool operations. Users can tune it in Settings -> Advanced -> Agent autonomy, or operators can override it with `OPENHUMAN_MAX_ACTIONS_PER_HOUR`. A hook returning `Stop` aborts the loop with a clear reason the caller can surface to the user. Stop hooks are distinct from interrupts (next section): they're policy-driven, not user-driven. diff --git a/src/openhuman/config/ops.rs b/src/openhuman/config/ops.rs index 3a77f81e50..5285a5b74b 100644 --- a/src/openhuman/config/ops.rs +++ b/src/openhuman/config/ops.rs @@ -324,11 +324,6 @@ pub struct ModelSettingsPatch { pub subconscious_provider: Option, } -#[derive(Debug, Clone, Default)] -pub struct AutonomySettingsPatch { - pub max_actions_per_hour: Option, -} - #[derive(Debug, Clone, Default)] pub struct MemorySettingsPatch { pub backend: Option, @@ -379,6 +374,11 @@ pub struct MeetSettingsPatch { pub auto_orchestrator_handoff: Option, } +#[derive(Debug, Clone, Default)] +pub struct AutonomySettingsPatch { + pub max_actions_per_hour: Option, +} + #[derive(Debug, Clone, Default)] pub struct LocalAiSettingsPatch { pub runtime_enabled: Option, @@ -634,7 +634,6 @@ pub async fn apply_memory_settings( )) } -/// Updates local autonomy and action budget settings in the configuration. /// Updates the screen intelligence settings in the configuration. pub async fn apply_screen_intelligence_settings( config: &mut Config, diff --git a/src/openhuman/security/policy.rs b/src/openhuman/security/policy.rs index 160b9794b0..5a03181c68 100644 --- a/src/openhuman/security/policy.rs +++ b/src/openhuman/security/policy.rs @@ -1037,7 +1037,7 @@ impl SecurityPolicy { operation_name ); return Err(format!( - "Rate limit exceeded: action budget exhausted ({} actions/hour). Increase the limit in Settings -> Advanced -> Action Budget or wait for the rolling one-hour window to refill.", + "Rate limit exceeded: action budget exhausted ({} actions/hour). Increase the limit in Settings -> Advanced -> Agent autonomy or wait for the rolling one-hour window to refill.", self.max_actions_per_hour )); } diff --git a/src/openhuman/security/policy_tests.rs b/src/openhuman/security/policy_tests.rs index d254171492..6329628bb7 100644 --- a/src/openhuman/security/policy_tests.rs +++ b/src/openhuman/security/policy_tests.rs @@ -92,7 +92,7 @@ fn action_budget_error_mentions_limit_and_settings() { assert!(err.contains("Rate limit exceeded: action budget exhausted")); assert!(err.contains("0 actions/hour")); - assert!(err.contains("Settings -> Advanced -> Action Budget")); + assert!(err.contains("Settings -> Advanced -> Agent autonomy")); } // -- is_command_allowed ------------------------------------------- From e8556c0d96beaf810b44ef8b9e7d2cd14ceb7c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Sat, 23 May 2026 20:39:10 +0800 Subject: [PATCH 6/9] chore: retrigger action budget ci From cba83202f04cdd008e4c7c30403ad6ae92c6a7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Tue, 26 May 2026 07:45:39 +0800 Subject: [PATCH 7/9] chore: rerun cancelled windows ci From 641c208a8a560a36459655229da95cc8ab90428a Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Tue, 26 May 2026 15:50:32 +0530 Subject: [PATCH 8/9] refactor(security): remove misleading public security_policy_info wrapper - Inline the two tests that called the wrapper to use security_policy_info_for_config(&Config::default()) directly, making it clear they test the default-config path. - Remove the security_policy_info() wrapper: it was pub but returned default-config data (not the active loaded config), creating a misleading public surface alongside the correct load_and_get_security_policy_info(). The RPC handler already calls the async loader; nothing in production used the wrapper. - Document OPENHUMAN_MAX_ACTIONS_PER_HOUR=0 in .env.example so operators know setting it to zero blocks all side-effecting tool actions. --- .env.example | 3 ++- src/openhuman/security/ops.rs | 9 ++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 29fa903a7c..2d5cefefb7 100644 --- a/.env.example +++ b/.env.example @@ -76,7 +76,8 @@ OPENHUMAN_CORE_BIN= # --------------------------------------------------------------------------- # Config overrides (override config.toml values at runtime) # --------------------------------------------------------------------------- -# [optional] Local safety cap for side-effecting tool actions in a rolling hour (default 20) +# [optional] Local safety cap for side-effecting tool actions in a rolling hour (default 20). +# Set to 0 to block all side-effecting tool actions entirely. OPENHUMAN_MAX_ACTIONS_PER_HOUR=20 # [optional] Default model to use OPENHUMAN_MODEL= diff --git a/src/openhuman/security/ops.rs b/src/openhuman/security/ops.rs index e59c3206a7..c2a3421e58 100644 --- a/src/openhuman/security/ops.rs +++ b/src/openhuman/security/ops.rs @@ -23,11 +23,6 @@ pub fn security_policy_info_for_config(config: &Config) -> RpcOutcome RpcOutcome { - let config = Config::default(); - security_policy_info_for_config(&config) -} - pub async fn load_and_get_security_policy_info() -> Result, String> { let config = crate::openhuman::config::ops::load_config_with_timeout().await?; Ok(security_policy_info_for_config(&config)) @@ -41,7 +36,7 @@ mod tests { fn security_policy_info_returns_all_documented_fields() { // Locks in the JSON shape the JSON-RPC clients depend on — // any rename / removal of a field would break the UI. - let outcome = security_policy_info(); + let outcome = security_policy_info_for_config(&Config::default()); for key in [ "autonomy", "workspace_only", @@ -64,7 +59,7 @@ mod tests { #[test] fn security_policy_info_matches_default_config_policy_values() { - let outcome = security_policy_info(); + let outcome = security_policy_info_for_config(&Config::default()); let config = Config::default(); let default = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); assert_eq!(outcome.value["autonomy"], json!(default.autonomy)); From 00782a30843a7254059e43384f9bce542102a159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Tue, 26 May 2026 18:37:42 +0800 Subject: [PATCH 9/9] chore(env): restore OPENHUMAN_WORKSPACE position in .env.example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per M3gA-Mind's review on PR #2499 — moving OPENHUMAN_WORKSPACE off its original line added unrelated noise to git blame for a config entry this PR does not touch. Put it back where main has it, leaving only the new OPENHUMAN_MAX_ACTIONS_PER_HOUR rows as the actual diff against main. --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 2d5cefefb7..f063f747b5 100644 --- a/.env.example +++ b/.env.example @@ -81,10 +81,10 @@ OPENHUMAN_CORE_BIN= OPENHUMAN_MAX_ACTIONS_PER_HOUR=20 # [optional] Default model to use OPENHUMAN_MODEL= -# [optional] Default: 0.7 -OPENHUMAN_TEMPERATURE=0.7 # [optional] Workspace directory (default: ~/.openhuman or ~/.openhuman-staging when OPENHUMAN_APP_ENV=staging) OPENHUMAN_WORKSPACE= +# [optional] Default: 0.7 +OPENHUMAN_TEMPERATURE=0.7 # [optional] Language for background LLM artifacts such as memory-tree summaries, # entity-extraction reasons, and learning reflections. Accepts UI locale tags # such as zh-CN or a language name. Leave unset for default behavior.